• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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