1 /*
2  * Copyright (C) 2013-2021 Canonical, Ltd.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * This code is a complete clean re-write of the stress tool by
19  * Colin Ian King <colin.king@canonical.com> and attempts to be
20  * backwardly compatible with the stress tool by Amos Waterland
21  * <apw@rossby.metr.ou.edu> but has more stress tests and more
22  * functionality.
23  *
24  */
25 #include "stress-ng.h"
26 
27 #if defined(HAVE_LIB_BSD) &&	\
28     defined(__linux__)
29 
30 #define MAX_MOUNTS	(256)
31 #if !defined(DEBUGFS_MAGIC)
32 #define DEBUGFS_MAGIC	(0x64626720)
33 #endif
34 
35 struct rb_node {
36 	RB_ENTRY(rb_node) rb;	/* red/black node entry */
37 	char *func_name;	/* ftrace'd kernel function name */
38 	int64_t start_count;	/* start number of calls to func */
39 	int64_t end_count;	/* end number of calls to func */
40 	double	start_time_us;	/* start time used by func in microsecs */
41 	double	end_time_us;	/* end time used by func microsecs */
42 };
43 
44 static bool tracing_enabled;
45 
46 /*
47  *  rb_node_cmp()
48  *	used for sorting functions by name
49  */
rb_node_cmp(struct rb_node * n1,struct rb_node * n2)50 static int rb_node_cmp(struct rb_node *n1, struct rb_node *n2)
51 {
52 	return strcmp(n1->func_name, n2->func_name);
53 }
54 
55 static RB_HEAD(rb_tree, rb_node) rb_root;
56 RB_PROTOTYPE(rb_tree, rb_node, rb, rb_node_cmp);
57 RB_GENERATE(rb_tree, rb_node, rb, rb_node_cmp);
58 
59 /*
60  *  stress_ftrace_get_debugfs_path()
61  *	find debugfs mount path, returns NULL if not found
62  */
stress_ftrace_get_debugfs_path(void)63 static char *stress_ftrace_get_debugfs_path(void)
64 {
65 	int i, n;
66 	char *mnts[MAX_MOUNTS];
67 	static char debugfs_path[1024];
68 
69 	/* Cached copy */
70 	if (*debugfs_path)
71 		return debugfs_path;
72 
73 	*debugfs_path = '\0';
74 	n = stress_mount_get(mnts, MAX_MOUNTS);
75 	for (i = 0; i < n; i++) {
76 		struct statfs buf;
77 
78 		(void)memset(&buf, 0, sizeof(buf));
79 		if (statfs(mnts[i], &buf) < 0)
80 			continue;
81 		if (buf.f_type == DEBUGFS_MAGIC) {
82 			shim_strlcpy(debugfs_path, mnts[i], sizeof(debugfs_path));
83 			stress_mount_free(mnts, n);
84 			return debugfs_path;
85 		}
86 	}
87 	stress_mount_free(mnts, n);
88 
89 	return NULL;
90 }
91 
92 /*
93  *  stress_ftrace_free()
94  *	free up rb tree
95  */
stress_ftrace_free(void)96 void stress_ftrace_free(void)
97 {
98 	struct rb_node *tn, *next;
99 
100 	if (!(g_opt_flags & OPT_FLAGS_FTRACE))
101 		return;
102 
103 	for (tn = RB_MIN(rb_tree, &rb_root); tn; tn = next) {
104 		free(tn->func_name);
105                 next = RB_NEXT(rb_tree, &rb_root, tn);
106                 RB_REMOVE(rb_tree, &rb_root, tn);
107 		free(tn);
108 	}
109 	RB_INIT(&rb_root);
110 }
111 
112 /*
113  *  stress_ftrace_parse_trace_stat_file()
114  *	parse the ftrace files for function timing stats
115  */
stress_ftrace_parse_trace_stat_file(const char * path,const bool start)116 static int stress_ftrace_parse_trace_stat_file(const char *path, const bool start)
117 {
118 	FILE *fp;
119 	char buffer[4096];
120 
121 	fp = fopen(path, "r");
122 	if (!fp)
123 		return 0;
124 
125 	while (fgets(buffer, sizeof(buffer), fp) != NULL) {
126 		struct rb_node *tn, node;
127 		char *ptr, *func_name, *num = "0";
128 		int64_t count;
129 		double time_us;
130 
131 		if (strstr(buffer, "Function"))
132 			continue;
133 		if (strstr(buffer, "----"))
134 			continue;
135 
136 		/*
137 		 *  Skip over leading spaces and find function name
138 		 */
139 		for (ptr = buffer; *ptr && isspace(*ptr); ptr++)
140 			;
141 		if (!*ptr)
142 			continue;
143 		func_name = ptr;
144 
145 		/*
146 		 *  Skip over leading spaces and find hit count
147 		 */
148 		for (; *ptr && !isspace(*ptr); ptr++)
149 			;
150 		if (!*ptr)
151 			continue;
152 		*ptr++ = '\0';
153 		for (; *ptr && isspace(*ptr); ptr++)
154 			;
155 		num = ptr;
156 		for (; *ptr && !isspace(*ptr); ptr++)
157 			;
158 		if (!*ptr)
159 			continue;
160 		*ptr++ = '\0';
161 		count = (int64_t)atoll(num);
162 
163 		/*
164 		 *  Skip over leading spaces and find time consumed
165 		 */
166 		for (; *ptr && isspace(*ptr); ptr++)
167 			;
168 		if (!*ptr)
169 			continue;
170 		if (sscanf(ptr, "%lf", &time_us) != 1)
171 			time_us = 0.0;
172 
173 		node.func_name = func_name;
174 
175 		tn = RB_FIND(rb_tree, &rb_root, &node);
176 		if (tn) {
177 			if (start) {
178 				tn->start_count += count;
179 				tn->start_time_us += time_us;
180 			} else {
181 				tn->end_count += count;
182 				tn->end_time_us += time_us;
183 			}
184 		} else {
185 			tn = malloc(sizeof(*tn));
186 			if (!tn)
187 				goto memory_fail;
188 			tn->func_name = strdup(func_name);
189 			if (!tn->func_name) {
190 				free(tn);
191 				goto memory_fail;
192 			}
193 			tn->start_count = 0;
194 			tn->end_count = 0;
195 			tn->start_time_us = 0.0;
196 			tn->end_time_us = 0.0;
197 
198 			if (start) {
199 				tn->start_count = count;
200 				tn->start_time_us = time_us;
201 			} else {
202 				tn->end_count = count;
203 				tn->end_time_us = time_us;
204 			}
205 			RB_INSERT(rb_tree, &rb_root, tn);
206 		}
207 	}
208 	(void)fclose(fp);
209 	return 0;
210 
211 memory_fail:
212 	(void)fclose(fp);
213 	pr_inf("ftrace: disabled, out of memory collecting function information\n");
214 	stress_ftrace_free();
215 	return -1;
216 }
217 
218 /*
219  *  stress_ftrace_parse_stat_files()
220  *	read trace stat files and parse the data into the rb tree
221  */
stress_ftrace_parse_stat_files(const char * path,const bool start)222 static int stress_ftrace_parse_stat_files(const char *path, const bool start)
223 {
224 	DIR *dp;
225 	struct dirent *de;
226 	char filename[PATH_MAX];
227 
228 	(void)snprintf(filename, sizeof(filename), "%s/tracing/trace_stat", path);
229 	dp = opendir(filename);
230 	if (!dp)
231 		return -1;
232 	while ((de = readdir(dp)) != NULL) {
233 		if (strncmp(de->d_name, "function", 8) == 0) {
234 			char funcfile[PATH_MAX];
235 
236 			(void)snprintf(funcfile, sizeof(funcfile),
237 				"%s/tracing/trace_stat/%s", path, de->d_name);
238 			stress_ftrace_parse_trace_stat_file(funcfile, start);
239 		}
240 	}
241 	(void)closedir(dp);
242 
243 	return 0;
244 }
245 
246 /*
247  *  stress_ftrace_add_pid()
248  *	enable/append/stop tracing on specific events.
249  *	if pid < 0 then tracing pids are all removed otherwise
250  *	the pid is added to the tracing events
251  */
stress_ftrace_add_pid(const pid_t pid)252 void stress_ftrace_add_pid(const pid_t pid)
253 {
254 	char filename[PATH_MAX];
255 	char *path;
256 	char buffer[32];
257 	int fd;
258 	ssize_t ret;
259 
260 	if (!(g_opt_flags & OPT_FLAGS_FTRACE))
261 		return;
262 
263 	path = stress_ftrace_get_debugfs_path();
264 	if (!path)
265 		return;
266 
267 	(void)snprintf(filename, sizeof(filename), "%s/tracing/set_ftrace_pid", path);
268 	fd = open(filename, O_WRONLY | (pid < 0 ? O_TRUNC :  O_APPEND));
269 	if (fd < 0)
270 		return;
271 	if (pid == -1) {
272 		strcpy(buffer, " ");
273 	} else {
274 		snprintf(buffer, sizeof(buffer), "%d", (int)pid);
275 	}
276 	ret = write(fd, buffer, strlen(buffer));
277 	(void)ret;
278 	(void)close(fd);
279 }
280 
281 /*
282  *  stress_ftrace_start()
283  *	start ftracing function calls
284  */
stress_ftrace_start(void)285 int stress_ftrace_start(void)
286 {
287 	char *path, filename[PATH_MAX];
288 
289 	if (!(g_opt_flags & OPT_FLAGS_FTRACE))
290 		return 0;
291 
292 	RB_INIT(&rb_root);
293 
294 	if (!stress_check_capability(SHIM_CAP_SYS_ADMIN)) {
295 		pr_inf("ftrace: requires CAP_SYS_ADMIN capability for tracing\n");
296 		return -1;
297 	}
298 
299 	path = stress_ftrace_get_debugfs_path();
300 	if (!path) {
301 		pr_inf("ftrace: cannot find a mounted debugfs\n");
302 		return -1;
303 	}
304 
305 	(void)snprintf(filename, sizeof(filename), "%s/tracing/function_profile_enabled", path);
306 	if (system_write(filename, "0", 1) < 0) {
307 		pr_inf("ftrace: cannot enable function profiling, errno=%d (%s)\n",
308 			errno, strerror(errno));
309 		return -1;
310 	}
311 	stress_ftrace_add_pid(-1);
312 	stress_ftrace_add_pid(getpid());
313 	(void)snprintf(filename, sizeof(filename), "%s/tracing/function_profile_enabled", path);
314 	if (system_write(filename, "1", 1) < 0) {
315 		pr_inf("ftrace: cannot enable function profiling, errno=%d (%s)\n",
316 			errno, strerror(errno));
317 		return -1;
318 	}
319 	if (stress_ftrace_parse_stat_files(path, true) < 0)
320 		return -1;
321 
322 	tracing_enabled = true;
323 
324 	return 0;
325 }
326 
327 /*
328  *  strace_ftrace_is_syscall()
329  *	return true if function name looks like a system call
330  */
strace_ftrace_is_syscall(const char * func_name)331 static inline bool strace_ftrace_is_syscall(const char *func_name)
332 {
333 	if (*func_name == '_' &&
334 	    strstr(func_name, "_sys_") &&
335 	    !strstr(func_name, "do_sys") &&
336 	    strncmp(func_name, "___", 3))
337 		return true;
338 
339 	return false;
340 }
341 
342 /*
343  *  stress_ftrace_start()
344  *	start ftracing function calls
345  */
stress_ftrace_analyze(void)346 static void stress_ftrace_analyze(void)
347 {
348 	struct rb_node *tn, *next;
349 	uint64_t sys_calls = 0, func_calls = 0;
350 
351 	pr_inf("ftrace: %-30.30s %15.15s %20.20s\n", "System Call", "Number of Calls", "Total Time (us)");
352 
353 	for (tn = RB_MIN(rb_tree, &rb_root); tn; tn = next) {
354 		int64_t count = tn->end_count - tn->start_count;
355 		if (count > 0) {
356 			func_calls++;
357 			if (strace_ftrace_is_syscall(tn->func_name)) {
358 				double time_us = tn->end_time_us -
359 						 tn->start_time_us;
360 
361 				pr_inf("ftrace: %-30.30s %15" PRIu64 " %20.2f\n", tn->func_name, count, time_us);
362 				sys_calls++;
363 			}
364 		}
365 
366                 next = RB_NEXT(rb_tree, &rb_root, tn);
367 	}
368 	pr_inf("ftrace: %" PRIu64 " kernel functions called, %" PRIu64 " were system calls\n",
369 		func_calls, sys_calls);
370 }
371 
372 /*
373  *  stress_ftrace_start()
374  *	stop ftracing function calls and analyze the collected
375  *	stats
376  */
stress_ftrace_stop(void)377 void stress_ftrace_stop(void)
378 {
379 	char *path, filename[PATH_MAX];
380 
381 	if (!(g_opt_flags & OPT_FLAGS_FTRACE))
382 		return;
383 
384 	if (!tracing_enabled)
385 		return;
386 
387 	path = stress_ftrace_get_debugfs_path();
388 	if (!path)
389 		return;
390 
391 	stress_ftrace_add_pid(-1);
392 	(void)snprintf(filename, sizeof(filename), "%s/tracing/function_profile_enabled", path);
393 	if (system_write(filename, "0", 1) < 0) {
394 		pr_inf("ftrace: cannot disable function profiling, errno=%d (%s)\n",
395 			errno, strerror(errno));
396 		return;
397 	}
398 
399 	(void)snprintf(filename, sizeof(filename), "%s/tracing/trace_stat", path);
400 	if (stress_ftrace_parse_stat_files(path, false) < 0)
401 		return;
402 	stress_ftrace_analyze();
403 }
404 
405 #else
stress_ftrace_add_pid(const pid_t pid)406 void stress_ftrace_add_pid(const pid_t pid)
407 {
408 	(void)pid;
409 }
410 
stress_ftrace_free(void)411 void stress_ftrace_free(void)
412 {
413 }
414 
stress_ftrace_start(void)415 int stress_ftrace_start(void)
416 {
417 	if (!(g_opt_flags & OPT_FLAGS_FTRACE))
418 		return 0;
419 	pr_inf("ftrace: this option is not implemented on this system: %s %s\n",
420 		stress_get_uname_info(), stress_get_compiler());
421 
422 	return 0;
423 }
424 
stress_ftrace_stop(void)425 void stress_ftrace_stop(void)
426 {
427 }
428 #endif
429