+++ /dev/null
-/* lttctl
- *
- * Linux Trace Toolkit Control
- *
- * Small program that controls LTT through libltt.
- *
- * Copyright 2005 -
- * Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>
- *
- * Copyright 2008 FUJITSU
- * Zhao Lei <zhaolei@cn.fujitsu.com>
- * Gui Jianfeng <guijianfeng@cn.fujitsu.com>
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <liblttctl/lttctl.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/wait.h>
-#include <unistd.h>
-#include <string.h>
-#include <limits.h>
-#define _GNU_SOURCE
-#include <getopt.h>
-
-#define OPT_MAX (1024)
-#define OPT_NAMELEN (256)
-#define OPT_VALSTRINGLEN (256)
-
-enum opt_type {
- CHANNEL,
-};
-
-struct channel_option {
- char chan_name[OPT_NAMELEN];
- int enable;
- int overwrite;
- int bufnum;
- int bufsize;
-};
-
-struct lttctl_option {
- union {
- struct channel_option chan_opt;
- } opt_mode;
- enum opt_type type;
- struct lttctl_option *next;
-};
-
-struct lttctl_option *opt_head, *last_opt;
-
-static int opt_create;
-static int opt_destroy;
-static int opt_start;
-static int opt_pause;
-static int opt_help;
-static const char *opt_transport;
-static const char *opt_write;
-static int opt_append;
-static unsigned int opt_dump_threads;
-static char channel_root_default[PATH_MAX];
-static const char *opt_channel_root;
-static const char *opt_tracename;
-
-/* Args :
- *
- */
-static void show_arguments(void)
-{
- printf("Linux Trace Toolkit Trace Control " VERSION"\n");
- printf("\n");
- printf("Usage: lttctl [OPTION]... [TRACENAME]\n");
- printf("\n");
- printf("Examples:\n");
- printf(" lttctl -c trace1 "
- "# Create a trace named trace1.\n");
- printf(" lttctl -s trace1 "
- "# start a trace named trace1.\n");
- printf(" lttctl -p trace1 "
- "# pause a trace named trace1.\n");
- printf(" lttctl -d trace1 "
- "# Destroy a trace named trace1.\n");
- printf(" lttctl -C -w /tmp/trace1 trace1 "
- "# Create a trace named trace1, start it and\n"
- " "
- "# write non-overwrite channels' data to\n"
- " "
- "# /tmp/trace1, debugfs must be mounted for\n"
- " "
- "# auto-find\n");
- printf(" lttctl -D -w /tmp/trace1 trace1 "
- "# Pause and destroy a trace named trace1 and\n"
- " "
- "# write overwrite channels' data to\n"
- " "
- "# /tmp/trace1, debugfs must be mounted for\n"
- " "
- "# auto-find\n");
- printf("\n");
- printf(" Basic options:\n");
- printf(" -c, --create\n");
- printf(" Create a trace.\n");
- printf(" -d, --destroy\n");
- printf(" Destroy a trace.\n");
- printf(" -s, --start\n");
- printf(" Start a trace.\n");
- printf(" -p, --pause\n");
- printf(" Pause a trace.\n");
- printf(" -h, --help\n");
- printf(" Show this help.\n");
- printf("\n");
- printf(" Advanced options:\n");
- printf(" --transport TRANSPORT\n");
- printf(" Set trace's transport. (ex. relay-locked or relay)\n");
- printf(" -o, --option OPTION\n");
- printf(" Set options, following operations are supported:\n");
- printf(" channel.<channelname>.enable=\n");
- printf(" channel.<channelname>.overwrite=\n");
- printf(" channel.<channelname>.bufnum=\n");
- printf(" channel.<channelname>.bufsize= (in bytes, rounded to "
- "next power of 2)\n");
- printf(" <channelname> can be set to all for all channels\n");
- printf("\n");
- printf(" Integration options:\n");
- printf(" -C, --create_start\n");
- printf(" Create and start a trace.\n");
- printf(" -D, --pause_destroy\n");
- printf(" Pause and destroy a trace.\n");
- printf(" -w, --write PATH\n");
- printf(" Path for write trace datas.\n");
- printf(" For -c, -C, -d, -D options\n");
- printf(" -a, --append\n");
- printf(" Append to trace, For -w option\n");
- printf(" -n, --dump_threads NUMBER\n");
- printf(" Number of lttd threads, For -w option\n");
- printf(" --channel_root PATH\n");
- printf(" Set channels root path, For -w option."
- " (ex. /mnt/debugfs/ltt)\n");
- printf("\n");
-}
-
-/*
- * Separate option name to 3 fields
- * Ex:
- * Input: name = channel.cpu.bufsize
- * Output: name1 = channel
- * name2 = cpu
- * name3 = bufsize
- * Ret: 0 on success
- * 1 on fail
- *
- * Note:
- * Make sure that name1~3 longer than OPT_NAMELEN.
- * name1~3 can be NULL to discard value
- *
- */
-static int separate_opt(const char *name, char *name1, char *name2, char *name3)
-{
- char *p;
-
- if (!name)
- return 1;
-
- /* segment1 */
- p = strchr(name, '.');
- if (!p)
- return 1;
- if (p - name >= OPT_NAMELEN)
- return 1;
- if (name1) {
- memcpy(name1, name, p - name);
- name1[p - name] = 0;
- }
- name = p + 1;
-
- /* segment2 */
- p = strchr(name, '.');
- if (!p)
- return 1;
- if (p - name >= OPT_NAMELEN)
- return 1;
- if (name2) {
- memcpy(name2, name, p - name);
- name2[p - name] = 0;
- }
- name = p + 1;
-
- /* segment3 */
- if (strlen(name) >= OPT_NAMELEN)
- return 1;
- if (name3)
- strcpy(name3, name);
-
- return 0;
-}
-
-static void init_channel_opt(struct channel_option *opt, char *opt_name)
-{
- if (opt && opt_name) {
- opt->enable = -1;
- opt->overwrite = -1;
- opt->bufnum = -1;
- opt->bufsize = -1;
- strcpy(opt->chan_name, opt_name);
- }
-}
-
-static struct lttctl_option *find_insert_channel_opt(char *opt_name)
-{
- struct lttctl_option *iter, *new_opt;
-
- if (!opt_head) {
- opt_head = (struct lttctl_option *)malloc(sizeof(struct lttctl_option));
- init_channel_opt(&opt_head->opt_mode.chan_opt, opt_name);
- opt_head->type = CHANNEL;
- opt_head->next = NULL;
- last_opt = opt_head;
- return opt_head;
- }
-
- for (iter = opt_head; iter; iter = iter->next) {
- if (iter->type != CHANNEL)
- continue;
- if (!strcmp(iter->opt_mode.chan_opt.chan_name, opt_name))
- return iter;
- }
-
- new_opt = (struct lttctl_option *)malloc(sizeof(struct lttctl_option));
- init_channel_opt(&new_opt->opt_mode.chan_opt, opt_name);
- new_opt->type = CHANNEL;
- new_opt->next = NULL;
- last_opt->next = new_opt;
- last_opt = new_opt;
- return new_opt;
-}
-
-int set_channel_opt(struct channel_option *opt, char *opt_name, char *opt_valstr)
-{
- int opt_val, ret;
-
- if (!strcmp("enable", opt_name)) {
- if (opt_valstr[1] != 0) {
- return -EINVAL;
- }
- if (opt_valstr[0] == 'Y' || opt_valstr[0] == 'y'
- || opt_valstr[0] == '1')
- opt_val = 1;
- else if (opt_valstr[0] == 'N' || opt_valstr[0] == 'n'
- || opt_valstr[0] == '0')
- opt_val = 0;
- else {
- return -EINVAL;
- }
-
- opt->enable = opt_val;
- return 0;
- } else if (!strcmp("overwrite", opt_name)) {
- if (opt_valstr[1] != 0) {
- return -EINVAL;
- }
- if (opt_valstr[0] == 'Y' || opt_valstr[0] == 'y'
- || opt_valstr[0] == '1')
- opt_val = 1;
- else if (opt_valstr[0] == 'N' || opt_valstr[0] == 'n'
- || opt_valstr[0] == '0')
- opt_val = 0;
- else {
- return -EINVAL;
- }
-
- opt->overwrite = opt_val;
- return 0;
-
- } else if (!strcmp("bufnum", opt_name)) {
- ret = sscanf(opt_valstr, "%d", &opt_val);
- if (ret != 1 || opt_val < 0) {
- return -EINVAL;
- }
-
- opt->bufnum = opt_val;
- return 0;
- } else if (!strcmp("bufsize", opt_name)) {
- ret = sscanf(opt_valstr, "%d", &opt_val);
- if (ret != 1 || opt_val < 0) {
- return -EINVAL;
- }
-
- opt->bufsize = opt_val;
- return 0;
- } else {
- return -EINVAL;
- }
-
-}
-
-static int parst_opt(const char *optarg)
-{
- int ret;
- char opt_name[OPT_NAMELEN * 3];
- char opt_valstr[OPT_VALSTRINGLEN];
- char *p;
-
- char name1[OPT_NAMELEN];
- char name2[OPT_NAMELEN];
- char name3[OPT_NAMELEN];
-
- int opt_intval;
- int opt_val;
- unsigned int opt_uintval;
- struct lttctl_option *opt;
-
- if (!optarg) {
- fprintf(stderr, "Option empty\n");
- return -EINVAL;
- }
-
- /* Get option name and val_str */
- p = strchr(optarg, '=');
- if (!p) {
- fprintf(stderr, "Option format error: %s\n", optarg);
- return -EINVAL;
- }
-
- if (p - optarg >= sizeof(opt_name)/sizeof(opt_name[0])) {
- fprintf(stderr, "Option name too long: %s\n", optarg);
- return -EINVAL;
- }
-
- if (strlen(p+1) >= OPT_VALSTRINGLEN) {
- fprintf(stderr, "Option value too long: %s\n", optarg);
- return -EINVAL;
- }
-
- memcpy(opt_name, optarg, p - optarg);
- opt_name[p - optarg] = 0;
- strcpy(opt_valstr, p+1);
-
- /* separate option name into 3 fields */
- ret = separate_opt(opt_name, name1, name2, name3);
- if (ret != 0) {
- fprintf(stderr, "Option name error1: %s\n", optarg);
- return -EINVAL;
- }
-
- if (!strcmp("channel", name1)) {
- opt = find_insert_channel_opt(name2);
- if ((ret = set_channel_opt(&opt->opt_mode.chan_opt,
- name3, opt_valstr) != 0)) {
- fprintf(stderr, "Option name error2: %s\n", optarg);
- return ret;
- }
- } else {
- fprintf(stderr, "Option name error3: %s\n", optarg);
- return -EINVAL;
- }
-
- return 0;
-}
-
-/* parse_arguments
- *
- * Parses the command line arguments.
- *
- * Returns -1 if the arguments were correct, but doesn't ask for program
- * continuation. Returns EINVAL if the arguments are incorrect, or 0 if OK.
- */
-static int parse_arguments(int argc, char **argv)
-{
- int ret = 0;
-
- static struct option longopts[] = {
- {"create", no_argument, NULL, 'c'},
- {"destroy", no_argument, NULL, 'd'},
- {"start", no_argument, NULL, 's'},
- {"pause", no_argument, NULL, 'p'},
- {"help", no_argument, NULL, 'h'},
- {"transport", required_argument, NULL, 2},
- {"option", required_argument, NULL, 'o'},
- {"create_start", no_argument, NULL, 'C'},
- {"pause_destroy", no_argument, NULL, 'D'},
- {"write", required_argument, NULL, 'w'},
- {"append", no_argument, NULL, 'a'},
- {"dump_threads", required_argument, NULL, 'n'},
- {"channel_root", required_argument, NULL, 3},
- { NULL, 0, NULL, 0 },
- };
-
- /*
- * Enable all channels in default
- * To make novice users happy
- */
- parst_opt("channel.all.enable=1");
-
- opterr = 1; /* Print error message on getopt_long */
- while (1) {
- int c;
- c = getopt_long(argc, argv, "cdspho:CDw:an:", longopts, NULL);
- if (-1 == c) {
- /* parse end */
- break;
- }
- switch (c) {
- case 'c':
- opt_create = 1;
- break;
- case 'd':
- opt_destroy = 1;
- break;
- case 's':
- opt_start = 1;
- break;
- case 'p':
- opt_pause = 1;
- break;
- case 'h':
- opt_help = 1;
- break;
- case 2:
- if (!opt_transport) {
- opt_transport = optarg;
- } else {
- fprintf(stderr,
- "Please specify only 1 transport\n");
- return -EINVAL;
- }
- break;
- case 'o':
- ret = parst_opt(optarg);
- if (ret)
- return ret;
- break;
- case 'C':
- opt_create = 1;
- opt_start = 1;
- break;
- case 'D':
- opt_pause = 1;
- opt_destroy = 1;
- break;
- case 'w':
- if (!opt_write) {
- opt_write = optarg;
- } else {
- fprintf(stderr,
- "Please specify only 1 write dir\n");
- return -EINVAL;
- }
- break;
- case 'a':
- opt_append = 1;
- break;
- case 'n':
- if (opt_dump_threads) {
- fprintf(stderr,
- "Please specify only 1 dump threads\n");
- return -EINVAL;
- }
-
- ret = sscanf(optarg, "%u", &opt_dump_threads);
- if (ret != 1) {
- fprintf(stderr,
- "Dump threads not positive number\n");
- return -EINVAL;
- }
- break;
- case 3:
- if (!opt_channel_root) {
- opt_channel_root = optarg;
- } else {
- fprintf(stderr,
- "Please specify only 1 channel root\n");
- return -EINVAL;
- }
- break;
- case '?':
- return -EINVAL;
- default:
- break;
- };
- };
-
- /* Don't check args when user needs help */
- if (opt_help)
- return 0;
-
- /* Get tracename */
- if (optind < argc - 1) {
- fprintf(stderr, "Please specify only 1 trace name\n");
- return -EINVAL;
- }
- if (optind > argc - 1) {
- fprintf(stderr, "Please specify trace name\n");
- return -EINVAL;
- }
- opt_tracename = argv[optind];
-
- /*
- * Check arguments
- */
- if (!opt_create && !opt_start && !opt_destroy && !opt_pause) {
- fprintf(stderr,
- "Please specify a option of "
- "create, destroy, start, or pause\n");
- return -EINVAL;
- }
-
- if ((opt_create || opt_start) && (opt_destroy || opt_pause)) {
- fprintf(stderr,
- "Create and start conflict with destroy and pause\n");
- return -EINVAL;
- }
-
- if (opt_create) {
- if (!opt_transport)
- opt_transport = "relay";
- }
-
- if (opt_transport) {
- if (!opt_create) {
- fprintf(stderr,
- "Transport option must be combine with create"
- " option\n");
- return -EINVAL;
- }
- }
-
- if (opt_write) {
- if (!opt_create && !opt_destroy) {
- fprintf(stderr,
- "Write option must be combine with create or"
- " destroy option\n");
- return -EINVAL;
- }
-
- if (!opt_channel_root)
- if (getdebugfsmntdir(channel_root_default) == 0) {
- strcat(channel_root_default, "/ltt");
- opt_channel_root = channel_root_default;
- } else {
- fprintf(stderr,
- "Channel_root is necessary for -w"
- " option, but neither --channel_root"
- " option\n"
- "specified, nor debugfs's mount dir"
- " found, mount debugfs also failed\n");
- return -EINVAL;
- }
-
- if (opt_dump_threads == 0)
- opt_dump_threads = 1;
- }
-
- if (opt_append) {
- if (!opt_write) {
- fprintf(stderr,
- "Append option must be combine with write"
- " option\n");
- return -EINVAL;
- }
- }
-
- if (opt_dump_threads) {
- if (!opt_write) {
- fprintf(stderr,
- "Dump_threads option must be combine with write"
- " option\n");
- return -EINVAL;
- }
- }
-
- if (opt_channel_root) {
- if (!opt_write) {
- fprintf(stderr,
- "Channel_root option must be combine with write"
- " option\n");
- return -EINVAL;
- }
- }
-
- return 0;
-}
-
-static void show_info(void)
-{
- printf("Linux Trace Toolkit Trace Control " VERSION"\n");
- printf("\n");
- if (opt_tracename != NULL) {
- printf("Controlling trace : %s\n", opt_tracename);
- printf("\n");
- }
-}
-
-static int lttctl_channel_setup(struct channel_option *opt)
-{
- int ret;
-
- if (opt->enable != -1) {
- if ((ret = lttctl_set_channel_enable(opt_tracename,
- opt->chan_name,
- opt->enable)) != 0)
- return ret;
- }
- if (opt->overwrite != -1) {
- if ((ret = lttctl_set_channel_overwrite(opt_tracename,
- opt->chan_name,
- opt->overwrite)) != 0)
- return ret;
- }
- if (opt->bufnum != -1) {
- if ((ret = lttctl_set_channel_subbuf_num(opt_tracename,
- opt->chan_name,
- opt->bufnum)) != 0)
- return ret;
- }
- if (opt->bufsize != -1) {
- if ((ret = lttctl_set_channel_subbuf_size(opt_tracename,
- opt->chan_name,
- opt->bufsize)) != 0)
- return ret;
- }
-
- return 0;
-}
-
-static int lttctl_create_trace(void)
-{
- int ret;
- int i;
- struct lttctl_option *opt;
-
- ret = lttctl_setup_trace(opt_tracename);
- if (ret)
- goto setup_trace_fail;
-
- for (opt = opt_head; opt; opt = opt->next) {
- if (opt->type != CHANNEL)
- continue;
- ret = lttctl_channel_setup(&opt->opt_mode.chan_opt);
- if (ret)
- goto set_option_fail;;
- }
-
- ret = lttctl_set_trans(opt_tracename, opt_transport);
- if (ret)
- goto set_option_fail;
-
- ret = lttctl_alloc_trace(opt_tracename);
- if (ret)
- goto alloc_trace_fail;
-
- return 0;
-
-alloc_trace_fail:
-set_option_fail:
- lttctl_destroy_trace(opt_tracename);
-setup_trace_fail:
- return ret;
-}
-
-/*
- * Start a lttd daemon to write trace datas
- * Dump overwrite channels on overwrite!=0
- * Dump normal(non-overwrite) channels on overwrite=0
- *
- * ret: 0 on success
- * !0 on fail
- */
-static int lttctl_daemon(int overwrite)
-{
- pid_t pid;
- int status;
-
- pid = fork();
- if (pid < 0) {
- perror("Error in forking for lttd daemon");
- return errno;
- }
-
- if (pid == 0) {
- /* child */
- char *argv[16];
- int argc = 0;
- char channel_path[PATH_MAX];
- char thread_num[16];
-
- /* prog path */
- argv[argc] = getenv("LTT_DAEMON");
- if (argv[argc] == NULL)
- argv[argc] = PACKAGE_BIN_DIR "/lttd";
- argc++;
-
- /* -t option */
- argv[argc] = "-t";
- argc++;
- /*
- * we allow modify of opt_write's content in new process
- * for get rid of warning of assign char * to const char *
- */
- argv[argc] = (char *)opt_write;
- argc++;
-
- /* -c option */
- strcpy(channel_path, opt_channel_root);
- strcat(channel_path, "/");
- strcat(channel_path, opt_tracename);
- argv[argc] = "-c";
- argc++;
- argv[argc] = channel_path;
- argc++;
-
- /* -N option */
- sprintf(thread_num, "%u", opt_dump_threads);
- argv[argc] = "-N";
- argc++;
- argv[argc] = thread_num;
- argc++;
-
- /* -a option */
- if (opt_append) {
- argv[argc] = "-a";
- argc++;
- }
-
- /* -d option */
- argv[argc] = "-d";
- argc++;
-
- /* overwrite option */
- if (overwrite) {
- argv[argc] = "-f";
- argc++;
- } else {
- argv[argc] = "-n";
- argc++;
- }
-
- argv[argc] = NULL;
-
- execvp(argv[0], argv);
-
- perror("Error in executing the lttd daemon");
- exit(errno);
- }
-
- /* parent */
- if (waitpid(pid, &status, 0) == -1) {
- perror("Error in waitpid\n");
- return errno;
- }
-
- if (!WIFEXITED(status)) {
- fprintf(stderr, "lttd process interrupted\n");
- return status;
- }
-
- if (WEXITSTATUS(status))
- fprintf(stderr, "lttd process running failed\n");
-
- return WEXITSTATUS(status);
-}
-
-int main(int argc, char **argv)
-{
- int ret;
-
- ret = parse_arguments(argc, argv);
- /* If user needs show help, we disregard other options */
- if (opt_help) {
- show_arguments();
- return 0;
- }
-
- /* exit program if arguments wrong */
- if (ret)
- return 1;
-
- show_info();
-
- ret = lttctl_init();
- if (ret != 0)
- return ret;
-
- if (opt_create) {
- printf("lttctl: Creating trace\n");
- ret = lttctl_create_trace();
- if (ret)
- goto op_fail;
-
- if (opt_write) {
- printf("lttctl: Forking lttd\n");
- ret = lttctl_daemon(0);
- if (ret)
- goto op_fail;
- }
- }
-
- if (opt_start) {
- printf("lttctl: Starting trace\n");
- ret = lttctl_start(opt_tracename);
- if (ret)
- goto op_fail;
- }
-
- if (opt_pause) {
- printf("lttctl: Pausing trace\n");
- ret = lttctl_pause(opt_tracename);
- if (ret)
- goto op_fail;
- }
-
- if (opt_destroy) {
- if (opt_write) {
- printf("lttctl: Forking lttd\n");
- ret = lttctl_daemon(1);
- if (ret)
- goto op_fail;
- }
-
- printf("lttctl: Destroying trace\n");
- ret = lttctl_destroy_trace(opt_tracename);
- if (ret)
- goto op_fail;
- }
-
-op_fail:
- lttctl_destroy();
-
- return ret;
-}