aboutsummaryrefslogtreecommitdiff
path: root/tracecmd/trace-stack.c
diff options
context:
space:
mode:
Diffstat (limited to 'tracecmd/trace-stack.c')
-rw-r--r--tracecmd/trace-stack.c216
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;
+}