xref: /linux/samples/bpf/offwaketime_user.c (revision 3f2fb9a834cb1fcddbae22deca7fde136944dc89)
1 /* Copyright (c) 2016 Facebook
2  *
3  * This program is free software; you can redistribute it and/or
4  * modify it under the terms of version 2 of the GNU General Public
5  * License as published by the Free Software Foundation.
6  */
7 #include <stdio.h>
8 #include <unistd.h>
9 #include <stdlib.h>
10 #include <signal.h>
11 #include <linux/bpf.h>
12 #include <string.h>
13 #include <linux/perf_event.h>
14 #include <errno.h>
15 #include <assert.h>
16 #include <stdbool.h>
17 #include <sys/resource.h>
18 #include "libbpf.h"
19 #include "bpf_load.h"
20 
21 #define MAX_SYMS 300000
22 #define PRINT_RAW_ADDR 0
23 
24 static struct ksym {
25 	long addr;
26 	char *name;
27 } syms[MAX_SYMS];
28 static int sym_cnt;
29 
30 static int ksym_cmp(const void *p1, const void *p2)
31 {
32 	return ((struct ksym *)p1)->addr - ((struct ksym *)p2)->addr;
33 }
34 
35 static int load_kallsyms(void)
36 {
37 	FILE *f = fopen("/proc/kallsyms", "r");
38 	char func[256], buf[256];
39 	char symbol;
40 	void *addr;
41 	int i = 0;
42 
43 	if (!f)
44 		return -ENOENT;
45 
46 	while (!feof(f)) {
47 		if (!fgets(buf, sizeof(buf), f))
48 			break;
49 		if (sscanf(buf, "%p %c %s", &addr, &symbol, func) != 3)
50 			break;
51 		if (!addr)
52 			continue;
53 		syms[i].addr = (long) addr;
54 		syms[i].name = strdup(func);
55 		i++;
56 	}
57 	sym_cnt = i;
58 	qsort(syms, sym_cnt, sizeof(struct ksym), ksym_cmp);
59 	return 0;
60 }
61 
62 static void *search(long key)
63 {
64 	int start = 0, end = sym_cnt;
65 	int result;
66 
67 	while (start < end) {
68 		size_t mid = start + (end - start) / 2;
69 
70 		result = key - syms[mid].addr;
71 		if (result < 0)
72 			end = mid;
73 		else if (result > 0)
74 			start = mid + 1;
75 		else
76 			return &syms[mid];
77 	}
78 
79 	if (start >= 1 && syms[start - 1].addr < key &&
80 	    key < syms[start].addr)
81 		/* valid ksym */
82 		return &syms[start - 1];
83 
84 	/* out of range. return _stext */
85 	return &syms[0];
86 }
87 
88 static void print_ksym(__u64 addr)
89 {
90 	struct ksym *sym;
91 
92 	if (!addr)
93 		return;
94 	sym = search(addr);
95 	if (PRINT_RAW_ADDR)
96 		printf("%s/%llx;", sym->name, addr);
97 	else
98 		printf("%s;", sym->name);
99 }
100 
101 #define TASK_COMM_LEN 16
102 
103 struct key_t {
104 	char waker[TASK_COMM_LEN];
105 	char target[TASK_COMM_LEN];
106 	__u32 wret;
107 	__u32 tret;
108 };
109 
110 static void print_stack(struct key_t *key, __u64 count)
111 {
112 	__u64 ip[PERF_MAX_STACK_DEPTH] = {};
113 	static bool warned;
114 	int i;
115 
116 	printf("%s;", key->target);
117 	if (bpf_lookup_elem(map_fd[3], &key->tret, ip) != 0) {
118 		printf("---;");
119 	} else {
120 		for (i = PERF_MAX_STACK_DEPTH - 1; i >= 0; i--)
121 			print_ksym(ip[i]);
122 	}
123 	printf("-;");
124 	if (bpf_lookup_elem(map_fd[3], &key->wret, ip) != 0) {
125 		printf("---;");
126 	} else {
127 		for (i = 0; i < PERF_MAX_STACK_DEPTH; i++)
128 			print_ksym(ip[i]);
129 	}
130 	printf(";%s %lld\n", key->waker, count);
131 
132 	if ((key->tret == -EEXIST || key->wret == -EEXIST) && !warned) {
133 		printf("stackmap collisions seen. Consider increasing size\n");
134 		warned = true;
135 	} else if (((int)(key->tret) < 0 || (int)(key->wret) < 0)) {
136 		printf("err stackid %d %d\n", key->tret, key->wret);
137 	}
138 }
139 
140 static void print_stacks(int fd)
141 {
142 	struct key_t key = {}, next_key;
143 	__u64 value;
144 
145 	while (bpf_get_next_key(fd, &key, &next_key) == 0) {
146 		bpf_lookup_elem(fd, &next_key, &value);
147 		print_stack(&next_key, value);
148 		key = next_key;
149 	}
150 }
151 
152 static void int_exit(int sig)
153 {
154 	print_stacks(map_fd[0]);
155 	exit(0);
156 }
157 
158 int main(int argc, char **argv)
159 {
160 	struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
161 	char filename[256];
162 	int delay = 1;
163 
164 	snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
165 	setrlimit(RLIMIT_MEMLOCK, &r);
166 
167 	signal(SIGINT, int_exit);
168 
169 	if (load_kallsyms()) {
170 		printf("failed to process /proc/kallsyms\n");
171 		return 2;
172 	}
173 
174 	if (load_bpf_file(filename)) {
175 		printf("%s", bpf_log_buf);
176 		return 1;
177 	}
178 
179 	if (argc > 1)
180 		delay = atoi(argv[1]);
181 	sleep(delay);
182 	print_stacks(map_fd[0]);
183 
184 	return 0;
185 }
186