1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
4 *
5 */
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <limits.h>
10 #include <getopt.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <sys/wait.h>
14 #include <fcntl.h>
15 #include <unistd.h>
16 #include <errno.h>
17
18 #include "tracefs.h"
19 #include "trace-local.h"
20
21 #define PROC_FILE "/proc/sys/kernel/stack_tracer_enabled"
22
23 enum stack_type {
24 STACK_START,
25 STACK_STOP,
26 STACK_RESET,
27 STACK_REPORT
28 };
29
test_available(void)30 static void test_available(void)
31 {
32 struct stat buf;
33 int fd;
34
35 fd = stat(PROC_FILE, &buf);
36 if (fd < 0)
37 die("stack tracer not configured on running kernel");
38 }
39
40 /* NOTE: this implementation only accepts new_status in the range [0..9]. */
change_stack_tracer_status(unsigned new_status)41 static void change_stack_tracer_status(unsigned new_status)
42 {
43 char buf[1];
44 int status;
45 int ret;
46 int fd;
47 int n;
48
49 if (new_status > 9) {
50 warning("invalid status %d\n", new_status);
51 return;
52 }
53
54 ret = tracecmd_stack_tracer_status(&status);
55 if (ret < 0)
56 die("error reading %s", PROC_FILE);
57
58 if (ret > 0 && status == new_status)
59 return; /* nothing to do */
60
61 fd = open(PROC_FILE, O_WRONLY);
62 if (fd < 0)
63 die("writing %s", PROC_FILE);
64
65 buf[0] = new_status + '0';
66
67 n = write(fd, buf, 1);
68 if (n < 0)
69 die("writing into %s", PROC_FILE);
70 close(fd);
71 }
72
start_trace(void)73 static void start_trace(void)
74 {
75 change_stack_tracer_status(1);
76 }
77
stop_trace(void)78 static void stop_trace(void)
79 {
80 change_stack_tracer_status(0);
81 }
82
reset_trace(void)83 static void reset_trace(void)
84 {
85 char *path;
86 char buf[1];
87 int fd;
88 int n;
89
90 path = tracefs_get_tracing_file("stack_max_size");
91 fd = open(path, O_WRONLY);
92 if (fd < 0)
93 die("writing %s", path);
94
95 buf[0] = '0';
96 n = write(fd, buf, 1);
97 if (n < 0)
98 die("writing into %s", path);
99 tracefs_put_tracing_file(path);
100 close(fd);
101 }
102
read_trace(void)103 static void read_trace(void)
104 {
105 char *buf = NULL;
106 int status;
107 char *path;
108 FILE *fp;
109 size_t n;
110 int r;
111
112 if (tracecmd_stack_tracer_status(&status) <= 0)
113 die("Invalid stack tracer state");
114
115 if (status > 0)
116 printf("(stack tracer running)\n");
117 else
118 printf("(stack tracer not running)\n");
119
120 path = tracefs_get_tracing_file("stack_trace");
121 fp = fopen(path, "r");
122 if (!fp)
123 die("reading to '%s'", path);
124 tracefs_put_tracing_file(path);
125
126 while ((r = getline(&buf, &n, fp)) >= 0) {
127 /*
128 * Skip any line that starts with a '#'.
129 * Those talk about how to enable stack tracing
130 * within the debugfs system. We don't care about that.
131 */
132 if (buf[0] != '#')
133 printf("%s", buf);
134
135 free(buf);
136 buf = NULL;
137 }
138
139 fclose(fp);
140 }
141
142 enum {
143 OPT_verbose = 252,
144 OPT_reset = 253,
145 OPT_stop = 254,
146 OPT_start = 255,
147 };
148
trace_stack(int argc,char ** argv)149 void trace_stack (int argc, char **argv)
150 {
151 enum stack_type trace_type = STACK_REPORT;
152 int c;
153
154 if (argc < 2)
155 usage(argv);
156
157 if (strcmp(argv[1], "stack") != 0)
158 usage(argv);
159
160 for (;;) {
161 int option_index = 0;
162 static struct option long_options[] = {
163 {"start", no_argument, NULL, OPT_start},
164 {"stop", no_argument, NULL, OPT_stop},
165 {"reset", no_argument, NULL, OPT_reset},
166 {"help", no_argument, NULL, '?'},
167 {"verbose", optional_argument, NULL, OPT_verbose},
168 {NULL, 0, NULL, 0}
169 };
170
171 c = getopt_long (argc-1, argv+1, "+h?",
172 long_options, &option_index);
173 if (c == -1)
174 break;
175
176 switch (c) {
177 case 'h':
178 usage(argv);
179 break;
180 case OPT_start:
181 trace_type = STACK_START;
182 break;
183 case OPT_stop:
184 trace_type = STACK_STOP;
185 break;
186 case OPT_reset:
187 trace_type = STACK_RESET;
188 break;
189 case OPT_verbose:
190 if (trace_set_verbose(optarg) < 0)
191 die("invalid verbose level %s", optarg);
192 break;
193 default:
194 usage(argv);
195 }
196 }
197
198 test_available();
199
200 switch (trace_type) {
201 case STACK_START:
202 start_trace();
203 break;
204 case STACK_STOP:
205 stop_trace();
206 break;
207 case STACK_RESET:
208 reset_trace();
209 break;
210 default:
211 read_trace();
212 break;
213 }
214
215 return;
216 }
217