blob: 6719fc3ac9356ec6f7f9d97aab77b7bc0ff9879e [file] [log] [blame]
/*
* Copyright (C) 2008,2009, 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#define _GNU_SOURCE
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <pthread.h>
#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#include "trace-local.h"
#define VERSION "0.5"
#define _STR(x) #x
#define STR(x) _STR(x)
#define MAX_PATH 256
#define TRACE_CTRL "tracing_on"
#define TRACE "trace"
#define AVAILABLE "available_tracers"
#define CURRENT "current_tracer"
#define ITER_CTRL "trace_options"
#define MAX_LATENCY "tracing_max_latency"
unsigned int page_size;
static const char *output_file = "trace.dat";
static int output_fd;
static int latency;
static int cpu_count;
static int *pids;
struct event_list {
struct event_list *next;
const char *event;
};
static struct event_list *event_selection;
struct events {
struct events *sibling;
struct events *children;
struct events *next;
char *name;
};
static void delete_temp_file(int cpu)
{
char file[MAX_PATH];
snprintf(file, MAX_PATH, "%s.cpu%d", output_file, cpu);
unlink(file);
}
static void kill_threads(void)
{
int i;
if (!cpu_count)
return;
for (i = 0; i < cpu_count; i++) {
if (pids[i] > 0) {
kill(pids[i], SIGKILL);
delete_temp_file(i);
pids[i] = 0;
}
}
}
static void delete_thread_data(void)
{
int i;
if (!cpu_count)
return;
for (i = 0; i < cpu_count; i++) {
if (pids[i]) {
delete_temp_file(i);
if (pids[i] < 0)
pids[i] = 0;
}
}
}
static void stop_threads(void)
{
int i;
if (!cpu_count)
return;
for (i = 0; i < cpu_count; i++) {
if (pids[i] > 0) {
kill(pids[i], SIGINT);
waitpid(pids[i], NULL, 0);
pids[i] = -1;
}
}
}
void die(char *fmt, ...)
{
va_list ap;
int ret = errno;
if (errno)
perror("trace-cmd");
else
ret = -1;
kill_threads();
va_start(ap, fmt);
fprintf(stderr, " ");
vfprintf(stderr, fmt, ap);
va_end(ap);
fprintf(stderr, "\n");
exit(ret);
}
void warning(char *fmt, ...)
{
va_list ap;
if (errno)
perror("trace-cmd");
errno = 0;
va_start(ap, fmt);
fprintf(stderr, " ");
vfprintf(stderr, fmt, ap);
va_end(ap);
fprintf(stderr, "\n");
}
void *malloc_or_die(unsigned int size)
{
void *data;
data = malloc(size);
if (!data)
die("malloc");
return data;
}
static const char *find_debugfs(void)
{
static char debugfs[MAX_PATH+1];
static int debugfs_found;
char type[100];
FILE *fp;
if (debugfs_found)
return debugfs;
if ((fp = fopen("/proc/mounts","r")) == NULL)
die("Can't open /proc/mounts for read");
while (fscanf(fp, "%*s %"
STR(MAX_PATH)
"s %99s %*s %*d %*d\n",
debugfs, type) == 2) {
if (strcmp(type, "debugfs") == 0)
break;
}
fclose(fp);
if (strcmp(type, "debugfs") != 0)
die("debugfs not mounted, please mount");
debugfs_found = 1;
return debugfs;
}
/*
* Finds the path to the debugfs/tracing
* Allocates the string and stores it.
*/
static const char *find_tracing_dir(void)
{
static char *tracing;
static int tracing_found;
const char *debugfs;
if (tracing_found)
return tracing;
debugfs = find_debugfs();
tracing = malloc_or_die(strlen(debugfs) + 9);
sprintf(tracing, "%s/tracing", debugfs);
tracing_found = 1;
return tracing;
}
static char *get_tracing_file(const char *name)
{
const char *tracing;
char *file;
tracing = find_tracing_dir();
if (!tracing)
return NULL;
file = malloc_or_die(strlen(tracing) + strlen(name) + 2);
sprintf(file, "%s/%s", tracing, name);
return file;
}
static void put_tracing_file(char *file)
{
free(file);
}
static int set_ftrace(int set)
{
struct stat buf;
char *path = "/proc/sys/kernel/ftrace_enabled";
int fd;
char *val = set ? "1" : "0";
/* if ftace_enable does not exist, simply ignore it */
fd = stat(path, &buf);
if (fd < 0)
return -ENODEV;
fd = open(path, O_WRONLY);
if (fd < 0)
die ("Can't %s ftrace", set ? "enable" : "disable");
write(fd, val, 1);
close(fd);
return 0;
}
void run_cmd(int argc, char **argv)
{
int status;
int pid;
if ((pid = fork()) < 0)
die("failed to fork");
if (!pid) {
/* child */
if (execvp(argv[0], argv))
exit(-1);
}
waitpid(pid, &status, 0);
}
static void show_events(void)
{
char buf[BUFSIZ];
char *path;
FILE *fp;
size_t n;
path = get_tracing_file("available_events");
fp = fopen(path, "r");
if (!fp)
die("reading %s", path);
put_tracing_file(path);
do {
n = fread(buf, 1, BUFSIZ, fp);
if (n > 0)
fwrite(buf, 1, n, stdout);
} while (n > 0);
fclose(fp);
}
static void show_plugins(void)
{
char buf[BUFSIZ];
char *path;
FILE *fp;
size_t n;
path = get_tracing_file("available_tracers");
fp = fopen(path, "r");
if (!fp)
die("reading %s", path);
put_tracing_file(path);
do {
n = fread(buf, 1, BUFSIZ, fp);
if (n > 0)
fwrite(buf, 1, n, stdout);
} while (n > 0);
fclose(fp);
}
static void set_plugin(const char *name)
{
FILE *fp;
char *path;
path = get_tracing_file("current_tracer");
fp = fopen(path, "w");
if (!fp)
die("writing to '%s'", path);
put_tracing_file(path);
fwrite(name, 1, strlen(name), fp);
fclose(fp);
}
static void show_options(void)
{
char buf[BUFSIZ];
char *path;
FILE *fp;
size_t n;
path = get_tracing_file("trace_options");
fp = fopen(path, "r");
if (!fp)
die("reading %s", path);
put_tracing_file(path);
do {
n = fread(buf, 1, BUFSIZ, fp);
if (n > 0)
fwrite(buf, 1, n, stdout);
} while (n > 0);
fclose(fp);
}
static void set_option(const char *option)
{
FILE *fp;
char *path;
path = get_tracing_file("trace_options");
fp = fopen(path, "w");
if (!fp)
die("writing to '%s'", path);
put_tracing_file(path);
fwrite(option, 1, strlen(option), fp);
fclose(fp);
}
static void enable_event(const char *name)
{
struct stat st;
FILE *fp;
char *path;
int ret;
fprintf(stderr, "enable %s\n", name);
if (strcmp(name, "all") == 0) {
path = get_tracing_file("events/enable");
ret = stat(path, &st);
if (ret < 0) {
put_tracing_file(path);
/* need to use old way */
path = get_tracing_file("set_event");
fp = fopen(path, "w");
if (!fp)
die("writing to '%s'", path);
put_tracing_file(path);
fwrite("*:*\n", 4, 1, fp);
fclose(fp);
return;
}
fp = fopen(path, "w");
if (!fp)
die("writing to '%s'", path);
put_tracing_file(path);
ret = fwrite("1", 1, 1, fp);
fclose(fp);
if (ret < 0)
die("writing to '%s'", path);
return;
}
path = get_tracing_file("set_event");
fp = fopen(path, "a");
if (!fp)
die("writing to '%s'", path);
put_tracing_file(path);
ret = fwrite(name, 1, strlen(name), fp);
if (ret < 0)
die("bad event '%s'", name);
ret = fwrite("\n", 1, 1, fp);
if (ret < 0)
die("bad event '%s'", name);
fclose(fp);
}
static void disable_event(const char *name)
{
struct stat st;
FILE *fp;
char *path;
int ret;
if (strcmp(name, "all") == 0) {
path = get_tracing_file("events/enable");
ret = stat(path, &st);
if (ret < 0) {
put_tracing_file(path);
/* need to use old way */
path = get_tracing_file("set_event");
fp = fopen(path, "w");
if (!fp)
die("writing to '%s'", path);
put_tracing_file(path);
fwrite("\n", 1, 1, fp);
fclose(fp);
return;
}
fp = fopen(path, "w");
if (!fp)
die("writing to '%s'", path);
put_tracing_file(path);
fwrite("0", 1, 1, fp);
fclose(fp);
}
}
static void enable_tracing(void)
{
FILE *fp;
char *path;
/* reset the trace */
path = get_tracing_file("tracing_on");
fp = fopen(path, "w");
if (!fp)
die("writing to '%s'", path);
put_tracing_file(path);
fwrite("1", 1, 1, fp);
fclose(fp);
}
static void disable_tracing(void)
{
FILE *fp;
char *path;
/* reset the trace */
path = get_tracing_file("tracing_on");
fp = fopen(path, "w");
if (!fp)
die("writing to '%s'", path);
put_tracing_file(path);
fwrite("0", 1, 1, fp);
fclose(fp);
}
static void disable_all(void)
{
FILE *fp;
char *path;
disable_tracing();
set_plugin("nop");
disable_event("all");
/* reset the trace */
path = get_tracing_file("trace");
fp = fopen(path, "w");
if (!fp)
die("writing to '%s'", path);
put_tracing_file(path);
fwrite("0", 1, 1, fp);
fclose(fp);
}
static void reset_max_latency(void)
{
FILE *fp;
char *path;
/* reset the trace */
path = get_tracing_file("tracing_max_latency");
fp = fopen(path, "w");
if (!fp)
die("writing to '%s'", path);
put_tracing_file(path);
fwrite("0", 1, 1, fp);
fclose(fp);
}
static void enable_events(void)
{
struct event_list *event;
for (event = event_selection; event; event = event->next) {
enable_event(event->event);
}
}
static int count_cpus(void)
{
FILE *fp;
char buf[1024];
int cpus = 0;
char *pbuf;
size_t *pn;
size_t n;
int r;
n = 1024;
pn = &n;
pbuf = buf;
fp = fopen("/proc/cpuinfo", "r");
if (!fp)
die("Can not read cpuinfo");
while ((r = getline(&pbuf, pn, fp)) >= 0) {
char *p;
if (strncmp(buf, "processor", 9) != 0)
continue;
for (p = buf+9; isspace(*p); p++)
;
if (*p == ':')
cpus++;
}
fclose(fp);
return cpus;
}
static int finished;
static void finish(int sig)
{
/* all done */
finished = 1;
}
static int create_recorder(int cpu)
{
char file[MAX_PATH];
const char *tracing;
char *path;
int out_fd;
int in_fd;
int brass[2];
int pid;
int ret;
char buf[page_size];
pid = fork();
if (pid < 0)
die("fork");
if (pid)
return pid;
signal(SIGINT, finish);
/* do not kill tasks on error */
cpu_count = 0;
snprintf(file, MAX_PATH, "%s.cpu%d", output_file, cpu);
out_fd = open(file, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);
if (out_fd < 0)
die("can't create file '%s'", file);
tracing = find_tracing_dir();
path = malloc_or_die(strlen(tracing) + 40);
sprintf(path, "%s/per_cpu/cpu%d/trace_pipe_raw", tracing, cpu);
in_fd = open(path, O_RDONLY);
if (in_fd < 0)
die("can not read '%s'", path);
ret = pipe(brass);
if (ret < 0)
die("can not create pipe");
do {
ret = splice(in_fd, NULL, brass[1], NULL, page_size, 1 /* SPLICE_F_MOVE */);
if (ret < 0)
die("splice in");
ret = splice(brass[0], NULL, out_fd, NULL, page_size, 3 /* and NON_BLOCK */);
if (ret < 0 && errno != EAGAIN)
die("splice out");
} while (!finished);
/* splice only reads full pages */
do {
ret = read(in_fd, buf, page_size);
if (ret > 0)
write(out_fd, buf, ret);
} while (ret > 0);
exit(0);
}
static void start_threads(void)
{
int cpus;
int i;
cpus = count_cpus();
/* make a thread for every CPU we have */
pids = malloc_or_die(sizeof(*pids) * cpu_count);
memset(pids, 0, sizeof(*pids) * cpu_count);
cpu_count = cpus;
for (i = 0; i < cpus; i++) {
pids[i] = create_recorder(i);
}
}
static ssize_t write_or_die(const void *buf, size_t len)
{
int ret;
ret = write(output_fd, buf, len);
if (ret < 0)
die("writing to '%s'", output_file);
return ret;
}
int bigendian(void)
{
unsigned char str[] = { 0x1, 0x2, 0x3, 0x4 };
unsigned int *ptr;
ptr = (unsigned int *)str;
return *ptr == 0x01020304;
}
static unsigned long long copy_file_fd(int fd)
{
unsigned long long size = 0;
char buf[BUFSIZ];
int r;
do {
r = read(fd, buf, BUFSIZ);
if (r > 0) {
size += r;
write_or_die(buf, r);
}
} while (r > 0);
return size;
}
static unsigned long long copy_file(const char *file)
{
unsigned long long size = 0;
int fd;
fd = open(file, O_RDONLY);
if (fd < 0)
die("Can't read '%s'", file);
size = copy_file_fd(fd);
close(fd);
return size;
}
static unsigned long get_size_fd(int fd)
{
unsigned long long size = 0;
char buf[BUFSIZ];
int r;
do {
r = read(fd, buf, BUFSIZ);
if (r > 0)
size += r;
} while (r > 0);
lseek(fd, 0, SEEK_SET);
return size;
}
static unsigned long get_size(const char *file)
{
unsigned long long size = 0;
int fd;
fd = open(file, O_RDONLY);
if (fd < 0)
die("Can't read '%s'", file);
size = get_size_fd(fd);
close(fd);
return size;
}
static void read_header_files(void)
{
unsigned long long size, check_size;
struct stat st;
char *path;
int fd;
int ret;
path = get_tracing_file("events/header_page");
ret = stat(path, &st);
if (ret < 0) {
/* old style did not show this info, just add zero */
put_tracing_file(path);
write_or_die("header_page", 12);
size = 0;
write_or_die(&size, 8);
write_or_die("header_event", 13);
write_or_die(&size, 8);
return;
}
fd = open(path, O_RDONLY);
if (fd < 0)
die("can't read '%s'", path);
/* unfortunately, you can not stat debugfs files for size */
size = get_size_fd(fd);
write_or_die("header_page", 12);
write_or_die(&size, 8);
check_size = copy_file_fd(fd);
if (size != check_size)
die("wrong size for '%s' size=%lld read=%lld",
path, size, check_size);
put_tracing_file(path);
path = get_tracing_file("events/header_event");
fd = open(path, O_RDONLY);
if (fd < 0)
die("can't read '%s'", path);
size = get_size_fd(fd);
write_or_die("header_event", 13);
write_or_die(&size, 8);
check_size = copy_file_fd(fd);
if (size != check_size)
die("wrong size for '%s'", path);
put_tracing_file(path);
}
static void copy_event_system(const char *sys)
{
unsigned long long size, check_size;
struct dirent *dent;
struct stat st;
char *format;
DIR *dir;
int count = 0;
int ret;
dir = opendir(sys);
if (!dir)
die("can't read directory '%s'", sys);
while ((dent = readdir(dir))) {
if (strcmp(dent->d_name, ".") == 0 ||
strcmp(dent->d_name, "..") == 0)
continue;
format = malloc_or_die(strlen(sys) + strlen(dent->d_name) + 10);
sprintf(format, "%s/%s/format", sys, dent->d_name);
ret = stat(format, &st);
free(format);
if (ret < 0)
continue;
count++;
}
write_or_die(&count, 4);
rewinddir(dir);
while ((dent = readdir(dir))) {
if (strcmp(dent->d_name, ".") == 0 ||
strcmp(dent->d_name, "..") == 0)
continue;
format = malloc_or_die(strlen(sys) + strlen(dent->d_name) + 10);
sprintf(format, "%s/%s/format", sys, dent->d_name);
ret = stat(format, &st);
if (ret >= 0) {
/* unfortunately, you can not stat debugfs files for size */
size = get_size(format);
write_or_die(&size, 8);
check_size = copy_file(format);
if (size != check_size)
die("error in size of file '%s'", format);
}
free(format);
}
}
static void read_ftrace_files(void)
{
char *path;
path = get_tracing_file("events/ftrace");
copy_event_system(path);
put_tracing_file(path);
}
static void read_event_files(void)
{
struct dirent *dent;
struct stat st;
char *path;
char *sys;
DIR *dir;
int count = 0;
int ret;
path = get_tracing_file("events");
dir = opendir(path);
if (!dir)
die("can't read directory '%s'", path);
while ((dent = readdir(dir))) {
if (strcmp(dent->d_name, ".") == 0 ||
strcmp(dent->d_name, "..") == 0 ||
strcmp(dent->d_name, "ftrace") == 0)
continue;
sys = malloc_or_die(strlen(path) + strlen(dent->d_name) + 2);
sprintf(sys, "%s/%s", path, dent->d_name);
ret = stat(sys, &st);
free(sys);
if (ret < 0)
continue;
if (S_ISDIR(st.st_mode))
count++;
}
write_or_die(&count, 4);
rewinddir(dir);
while ((dent = readdir(dir))) {
if (strcmp(dent->d_name, ".") == 0 ||
strcmp(dent->d_name, "..") == 0 ||
strcmp(dent->d_name, "ftrace") == 0)
continue;
sys = malloc_or_die(strlen(path) + strlen(dent->d_name) + 2);
sprintf(sys, "%s/%s", path, dent->d_name);
ret = stat(sys, &st);
if (ret >= 0) {
if (S_ISDIR(st.st_mode)) {
write_or_die(dent->d_name, strlen(dent->d_name) + 1);
copy_event_system(sys);
}
}
free(sys);
}
put_tracing_file(path);
}
static void read_proc_kallsyms(void)
{
unsigned int size, check_size;
const char *path = "/proc/kallsyms";
struct stat st;
int ret;
ret = stat(path, &st);
if (ret < 0) {
/* not found */
size = 0;
write_or_die(&size, 4);
return;
}
size = get_size(path);
write_or_die(&size, 4);
check_size = copy_file(path);
if (size != check_size)
die("error in size of file '%s'", path);
}
static void read_ftrace_printk(void)
{
unsigned int size, check_size;
const char *path;
struct stat st;
int ret;
path = get_tracing_file("printk_formats");
ret = stat(path, &st);
if (ret < 0) {
/* not found */
size = 0;
write_or_die(&size, 4);
return;
}
size = get_size(path);
write_or_die(&size, 4);
check_size = copy_file(path);
if (size != check_size)
die("error in size of file '%s'", path);
}
static void read_tracing_data(void)
{
char buf[BUFSIZ];
output_fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC | O_LARGEFILE, 0644);
if (output_fd < 0)
die("creating file '%s'", output_file);
buf[0] = 23;
buf[1] = 8;
buf[2] = 68;
memcpy(buf + 3, "tracing", 7);
write_or_die(buf, 10);
write_or_die(VERSION, strlen(VERSION) + 1);
/* save endian */
if (bigendian())
buf[0] = 1;
else
buf[0] = 0;
write_or_die(buf, 1);
/* save size of long */
buf[0] = sizeof(long);
write_or_die(buf, 1);
/* save page_size */
page_size = getpagesize();
write_or_die(&page_size, 4);
read_header_files();
read_ftrace_files();
read_event_files();
read_proc_kallsyms();
read_ftrace_printk();
}
static unsigned long long read_thread_file(int cpu)
{
unsigned long long size;
char *file;
file = malloc_or_die(strlen(output_file) + 20);
snprintf(file, MAX_PATH, "%s.cpu%d", output_file, cpu);
size = copy_file(file);
free(file);
return size;
}
static void read_trace_data(void)
{
char *path;
write_or_die("latency ", 10);
path = get_tracing_file("trace");
copy_file(path);
put_tracing_file(path);
}
static void read_thread_data(void)
{
unsigned long long offset, check_size;
unsigned long long *offsets;
unsigned long long *sizes;
unsigned long long size;
long long ret;
struct stat st;
char *file;
int i;
if (!cpu_count)
return;
/*
* Save the command lines;
*/
file = get_tracing_file("saved_cmdlines");
ret = stat(file, &st);
if (ret >= 0) {
size = get_size(file);
write_or_die(&size, 8);
check_size = copy_file(file);
if (size != check_size)
die("error in size of file '%s'", file);
} else {
size = 0;
write_or_die(&size, 8);
}
put_tracing_file(file);
write_or_die(&cpu_count, 4);
if (latency) {
read_trace_data();
return;
}
write_or_die("flyrecord", 10);
offsets = malloc_or_die(sizeof(*offsets) * cpu_count);
sizes = malloc_or_die(sizeof(*sizes) * cpu_count);
offset = lseek(output_fd, 0, SEEK_CUR);
/* hold any extra data for data */
offset += cpu_count * (16);
offset = (offset + (page_size - 1)) & ~(PAGE_MASK);
for (i = 0; i < cpu_count; i++) {
file = malloc_or_die(strlen(output_file) + 20);
sprintf(file, "%s.cpu%d", output_file, i);
ret = stat(file, &st);
if (ret < 0)
die("can not stat '%s'", file);
free(file);
offsets[i] = offset;
sizes[i] = st.st_size;
offset += st.st_size;
offset = (offset + (page_size - 1)) & ~(PAGE_MASK);
write_or_die(&offsets[i], 8);
write_or_die(&sizes[i], 8);
}
for (i = 0; i < cpu_count; i++) {
fprintf(stderr, "offset=%llx\n", offsets[i]);
ret = lseek64(output_fd, offsets[i], SEEK_SET);
if (ret < 0)
die("could not seek to %lld\n", offsets[i]);
check_size = read_thread_file(i);
if (check_size != sizes[i])
die("did not match size of %lld to %lld",
check_size, sizes[i]);
}
}
void usage(char **argv)
{
char *arg = argv[0];
char *p = arg+strlen(arg);
while (p >= arg && *p != '/')
p--;
p++;
printf("\n"
"%s version %s\n\n"
"usage: %s record [-e event][-p plugin] [-d] [-o file] [-O option ] command ...\n"
" -e run command with event enabled\n"
" -p run command with plugin enabled\n"
" -d disable function tracer when running\n"
" -o data output file [default trace.dat]\n"
" -O option to enable (or disable)\n"
"\n"
" %s report [-i file] [--cpu cpu] [-e][-f][-l][-P][-E]\n"
" -i input file [default trace.dat]\n"
" -e show file endianess\n"
" -f show function list\n"
" -P show printk list\n"
" -E show event files stored\n"
" -l show latency format (default with latency tracers)\n"
"\n"
" %s list [-e][-p]\n"
" -e list available events\n"
" -p list available plugins\n"
" -o list available options\n"
"\n", p, VERSION, p, p, p);
exit(-1);
}
int main (int argc, char **argv)
{
const char *plugin = NULL;
const char *output = NULL;
const char *option;
struct event_list *event;
int disable = 0;
int plug = 0;
int events = 0;
int options = 0;
int fset;
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], "record") == 0) {
while ((c = getopt(argc-1, argv+1, "+he:p:do:O:")) >= 0) {
switch (c) {
case 'h':
usage(argv);
break;
case 'e':
events = 1;
event = malloc_or_die(sizeof(*event));
event->event = optarg;
event->next = event_selection;
event_selection = event;
break;
case 'p':
if (plugin)
die("only one plugin allowed");
plugin = optarg;
fprintf(stderr, " plugin %s\n", plugin);
break;
case 'd':
disable = 1;
break;
case 'o':
if (output)
die("only one output file allowed");
output = optarg;
break;
case 'O':
option = optarg;
set_option(option);
break;
}
}
} else if (strcmp(argv[1], "list") == 0) {
while ((c = getopt(argc-1, argv+1, "+hepo")) >= 0) {
switch (c) {
case 'h':
usage(argv);
break;
case 'e':
events = 1;
break;
case 'p':
plug = 1;
break;
case 'o':
options = 1;
break;
default:
usage(argv);
}
}
if (events)
show_events();
if (plug)
show_plugins();
if (options)
show_options();
if (!events && !plug && !options) {
printf("events:\n");
show_events();
printf("\nplugins:\n");
show_plugins();
printf("\noptions:\n");
show_options();
}
exit(0);
} else {
fprintf(stderr, "unknown command: %s\n", argv[1]);
usage(argv);
}
if ((argc - optind) < 2)
usage(argv);
if (output)
output_file = output;
read_tracing_data();
fset = set_ftrace(!disable);
disable_all();
start_threads();
signal(SIGINT, finish);
if (events)
enable_events();
if (plugin) {
/*
* Latency tracers just save the trace and kill
* the threads.
*/
if (strcmp(plugin, "irqsoff") == 0 ||
strcmp(plugin, "preemptoff") == 0 ||
strcmp(plugin, "preemptirqsoff") == 0 ||
strcmp(plugin, "wakeup") == 0 ||
strcmp(plugin, "wakeup_rt") == 0) {
latency = 1;
stop_threads();
reset_max_latency();
}
if (fset < 0 && (strcmp(plugin, "function") == 0 ||
strcmp(plugin, "function_graph") == 0))
die("function tracing not configured on this kernel");
set_plugin(plugin);
}
enable_tracing();
run_cmd((argc - optind) - 1, &argv[optind + 1]);
disable_tracing();
stop_threads();
read_thread_data();
delete_thread_data();
exit(0);
return 0;
}