mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-09-07 20:00:13 +00:00
HV:Acrn-hypvervisor Root Directory Clean-up and create misc/ folder for Acrn daemons, services and tools.
This patch is to clean-up acrn-hypervisor root directory, targt only 5 folders under acrn-hypervisor:1.hypervisor,2.devicemodel,3.misc,4.doc,5.build Tracked-On: #3482 Signed-off-by: Terry Zou <terry.zou@intel.com> Acked-by: Eddie Dong <eddie.dong@intel.com>
This commit is contained in:
54
misc/tools/acrnlog/Makefile
Normal file
54
misc/tools/acrnlog/Makefile
Normal file
@@ -0,0 +1,54 @@
|
||||
T := $(CURDIR)
|
||||
OUT_DIR ?= $(shell mkdir -p $(T)/build;cd $(T)/build;pwd)
|
||||
CC ?= gcc
|
||||
|
||||
LOG_CFLAGS := -g -O0 -std=gnu11
|
||||
LOG_CFLAGS += -D_GNU_SOURCE
|
||||
LOG_CFLAGS += -DNO_OPENSSL
|
||||
LOG_CFLAGS += -m64
|
||||
LOG_CFLAGS += -Wall -ffunction-sections
|
||||
LOG_CFLAGS += -Werror
|
||||
LOG_CFLAGS += -O2 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2
|
||||
LOG_CFLAGS += -Wformat -Wformat-security -fno-strict-aliasing
|
||||
LOG_CFLAGS += -fpie -fpic
|
||||
LOG_CFLAGS += $(CFLAGS)
|
||||
|
||||
GCC_MAJOR=$(shell echo __GNUC__ | $(CC) -E -x c - | tail -n 1)
|
||||
GCC_MINOR=$(shell echo __GNUC_MINOR__ | $(CC) -E -x c - | tail -n 1)
|
||||
|
||||
#enable stack overflow check
|
||||
STACK_PROTECTOR := 1
|
||||
|
||||
ifdef STACK_PROTECTOR
|
||||
ifeq (true, $(shell [ $(GCC_MAJOR) -gt 4 ] && echo true))
|
||||
LOG_CFLAGS += -fstack-protector-strong
|
||||
else
|
||||
ifeq (true, $(shell [ $(GCC_MAJOR) -eq 4 ] && [ $(GCC_MINOR) -ge 9 ] && echo true))
|
||||
LOG_CFLAGS += -fstack-protector-strong
|
||||
else
|
||||
LOG_CFLAGS += -fstack-protector
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
LOG_LDFLAGS := -Wl,-z,noexecstack
|
||||
LOG_LDFLAGS += -Wl,-z,relro,-z,now
|
||||
LOG_LDFLAGS += -pie
|
||||
LOG_LDFLAGS += $(LDFLAGS)
|
||||
|
||||
all:
|
||||
$(CC) -g acrnlog.c -o $(OUT_DIR)/acrnlog -lpthread $(LOG_CFLAGS) $(LOG_LDFLAGS)
|
||||
cp acrnlog.service $(OUT_DIR)/acrnlog.service
|
||||
|
||||
clean:
|
||||
rm -f $(OUT_DIR)/acrnlog
|
||||
ifneq ($(OUT_DIR),.)
|
||||
rm -f $(OUT_DIR)/acrnlog.service
|
||||
rm -rf $(OUT_DIR)
|
||||
endif
|
||||
|
||||
install: $(OUT_DIR)/acrnlog
|
||||
install -d $(DESTDIR)/usr/bin
|
||||
install -t $(DESTDIR)/usr/bin $(OUT_DIR)/acrnlog
|
||||
install -d $(DESTDIR)/usr/lib/systemd/system
|
||||
install -p -D -m 0644 $(OUT_DIR)/acrnlog.service $(DESTDIR)/usr/lib/systemd/system
|
105
misc/tools/acrnlog/README.rst
Normal file
105
misc/tools/acrnlog/README.rst
Normal file
@@ -0,0 +1,105 @@
|
||||
.. _acrnlog:
|
||||
|
||||
acrnlog
|
||||
#######
|
||||
|
||||
Description
|
||||
***********
|
||||
|
||||
``acrnlog`` is a userland tool used to capture a ACRN hypervisor log. It runs as an
|
||||
SOS service at boot, capturing two kinds of logs:
|
||||
|
||||
- log of the currently running hypervisor
|
||||
- log of last running hypervisor if it crashed and the logs remain.
|
||||
|
||||
Log files are saved in ``/tmp/acrnlog/``, so the log files would be lost
|
||||
after a system reset.
|
||||
|
||||
Usage
|
||||
*****
|
||||
|
||||
The ``acrnlog`` tool is launched as a service at boot, and limited to
|
||||
supporting four 1MB log files by default. You can change this log file
|
||||
limitation temporarily or permanently.
|
||||
|
||||
Options:
|
||||
|
||||
-h display help
|
||||
-t specify a polling interval (ms). Once buffer is empty, acrnlog stops
|
||||
and starts reading in specified interval.
|
||||
If an incomplete log warning is reported, please try with a smaller
|
||||
interval to get a complete log.
|
||||
-s limit the size of each log file, in KB. 0 means no limitation.
|
||||
-n specify the number of log files to keep, old files would be deleted.
|
||||
|
||||
Temporary log file changes
|
||||
==========================
|
||||
|
||||
You can temporarily change the log file setting by following these
|
||||
steps:
|
||||
|
||||
1. Stop the ``acrnlog`` service:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
# systemctl disable acrnlog
|
||||
|
||||
2. Restart ``acrnlog``, running in the background, and specify the new
|
||||
number of log files and their size (in MB). For example:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
# acrnlog -n 8 -s 4 &
|
||||
|
||||
You can use ``loglevel`` command in the hypervisor shell (not the Service
|
||||
OS shell) to query and change the hypervisor log level.
|
||||
|
||||
The ``mem_loglevel`` parameter controls the log to be saved using
|
||||
``acrnlog``, while the ``console_loglevel`` parameter controls the log
|
||||
output to the console. For example, in the hypervisor shell you
|
||||
can use these commands:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
ACRN:\>loglevel
|
||||
console_loglevel: 3, mem_loglevel: 5, npk_loglevel: 5
|
||||
ACRN:\>loglevel 2 5
|
||||
ACRN:\>loglevel
|
||||
console_loglevel: 2, mem_loglevel: 5, npk_loglevel: 5
|
||||
|
||||
|
||||
Permanent log file changes
|
||||
==========================
|
||||
|
||||
You can also permanently change the log file settings by
|
||||
editing ``/usr/lib/systemd/system/acrnlog.service`` and use the ``-n``
|
||||
and ``-s`` options on the ``ExecStart`` cmd, and restart the service.
|
||||
For example, ``acrnlog.service`` could have these parameters added:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
ExecStart=/usr/bin/acrnlog -n 8 -s 4
|
||||
|
||||
and then restart the service with:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
# systemctl daemon-reload
|
||||
# systemctl restart acrnlog
|
||||
|
||||
Build and Install
|
||||
*****************
|
||||
|
||||
Source code for the ``acrnlog`` tools is in the ``tools/acrnlog``
|
||||
folder. Build and install the tools from source using:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
# make
|
||||
# make install
|
||||
|
||||
and if you changed the ``acrnlog.service`` file, install it using:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
# cp acrnlog.service /usr/lib/systemd/system/
|
595
misc/tools/acrnlog/acrnlog.c
Normal file
595
misc/tools/acrnlog/acrnlog.c
Normal file
@@ -0,0 +1,595 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <linux/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#define LOG_ELEMENT_SIZE 80
|
||||
#define LOG_MSG_SIZE 480
|
||||
#define DEFAULT_POLL_INTERVAL 100000
|
||||
#define LOG_INCOMPLETE_WARNING "WARNING: logs missing here! "\
|
||||
"Try reducing polling interval"
|
||||
|
||||
/* Count of /dev/acrn_hvlog_cur_xxx */
|
||||
static int cur_cnt,last_cnt;
|
||||
static unsigned long interval = DEFAULT_POLL_INTERVAL;
|
||||
|
||||
/* this is for log file */
|
||||
#define LOG_FILE_SIZE (1024*1024)
|
||||
#define LOG_FILE_NUM 4
|
||||
static size_t hvlog_log_size = LOG_FILE_SIZE;
|
||||
static unsigned short hvlog_log_num = LOG_FILE_NUM;
|
||||
|
||||
struct hvlog_file {
|
||||
const char *path;
|
||||
int fd;
|
||||
|
||||
size_t left_space;
|
||||
unsigned short index;
|
||||
unsigned short num;
|
||||
};
|
||||
|
||||
static struct hvlog_file cur_log = {
|
||||
.path = "/tmp/acrnlog/acrnlog_cur",
|
||||
.fd = -1,
|
||||
.left_space = 0,
|
||||
.index = ~0,
|
||||
.num = LOG_FILE_NUM
|
||||
};
|
||||
|
||||
static struct hvlog_file last_log = {
|
||||
.path = "/tmp/acrnlog/acrnlog_last",
|
||||
.fd = -1,
|
||||
.left_space = 0,
|
||||
.index = ~0,
|
||||
.num = LOG_FILE_NUM
|
||||
};
|
||||
|
||||
struct hvlog_msg {
|
||||
__u64 usec; /* timestamp, from tsc reset in usec */
|
||||
int cpu; /* which physical cpu output the log */
|
||||
int sev; /* log severity level */
|
||||
__u64 seq; /* sequence num, used to reorder logs */
|
||||
|
||||
size_t len; /* length of message raw string */
|
||||
char raw[0]; /* raw log string, end with '\0' */
|
||||
};
|
||||
|
||||
/*
|
||||
* describe the hvlog device, eg., /dev/acrn_hvlog_*
|
||||
* Note: this is thread-unsafe!
|
||||
*/
|
||||
struct hvlog_dev {
|
||||
int fd;
|
||||
struct hvlog_msg *msg; /* pointer to msg */
|
||||
|
||||
int latched; /* 1 if an sbuf element latched */
|
||||
char entry_latch[LOG_ELEMENT_SIZE]; /* latch for an sbuf element */
|
||||
struct hvlog_msg latched_msg; /* latch for parsed msg */
|
||||
};
|
||||
|
||||
size_t write_log_file(struct hvlog_file * log, const char *buf, size_t len);
|
||||
|
||||
static int get_dev_cnt(char *prefix)
|
||||
{
|
||||
struct dirent *pdir;
|
||||
int cnt = 0;
|
||||
char *ret;
|
||||
DIR *dir;
|
||||
|
||||
dir = opendir("/dev");
|
||||
if (!dir) {
|
||||
printf("Error opening /dev: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
while ((pdir = readdir(dir)) != NULL) {
|
||||
ret = strstr(pdir->d_name, prefix);
|
||||
if (ret)
|
||||
cnt++;
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/*
|
||||
* The function read a complete msg from acrnlog dev.
|
||||
* read one more sbuf entry if read an entry doesn't end with '\0'
|
||||
* however, if the new entry contains a new msg - which means the ending char
|
||||
* is lost, it will be latched to be process next time.
|
||||
*/
|
||||
struct hvlog_msg *hvlog_read_dev(struct hvlog_dev *dev)
|
||||
{
|
||||
char *p = NULL;
|
||||
int ret;
|
||||
size_t len = 0;
|
||||
struct hvlog_msg *msg[2];
|
||||
int msg_num;
|
||||
char warn_msg[LOG_MSG_SIZE] = {0};
|
||||
|
||||
msg[0] = dev->msg;
|
||||
msg[1] = &dev->latched_msg;
|
||||
|
||||
memset(msg[0], 0, sizeof(struct hvlog_msg) + LOG_MSG_SIZE);
|
||||
msg_num = 0;
|
||||
|
||||
do {
|
||||
if (dev->latched) {
|
||||
/* handle the latched msg first */
|
||||
dev->latched = 0;
|
||||
memcpy(&msg[0]->raw[msg[0]->len], dev->entry_latch,
|
||||
LOG_ELEMENT_SIZE);
|
||||
msg_num++;
|
||||
memcpy(msg[0], msg[1], sizeof(struct hvlog_msg));
|
||||
} else {
|
||||
ret =
|
||||
read(dev->fd, &msg[0]->raw[msg[0]->len],
|
||||
LOG_ELEMENT_SIZE);
|
||||
if (!ret)
|
||||
break;
|
||||
/* do we read a new meaasge?
|
||||
* msg[0]->raw[msg[0]->len format: [%lluus][cpu=%d][sev=%d][seq=%llu]: */
|
||||
p = strstr(&msg[0]->raw[msg[0]->len], "][seq=");
|
||||
if (p) {
|
||||
p = p + strlen("][seq=");
|
||||
msg[msg_num]->seq = strtoull(p, NULL, 10);
|
||||
if ((errno == ERANGE && (msg[msg_num]->seq == ULLONG_MAX))
|
||||
|| (errno != 0 && msg[msg_num]->seq == 0)) {
|
||||
if (snprintf(warn_msg, LOG_MSG_SIZE, "\n\n\t%s[invalid seq]\n\n\n",
|
||||
LOG_INCOMPLETE_WARNING) >= LOG_MSG_SIZE) {
|
||||
printf("WARN: warning message is truncated\n");
|
||||
}
|
||||
write_log_file(&cur_log, warn_msg, strnlen(warn_msg, LOG_MSG_SIZE));
|
||||
} else {
|
||||
msg_num++;
|
||||
/* if we read another new msg, latch it */
|
||||
/* to process next time */
|
||||
if (msg_num > 1) {
|
||||
dev->latched = 1;
|
||||
memcpy(dev->entry_latch,
|
||||
&msg[0]->raw[msg[0]->len],
|
||||
LOG_ELEMENT_SIZE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (msg_num == 0) {
|
||||
/* if head of a message lost, continue to read */
|
||||
memset(msg[0], 0, sizeof(struct hvlog_msg)
|
||||
+ LOG_MSG_SIZE);
|
||||
continue;
|
||||
}
|
||||
|
||||
len = strnlen(&msg[0]->raw[msg[0]->len], LOG_MSG_SIZE);
|
||||
msg[0]->len += len;
|
||||
} while (len == LOG_ELEMENT_SIZE &&
|
||||
msg[0]->len < LOG_MSG_SIZE - LOG_ELEMENT_SIZE);
|
||||
|
||||
if (!msg[0]->len)
|
||||
return NULL;
|
||||
|
||||
msg[0]->raw[msg[0]->len] = '\n';
|
||||
msg[0]->raw[msg[0]->len + 1] = 0;
|
||||
msg[0]->len++;
|
||||
|
||||
return msg[0];
|
||||
}
|
||||
|
||||
struct hvlog_dev *hvlog_open_dev(const char *path)
|
||||
{
|
||||
struct hvlog_dev *dev;
|
||||
|
||||
dev = calloc(1, sizeof(struct hvlog_dev));
|
||||
if (!dev) {
|
||||
printf("%s %d\n", __FUNCTION__, __LINE__);
|
||||
goto open_dev;
|
||||
}
|
||||
|
||||
dev->fd = open(path, O_RDONLY);
|
||||
if (dev->fd < 0) {
|
||||
printf("%s %d\n", __FUNCTION__, __LINE__);
|
||||
goto open_fd;
|
||||
}
|
||||
|
||||
/* actual allocated size is 512B */
|
||||
dev->msg = calloc(1, sizeof(struct hvlog_msg) + LOG_MSG_SIZE);
|
||||
if (!dev->msg) {
|
||||
printf("%s %d\n", __FUNCTION__, __LINE__);
|
||||
goto alloc_msg;
|
||||
}
|
||||
|
||||
return dev;
|
||||
|
||||
open_fd:
|
||||
close(dev->fd);
|
||||
alloc_msg:
|
||||
free(dev);
|
||||
open_dev:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void hvlog_close_dev(struct hvlog_dev *dev)
|
||||
{
|
||||
if (!dev)
|
||||
return;
|
||||
|
||||
if (dev->msg)
|
||||
free(dev->msg);
|
||||
if (dev->fd > 0)
|
||||
close(dev->fd);
|
||||
free(dev);
|
||||
dev = NULL;
|
||||
}
|
||||
|
||||
/* this is for reading msg from hvlog devices array */
|
||||
static struct hvlog_data {
|
||||
struct hvlog_dev *dev;
|
||||
struct hvlog_msg *msg; /* clean it after use */
|
||||
} *cur, *last;
|
||||
|
||||
/*
|
||||
* read the earliest msg from each dev, to hvlog_data[].msg
|
||||
* hvlog_data[] will be used for reordering
|
||||
*/
|
||||
static int hvlog_dev_read_msg(struct hvlog_data *data, int num_dev)
|
||||
{
|
||||
int i, new_read;
|
||||
|
||||
new_read = 0;
|
||||
for (i = 0; i < num_dev; i++) {
|
||||
if (data[i].msg)
|
||||
continue;
|
||||
if (!data[i].dev)
|
||||
continue;
|
||||
|
||||
data[i].msg = hvlog_read_dev(data[i].dev);
|
||||
if (data[i].msg)
|
||||
new_read++;
|
||||
}
|
||||
|
||||
return new_read;
|
||||
}
|
||||
|
||||
static struct hvlog_msg *get_min_seq_msg(struct hvlog_data *data, int num_dev)
|
||||
{
|
||||
int i, index_min = -1;
|
||||
__u64 min_seq = 0;
|
||||
struct hvlog_msg *msg;
|
||||
|
||||
for (i = 0; i < num_dev; i++) {
|
||||
if (!data[i].msg)
|
||||
continue;
|
||||
|
||||
if (index_min == -1) {
|
||||
index_min = i;
|
||||
min_seq = data[i].msg->seq;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (data[i].msg->seq > min_seq)
|
||||
continue;
|
||||
index_min = i;
|
||||
min_seq = data[i].msg->seq;
|
||||
}
|
||||
|
||||
if (index_min == -1)
|
||||
return NULL;
|
||||
|
||||
msg = data[index_min].msg;
|
||||
data[index_min].msg = NULL;
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
static int new_log_file(struct hvlog_file *log)
|
||||
{
|
||||
char file_name[32] = { };
|
||||
|
||||
if (log->fd >= 0) {
|
||||
if (!hvlog_log_size)
|
||||
return 0;
|
||||
close(log->fd);
|
||||
log->fd = -1;
|
||||
}
|
||||
|
||||
if (snprintf(file_name, sizeof(file_name), "%s.%hu", log->path,
|
||||
log->index + 1) >= sizeof(file_name)) {
|
||||
printf("WARN: log path is truncated\n");
|
||||
} else
|
||||
remove(file_name);
|
||||
|
||||
log->fd = open(file_name, O_RDWR | O_CREAT | O_TRUNC, 0666);
|
||||
if (log->fd < 0) {
|
||||
perror(file_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
log->left_space = hvlog_log_size;
|
||||
log->index++;
|
||||
if (snprintf(file_name, sizeof(file_name), "%s.%hu", log->path,
|
||||
log->index - hvlog_log_num) >= sizeof(file_name)) {
|
||||
printf("WARN: log path is truncated\n");
|
||||
} else
|
||||
remove(file_name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t write_log_file(struct hvlog_file * log, const char *buf, size_t len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (len >= log->left_space)
|
||||
if (new_log_file(log))
|
||||
return 0;
|
||||
|
||||
ret = write(log->fd, buf, len);
|
||||
log->left_space -= ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void *cur_read_func(void *arg)
|
||||
{
|
||||
struct hvlog_msg *msg;
|
||||
__u64 last_seq = 0;
|
||||
char warn_msg[LOG_MSG_SIZE] = {0};
|
||||
|
||||
while (1) {
|
||||
hvlog_dev_read_msg(cur, cur_cnt);
|
||||
msg = get_min_seq_msg(cur, cur_cnt);
|
||||
if (!msg) {
|
||||
usleep(interval);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* if msg->seq is not contineous, warn for logs missing */
|
||||
if (last_seq + 1 < msg->seq) {
|
||||
if (snprintf(warn_msg, LOG_MSG_SIZE,
|
||||
"\n\n\t%s[%lu ms]\n\n\n",
|
||||
LOG_INCOMPLETE_WARNING, interval) >= LOG_MSG_SIZE) {
|
||||
printf("WARN: warning message is truncated\n");
|
||||
}
|
||||
|
||||
write_log_file(&cur_log, warn_msg, strnlen(warn_msg, LOG_MSG_SIZE));
|
||||
}
|
||||
|
||||
last_seq = msg->seq;
|
||||
|
||||
write_log_file(&cur_log, msg->raw, msg->len);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* If dir *path does't exist, create a new one.
|
||||
* Otherwise, remove all the old acrnlog files in the dir.
|
||||
*/
|
||||
static int mk_dir(const char *path)
|
||||
{
|
||||
char prefix[32] = "acrnlog_cur."; /* acrnlog file prefix */
|
||||
char acrnlog_file[64] = { };
|
||||
struct dirent *pdir;
|
||||
struct stat st;
|
||||
int index = 0;
|
||||
char *find;
|
||||
DIR *dir;
|
||||
|
||||
if (stat(path, &st)) {
|
||||
if (mkdir(path, 0644))
|
||||
return -1;
|
||||
} else {
|
||||
/* Remove all the old acrnlogs */
|
||||
dir = opendir(path);
|
||||
if (!dir) {
|
||||
printf("Error opening directory %s. Error: %s\n",
|
||||
path, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
while ((pdir = readdir(dir)) != NULL) {
|
||||
find = strstr(pdir->d_name, prefix);
|
||||
if (!find)
|
||||
continue;
|
||||
|
||||
if (snprintf(acrnlog_file, sizeof(acrnlog_file), "%s/%s%d",
|
||||
path, prefix, index++) >= sizeof(acrnlog_file)) {
|
||||
printf("WARN: acrnlog file path is truncated\n");
|
||||
} else
|
||||
remove(acrnlog_file);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* for user optinal args */
|
||||
static const char optString[] = "s:n:t:h";
|
||||
|
||||
static void display_usage(void)
|
||||
{
|
||||
printf("acrnlog - tool to collect ACRN hypervisor log\n"
|
||||
"[Usage] acrnlog [-s size] [-n number] [-t interval] [-h]\n\n"
|
||||
"[Options]\n"
|
||||
"\t-h: print this message\n"
|
||||
"\t-t: polling interval to collect logs, in ms\n"
|
||||
"\t-s: size limitation for each log file, in MB.\n"
|
||||
"\t 0 means no limitation.\n"
|
||||
"\t-n: how many files you would like to keep on disk\n"
|
||||
"[Output] capatured log files under /tmp/acrnlog/\n");
|
||||
}
|
||||
|
||||
static int parse_opt(int argc, char *argv[])
|
||||
{
|
||||
int opt, ret;
|
||||
|
||||
while ((opt = getopt(argc, argv, optString)) != -1) {
|
||||
switch (opt) {
|
||||
case 's':
|
||||
ret = strtoll(optarg, NULL, 10);
|
||||
if ((errno == ERANGE && (ret == LLONG_MAX || ret == LLONG_MIN))
|
||||
|| (errno != 0 && ret == 0)) {
|
||||
printf("'-s' invalid parameter: %s\n", optarg);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
hvlog_log_size = ret * 1024;
|
||||
break;
|
||||
case 'n':
|
||||
ret = strtol(optarg, NULL, 10);
|
||||
if ((errno == ERANGE && (ret == LONG_MAX || ret == LONG_MIN))
|
||||
|| (errno != 0 && ret == 0)) {
|
||||
printf("'-n' invalid parameter: %s\n", optarg);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (ret > 3)
|
||||
hvlog_log_num = ret;
|
||||
break;
|
||||
case 't':
|
||||
ret = strtol(optarg, NULL, 10);
|
||||
if (ret <= 0 || ret >=1000) {
|
||||
printf("'-t' require integer between [1-999]\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
interval = ret * 1000;
|
||||
printf("Polling interval is %u ms\n", ret);
|
||||
break;
|
||||
case 'h':
|
||||
display_usage();
|
||||
return -EINVAL;
|
||||
default:
|
||||
/* Undefined operation. */
|
||||
display_usage();
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static pthread_t cur_thread;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char name[32];
|
||||
int i, ret;
|
||||
int num_cur, num_last;
|
||||
struct hvlog_msg *msg;
|
||||
|
||||
if (parse_opt(argc, argv))
|
||||
return -1;
|
||||
|
||||
ret = mk_dir("/tmp/acrnlog");
|
||||
if (ret) {
|
||||
printf("Cannot create /tmp/acrnlog. Error: %s\n",
|
||||
strerror(errno));
|
||||
return ret;
|
||||
}
|
||||
|
||||
cur_cnt = get_dev_cnt("acrn_hvlog_cur_");
|
||||
last_cnt = get_dev_cnt("acrn_hvlog_last_");
|
||||
|
||||
if (cur_cnt == 0) {
|
||||
printf("Failed to find acrn hvlog devices, please check whether module acrn_hvlog is inserted\n");
|
||||
return -1;
|
||||
} else if (cur_cnt == -1)
|
||||
return -1;
|
||||
|
||||
cur = calloc(cur_cnt, sizeof(struct hvlog_data));
|
||||
if (!cur) {
|
||||
printf("Failed to allocate buf for cur log buf\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (last_cnt) {
|
||||
last = calloc(cur_cnt, sizeof(struct hvlog_data));
|
||||
if (!last) {
|
||||
printf("Failed to allocate buf for last log buf\n");
|
||||
free(cur);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
num_cur = 0;
|
||||
for (i = 0; i < cur_cnt; i++) {
|
||||
if (snprintf(name, sizeof(name), "/dev/acrn_hvlog_cur_%d", i) >= sizeof(name)) {
|
||||
printf("ERROR: cur hvlog path is truncated\n");
|
||||
return -1;
|
||||
}
|
||||
cur[i].dev = hvlog_open_dev(name);
|
||||
if (!cur[i].dev)
|
||||
perror(name);
|
||||
else
|
||||
num_cur++;
|
||||
cur[i].msg = NULL;
|
||||
}
|
||||
|
||||
num_last = 0;
|
||||
if (last_cnt) {
|
||||
for (i = 0; i < cur_cnt; i++) {
|
||||
if (snprintf(name, sizeof(name), "/dev/acrn_hvlog_last_%d", i) >= sizeof(name)) {
|
||||
printf("ERROR: last hvlog path is truncated\n");
|
||||
return -1;
|
||||
}
|
||||
last[i].dev = hvlog_open_dev(name);
|
||||
if (!last[i].dev)
|
||||
perror(name);
|
||||
else
|
||||
num_last++;
|
||||
last[i].msg = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
printf("open cur:%d last:%d\n", num_cur, num_last);
|
||||
|
||||
/* create thread to read cur log */
|
||||
if (num_cur) {
|
||||
ret = pthread_create(&cur_thread, NULL, cur_read_func, cur);
|
||||
if (ret) {
|
||||
printf("%s %d\n", __FUNCTION__, __LINE__);
|
||||
cur_thread = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (num_last) {
|
||||
while (1) {
|
||||
hvlog_dev_read_msg(last, cur_cnt);
|
||||
msg = get_min_seq_msg(last, cur_cnt);
|
||||
if (!msg)
|
||||
break;
|
||||
write_log_file(&last_log, msg->raw, msg->len);
|
||||
}
|
||||
}
|
||||
|
||||
if (cur_thread)
|
||||
pthread_join(cur_thread, NULL);
|
||||
|
||||
for (i = 0; i < cur_cnt; i++) {
|
||||
hvlog_close_dev(cur[i].dev);
|
||||
}
|
||||
|
||||
for (i = 0; i < last_cnt; i++) {
|
||||
hvlog_close_dev(last[i].dev);
|
||||
}
|
||||
|
||||
free(cur);
|
||||
if (last_cnt)
|
||||
free(last);
|
||||
|
||||
return 0;
|
||||
}
|
10
misc/tools/acrnlog/acrnlog.service
Normal file
10
misc/tools/acrnlog/acrnlog.service
Normal file
@@ -0,0 +1,10 @@
|
||||
[Unit]
|
||||
Description=ACRN hypervisor log
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/acrnlog
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
Reference in New Issue
Block a user