blob: 1a62faaf71ca94754b94de75294890ed64f8b464 [file] [log] [blame]
/*
* Copyright (C) 2008, 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License (not later!)
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <stdio.h>
#include <string.h>
#include <getopt.h>
#include <unistd.h>
#include <dirent.h>
#include <errno.h>
#include "trace-local.h"
int silence_warnings;
int show_status;
void warning(const char *fmt, ...)
{
va_list ap;
if (silence_warnings)
return;
if (errno)
perror("trace-cmd");
errno = 0;
va_start(ap, fmt);
fprintf(stderr, " ");
vfprintf(stderr, fmt, ap);
va_end(ap);
fprintf(stderr, "\n");
}
void pr_stat(const char *fmt, ...)
{
va_list ap;
if (!show_status)
return;
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
printf("\n");
}
void *malloc_or_die(unsigned int size)
{
void *data;
data = malloc(size);
if (!data)
die("malloc");
return data;
}
static void dump_file_content(const char *path)
{
char buf[BUFSIZ];
ssize_t n;
FILE *fp;
fp = fopen(path, "r");
if (!fp)
die("reading %s", path);
do {
n = fread(buf, 1, BUFSIZ, fp);
if (n > 0)
fwrite(buf, 1, n, stdout);
} while (n > 0);
fclose(fp);
}
void show_file(const char *name)
{
char *path;
path = tracecmd_get_tracing_file(name);
dump_file_content(path);
tracecmd_put_tracing_file(path);
}
typedef int (*process_file_func)(char *buf, int len);
static void process_file_re(process_file_func func,
const char *name, const char *re)
{
regex_t reg;
char *path;
char *buf = NULL;
char *str;
FILE *fp;
ssize_t n;
size_t l = strlen(re);
/* Just in case :-p */
if (!re || l == 0) {
show_file(name);
return;
}
/* Handle the newline at end of names for the user */
str = malloc(l + 3);
if (!str)
die("Failed to allocate reg ex %s", re);
strcpy(str, re);
if (re[l-1] == '$')
strcpy(&str[l-1], "\n*$");
if (regcomp(&reg, str, REG_ICASE|REG_NOSUB))
die("invalid function regex '%s'", re);
free(str);
path = tracecmd_get_tracing_file(name);
fp = fopen(path, "r");
if (!fp)
die("reading %s", path);
tracecmd_put_tracing_file(path);
do {
n = getline(&buf, &l, fp);
if (n > 0 && regexec(&reg, buf, 0, NULL, 0) == 0)
func(buf, n);
} while (n > 0);
free(buf);
fclose(fp);
regfree(&reg);
}
static int show_file_write(char *buf, int len)
{
return fwrite(buf, 1, len, stdout);
}
static void show_file_re(const char *name, const char *re)
{
process_file_re(show_file_write, name, re);
}
static char *get_event_file(const char *type, char *buf, int len)
{
char *system;
char *event;
char *path;
char *file;
if (buf[len-1] == '\n')
buf[len-1] = '\0';
system = strtok(buf, ":");
if (!system)
die("no system found in %s", buf);
event = strtok(NULL, ":");
if (!event)
die("no event found in %s\n", buf);
path = tracecmd_get_tracing_file("events");
file = malloc(strlen(path) + strlen(system) + strlen(event) +
strlen(type) + strlen("///") + 1);
if (!file)
die("Failed to allocate event file %s %s", system, event);
sprintf(file, "%s/%s/%s/%s", path, system, event, type);
tracecmd_put_tracing_file(path);
return file;
}
static int event_filter_write(char *buf, int len)
{
char *file;
if (buf[len-1] == '\n')
buf[len-1] = '\0';
printf("%s\n", buf);
file = get_event_file("filter", buf, len);
dump_file_content(file);
free(file);
printf("\n");
return 0;
}
static int event_trigger_write(char *buf, int len)
{
char *file;
if (buf[len-1] == '\n')
buf[len-1] = '\0';
printf("%s\n", buf);
file = get_event_file("trigger", buf, len);
dump_file_content(file);
free(file);
printf("\n");
return 0;
}
static int event_format_write(char *fbuf, int len)
{
char *file = get_event_file("format", fbuf, len);
char *buf = NULL;
size_t l;
FILE *fp;
int n;
/* The get_event_file() crops system in fbuf */
printf("system: %s\n", fbuf);
/* Don't print the print fmt, it's ugly */
fp = fopen(file, "r");
if (!fp)
die("reading %s", file);
do {
n = getline(&buf, &l, fp);
if (n > 0) {
if (strncmp(buf, "print fmt", 9) == 0)
break;
fwrite(buf, 1, n, stdout);
}
} while (n > 0);
fclose(fp);
free(buf);
free(file);
return 0;
}
static void show_event_filter_re(const char *re)
{
process_file_re(event_filter_write, "available_events", re);
}
static void show_event_trigger_re(const char *re)
{
process_file_re(event_trigger_write, "available_events", re);
}
static void show_event_format_re(const char *re)
{
process_file_re(event_format_write, "available_events", re);
}
void show_instance_file(struct buffer_instance *instance, const char *name)
{
char *path;
path = get_instance_file(instance, name);
dump_file_content(path);
tracecmd_put_tracing_file(path);
}
enum {
SHOW_EVENT_FORMAT = 1 << 0,
SHOW_EVENT_FILTER = 1 << 1,
SHOW_EVENT_TRIGGER = 1 << 2,
};
static void show_events(const char *eventre, int flags)
{
if (flags && !eventre)
die("When specifying event files, an event must be named");
if (eventre) {
if (flags & SHOW_EVENT_FORMAT)
show_event_format_re(eventre);
else if (flags & SHOW_EVENT_FILTER)
show_event_filter_re(eventre);
else if (flags & SHOW_EVENT_TRIGGER)
show_event_trigger_re(eventre);
else
show_file_re("available_events", eventre);
} else
show_file("available_events");
}
static void show_tracers(void)
{
show_file("available_tracers");
}
static void show_options(void)
{
show_file("trace_options");
}
static void show_clocks(void)
{
show_file("trace_clock");
}
static void show_functions(const char *funcre)
{
if (funcre)
show_file_re("available_filter_functions", funcre);
else
show_file("available_filter_functions");
}
static void show_buffers(void)
{
struct dirent *dent;
DIR *dir;
char *path;
int printed = 0;
path = tracecmd_get_tracing_file("instances");
dir = opendir(path);
tracecmd_put_tracing_file(path);
if (!dir)
die("Can not read instance directory");
while ((dent = readdir(dir))) {
const char *name = dent->d_name;
if (strcmp(name, ".") == 0 ||
strcmp(name, "..") == 0)
continue;
printf("%s\n", name);
printed = 1;
}
closedir(dir);
if (!printed)
printf("No buffer instances defined\n");
}
static void show_plugins(void)
{
struct pevent *pevent;
struct plugin_list *list;
struct trace_seq s;
pevent = pevent_alloc();
if (!pevent)
die("Can not allocate pevent\n");
trace_seq_init(&s);
list = tracecmd_load_plugins(pevent);
trace_util_print_plugins(&s, " ", "\n", list);
trace_seq_do_printf(&s);
tracecmd_unload_plugins(list, pevent);
pevent_free(pevent);
}
static void show_plugin_options(void)
{
struct pevent *pevent;
struct plugin_list *list;
struct trace_seq s;
tracecmd_ftrace_load_options();
pevent = pevent_alloc();
if (!pevent)
die("Can not allocate pevent\n");
trace_seq_init(&s);
list = tracecmd_load_plugins(pevent);
trace_util_print_plugin_options(&s);
trace_seq_do_printf(&s);
tracecmd_unload_plugins(list, pevent);
pevent_free(pevent);
}
enum {
OPT_tracing_on = 255,
OPT_current_tracer = 254,
OPT_buffer_size_kb = 253,
OPT_buffer_total_size_kb = 252,
OPT_ftrace_filter = 251,
OPT_ftrace_notrace = 250,
OPT_ftrace_pid = 249,
OPT_graph_function = 248,
OPT_graph_notrace = 247,
OPT_cpumask = 246,
};
int main (int argc, char **argv)
{
int c;
errno = 0;
if (argc < 2)
usage(argv);
if (strcmp(argv[1], "report") == 0) {
trace_report(argc, argv);
exit(0);
} else if (strcmp(argv[1], "snapshot") == 0) {
trace_snapshot(argc, argv);
exit(0);
} else if (strcmp(argv[1], "hist") == 0) {
trace_hist(argc, argv);
exit(0);
} else if (strcmp(argv[1], "mem") == 0) {
trace_mem(argc, argv);
exit(0);
} else if (strcmp(argv[1], "listen") == 0) {
trace_listen(argc, argv);
exit(0);
} else if (strcmp(argv[1], "split") == 0) {
trace_split(argc, argv);
exit(0);
} else if (strcmp(argv[1], "restore") == 0) {
trace_restore(argc, argv);
exit(0);
} else if (strcmp(argv[1], "stack") == 0) {
trace_stack(argc, argv);
exit(0);
} else if (strcmp(argv[1], "check-events") == 0) {
const char *tracing;
int ret;
struct pevent *pevent = NULL;
struct plugin_list *list = NULL;
while ((c = getopt(argc-1, argv+1, "+hN")) >= 0) {
switch (c) {
case 'h':
default:
usage(argv);
break;
case 'N':
tracecmd_disable_plugins = 1;
break;
}
}
tracing = tracecmd_get_tracing_dir();
if (!tracing) {
printf("Can not find or mount tracing directory!\n"
"Either tracing is not configured for this "
"kernel\n"
"or you do not have the proper permissions to "
"mount the directory");
exit(EINVAL);
}
pevent = pevent_alloc();
if (!pevent)
exit(EINVAL);
list = tracecmd_load_plugins(pevent);
ret = tracecmd_fill_local_events(tracing, pevent);
if (ret || pevent->parsing_failures)
ret = EINVAL;
tracecmd_unload_plugins(list, pevent);
pevent_free(pevent);
exit(ret);
} else if (strcmp(argv[1], "record") == 0 ||
strcmp(argv[1], "start") == 0 ||
strcmp(argv[1], "extract") == 0 ||
strcmp(argv[1], "stop") == 0 ||
strcmp(argv[1], "stream") == 0 ||
strcmp(argv[1], "profile") == 0 ||
strcmp(argv[1], "restart") == 0 ||
strcmp(argv[1], "reset") == 0) {
trace_record(argc, argv);
exit(0);
} else if (strcmp(argv[1], "stat") == 0) {
trace_stat(argc, argv);
exit(0);
} else if (strcmp(argv[1], "options") == 0) {
show_plugin_options();
exit(0);
} else if (strcmp(argv[1], "show") == 0) {
const char *buffer = NULL;
const char *file = "trace";
const char *cpu = NULL;
struct buffer_instance *instance = &top_instance;
char cpu_path[128];
char *path;
int snap = 0;
int pipe = 0;
int show_name = 0;
int option_index = 0;
int stop = 0;
static struct option long_options[] = {
{"tracing_on", no_argument, NULL, OPT_tracing_on},
{"current_tracer", no_argument, NULL, OPT_current_tracer},
{"buffer_size", no_argument, NULL, OPT_buffer_size_kb},
{"buffer_total_size", no_argument, NULL, OPT_buffer_total_size_kb},
{"ftrace_filter", no_argument, NULL, OPT_ftrace_filter},
{"ftrace_notrace", no_argument, NULL, OPT_ftrace_notrace},
{"ftrace_pid", no_argument, NULL, OPT_ftrace_pid},
{"graph_function", no_argument, NULL, OPT_graph_function},
{"graph_notrace", no_argument, NULL, OPT_graph_notrace},
{"cpumask", no_argument, NULL, OPT_cpumask},
{"help", no_argument, NULL, '?'},
{NULL, 0, NULL, 0}
};
while ((c = getopt_long(argc-1, argv+1, "B:c:fsp",
long_options, &option_index)) >= 0) {
switch (c) {
case 'h':
usage(argv);
break;
case 'B':
if (buffer)
die("Can only show one buffer at a time");
buffer = optarg;
instance = create_instance(optarg);
if (!instance)
die("Failed to create instance");
break;
case 'c':
if (cpu)
die("Can only show one CPU at a time");
cpu = optarg;
break;
case 'f':
show_name = 1;
break;
case 's':
snap = 1;
if (pipe)
die("Can not have -s and -p together");
break;
case 'p':
pipe = 1;
if (snap)
die("Can not have -s and -p together");
break;
case OPT_tracing_on:
show_instance_file(instance, "tracing_on");
stop = 1;
break;
case OPT_current_tracer:
show_instance_file(instance, "current_tracer");
stop = 1;
break;
case OPT_buffer_size_kb:
show_instance_file(instance, "buffer_size_kb");
stop = 1;
break;
case OPT_buffer_total_size_kb:
show_instance_file(instance, "buffer_total_size_kb");
stop = 1;
break;
case OPT_ftrace_filter:
show_instance_file(instance, "set_ftrace_filter");
stop = 1;
break;
case OPT_ftrace_notrace:
show_instance_file(instance, "set_ftrace_notrace");
stop = 1;
break;
case OPT_ftrace_pid:
show_instance_file(instance, "set_ftrace_pid");
stop = 1;
break;
case OPT_graph_function:
show_instance_file(instance, "set_graph_function");
stop = 1;
break;
case OPT_graph_notrace:
show_instance_file(instance, "set_graph_notrace");
stop = 1;
break;
case OPT_cpumask:
show_instance_file(instance, "tracing_cpumask");
stop = 1;
break;
default:
usage(argv);
}
}
if (stop)
exit(0);
if (pipe)
file = "trace_pipe";
else if (snap)
file = "snapshot";
if (cpu) {
snprintf(cpu_path, 128, "per_cpu/cpu%d/%s", atoi(cpu), file);
file = cpu_path;
}
if (buffer) {
path = malloc(strlen(buffer) + strlen("instances//") +
strlen(file) + 1);
if (path)
die("Failed to allocate instance path %s", file);
sprintf(path, "instances/%s/%s", buffer, file);
file = path;
}
if (show_name) {
char *name;
name = tracecmd_get_tracing_file(file);
printf("%s\n", name);
tracecmd_put_tracing_file(name);
}
show_file(file);
if (buffer)
free(path);
exit(0);
} else if (strcmp(argv[1], "list") == 0) {
int events = 0;
int tracer = 0;
int options = 0;
int funcs = 0;
int buffers = 0;
int clocks = 0;
int plug = 0;
int plug_op = 0;
int flags = 0;
int show_all = 1;
int i;
const char *arg;
const char *funcre = NULL;
const char *eventre = NULL;
for (i = 2; i < argc; i++) {
arg = NULL;
if (argv[i][0] == '-') {
if (i < argc - 1) {
if (argv[i+1][0] != '-')
arg = argv[i+1];
}
switch (argv[i][1]) {
case 'h':
usage(argv);
break;
case 'e':
events = 1;
eventre = arg;
show_all = 0;
break;
case 'B':
buffers = 1;
show_all = 0;
break;
case 'C':
clocks = 1;
show_all = 0;
break;
case 'F':
flags |= SHOW_EVENT_FORMAT;
break;
case 'R':
flags |= SHOW_EVENT_TRIGGER;
break;
case 'l':
flags |= SHOW_EVENT_FILTER;
break;
case 'p':
case 't':
tracer = 1;
show_all = 0;
break;
case 'P':
plug = 1;
show_all = 0;
break;
case 'O':
plug_op = 1;
show_all = 0;
break;
case 'o':
options = 1;
show_all = 0;
break;
case 'f':
funcs = 1;
funcre = arg;
show_all = 0;
break;
default:
fprintf(stderr, "list: invalid option -- '%c'\n",
argv[optind][1]);
usage(argv);
}
}
}
if (events)
show_events(eventre, flags);
if (tracer)
show_tracers();
if (options)
show_options();
if (plug)
show_plugins();
if (plug_op)
show_plugin_options();
if (funcs)
show_functions(funcre);
if (buffers)
show_buffers();
if (clocks)
show_clocks();
if (show_all) {
printf("events:\n");
show_events(NULL, 0);
printf("\ntracers:\n");
show_tracers();
printf("\noptions:\n");
show_options();
}
exit(0);
} else if (strcmp(argv[1], "-h") == 0 ||
strcmp(argv[1], "help") == 0) {
usage(argv);
} else {
fprintf(stderr, "unknown command: %s\n", argv[1]);
usage(argv);
}
return 0;
}