blob: a0d743fbec92428ed00376b3a0da3f76746db947 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
*
*/
#include <stdlib.h>
#include <sys/stat.h>
#include "tracefs.h"
#include "trace-local.h"
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_instance_file(struct buffer_instance *instance, const char *name)
{
char *path;
path = tracefs_instance_get_file(instance->tracefs, name);
dump_file_content(path);
tracefs_put_tracing_file(path);
}
enum {
SHOW_EVENT_FORMAT = 1 << 0,
SHOW_EVENT_FILTER = 1 << 1,
SHOW_EVENT_TRIGGER = 1 << 2,
SHOW_EVENT_FULL = 1 << 3,
};
void show_file(const char *name)
{
char *path;
path = tracefs_get_tracing_file(name);
dump_file_content(path);
tracefs_put_tracing_file(path);
}
typedef int (*process_file_func)(char *buf, int len, int flags);
static void process_file_re(process_file_func func,
const char *name, const char *re, int flags)
{
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 = tracefs_get_tracing_file(name);
fp = fopen(path, "r");
if (!fp)
die("reading %s", path);
tracefs_put_tracing_file(path);
do {
n = getline(&buf, &l, fp);
if (n > 0 && regexec(&reg, buf, 0, NULL, 0) == 0)
func(buf, n, flags);
} while (n > 0);
free(buf);
fclose(fp);
regfree(&reg);
}
static void show_event(process_file_func func, const char *system,
const char *event, int flags)
{
char *buf;
int ret;
ret = asprintf(&buf, "%s:%s", system, event);
if (ret < 0)
die("Can not allocate event");
func(buf, strlen(buf), flags);
free(buf);
}
static void show_system(process_file_func func, const char *system, int flags)
{
char **events;
int e;
events = tracefs_system_events(NULL, system);
if (!events) /* die? */
return;
for (e = 0; events[e]; e++)
show_event(func, system, events[e], flags);
}
static void show_event_systems(process_file_func func, char **systems, int flags)
{
int s;
for (s = 0; systems[s]; s++)
show_system(func, systems[s], flags);
}
static void process_events(process_file_func func, const char *re, int flags)
{
regex_t system_reg;
regex_t event_reg;
char *str;
size_t l = strlen(re);
bool just_systems = true;
char **systems;
char **events;
char *system;
char *event;
int s, e;
systems = tracefs_event_systems(NULL);
if (!systems)
return process_file_re(func, "available_events", re, flags);
if (!re || l == 0) {
show_event_systems(func, systems, flags);
return;
}
str = strdup(re);
if (!str)
die("Can not allocate momory for regex");
system = strtok(str, ":");
event = strtok(NULL, "");
if (regcomp(&system_reg, system, REG_ICASE|REG_NOSUB))
die("invalid regex '%s'", system);
if (event) {
if (regcomp(&event_reg, event, REG_ICASE|REG_NOSUB))
die("invalid regex '%s'", event);
} else {
/*
* If the regex ends with ":", then event would be null,
* but we do not want to match events.
*/
if (re[l-1] != ':')
just_systems = false;
}
free(str);
for (s = 0; systems[s]; s++) {
if (regexec(&system_reg, systems[s], 0, NULL, 0) == 0) {
if (!event) {
show_system(func, systems[s], flags);
continue;
}
events = tracefs_system_events(NULL, systems[s]);
if (!events) /* die? */
continue;
for (e = 0; events[e]; e++) {
if (regexec(&event_reg, events[e], 0, NULL, 0) == 0)
show_event(func, systems[s], events[e], flags);
}
tracefs_list_free(events);
continue;
}
if (just_systems)
continue;
events = tracefs_system_events(NULL, systems[s]);
if (!events) /* die? */
continue;
for (e = 0; events[e]; e++) {
if (regexec(&system_reg, events[e], 0, NULL, 0) == 0)
show_event(func, systems[s], events[e], flags);
}
tracefs_list_free(events);
}
tracefs_list_free(systems);
regfree(&system_reg);
if (event)
regfree(&event_reg);
}
static int show_file_write(char *buf, int len, int flags)
{
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, 0);
}
static char *get_event_file(const char *type, char *buf, int len)
{
char *system;
char *event;
char *path;
char *file;
int ret;
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 = tracefs_get_tracing_file("events");
ret = asprintf(&file, "%s/%s/%s/%s", path, system, event, type);
if (ret < 0)
die("Failed to allocate event file %s %s", system, event);
tracefs_put_tracing_file(path);
return file;
}
static int event_filter_write(char *buf, int len, int flags)
{
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, int flags)
{
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, int flags)
{
char *file = get_event_file("format", fbuf, len);
char *buf = NULL;
size_t l;
FILE *fp;
bool full;
int n;
full = flags & SHOW_EVENT_FULL;
/* 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 (!full && strncmp(buf, "print fmt", 9) == 0)
break;
fwrite(buf, 1, n, stdout);
}
} while (n > 0);
fclose(fp);
free(buf);
free(file);
return 0;
}
static int event_name(char *buf, int len, int flags)
{
printf("%s\n", buf);
return 0;
}
static void show_event_filter_re(const char *re)
{
process_events(event_filter_write, re, 0);
}
static void show_event_trigger_re(const char *re)
{
process_events(event_trigger_write, re, 0);
}
static void show_event_format_re(const char *re, int flags)
{
process_events(event_format_write, re, flags);
}
static void show_event_names_re(const char *re)
{
process_events(event_name, re, 0);
}
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, flags);
else if (flags & SHOW_EVENT_FILTER)
show_event_filter_re(eventre);
else if (flags & SHOW_EVENT_TRIGGER)
show_event_trigger_re(eventre);
else
show_event_names_re(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)
{
char *clocks;
int size;
clocks = tracefs_instance_file_read(NULL, "trace_clock", &size);
if (!clocks)
die("getting clocks");
if (clocks[size - 1] == '\n')
clocks[size - 1] = 0;
if (trace_tsc2nsec_is_supported())
printf("%s %s\n", clocks, TSCNSEC_CLOCK);
else
printf("%s\n", clocks);
free(clocks);
}
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 = tracefs_get_tracing_file("instances");
dir = opendir(path);
tracefs_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_systems(void)
{
struct dirent *dent;
char *path;
DIR *dir;
path = tracefs_get_tracing_file("events");
dir = opendir(path);
if (!dir)
die("Can not read events directory");
while ((dent = readdir(dir))) {
const char *name = dent->d_name;
struct stat st;
char *spath;
int ret;
if (strcmp(name, ".") == 0 ||
strcmp(name, "..") == 0)
continue;
if (asprintf(&spath, "%s/%s", path, name) < 0)
continue;
ret = stat(spath, &st);
if (!ret && S_ISDIR(st.st_mode))
printf("%s\n", name);
free(spath);
}
printf("\n");
closedir(dir);
tracefs_put_tracing_file(path);
}
static void show_plugin_options(void)
{
struct tep_handle *pevent;
struct tep_plugin_list *list;
struct trace_seq s;
tracecmd_ftrace_load_options();
pevent = tep_alloc();
if (!pevent)
die("Can not allocate pevent\n");
trace_seq_init(&s);
list = trace_load_plugins(pevent, 0);
tep_plugin_print_options(&s);
trace_seq_do_printf(&s);
tep_unload_plugins(list, pevent);
tep_free(pevent);
}
void trace_option(int argc, char **argv)
{
show_plugin_options();
}
static void show_plugins(void)
{
struct tep_handle *pevent;
struct tep_plugin_list *list;
struct trace_seq s;
pevent = tep_alloc();
if (!pevent)
die("Can not allocate pevent\n");
trace_seq_init(&s);
list = trace_load_plugins(pevent, 0);
tep_print_plugins(&s, " ", "\n", list);
trace_seq_do_printf(&s);
tep_unload_plugins(list, pevent);
tep_free(pevent);
}
void trace_list(int argc, char **argv)
{
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 systems = 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;
case 's':
systems = 1;
show_all = 0;
break;
case '-':
if (strcmp(argv[i], "--debug") == 0) {
tracecmd_set_debug(true);
break;
}
if (strcmp(argv[i], "--full") == 0) {
flags |= SHOW_EVENT_FULL;
break;
}
fprintf(stderr, "list: invalid option -- '%s'\n",
argv[i]);
default:
fprintf(stderr, "list: invalid option -- '%c'\n",
argv[i][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 (systems)
show_systems();
if (show_all) {
printf("event systems:\n");
show_systems();
printf("events:\n");
show_events(NULL, 0);
printf("\ntracers:\n");
show_tracers();
printf("\noptions:\n");
show_options();
}
return;
}