acrn-hypervisor/tools/acrntrace/acrntrace.c
Kaige Fu a9a2f91353 tools: acrntrace: Remove unused paramters "-r" related things
Option "-r" has been removed by commit 3d6ff0e (tools: acrntrace: save trace
data file under current dir by default). But leaves something unused in optstring
and README.rst.

This patch remove thest related things.

BTW, this patch refine the usage output to print [-t max_time].

Signed-off-by: Kaige Fu <kaige.fu@intel.com>
Acked-by: Geoffroy Van Cutsem <geoffroy.vancutsem@intel.com>
Reviewed-by: Like Yan <like.yan@intel.com>
2018-08-14 10:01:18 +08:00

380 lines
7.9 KiB
C

/*
* Copyright (C) 2018 Intel Corporation. All rights reserved.
*
* 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 "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:hct:";
static const char dev_prefix[] = "acrn_trace_";
static uint32_t flags;
static char trace_file_dir[TRACE_FILE_DIR_LEN];
static reader_struct *reader;
static int pcpu_num = 0;
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\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 = atoi(optarg);
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 = atoi(optarg);
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;
case 'c':
flags |= FLAG_CLEAR_BUF;
break;
case 'h':
display_usage();
return -EINVAL;
default:
/* Undefined operation. */
display_usage();
return -EINVAL;
}
};
return 0;
}
static int get_cpu_num(void)
{
struct dirent *pdir;
int cpu_num = 0;
char *ret;
DIR *dir;
dir = opendir("/dev");
if (!dir) {
printf("Error opening /dev: %s\n", strerror(errno));
return -1;
}
while (pdir = readdir(dir)) {
ret = strstr(pdir->d_name, dev_prefix);
if (ret)
cpu_num++;
}
closedir(dir);
return cpu_num;
}
static int create_trace_file_dir(char *dir)
{
int err;
char cmd[CMD_MAX_LEN];
char time_str[TIME_STR_LEN];
time_t timep;
struct tm *p;
struct stat st;
time(&timep);
p = localtime(&timep);
if (p)
snprintf(time_str, TIME_STR_LEN, "%d%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);
else
snprintf(time_str, TIME_STR_LEN, "00000000-000000");
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;
}
}
snprintf(dir, TRACE_FILE_DIR_LEN, "%s%s", TRACE_FILE_ROOT, time_str);
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 WIFEXITED(err) ? WEXITSTATUS(err) : EXIT_FAILURE;
}
/* function executed in each consumer thread */
static void reader_fn(param_t * param)
{
int ret;
uint32_t cpuid = param->cpuid;
int fd = param->trace_fd;
shared_buf_t *sbuf = param->sbuf;
trace_ev_t e;
struct statvfs stat;
uint64_t freespace;
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 cpu)
{
char trace_file_name[TRACE_FILE_NAME_LEN];
snprintf(reader->dev_name, DEV_PATH_LEN, "/dev/%s%u", dev_prefix, cpu);
reader->param.cpuid = cpu;
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 cpu%d, errno %d\n", cpu, errno);
reader->param.sbuf = NULL;
return -2;
}
pr_dbg("sbuf[%d]:\nmagic_num: %lx\nele_num: %u\n ele_size: %u\n",
cpu, reader->param.sbuf->magic, reader->param.sbuf->ele_num,
reader->param.sbuf->ele_size);
snprintf(trace_file_name, TRACE_FILE_NAME_LEN, "%s/%d", trace_file_dir,
cpu);
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", cpu);
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 cpu;
/* if nothing to release */
if (!(flags & FLAG_TO_REL))
return;
pr_info("exiting - to release resources...\n");
foreach_cpu(cpu)
destory_reader(&reader[cpu]);
}
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 cpu = 0;
int err;
/* parse options */
if (parse_opt(argc, argv))
exit(EXIT_FAILURE);
/* how many cpus */
pcpu_num = get_cpu_num();
reader = calloc(1, sizeof(reader_struct) * pcpu_num);
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_cpu(cpu)
if (create_reader(&reader[cpu], cpu) < 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_cpu(cpu)
destory_reader(&reader[cpu]);
free(reader);
flags &= ~FLAG_TO_REL;
return EXIT_SUCCESS;
}