acrn-hypervisor/misc/debug_tools/acrn_trace/acrntrace.c
Geoffroy Van Cutsem 8b16be9185 Remove "All rights reserved" string headers
Many of the license and Intel copyright headers include the "All rights
reserved" string. It is not relevant in the context of the BSD-3-Clause
license that the code is released under. This patch removes those strings
throughout the code (hypervisor, devicemodel and misc).

Tracked-On: #7254
Signed-off-by: Geoffroy Van Cutsem <geoffroy.vancutsem@intel.com>
2022-04-06 13:21:02 +08:00

419 lines
9.3 KiB
C

/*
* Copyright (C) 2018 Intel Corporation.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/statvfs.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <dirent.h>
#include <signal.h>
#include <errno.h>
#include <pthread.h>
#include <string.h>
#include <signal.h>
#include <numa.h>
#include "acrntrace.h"
#define TIMER_ID (128)
static uint32_t timeout = 0;
static int exiting = 0;
/* for opt */
static uint64_t period = 10000;
static const char optString[] = "i:hcrt:a:";
static const char dev_prefix[] = "acrn_trace_";
static uint32_t flags = FLAG_CLEAR_BUF;
static char trace_file_dir[TRACE_FILE_DIR_LEN];
static reader_struct *reader;
static int dev_cnt = 0; /* Count of /dev/acrn_trace_xxx devices */
static struct bitmask *cpu_bitmask = NULL;
static void display_usage(void)
{
printf("acrntrace - tool to collect ACRN trace data\n"
"[Usage] acrntrace [-i period] [-t max_time] [-ch]\n\n"
"[Options]\n"
"\t-h: print this message\n"
"\t-i: period_in_ms: specify polling interval [1-999]\n"
"\t-t: max time to capture trace data (in second)\n"
"\t-c: clear the buffered old data (deprecated)\n"
"\t-r: capture the buffered old data instead of clearing it\n"
"\t-a: cpu-set: only capture the trace data on these configured cpu-set\n");
}
static void timer_handler(union sigval sv)
{
exiting = 1;
exit(0);
}
static int init_timer(int timeout)
{
struct sigevent event;
struct itimerspec it;
timer_t tm_id;
int err;
memset(&event, 0, sizeof(struct sigevent));
event.sigev_value.sival_int = TIMER_ID;
event.sigev_notify = SIGEV_THREAD;
event.sigev_notify_function = timer_handler;
err = timer_create(CLOCK_MONOTONIC, &event, &tm_id);
if (err < 0) {
pr_err("Error to create timer, errno(%d)\n", err);
return err;
}
it.it_interval.tv_sec = timeout;
it.it_interval.tv_nsec = 0;
it.it_value.tv_sec = timeout;
it.it_value.tv_nsec = 0;
err = timer_settime(tm_id, 0, &it, NULL);
if (err < 0) {
pr_err("Error to set timer, errno(%d)\n", err);
return err;
}
pr_info("Capture trace data for about %ds and exit\n", timeout);
return 0;
}
static int parse_opt(int argc, char *argv[])
{
int opt, ret;
while ((opt = getopt(argc, argv, optString)) != -1) {
switch (opt) {
case 'i':
ret = strtol(optarg, NULL, 10);
if (ret <= 0 || ret >=1000) {
pr_err("'-i' require integer between [1-999]\n");
return -EINVAL;
}
period = ret * 1000;
pr_dbg("Period is %lu\n", period);
break;
case 't':
ret = strtol(optarg, NULL, 10);
if (ret <= 0) {
pr_err("'-t' require integer greater than 0\n");
return -EINVAL;
}
timeout = ret;
pr_dbg("Capture trace data for at most %ds\n", ret);
break;
/*
* We have set the FLAG_CLEAR_BUF by default.
* Here we just keep the -c for backward compatibility.
*/
case 'c':
break;
case 'r':
flags &= ~FLAG_CLEAR_BUF;
break;
case 'a':
cpu_bitmask = numa_parse_cpustring_all(optarg);
break;
case 'h':
display_usage();
return -EINVAL;
default:
/* Undefined operation. */
display_usage();
return -EINVAL;
}
};
return 0;
}
static int get_dev_cnt(void)
{
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, dev_prefix);
if (ret)
cnt++;
}
closedir(dir);
return cnt;
}
static int create_trace_file_dir(char *dir)
{
int err = 0, ret;
char time_str[TIME_STR_LEN + 1];
time_t timep;
struct tm *p;
struct stat st;
time(&timep);
p = localtime(&timep);
if (p) {
ret = snprintf(time_str, TIME_STR_LEN + 1,
"%04d%02d%02d-%02d%02d%02d",
(1900 + p->tm_year), (1 + p->tm_mon),
p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec);
if (ret > TIME_STR_LEN)
printf("WARN: time_str may be truncated\n");
} else {
if (snprintf(time_str, TIME_STR_LEN, "00000000-000000") >= TIME_STR_LEN)
printf("WARN: time_str is truncated\n\n");
}
pr_info("start tracing at %s\n", time_str);
if (stat(TRACE_FILE_ROOT, &st)) {
err = mkdir(TRACE_FILE_ROOT, 0644);
if (err) {
pr_err("Fail to create dir %s, Error: %s\n",
TRACE_FILE_ROOT, strerror(errno));
return -1;
}
}
ret = snprintf(dir, TRACE_FILE_DIR_LEN, "%s%s",
TRACE_FILE_ROOT, time_str);
if (ret >= TRACE_FILE_DIR_LEN)
printf("WARN: trace file dir name is truncated\n");
if (stat(dir, &st)) {
err = mkdir(dir, 0644);
if (err) {
pr_err("Fail to create dir %s, Error: %s\n",
dir, strerror(errno));
return -1;
}
}
pr_dbg("dir %s creted\n", dir);
return err;
}
/* function executed in each consumer thread */
static void reader_fn(param_t * param)
{
int ret;
int fd = param->trace_fd;
shared_buf_t *sbuf = param->sbuf;
pr_dbg("reader thread[%lu] created for FILE*[0x%p]\n",
pthread_self(), fp);
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
/* Clear the old data in sbuf */
if (flags & FLAG_CLEAR_BUF)
sbuf_clear_buffered(sbuf);
while (1) {
do {
ret = sbuf_write(fd, sbuf);
} while (ret > 0);
usleep(period);
}
}
static int create_reader(reader_struct * reader, uint32_t dev_id)
{
char trace_file_name[TRACE_FILE_NAME_LEN];
if (snprintf(reader->dev_name, DEV_PATH_LEN, "/dev/%s%u", dev_prefix, dev_id)
>= DEV_PATH_LEN)
printf("WARN: device name is truncated\n");
reader->param.devid = dev_id;
reader->dev_fd = open(reader->dev_name, O_RDWR);
if (reader->dev_fd < 0) {
pr_err("Failed to open %s, err %d\n", reader->dev_name, errno);
reader->dev_fd = 0;
return -1;
}
reader->param.sbuf = mmap(NULL, MMAP_SIZE,
PROT_READ | PROT_WRITE,
MAP_SHARED, reader->dev_fd, 0);
if (reader->param.sbuf == MAP_FAILED) {
pr_err("mmap failed for %s, errno %d\n", reader->dev_name, errno);
reader->param.sbuf = NULL;
return -2;
}
pr_dbg("sbuf[%d]:\nmagic_num: %lx\nele_num: %u\n ele_size: %u\n",
dev_id, reader->param.sbuf->magic, reader->param.sbuf->ele_num,
reader->param.sbuf->ele_size);
if(snprintf(trace_file_name, TRACE_FILE_NAME_LEN, "%s/%d", trace_file_dir,
dev_id) >= TRACE_FILE_NAME_LEN)
printf("WARN: trace file name is truncated\n");
reader->param.trace_fd = open(trace_file_name,
O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (!reader->param.trace_fd) {
pr_err("Failed to open %s, err %d\n", trace_file_name, errno);
return -3;
}
pr_info("trace data file %s created for %s\n",
trace_file_name, reader->dev_name);
if (pthread_create(&reader->thrd, NULL,
(void *)&reader_fn, &reader->param)) {
pr_err("failed to create reader thread, %d\n", dev_id);
return -4;
}
return 0;
}
static void destory_reader(reader_struct * reader)
{
if (reader->thrd) {
pthread_cancel(reader->thrd);
if (pthread_join(reader->thrd, NULL) != 0)
pr_err("failed to cancel thread[%lu]\n", reader->thrd);
else
reader->thrd = 0;
}
if (reader->param.sbuf) {
munmap(reader->param.sbuf, MMAP_SIZE);
reader->param.sbuf = NULL;
}
if (reader->dev_fd) {
close(reader->dev_fd);
reader->dev_fd = 0;
}
if (reader->param.trace_fd) {
close(reader->param.trace_fd);
}
}
static void handle_on_exit(void)
{
uint32_t dev_id;
/* if nothing to release */
if (!(flags & FLAG_TO_REL))
return;
pr_info("exiting - to release resources...\n");
foreach_dev(dev_id) {
if (numa_bitmask_isbitset(cpu_bitmask, dev_id))
destory_reader(&reader[dev_id]);
}
}
static void signal_exit_handler(int sig)
{
pr_info("exit on signal %d\n", sig);
exit(0);
}
int main(int argc, char *argv[])
{
uint32_t dev_id = 0;
int err;
/* parse options */
if (parse_opt(argc, argv))
exit(EXIT_FAILURE);
dev_cnt = get_dev_cnt();
if (dev_cnt == 0) {
pr_err("Failed to find acrn trace devices, please check whether module acrn_trace is inserted\n");
exit(EXIT_FAILURE);
}
/* if we don't set the -a option or set it by mistake, capture the trace on all possible dev */
if (!cpu_bitmask) {
/* numa_bitmask_alloc will cause the process exiting on failure */
cpu_bitmask = numa_bitmask_alloc(dev_cnt);
foreach_dev(dev_id)
numa_bitmask_setbit(cpu_bitmask, dev_id);
}
reader = calloc(1, sizeof(reader_struct) * dev_cnt);
if (!reader) {
pr_err("Failed to allocate reader memory\n");
exit(EXIT_FAILURE);
}
/* create dir for trace file */
if (create_trace_file_dir(trace_file_dir)) {
pr_err("Failed to create dir for trace files\n");
exit(EXIT_FAILURE);
}
/* Set timer if timeout configured */
if (timeout) {
err = init_timer(timeout);
if (err < 0) {
pr_err("Failed to set timer\n");
exit(EXIT_FAILURE);
}
}
atexit(handle_on_exit);
/* acquair res for each trace dev */
flags |= FLAG_TO_REL;
foreach_dev(dev_id) {
if (numa_bitmask_isbitset(cpu_bitmask, dev_id))
if (create_reader(&reader[dev_id], dev_id) < 0)
goto out_free;
}
/* for kill exit handling */
signal(SIGTERM, signal_exit_handler);
signal(SIGINT, signal_exit_handler);
/* wait for user input to stop */
printf("q <enter> to quit:\n");
while (!exiting && getchar() != 'q')
printf("q <enter> to quit:\n");
out_free:
foreach_dev(dev_id) {
if (numa_bitmask_isbitset(cpu_bitmask, dev_id))
destory_reader(&reader[dev_id]);
}
free(reader);
flags &= ~FLAG_TO_REL;
return EXIT_SUCCESS;
}