927 lines
17 KiB
C
927 lines
17 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2014 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
|
|
*
|
|
*/
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <getopt.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <ctype.h>
|
|
|
|
#include "tracefs.h"
|
|
#include "trace-local.h"
|
|
|
|
#ifndef BUFSIZ
|
|
#define BUFSIZ 1024
|
|
#endif
|
|
|
|
static inline int is_top_instance(struct buffer_instance *instance)
|
|
{
|
|
return instance == &top_instance;
|
|
}
|
|
|
|
static int get_instance_file_fd(struct buffer_instance *instance,
|
|
const char *file)
|
|
{
|
|
char *path;
|
|
int fd;
|
|
|
|
path = tracefs_instance_get_file(instance->tracefs, file);
|
|
fd = open(path, O_RDONLY);
|
|
tracefs_put_tracing_file(path);
|
|
|
|
return fd;
|
|
}
|
|
|
|
char *strstrip(char *str)
|
|
{
|
|
char *s;
|
|
|
|
if (!str)
|
|
return NULL;
|
|
|
|
s = str + strlen(str) - 1;
|
|
while (s >= str && isspace(*s))
|
|
s--;
|
|
s++;
|
|
*s = '\0';
|
|
|
|
for (s = str; *s && isspace(*s); s++)
|
|
;
|
|
|
|
return s;
|
|
}
|
|
|
|
/* FIXME: append_file() is duplicated and could be consolidated */
|
|
char *append_file(const char *dir, const char *name)
|
|
{
|
|
char *file;
|
|
int ret;
|
|
|
|
ret = asprintf(&file, "%s/%s", dir, name);
|
|
if (ret < 0)
|
|
die("Failed to allocate %s/%s", dir, name);
|
|
|
|
return file;
|
|
}
|
|
|
|
static char *get_fd_content(int fd, const char *file)
|
|
{
|
|
char *str = NULL;
|
|
int cnt = 0;
|
|
int ret;
|
|
|
|
for (;;) {
|
|
str = realloc(str, BUFSIZ * ++cnt);
|
|
if (!str)
|
|
die("malloc");
|
|
ret = read(fd, str + BUFSIZ * (cnt - 1), BUFSIZ);
|
|
if (ret < 0)
|
|
die("reading %s\n", file);
|
|
if (ret < BUFSIZ)
|
|
break;
|
|
}
|
|
str[BUFSIZ * (cnt-1) + ret] = 0;
|
|
|
|
return str;
|
|
}
|
|
|
|
char *get_file_content(const char *file)
|
|
{
|
|
char *str;
|
|
int fd;
|
|
|
|
fd = open(file, O_RDONLY);
|
|
if (fd < 0)
|
|
return NULL;
|
|
|
|
str = get_fd_content(fd, file);
|
|
close(fd);
|
|
|
|
return str;
|
|
}
|
|
|
|
static char *get_instance_file_content(struct buffer_instance *instance,
|
|
const char *file)
|
|
{
|
|
char *str = NULL;
|
|
int fd;
|
|
|
|
fd = get_instance_file_fd(instance, file);
|
|
if (fd < 0)
|
|
return NULL;
|
|
|
|
str = get_fd_content(fd, file);
|
|
|
|
close(fd);
|
|
return str;
|
|
}
|
|
|
|
static void report_file(struct buffer_instance *instance,
|
|
char *name, char *def_value, char *description)
|
|
{
|
|
char *str;
|
|
char *cont;
|
|
|
|
if (!tracefs_file_exists(instance->tracefs, name))
|
|
return;
|
|
str = get_instance_file_content(instance, name);
|
|
if (!str)
|
|
return;
|
|
cont = strstrip(str);
|
|
if (cont[0] && strcmp(cont, def_value) != 0)
|
|
printf("\n%s%s\n", description, cont);
|
|
|
|
free(str);
|
|
}
|
|
|
|
static int report_instance(const char *name, void *data)
|
|
{
|
|
bool *first = (bool *)data;
|
|
|
|
if (*first) {
|
|
*first = false;
|
|
printf("\nInstances:\n");
|
|
}
|
|
printf(" %s\n", name);
|
|
return 0;
|
|
}
|
|
|
|
static void report_instances(void)
|
|
{
|
|
bool first = true;
|
|
|
|
tracefs_instances_walk(report_instance, &first);
|
|
}
|
|
|
|
struct event_iter *trace_event_iter_alloc(const char *path)
|
|
{
|
|
struct event_iter *iter;
|
|
|
|
iter = malloc(sizeof(*iter));
|
|
if (!iter)
|
|
die("Failed to allocate event_iter for path %s", path);
|
|
memset(iter, 0, sizeof(*iter));
|
|
|
|
iter->system_dir = opendir(path);
|
|
if (!iter->system_dir)
|
|
die("opendir");
|
|
|
|
return iter;
|
|
}
|
|
|
|
enum event_iter_type
|
|
trace_event_iter_next(struct event_iter *iter, const char *path, const char *system)
|
|
{
|
|
struct dirent *dent;
|
|
|
|
if (system && !iter->event_dir) {
|
|
char *event;
|
|
struct stat st;
|
|
|
|
event = append_file(path, system);
|
|
|
|
stat(event, &st);
|
|
if (!S_ISDIR(st.st_mode)) {
|
|
free(event);
|
|
goto do_system;
|
|
}
|
|
|
|
iter->event_dir = opendir(event);
|
|
if (!iter->event_dir)
|
|
die("opendir %s", event);
|
|
free(event);
|
|
}
|
|
|
|
if (iter->event_dir) {
|
|
while ((dent = readdir(iter->event_dir))) {
|
|
const char *name = dent->d_name;
|
|
|
|
if (strcmp(name, ".") == 0 ||
|
|
strcmp(name, "..") == 0)
|
|
continue;
|
|
|
|
iter->event_dent = dent;
|
|
return EVENT_ITER_EVENT;
|
|
}
|
|
closedir(iter->event_dir);
|
|
iter->event_dir = NULL;
|
|
}
|
|
|
|
do_system:
|
|
while ((dent = readdir(iter->system_dir))) {
|
|
const char *name = dent->d_name;
|
|
|
|
if (strcmp(name, ".") == 0 ||
|
|
strcmp(name, "..") == 0)
|
|
continue;
|
|
|
|
iter->system_dent = dent;
|
|
|
|
return EVENT_ITER_SYSTEM;
|
|
}
|
|
|
|
return EVENT_ITER_NONE;
|
|
}
|
|
|
|
void trace_event_iter_free(struct event_iter *iter)
|
|
{
|
|
if (!iter)
|
|
return;
|
|
|
|
if (iter->event_dir)
|
|
closedir(iter->event_dir);
|
|
|
|
closedir(iter->system_dir);
|
|
free(iter);
|
|
}
|
|
|
|
static void reset_event_iter(struct event_iter *iter)
|
|
{
|
|
if (iter->event_dir) {
|
|
closedir(iter->event_dir);
|
|
iter->event_dir = NULL;
|
|
}
|
|
|
|
rewinddir(iter->system_dir);
|
|
}
|
|
|
|
static int process_individual_events(const char *path, struct event_iter *iter)
|
|
{
|
|
struct stat st;
|
|
const char *system = iter->system_dent->d_name;
|
|
char *file;
|
|
char *enable = NULL;
|
|
char *str;
|
|
int ret = 0;
|
|
|
|
file = append_file(path, system);
|
|
|
|
stat(file, &st);
|
|
if (!S_ISDIR(st.st_mode))
|
|
goto out;
|
|
|
|
enable = append_file(file, "enable");
|
|
str = get_file_content(enable);
|
|
if (!str)
|
|
goto out;
|
|
|
|
if (*str != '1' && *str != '0')
|
|
ret = 1;
|
|
free(str);
|
|
|
|
out:
|
|
free(enable);
|
|
free(file);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
process_event_enable(char *path, const char *system, const char *name,
|
|
enum event_process *processed)
|
|
{
|
|
struct stat st;
|
|
char *enable = NULL;
|
|
char *file;
|
|
char *str;
|
|
|
|
if (system)
|
|
path = append_file(path, system);
|
|
|
|
file = append_file(path, name);
|
|
|
|
if (system)
|
|
free(path);
|
|
|
|
stat(file, &st);
|
|
if (!S_ISDIR(st.st_mode))
|
|
goto out;
|
|
|
|
enable = append_file(file, "enable");
|
|
str = get_file_content(enable);
|
|
if (!str)
|
|
goto out;
|
|
|
|
if (*str == '1') {
|
|
if (!system) {
|
|
if (!*processed)
|
|
printf(" Individual systems:\n");
|
|
printf( " %s\n", name);
|
|
*processed = PROCESSED_SYSTEM;
|
|
} else {
|
|
if (!*processed) {
|
|
printf(" Individual events:\n");
|
|
*processed = PROCESSED_SYSTEM;
|
|
}
|
|
if (*processed == PROCESSED_SYSTEM) {
|
|
printf(" %s\n", system);
|
|
*processed = PROCESSED_EVENT;
|
|
}
|
|
printf( " %s\n", name);
|
|
}
|
|
}
|
|
free(str);
|
|
|
|
out:
|
|
free(enable);
|
|
free(file);
|
|
}
|
|
|
|
static void report_events(struct buffer_instance *instance)
|
|
{
|
|
struct event_iter *iter;
|
|
char *str;
|
|
char *cont;
|
|
char *path;
|
|
char *system;
|
|
enum event_iter_type type;
|
|
enum event_process processed = PROCESSED_NONE;
|
|
enum event_process processed_part = PROCESSED_NONE;
|
|
|
|
str = get_instance_file_content(instance, "events/enable");
|
|
if (!str)
|
|
return;
|
|
|
|
cont = strstrip(str);
|
|
|
|
printf("\nEvents:\n");
|
|
|
|
switch(*cont) {
|
|
case '1':
|
|
printf(" All enabled\n");
|
|
free(str);
|
|
return;
|
|
case '0':
|
|
printf(" All disabled\n");
|
|
free(str);
|
|
return;
|
|
}
|
|
|
|
free(str);
|
|
|
|
path = tracefs_instance_get_file(instance->tracefs, "events");
|
|
if (!path)
|
|
die("malloc");
|
|
|
|
iter = trace_event_iter_alloc(path);
|
|
|
|
while (trace_event_iter_next(iter, path, NULL)) {
|
|
process_event_enable(path, NULL, iter->system_dent->d_name, &processed);
|
|
}
|
|
|
|
reset_event_iter(iter);
|
|
|
|
system = NULL;
|
|
while ((type = trace_event_iter_next(iter, path, system))) {
|
|
|
|
if (type == EVENT_ITER_SYSTEM) {
|
|
|
|
/* Only process systems that are not fully enabled */
|
|
if (!process_individual_events(path, iter))
|
|
continue;
|
|
|
|
system = iter->system_dent->d_name;
|
|
if (processed_part)
|
|
processed_part = PROCESSED_SYSTEM;
|
|
continue;
|
|
}
|
|
|
|
process_event_enable(path, iter->system_dent->d_name,
|
|
iter->event_dent->d_name, &processed_part);
|
|
}
|
|
|
|
trace_event_iter_free(iter);
|
|
|
|
if (!processed && !processed_part)
|
|
printf(" (none enabled)\n");
|
|
|
|
tracefs_put_tracing_file(path);
|
|
}
|
|
|
|
static void
|
|
process_event_filter(char *path, struct event_iter *iter, enum event_process *processed)
|
|
{
|
|
const char *system = iter->system_dent->d_name;
|
|
const char *event = iter->event_dent->d_name;
|
|
struct stat st;
|
|
char *filter = NULL;
|
|
char *file;
|
|
char *str;
|
|
char *cont;
|
|
|
|
path = append_file(path, system);
|
|
file = append_file(path, event);
|
|
free(path);
|
|
|
|
stat(file, &st);
|
|
if (!S_ISDIR(st.st_mode))
|
|
goto out;
|
|
|
|
filter = append_file(file, "filter");
|
|
str = get_file_content(filter);
|
|
if (!str)
|
|
goto out;
|
|
|
|
cont = strstrip(str);
|
|
|
|
if (strcmp(cont, "none") == 0) {
|
|
free(str);
|
|
goto out;
|
|
}
|
|
|
|
if (!*processed)
|
|
printf("\nFilters:\n");
|
|
printf( " %s:%s \"%s\"\n", system, event, cont);
|
|
*processed = PROCESSED_SYSTEM;
|
|
free(str);
|
|
|
|
out:
|
|
free(filter);
|
|
free(file);
|
|
}
|
|
|
|
static void report_event_filters(struct buffer_instance *instance)
|
|
{
|
|
struct event_iter *iter;
|
|
char *path;
|
|
char *system;
|
|
enum event_iter_type type;
|
|
enum event_process processed = PROCESSED_NONE;
|
|
|
|
path = tracefs_instance_get_file(instance->tracefs, "events");
|
|
if (!path)
|
|
die("malloc");
|
|
|
|
iter = trace_event_iter_alloc(path);
|
|
|
|
processed = PROCESSED_NONE;
|
|
system = NULL;
|
|
while ((type = trace_event_iter_next(iter, path, system))) {
|
|
|
|
if (type == EVENT_ITER_SYSTEM) {
|
|
system = iter->system_dent->d_name;
|
|
continue;
|
|
}
|
|
|
|
process_event_filter(path, iter, &processed);
|
|
}
|
|
|
|
trace_event_iter_free(iter);
|
|
|
|
tracefs_put_tracing_file(path);
|
|
}
|
|
|
|
static void
|
|
process_event_trigger(char *path, struct event_iter *iter, enum event_process *processed)
|
|
{
|
|
const char *system = iter->system_dent->d_name;
|
|
const char *event = iter->event_dent->d_name;
|
|
struct stat st;
|
|
char *trigger = NULL;
|
|
char *file;
|
|
char *str;
|
|
char *cont;
|
|
|
|
path = append_file(path, system);
|
|
file = append_file(path, event);
|
|
free(path);
|
|
|
|
stat(file, &st);
|
|
if (!S_ISDIR(st.st_mode))
|
|
goto out;
|
|
|
|
trigger = append_file(file, "trigger");
|
|
str = get_file_content(trigger);
|
|
if (!str)
|
|
goto out;
|
|
|
|
cont = strstrip(str);
|
|
|
|
if (cont[0] == '#') {
|
|
free(str);
|
|
goto out;
|
|
}
|
|
|
|
if (!*processed)
|
|
printf("\nTriggers:\n");
|
|
printf( " %s:%s \"%s\"\n", system, event, cont);
|
|
*processed = PROCESSED_SYSTEM;
|
|
free(str);
|
|
|
|
out:
|
|
free(trigger);
|
|
free(file);
|
|
}
|
|
|
|
static void report_event_triggers(struct buffer_instance *instance)
|
|
{
|
|
struct event_iter *iter;
|
|
char *path;
|
|
char *system;
|
|
enum event_iter_type type;
|
|
enum event_process processed = PROCESSED_NONE;
|
|
|
|
path = tracefs_instance_get_file(instance->tracefs, "events");
|
|
if (!path)
|
|
die("malloc");
|
|
|
|
iter = trace_event_iter_alloc(path);
|
|
|
|
processed = PROCESSED_NONE;
|
|
system = NULL;
|
|
while ((type = trace_event_iter_next(iter, path, system))) {
|
|
|
|
if (type == EVENT_ITER_SYSTEM) {
|
|
system = iter->system_dent->d_name;
|
|
continue;
|
|
}
|
|
|
|
process_event_trigger(path, iter, &processed);
|
|
}
|
|
|
|
trace_event_iter_free(iter);
|
|
|
|
tracefs_put_tracing_file(path);
|
|
}
|
|
|
|
enum func_states {
|
|
FUNC_STATE_START,
|
|
FUNC_STATE_SKIP,
|
|
FUNC_STATE_PRINT,
|
|
};
|
|
|
|
static void list_functions(const char *path, char *string)
|
|
{
|
|
enum func_states state;
|
|
struct stat st;
|
|
char *str;
|
|
int ret = 0;
|
|
int len;
|
|
int i;
|
|
int first = 0;
|
|
|
|
/* Ignore if it does not exist. */
|
|
ret = stat(path, &st);
|
|
if (ret < 0)
|
|
return;
|
|
|
|
str = get_file_content(path);
|
|
if (!str)
|
|
return;
|
|
|
|
len = strlen(str);
|
|
|
|
state = FUNC_STATE_START;
|
|
|
|
/* Skip all lines that start with '#' */
|
|
for (i = 0; i < len; i++) {
|
|
|
|
if (state == FUNC_STATE_PRINT)
|
|
putchar(str[i]);
|
|
|
|
if (str[i] == '\n') {
|
|
state = FUNC_STATE_START;
|
|
continue;
|
|
}
|
|
|
|
if (state == FUNC_STATE_SKIP)
|
|
continue;
|
|
|
|
if (state == FUNC_STATE_START && str[i] == '#') {
|
|
state = FUNC_STATE_SKIP;
|
|
continue;
|
|
}
|
|
|
|
if (!first) {
|
|
printf("\n%s:\n", string);
|
|
first = 1;
|
|
}
|
|
|
|
if (state != FUNC_STATE_PRINT) {
|
|
state = FUNC_STATE_PRINT;
|
|
printf(" ");
|
|
putchar(str[i]);
|
|
}
|
|
}
|
|
free(str);
|
|
}
|
|
|
|
static void report_graph_funcs(struct buffer_instance *instance)
|
|
{
|
|
char *path;
|
|
|
|
path = tracefs_instance_get_file(instance->tracefs, "set_graph_function");
|
|
if (!path)
|
|
die("malloc");
|
|
|
|
list_functions(path, "Function Graph Filter");
|
|
|
|
tracefs_put_tracing_file(path);
|
|
|
|
path = tracefs_instance_get_file(instance->tracefs, "set_graph_notrace");
|
|
if (!path)
|
|
die("malloc");
|
|
|
|
list_functions(path, "Function Graph No Trace");
|
|
|
|
tracefs_put_tracing_file(path);
|
|
}
|
|
|
|
static void report_ftrace_filters(struct buffer_instance *instance)
|
|
{
|
|
char *path;
|
|
|
|
path = tracefs_instance_get_file(instance->tracefs, "set_ftrace_filter");
|
|
if (!path)
|
|
die("malloc");
|
|
|
|
list_functions(path, "Function Filter");
|
|
|
|
tracefs_put_tracing_file(path);
|
|
|
|
path = tracefs_instance_get_file(instance->tracefs, "set_ftrace_notrace");
|
|
if (!path)
|
|
die("malloc");
|
|
|
|
list_functions(path, "Function No Trace");
|
|
|
|
tracefs_put_tracing_file(path);
|
|
}
|
|
|
|
static void report_buffers(struct buffer_instance *instance)
|
|
{
|
|
#define FILE_SIZE 100
|
|
char *str;
|
|
char *cont;
|
|
char file[FILE_SIZE];
|
|
int cpu;
|
|
|
|
str = get_instance_file_content(instance, "buffer_size_kb");
|
|
if (!str)
|
|
return;
|
|
|
|
cont = strstrip(str);
|
|
|
|
/* If it's not expanded yet, just skip */
|
|
if (strstr(cont, "expanded") != NULL)
|
|
goto out;
|
|
|
|
if (strcmp(cont, "X") != 0) {
|
|
printf("\nBuffer size in kilobytes (per cpu):\n");
|
|
printf(" %s\n", str);
|
|
goto total;
|
|
}
|
|
|
|
/* Read the sizes of each CPU buffer */
|
|
for (cpu = 0; ; cpu++) {
|
|
|
|
snprintf(file, FILE_SIZE, "per_cpu/cpu%d/buffer_size_kb", cpu);
|
|
str = get_instance_file_content(instance, file);
|
|
if (!str)
|
|
break;
|
|
|
|
cont = strstrip(str);
|
|
if (!cpu)
|
|
putchar('\n');
|
|
|
|
printf("CPU %d buffer size (kb): %s\n", cpu, cont);
|
|
free(str);
|
|
}
|
|
|
|
total:
|
|
free(str);
|
|
|
|
str = get_instance_file_content(instance, "buffer_total_size_kb");
|
|
if (!str)
|
|
return;
|
|
|
|
cont = strstrip(str);
|
|
printf("\nBuffer total size in kilobytes:\n");
|
|
printf(" %s\n", str);
|
|
|
|
out:
|
|
free(str);
|
|
}
|
|
|
|
static void report_clock(struct buffer_instance *instance)
|
|
{
|
|
struct tracefs_instance *tracefs = instance ? instance->tracefs : NULL;
|
|
char *clock;
|
|
|
|
clock = tracefs_get_clock(tracefs);
|
|
|
|
/* Default clock is "local", only show others */
|
|
if (clock && strcmp(clock, "local") != 0)
|
|
printf("\nClock: %s\n", clock);
|
|
|
|
free(clock);
|
|
}
|
|
|
|
static void report_cpumask(struct buffer_instance *instance)
|
|
{
|
|
char *str;
|
|
char *cont;
|
|
int cpus;
|
|
int n;
|
|
int i;
|
|
|
|
str = get_instance_file_content(instance, "tracing_cpumask");
|
|
if (!str)
|
|
return;
|
|
|
|
cont = strstrip(str);
|
|
|
|
/* check to make sure all CPUs on this machine are set */
|
|
cpus = tracecmd_count_cpus();
|
|
|
|
for (i = strlen(cont) - 1; i >= 0 && cpus > 0; i--) {
|
|
if (cont[i] == ',')
|
|
continue;
|
|
|
|
if (cont[i] == 'f') {
|
|
cpus -= 4;
|
|
continue;
|
|
}
|
|
|
|
if (cpus >= 4)
|
|
break;
|
|
|
|
if (cont[i] >= '0' && cont[i] <= '9')
|
|
n = cont[i] - '0';
|
|
else
|
|
n = 10 + (cont[i] - 'a');
|
|
|
|
while (cpus > 0) {
|
|
if (!(n & 1))
|
|
break;
|
|
n >>= 1;
|
|
cpus--;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* If cpus is greater than zero, one isn't set */
|
|
if (cpus > 0)
|
|
printf("\nCPU mask: %s\n", cont);
|
|
|
|
free(str);
|
|
}
|
|
|
|
static void report_probes(struct buffer_instance *instance,
|
|
const char *file, const char *string)
|
|
{
|
|
char *str;
|
|
char *cont;
|
|
int newline;
|
|
int i;
|
|
|
|
str = get_instance_file_content(instance, file);
|
|
if (!str)
|
|
return;
|
|
|
|
cont = strstrip(str);
|
|
if (strlen(cont) == 0)
|
|
goto out;
|
|
|
|
printf("\n%s:\n", string);
|
|
|
|
newline = 1;
|
|
for (i = 0; cont[i]; i++) {
|
|
if (newline)
|
|
printf(" ");
|
|
putchar(cont[i]);
|
|
if (cont[i] == '\n')
|
|
newline = 1;
|
|
else
|
|
newline = 0;
|
|
}
|
|
putchar('\n');
|
|
out:
|
|
free(str);
|
|
}
|
|
|
|
static void report_kprobes(struct buffer_instance *instance)
|
|
{
|
|
report_probes(instance, "kprobe_events", "Kprobe events");
|
|
}
|
|
|
|
static void report_uprobes(struct buffer_instance *instance)
|
|
{
|
|
report_probes(instance, "uprobe_events", "Uprobe events");
|
|
}
|
|
|
|
static void report_traceon(struct buffer_instance *instance)
|
|
{
|
|
char *str;
|
|
char *cont;
|
|
|
|
str = get_instance_file_content(instance, "tracing_on");
|
|
if (!str)
|
|
return;
|
|
|
|
cont = strstrip(str);
|
|
|
|
/* double newline as this is the last thing printed */
|
|
if (strcmp(cont, "0") == 0)
|
|
printf("\nTracing is disabled\n\n");
|
|
else
|
|
printf("\nTracing is enabled\n\n");
|
|
|
|
free(str);
|
|
}
|
|
|
|
static void stat_instance(struct buffer_instance *instance, bool opt)
|
|
{
|
|
if (instance != &top_instance) {
|
|
if (instance != first_instance)
|
|
printf("---------------\n");
|
|
printf("Instance: %s\n",
|
|
tracefs_instance_get_name(instance->tracefs));
|
|
}
|
|
|
|
report_file(instance, "current_tracer", "nop", "Tracer: ");
|
|
report_events(instance);
|
|
report_event_filters(instance);
|
|
report_event_triggers(instance);
|
|
report_ftrace_filters(instance);
|
|
report_graph_funcs(instance);
|
|
report_buffers(instance);
|
|
report_clock(instance);
|
|
report_cpumask(instance);
|
|
report_file(instance, "tracing_max_latency", "0", "Max Latency: ");
|
|
report_kprobes(instance);
|
|
report_uprobes(instance);
|
|
report_file(instance, "set_event_pid", "", "Filtered event PIDs:\n");
|
|
report_file(instance, "set_ftrace_pid", "no pid",
|
|
"Filtered function tracer PIDs:\n");
|
|
if (opt) {
|
|
printf("\nOptions:\n");
|
|
show_options(" ", instance);
|
|
}
|
|
report_traceon(instance);
|
|
report_file(instance, "error_log", "", "Error log:\n");
|
|
if (instance == &top_instance)
|
|
report_instances();
|
|
}
|
|
|
|
void trace_stat (int argc, char **argv)
|
|
{
|
|
struct buffer_instance *instance = &top_instance;
|
|
bool opt = false;
|
|
int topt = 0;
|
|
int status;
|
|
int c;
|
|
|
|
init_top_instance();
|
|
|
|
for (;;) {
|
|
c = getopt(argc-1, argv+1, "htoB:");
|
|
if (c == -1)
|
|
break;
|
|
switch (c) {
|
|
case 'h':
|
|
usage(argv);
|
|
break;
|
|
case 'B':
|
|
instance = allocate_instance(optarg);
|
|
if (!instance)
|
|
die("Failed to create instance");
|
|
add_instance(instance, tracecmd_count_cpus());
|
|
/* top instance requires direct access */
|
|
if (!topt && is_top_instance(first_instance))
|
|
first_instance = instance;
|
|
break;
|
|
case 't':
|
|
/* Force to use top instance */
|
|
topt = 1;
|
|
instance = &top_instance;
|
|
break;
|
|
case 'o':
|
|
opt = 1;
|
|
break;
|
|
default:
|
|
usage(argv);
|
|
}
|
|
}
|
|
|
|
update_first_instance(instance, topt);
|
|
|
|
for_all_instances(instance) {
|
|
stat_instance(instance, opt);
|
|
}
|
|
|
|
if (tracecmd_stack_tracer_status(&status) >= 0) {
|
|
if (status > 0)
|
|
printf("Stack tracing is enabled\n\n");
|
|
} else {
|
|
printf("Error reading stack tracer status\n\n");
|
|
}
|
|
|
|
exit(0);
|
|
}
|