diff options
Diffstat (limited to 'tracecmd/trace-stack.c')
-rw-r--r-- | tracecmd/trace-stack.c | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/tracecmd/trace-stack.c b/tracecmd/trace-stack.c new file mode 100644 index 00000000..80364949 --- /dev/null +++ b/tracecmd/trace-stack.c @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> + * + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <getopt.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> + +#include "tracefs.h" +#include "trace-local.h" + +#define PROC_FILE "/proc/sys/kernel/stack_tracer_enabled" + +enum stack_type { + STACK_START, + STACK_STOP, + STACK_RESET, + STACK_REPORT +}; + +static void test_available(void) +{ + struct stat buf; + int fd; + + fd = stat(PROC_FILE, &buf); + if (fd < 0) + die("stack tracer not configured on running kernel"); +} + +/* NOTE: this implementation only accepts new_status in the range [0..9]. */ +static void change_stack_tracer_status(unsigned new_status) +{ + char buf[1]; + int status; + int ret; + int fd; + int n; + + if (new_status > 9) { + warning("invalid status %d\n", new_status); + return; + } + + ret = tracecmd_stack_tracer_status(&status); + if (ret < 0) + die("error reading %s", PROC_FILE); + + if (ret > 0 && status == new_status) + return; /* nothing to do */ + + fd = open(PROC_FILE, O_WRONLY); + if (fd < 0) + die("writing %s", PROC_FILE); + + buf[0] = new_status + '0'; + + n = write(fd, buf, 1); + if (n < 0) + die("writing into %s", PROC_FILE); + close(fd); +} + +static void start_trace(void) +{ + change_stack_tracer_status(1); +} + +static void stop_trace(void) +{ + change_stack_tracer_status(0); +} + +static void reset_trace(void) +{ + char *path; + char buf[1]; + int fd; + int n; + + path = tracefs_get_tracing_file("stack_max_size"); + fd = open(path, O_WRONLY); + if (fd < 0) + die("writing %s", path); + + buf[0] = '0'; + n = write(fd, buf, 1); + if (n < 0) + die("writing into %s", path); + tracefs_put_tracing_file(path); + close(fd); +} + +static void read_trace(void) +{ + char *buf = NULL; + int status; + char *path; + FILE *fp; + size_t n; + int r; + + if (tracecmd_stack_tracer_status(&status) <= 0) + die("Invalid stack tracer state"); + + if (status > 0) + printf("(stack tracer running)\n"); + else + printf("(stack tracer not running)\n"); + + path = tracefs_get_tracing_file("stack_trace"); + fp = fopen(path, "r"); + if (!fp) + die("reading to '%s'", path); + tracefs_put_tracing_file(path); + + while ((r = getline(&buf, &n, fp)) >= 0) { + /* + * Skip any line that starts with a '#'. + * Those talk about how to enable stack tracing + * within the debugfs system. We don't care about that. + */ + if (buf[0] != '#') + printf("%s", buf); + + free(buf); + buf = NULL; + } + + fclose(fp); +} + +enum { + OPT_verbose = 252, + OPT_reset = 253, + OPT_stop = 254, + OPT_start = 255, +}; + +void trace_stack (int argc, char **argv) +{ + enum stack_type trace_type = STACK_REPORT; + int c; + + if (argc < 2) + usage(argv); + + if (strcmp(argv[1], "stack") != 0) + usage(argv); + + for (;;) { + int option_index = 0; + static struct option long_options[] = { + {"start", no_argument, NULL, OPT_start}, + {"stop", no_argument, NULL, OPT_stop}, + {"reset", no_argument, NULL, OPT_reset}, + {"help", no_argument, NULL, '?'}, + {"verbose", optional_argument, NULL, OPT_verbose}, + {NULL, 0, NULL, 0} + }; + + c = getopt_long (argc-1, argv+1, "+h?", + long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 'h': + usage(argv); + break; + case OPT_start: + trace_type = STACK_START; + break; + case OPT_stop: + trace_type = STACK_STOP; + break; + case OPT_reset: + trace_type = STACK_RESET; + break; + case OPT_verbose: + if (trace_set_verbose(optarg) < 0) + die("invalid verbose level %s", optarg); + break; + default: + usage(argv); + } + } + + test_available(); + + switch (trace_type) { + case STACK_START: + start_trace(); + break; + case STACK_STOP: + stop_trace(); + break; + case STACK_RESET: + reset_trace(); + break; + default: + read_trace(); + break; + } + + return; +} |