xref: /aosp_15_r20/external/bcc/libbpf-tools/klockstat.c (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
1 // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2 /* Copyright (c) 2021 Google LLC.
3  *
4  * Based on klockstat from BCC by Jiri Olsa and others
5  * 2021-10-26   Barret Rhoden   Created this.
6  */
7 /* Differences from BCC python tool:
8  * - can specify a lock by ksym name, using '-L'
9  * - tracks whichever task had the max time for acquire and hold, outputted
10  *     when '-s' > 1 (otherwise it's cluttered).
11  * - does not reset stats each interval by default. Can request with -R.
12  */
13 #ifndef _GNU_SOURCE
14 #define _GNU_SOURCE
15 #endif
16 #include <argp.h>
17 #include <errno.h>
18 #include <signal.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <time.h>
23 #include <unistd.h>
24 #include <sys/param.h>
25 
26 #include <bpf/libbpf.h>
27 #include <bpf/bpf.h>
28 #include "klockstat.h"
29 #include "klockstat.skel.h"
30 #include "compat.h"
31 #include "trace_helpers.h"
32 
33 #define warn(...) fprintf(stderr, __VA_ARGS__)
34 
35 enum {
36 	SORT_ACQ_MAX,
37 	SORT_ACQ_COUNT,
38 	SORT_ACQ_TOTAL,
39 	SORT_HLD_MAX,
40 	SORT_HLD_COUNT,
41 	SORT_HLD_TOTAL,
42 };
43 
44 static struct prog_env {
45 	pid_t pid;
46 	pid_t tid;
47 	char *caller;
48 	char *lock_name;
49 	unsigned int nr_locks;
50 	unsigned int nr_stack_entries;
51 	unsigned int sort_acq;
52 	unsigned int sort_hld;
53 	unsigned int duration;
54 	unsigned int interval;
55 	unsigned int iterations;
56 	bool reset;
57 	bool timestamp;
58 	bool verbose;
59 	bool per_thread;
60 } env = {
61 	.nr_locks = 99999999,
62 	.nr_stack_entries = 1,
63 	.sort_acq = SORT_ACQ_MAX,
64 	.sort_hld = SORT_HLD_MAX,
65 	.interval = 99999999,
66 	.iterations = 99999999,
67 };
68 
69 const char *argp_program_version = "klockstat 0.2";
70 const char *argp_program_bug_address =
71 	"https://github.com/iovisor/bcc/tree/master/libbpf-tools";
72 static const char args_doc[] = "FUNCTION";
73 static const char program_doc[] =
74 "Trace mutex/sem lock acquisition and hold times, in nsec\n"
75 "\n"
76 "Usage: klockstat [-hPRTv] [-p PID] [-t TID] [-c FUNC] [-L LOCK] [-n NR_LOCKS]\n"
77 "                 [-s NR_STACKS] [-S SORT] [-d DURATION] [-i INTERVAL]\n"
78 "\v"
79 "Examples:\n"
80 "  klockstat                     # trace system wide until ctrl-c\n"
81 "  klockstat -d 5                # trace for 5 seconds\n"
82 "  klockstat -i 5                # print stats every 5 seconds\n"
83 "  klockstat -p 181              # trace process 181 only\n"
84 "  klockstat -t 181              # trace thread 181 only\n"
85 "  klockstat -c pipe_            # print only for lock callers with 'pipe_'\n"
86 "                                # prefix\n"
87 "  klockstat -L cgroup_mutex     # trace the cgroup_mutex lock only (accepts addr too)\n"
88 "  klockstat -S acq_count        # sort lock acquired results by acquire count\n"
89 "  klockstat -S hld_total        # sort lock held results by total held time\n"
90 "  klockstat -S acq_count,hld_total  # combination of above\n"
91 "  klockstat -n 3                # display top 3 locks/threads\n"
92 "  klockstat -s 6                # display 6 stack entries per lock\n"
93 "  klockstat -P                  # print stats per thread\n"
94 ;
95 
96 static const struct argp_option opts[] = {
97 	{ "pid", 'p', "PID", 0, "Filter by process ID" },
98 	{ "tid", 't', "TID", 0, "Filter by thread ID" },
99 	{ 0, 0, 0, 0, "" },
100 	{ "caller", 'c', "FUNC", 0, "Filter by caller string prefix" },
101 	{ "lock", 'L', "LOCK", 0, "Filter by specific ksym lock name" },
102 	{ 0, 0, 0, 0, "" },
103 	{ "locks", 'n', "NR_LOCKS", 0, "Number of locks or threads to print" },
104 	{ "stacks", 's', "NR_STACKS", 0, "Number of stack entries to print per lock" },
105 	{ "sort", 'S', "SORT", 0, "Sort by field:\n  acq_[max|total|count]\n  hld_[max|total|count]" },
106 	{ 0, 0, 0, 0, "" },
107 	{ "duration", 'd', "SECONDS", 0, "Duration to trace" },
108 	{ "interval", 'i', "SECONDS", 0, "Print interval" },
109 	{ "reset", 'R', NULL, 0, "Reset stats each interval" },
110 	{ "timestamp", 'T', NULL, 0, "Print timestamp" },
111 	{ "verbose", 'v', NULL, 0, "Verbose debug output" },
112 	{ "per-thread", 'P', NULL, 0, "Print per-thread stats" },
113 
114 	{ NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help" },
115 	{},
116 };
117 
parse_lock_addr(const char * lock_name)118 static void *parse_lock_addr(const char *lock_name)
119 {
120 	unsigned long lock_addr;
121 
122 	return sscanf(lock_name, "0x%lx", &lock_addr) ? (void*)lock_addr : NULL;
123 }
124 
get_lock_addr(struct ksyms * ksyms,const char * lock_name)125 static void *get_lock_addr(struct ksyms *ksyms, const char *lock_name)
126 {
127 	const struct ksym *ksym = ksyms__get_symbol(ksyms, lock_name);
128 
129 	return ksym ? (void*)ksym->addr : parse_lock_addr(lock_name);
130 }
131 
get_lock_name(struct ksyms * ksyms,unsigned long addr)132 static const char *get_lock_name(struct ksyms *ksyms, unsigned long addr)
133 {
134 	const struct ksym *ksym = ksyms__map_addr(ksyms, addr);
135 
136 	return (ksym && ksym->addr == addr) ? ksym->name : "no-ksym";
137 }
138 
parse_one_sort(struct prog_env * env,const char * sort)139 static bool parse_one_sort(struct prog_env *env, const char *sort)
140 {
141 	const char *field = sort + 4;
142 
143 	if (!strncmp(sort, "acq_", 4)) {
144 		if (!strcmp(field, "max")) {
145 			env->sort_acq = SORT_ACQ_MAX;
146 			return true;
147 		} else if (!strcmp(field, "total")) {
148 			env->sort_acq = SORT_ACQ_TOTAL;
149 			return true;
150 		} else if (!strcmp(field, "count")) {
151 			env->sort_acq = SORT_ACQ_COUNT;
152 			return true;
153 		}
154 	} else if (!strncmp(sort, "hld_", 4)) {
155 		if (!strcmp(field, "max")) {
156 			env->sort_hld = SORT_HLD_MAX;
157 			return true;
158 		} else if (!strcmp(field, "total")) {
159 			env->sort_hld = SORT_HLD_TOTAL;
160 			return true;
161 		} else if (!strcmp(field, "count")) {
162 			env->sort_hld = SORT_HLD_COUNT;
163 			return true;
164 		}
165 	}
166 
167 	return false;
168 }
169 
parse_sorts(struct prog_env * env,char * arg)170 static bool parse_sorts(struct prog_env *env, char *arg)
171 {
172 	char *comma = strchr(arg, ',');
173 
174 	if (comma) {
175 		*comma = '\0';
176 		comma++;
177 		if (!parse_one_sort(env, comma))
178 			return false;
179 	}
180 	return parse_one_sort(env, arg);
181 }
182 
parse_arg(int key,char * arg,struct argp_state * state)183 static error_t parse_arg(int key, char *arg, struct argp_state *state)
184 {
185 	struct prog_env *env = state->input;
186 	long duration, interval;
187 
188 	switch (key) {
189 	case 'p':
190 		errno = 0;
191 		env->pid = strtol(arg, NULL, 10);
192 		if (errno || env->pid <= 0) {
193 			warn("Invalid PID: %s\n", arg);
194 			argp_usage(state);
195 		}
196 		break;
197 	case 't':
198 		errno = 0;
199 		env->tid = strtol(arg, NULL, 10);
200 		if (errno || env->tid <= 0) {
201 			warn("Invalid TID: %s\n", arg);
202 			argp_usage(state);
203 		}
204 		break;
205 	case 'c':
206 		env->caller = arg;
207 		break;
208 	case 'L':
209 		env->lock_name = arg;
210 		break;
211 	case 'n':
212 		errno = 0;
213 		env->nr_locks = strtol(arg, NULL, 10);
214 		if (errno || env->nr_locks <= 0) {
215 			warn("Invalid NR_LOCKS: %s\n", arg);
216 			argp_usage(state);
217 		}
218 		break;
219 	case 's':
220 		errno = 0;
221 		env->nr_stack_entries = strtol(arg, NULL, 10);
222 		if (errno || env->nr_stack_entries <= 0) {
223 			warn("Invalid NR_STACKS: %s\n", arg);
224 			argp_usage(state);
225 		}
226 		break;
227 	case 'S':
228 		if (!parse_sorts(env, arg)) {
229 			warn("Bad sort string: %s\n", arg);
230 			argp_usage(state);
231 		}
232 		break;
233 	case 'd':
234 		errno = 0;
235 		duration = strtol(arg, NULL, 10);
236 		if (errno || duration <= 0) {
237 			warn("Invalid duration: %s\n", arg);
238 			argp_usage(state);
239 		}
240 		env->duration = duration;
241 		break;
242 	case 'i':
243 		errno = 0;
244 		interval = strtol(arg, NULL, 10);
245 		if (errno || interval <= 0) {
246 			warn("Invalid interval: %s\n", arg);
247 			argp_usage(state);
248 		}
249 		env->interval = interval;
250 		break;
251 	case 'R':
252 		env->reset = true;
253 		break;
254 	case 'T':
255 		env->timestamp = true;
256 		break;
257 	case 'P':
258 		env->per_thread = true;
259 		break;
260 	case 'h':
261 		argp_state_help(state, stderr, ARGP_HELP_STD_HELP);
262 		break;
263 	case 'v':
264 		env->verbose = true;
265 		break;
266 	case ARGP_KEY_END:
267 		if (env->duration) {
268 			if (env->interval > env->duration)
269 				env->interval = env->duration;
270 			env->iterations = env->duration / env->interval;
271 		}
272 		if (env->per_thread && env->nr_stack_entries != 1) {
273 			warn("--per-thread and --stacks cannot be used together\n");
274 			argp_usage(state);
275 		}
276 		break;
277 	default:
278 		return ARGP_ERR_UNKNOWN;
279 	}
280 	return 0;
281 }
282 
283 struct stack_stat {
284 	uint32_t stack_id;
285 	struct lock_stat ls;
286 	uint64_t bt[PERF_MAX_STACK_DEPTH];
287 };
288 
caller_is_traced(struct ksyms * ksyms,uint64_t caller_pc)289 static bool caller_is_traced(struct ksyms *ksyms, uint64_t caller_pc)
290 {
291 	const struct ksym *ksym;
292 
293 	if (!env.caller)
294 		return true;
295 	ksym = ksyms__map_addr(ksyms, caller_pc);
296 	if (!ksym)
297 		return true;
298 	return strncmp(env.caller, ksym->name, strlen(env.caller)) == 0;
299 }
300 
larger_first(uint64_t x,uint64_t y)301 static int larger_first(uint64_t x, uint64_t y)
302 {
303 	if (x > y)
304 		return -1;
305 	if (x == y)
306 		return 0;
307 	return 1;
308 }
309 
sort_by_acq(const void * x,const void * y)310 static int sort_by_acq(const void *x, const void *y)
311 {
312 	struct stack_stat *ss_x = *(struct stack_stat**)x;
313 	struct stack_stat *ss_y = *(struct stack_stat**)y;
314 
315 	switch (env.sort_acq) {
316 	case SORT_ACQ_MAX:
317 		return larger_first(ss_x->ls.acq_max_time,
318 				    ss_y->ls.acq_max_time);
319 	case SORT_ACQ_COUNT:
320 		return larger_first(ss_x->ls.acq_count,
321 				    ss_y->ls.acq_count);
322 	case SORT_ACQ_TOTAL:
323 		return larger_first(ss_x->ls.acq_total_time,
324 				    ss_y->ls.acq_total_time);
325 	}
326 
327 	warn("bad sort_acq %d\n", env.sort_acq);
328 	return -1;
329 }
330 
sort_by_hld(const void * x,const void * y)331 static int sort_by_hld(const void *x, const void *y)
332 {
333 	struct stack_stat *ss_x = *(struct stack_stat**)x;
334 	struct stack_stat *ss_y = *(struct stack_stat**)y;
335 
336 	switch (env.sort_hld) {
337 	case SORT_HLD_MAX:
338 		return larger_first(ss_x->ls.hld_max_time,
339 				    ss_y->ls.hld_max_time);
340 	case SORT_HLD_COUNT:
341 		return larger_first(ss_x->ls.hld_count,
342 				    ss_y->ls.hld_count);
343 	case SORT_HLD_TOTAL:
344 		return larger_first(ss_x->ls.hld_total_time,
345 				    ss_y->ls.hld_total_time);
346 	}
347 
348 	warn("bad sort_hld %d\n", env.sort_hld);
349 	return -1;
350 }
351 
symname(struct ksyms * ksyms,uint64_t pc,char * buf,size_t n)352 static char *symname(struct ksyms *ksyms, uint64_t pc, char *buf, size_t n)
353 {
354 	const struct ksym *ksym = ksyms__map_addr(ksyms, pc);
355 
356 	if (!ksym)
357 		return "Unknown";
358 	snprintf(buf, n, "%s+0x%lx", ksym->name, pc - ksym->addr);
359 	return buf;
360 }
361 
print_caller(char * buf,int size,struct stack_stat * ss)362 static char *print_caller(char *buf, int size, struct stack_stat *ss)
363 {
364 	snprintf(buf, size, "%u  %16s", ss->stack_id, ss->ls.acq_max_comm);
365 	return buf;
366 }
367 
print_time(char * buf,int size,uint64_t nsec)368 static char *print_time(char *buf, int size, uint64_t nsec)
369 {
370 	struct {
371 		float base;
372 		char *unit;
373 	} table[] = {
374 		{ 1e9 * 3600, "h " },
375 		{ 1e9 * 60, "m " },
376 		{ 1e9, "s " },
377 		{ 1e6, "ms" },
378 		{ 1e3, "us" },
379 		{ 0, NULL },
380 	};
381 
382 	for (int i = 0; table[i].base; i++) {
383 		if (nsec < table[i].base)
384 			continue;
385 
386 		snprintf(buf, size, "%.1f %s", nsec / table[i].base, table[i].unit);
387 		return buf;
388 	}
389 
390 	snprintf(buf, size, "%u ns", (unsigned)nsec);
391 	return buf;
392 }
393 
print_acq_header(void)394 static void print_acq_header(void)
395 {
396 	if (env.per_thread)
397 		printf("\n                Tid              Comm");
398 	else
399 		printf("\n                               Caller");
400 
401 	printf("  Avg Wait    Count   Max Wait   Total Wait\n");
402 }
403 
print_acq_stat(struct ksyms * ksyms,struct stack_stat * ss,int nr_stack_entries)404 static void print_acq_stat(struct ksyms *ksyms, struct stack_stat *ss,
405 			   int nr_stack_entries)
406 {
407 	char buf[40];
408 	char avg[40];
409 	char max[40];
410 	char tot[40];
411 	int i;
412 
413 	printf("%37s %9s %8llu %10s %12s\n",
414 	       symname(ksyms, ss->bt[0], buf, sizeof(buf)),
415 	       print_time(avg, sizeof(avg), ss->ls.acq_total_time / ss->ls.acq_count),
416 	       ss->ls.acq_count,
417 	       print_time(max, sizeof(max), ss->ls.acq_max_time),
418 	       print_time(tot, sizeof(tot), ss->ls.acq_total_time));
419 	for (i = 1; i < nr_stack_entries; i++) {
420 		if (!ss->bt[i] || env.per_thread)
421 			break;
422 		printf("%37s\n", symname(ksyms, ss->bt[i], buf, sizeof(buf)));
423 	}
424 	if (nr_stack_entries > 1 && !env.per_thread)
425 		printf("                              Max PID %llu, COMM %s, Lock %s (0x%llx)\n",
426 		       ss->ls.acq_max_id >> 32,
427 		       ss->ls.acq_max_comm,
428 			   get_lock_name(ksyms, ss->ls.acq_max_lock_ptr),
429 			   ss->ls.acq_max_lock_ptr);
430 }
431 
print_acq_task(struct stack_stat * ss)432 static void print_acq_task(struct stack_stat *ss)
433 {
434 	char buf[40];
435 	char avg[40];
436 	char max[40];
437 	char tot[40];
438 
439 	printf("%37s %9s %8llu %10s %12s\n",
440 	       print_caller(buf, sizeof(buf), ss),
441 	       print_time(avg, sizeof(avg), ss->ls.acq_total_time / ss->ls.acq_count),
442 	       ss->ls.acq_count,
443 	       print_time(max, sizeof(max), ss->ls.acq_max_time),
444 	       print_time(tot, sizeof(tot), ss->ls.acq_total_time));
445 }
446 
print_hld_header(void)447 static void print_hld_header(void)
448 {
449 	if (env.per_thread)
450 		printf("\n                Tid              Comm");
451 	else
452 		printf("\n                               Caller");
453 
454 	printf("  Avg Hold    Count   Max Hold   Total Hold\n");
455 }
456 
print_hld_stat(struct ksyms * ksyms,struct stack_stat * ss,int nr_stack_entries)457 static void print_hld_stat(struct ksyms *ksyms, struct stack_stat *ss,
458 			   int nr_stack_entries)
459 {
460 	char buf[40];
461 	char avg[40];
462 	char max[40];
463 	char tot[40];
464 	int i;
465 
466 	printf("%37s %9s %8llu %10s %12s\n",
467 	       symname(ksyms, ss->bt[0], buf, sizeof(buf)),
468 	       print_time(avg, sizeof(avg), ss->ls.hld_total_time / ss->ls.hld_count),
469 	       ss->ls.hld_count,
470 	       print_time(max, sizeof(max), ss->ls.hld_max_time),
471 	       print_time(tot, sizeof(tot), ss->ls.hld_total_time));
472 	for (i = 1; i < nr_stack_entries; i++) {
473 		if (!ss->bt[i] || env.per_thread)
474 			break;
475 		printf("%37s\n", symname(ksyms, ss->bt[i], buf, sizeof(buf)));
476 	}
477 	if (nr_stack_entries > 1 && !env.per_thread)
478 		printf("                              Max PID %llu, COMM %s, Lock %s (0x%llx)\n",
479 		       ss->ls.hld_max_id >> 32,
480 		       ss->ls.hld_max_comm,
481 			   get_lock_name(ksyms, ss->ls.hld_max_lock_ptr),
482 			   ss->ls.hld_max_lock_ptr);
483 }
484 
print_hld_task(struct stack_stat * ss)485 static void print_hld_task(struct stack_stat *ss)
486 {
487 	char buf[40];
488 	char avg[40];
489 	char max[40];
490 	char tot[40];
491 
492 	printf("%37s %9s %8llu %10s %12s\n",
493 	       print_caller(buf, sizeof(buf), ss),
494 	       print_time(avg, sizeof(avg), ss->ls.hld_total_time / ss->ls.hld_count),
495 	       ss->ls.hld_count,
496 	       print_time(max, sizeof(max), ss->ls.hld_max_time),
497 	       print_time(tot, sizeof(tot), ss->ls.hld_total_time));
498 }
499 
print_stats(struct ksyms * ksyms,int stack_map,int stat_map)500 static int print_stats(struct ksyms *ksyms, int stack_map, int stat_map)
501 {
502 	struct stack_stat **stats, *ss;
503 	size_t stat_idx = 0;
504 	size_t stats_sz = 1;
505 	uint32_t lookup_key = 0;
506 	uint32_t stack_id;
507 	int ret, i;
508 	int nr_stack_entries;
509 
510 	stats = calloc(stats_sz, sizeof(void *));
511 	if (!stats) {
512 		warn("Out of memory\n");
513 		return -1;
514 	}
515 
516 	while (bpf_map_get_next_key(stat_map, &lookup_key, &stack_id) == 0) {
517 		if (stat_idx == stats_sz) {
518 			stats_sz *= 2;
519 			stats = libbpf_reallocarray(stats, stats_sz, sizeof(void *));
520 			if (!stats) {
521 				warn("Out of memory\n");
522 				return -1;
523 			}
524 		}
525 		ss = malloc(sizeof(struct stack_stat));
526 		if (!ss) {
527 			warn("Out of memory\n");
528 			return -1;
529 		}
530 
531 		lookup_key = ss->stack_id = stack_id;
532 		ret = bpf_map_lookup_elem(stat_map, &stack_id, &ss->ls);
533 		if (ret) {
534 			free(ss);
535 			continue;
536 		}
537 		if (!env.per_thread && bpf_map_lookup_elem(stack_map, &stack_id, &ss->bt)) {
538 			/* Can still report the results without a backtrace. */
539 			warn("failed to lookup stack_id %u\n", stack_id);
540 		}
541 		if (!env.per_thread && !caller_is_traced(ksyms, ss->bt[0])) {
542 			free(ss);
543 			continue;
544 		}
545 		stats[stat_idx++] = ss;
546 	}
547 
548 	nr_stack_entries = MIN(env.nr_stack_entries, PERF_MAX_STACK_DEPTH);
549 
550 	qsort(stats, stat_idx, sizeof(void*), sort_by_acq);
551 	for (i = 0; i < MIN(env.nr_locks, stat_idx); i++) {
552 		if (i == 0 || env.nr_stack_entries > 1)
553 			print_acq_header();
554 
555 		if (env.per_thread)
556 			print_acq_task(stats[i]);
557 		else
558 			print_acq_stat(ksyms, stats[i], nr_stack_entries);
559 	}
560 
561 	qsort(stats, stat_idx, sizeof(void*), sort_by_hld);
562 	for (i = 0; i < MIN(env.nr_locks, stat_idx); i++) {
563 		if (i == 0 || env.nr_stack_entries > 1)
564 			print_hld_header();
565 
566 		if (env.per_thread)
567 			print_hld_task(stats[i]);
568 		else
569 			print_hld_stat(ksyms, stats[i], nr_stack_entries);
570 	}
571 
572 	for (i = 0; i < stat_idx; i++) {
573 		if (env.reset)
574 			bpf_map_delete_elem(stat_map, &ss->stack_id);
575 		free(stats[i]);
576         }
577 	free(stats);
578 
579 	return 0;
580 }
581 
582 static volatile bool exiting;
583 
sig_hand(int signr)584 static void sig_hand(int signr)
585 {
586 	exiting = true;
587 }
588 
589 static struct sigaction sigact = {.sa_handler = sig_hand};
590 
libbpf_print_fn(enum libbpf_print_level level,const char * format,va_list args)591 static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
592 {
593 	if (level == LIBBPF_DEBUG && !env.verbose)
594 		return 0;
595 	return vfprintf(stderr, format, args);
596 }
597 
enable_fentry(struct klockstat_bpf * obj)598 static void enable_fentry(struct klockstat_bpf *obj)
599 {
600 	bool debug_lock;
601 
602 	bpf_program__set_autoload(obj->progs.kprobe_mutex_lock, false);
603 	bpf_program__set_autoload(obj->progs.kprobe_mutex_lock_exit, false);
604 	bpf_program__set_autoload(obj->progs.kprobe_mutex_trylock, false);
605 	bpf_program__set_autoload(obj->progs.kprobe_mutex_trylock_exit, false);
606 	bpf_program__set_autoload(obj->progs.kprobe_mutex_lock_interruptible, false);
607 	bpf_program__set_autoload(obj->progs.kprobe_mutex_lock_interruptible_exit, false);
608 	bpf_program__set_autoload(obj->progs.kprobe_mutex_lock_killable, false);
609 	bpf_program__set_autoload(obj->progs.kprobe_mutex_lock_killable_exit, false);
610 	bpf_program__set_autoload(obj->progs.kprobe_mutex_unlock, false);
611 
612 	bpf_program__set_autoload(obj->progs.kprobe_down_read, false);
613 	bpf_program__set_autoload(obj->progs.kprobe_down_read_exit, false);
614 	bpf_program__set_autoload(obj->progs.kprobe_down_read_trylock, false);
615 	bpf_program__set_autoload(obj->progs.kprobe_down_read_trylock_exit, false);
616 	bpf_program__set_autoload(obj->progs.kprobe_down_read_interruptible, false);
617 	bpf_program__set_autoload(obj->progs.kprobe_down_read_interruptible_exit, false);
618 	bpf_program__set_autoload(obj->progs.kprobe_down_read_killable, false);
619 	bpf_program__set_autoload(obj->progs.kprobe_down_read_killable_exit, false);
620 	bpf_program__set_autoload(obj->progs.kprobe_up_read, false);
621 	bpf_program__set_autoload(obj->progs.kprobe_down_write, false);
622 	bpf_program__set_autoload(obj->progs.kprobe_down_write_exit, false);
623 	bpf_program__set_autoload(obj->progs.kprobe_down_write_trylock, false);
624 	bpf_program__set_autoload(obj->progs.kprobe_down_write_trylock_exit, false);
625 	bpf_program__set_autoload(obj->progs.kprobe_down_write_killable, false);
626 	bpf_program__set_autoload(obj->progs.kprobe_down_write_killable_exit, false);
627 	bpf_program__set_autoload(obj->progs.kprobe_up_write, false);
628 
629 	/* CONFIG_DEBUG_LOCK_ALLOC is on */
630 	debug_lock = fentry_can_attach("mutex_lock_nested", NULL);
631 	if (!debug_lock)
632 		return;
633 
634 	bpf_program__set_attach_target(obj->progs.mutex_lock, 0,
635 				       "mutex_lock_nested");
636 	bpf_program__set_attach_target(obj->progs.mutex_lock_exit, 0,
637 				       "mutex_lock_nested");
638 	bpf_program__set_attach_target(obj->progs.mutex_lock_interruptible, 0,
639 				       "mutex_lock_interruptible_nested");
640 	bpf_program__set_attach_target(obj->progs.mutex_lock_interruptible_exit, 0,
641 				       "mutex_lock_interruptible_nested");
642 	bpf_program__set_attach_target(obj->progs.mutex_lock_killable, 0,
643 				       "mutex_lock_killable_nested");
644 	bpf_program__set_attach_target(obj->progs.mutex_lock_killable_exit, 0,
645 				       "mutex_lock_killable_nested");
646 
647 	bpf_program__set_attach_target(obj->progs.down_read, 0,
648 				       "down_read_nested");
649 	bpf_program__set_attach_target(obj->progs.down_read_exit, 0,
650 				       "down_read_nested");
651 	bpf_program__set_attach_target(obj->progs.down_read_killable, 0,
652 				       "down_read_killable_nested");
653 	bpf_program__set_attach_target(obj->progs.down_read_killable_exit, 0,
654 				       "down_read_killable_nested");
655 	bpf_program__set_attach_target(obj->progs.down_write, 0,
656 				       "down_write_nested");
657 	bpf_program__set_attach_target(obj->progs.down_write_exit, 0,
658 				       "down_write_nested");
659 	bpf_program__set_attach_target(obj->progs.down_write_killable, 0,
660 				       "down_write_killable_nested");
661 	bpf_program__set_attach_target(obj->progs.down_write_killable_exit, 0,
662 				       "down_write_killable_nested");
663 }
664 
enable_kprobes(struct klockstat_bpf * obj)665 static void enable_kprobes(struct klockstat_bpf *obj)
666 {
667 	bpf_program__set_autoload(obj->progs.mutex_lock, false);
668 	bpf_program__set_autoload(obj->progs.mutex_lock_exit, false);
669 	bpf_program__set_autoload(obj->progs.mutex_trylock_exit, false);
670 	bpf_program__set_autoload(obj->progs.mutex_lock_interruptible, false);
671 	bpf_program__set_autoload(obj->progs.mutex_lock_interruptible_exit, false);
672 	bpf_program__set_autoload(obj->progs.mutex_lock_killable, false);
673 	bpf_program__set_autoload(obj->progs.mutex_lock_killable_exit, false);
674 	bpf_program__set_autoload(obj->progs.mutex_unlock, false);
675 
676 	bpf_program__set_autoload(obj->progs.down_read, false);
677 	bpf_program__set_autoload(obj->progs.down_read_exit, false);
678 	bpf_program__set_autoload(obj->progs.down_read_trylock_exit, false);
679 	bpf_program__set_autoload(obj->progs.down_read_interruptible, false);
680 	bpf_program__set_autoload(obj->progs.down_read_interruptible_exit, false);
681 	bpf_program__set_autoload(obj->progs.down_read_killable, false);
682 	bpf_program__set_autoload(obj->progs.down_read_killable_exit, false);
683 	bpf_program__set_autoload(obj->progs.up_read, false);
684 	bpf_program__set_autoload(obj->progs.down_write, false);
685 	bpf_program__set_autoload(obj->progs.down_write_exit, false);
686 	bpf_program__set_autoload(obj->progs.down_write_trylock_exit, false);
687 	bpf_program__set_autoload(obj->progs.down_write_killable, false);
688 	bpf_program__set_autoload(obj->progs.down_write_killable_exit, false);
689 	bpf_program__set_autoload(obj->progs.up_write, false);
690 }
691 
main(int argc,char ** argv)692 int main(int argc, char **argv)
693 {
694 	static const struct argp argp = {
695 		.options = opts,
696 		.parser = parse_arg,
697 		.args_doc = args_doc,
698 		.doc = program_doc,
699 	};
700 	struct klockstat_bpf *obj = NULL;
701 	struct ksyms *ksyms = NULL;
702 	int i, err;
703 	struct tm *tm;
704 	char ts[32];
705 	time_t t;
706 	void *lock_addr = NULL;
707 
708 	err = argp_parse(&argp, argc, argv, 0, NULL, &env);
709 	if (err)
710 		return err;
711 
712 	sigaction(SIGINT, &sigact, 0);
713 
714 	libbpf_set_print(libbpf_print_fn);
715 
716 	ksyms = ksyms__load();
717 	if (!ksyms) {
718 		warn("failed to load kallsyms\n");
719 		err = 1;
720 		goto cleanup;
721 	}
722 	if (env.lock_name) {
723 		lock_addr = get_lock_addr(ksyms, env.lock_name);
724 		if (!lock_addr) {
725 			warn("failed to find lock %s\n", env.lock_name);
726 			err = 1;
727 			goto cleanup;
728 		}
729 	}
730 
731 	obj = klockstat_bpf__open();
732 	if (!obj) {
733 		warn("failed to open BPF object\n");
734 		err = 1;
735 		goto cleanup;
736 	}
737 
738 	obj->rodata->targ_tgid = env.pid;
739 	obj->rodata->targ_pid = env.tid;
740 	obj->rodata->targ_lock = lock_addr;
741 	obj->rodata->per_thread = env.per_thread;
742 
743 	if (fentry_can_attach("mutex_lock", NULL) ||
744 	    fentry_can_attach("mutex_lock_nested", NULL))
745 		enable_fentry(obj);
746 	else
747 		enable_kprobes(obj);
748 
749 	err = klockstat_bpf__load(obj);
750 	if (err) {
751 		warn("failed to load BPF object\n");
752 		return 1;
753 	}
754 	err = klockstat_bpf__attach(obj);
755 	if (err) {
756 		warn("failed to attach BPF object\n");
757 		goto cleanup;
758 	}
759 
760 	printf("Tracing mutex/sem lock events...  Hit Ctrl-C to end\n");
761 
762 	for (i = 0; i < env.iterations && !exiting; i++) {
763 		sleep(env.interval);
764 
765 		printf("\n");
766 		if (env.timestamp) {
767 			time(&t);
768 			tm = localtime(&t);
769 			strftime(ts, sizeof(ts), "%H:%M:%S", tm);
770 			printf("%-8s\n", ts);
771 		}
772 
773 		if (print_stats(ksyms, bpf_map__fd(obj->maps.stack_map),
774 				bpf_map__fd(obj->maps.stat_map))) {
775 			warn("print_stats error, aborting.\n");
776 			break;
777 		}
778 		fflush(stdout);
779 	}
780 
781 	printf("Exiting trace of mutex/sem locks\n");
782 
783 cleanup:
784 	klockstat_bpf__destroy(obj);
785 	ksyms__free(ksyms);
786 
787 	return err != 0;
788 }
789