tprof_analyze.c revision 1.1 1 /* $NetBSD: tprof_analyze.c,v 1.1 2018/07/13 11:03:36 maxv Exp $ */
2
3 /*
4 * Copyright (c) 2010,2011,2012 YAMAMOTO Takashi,
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 #ifndef lint
31 __RCSID("$NetBSD: tprof_analyze.c,v 1.1 2018/07/13 11:03:36 maxv Exp $");
32 #endif /* not lint */
33
34 #include <assert.h>
35 #include <err.h>
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <gelf.h>
39 #include <inttypes.h>
40 #include <libelf.h>
41 #include <stdbool.h>
42 #include <stdlib.h>
43 #include <stdio.h>
44 #include <unistd.h>
45 #include <string.h>
46 #include <util.h>
47 #include <dev/tprof/tprof_ioctl.h>
48 #include "tprof.h"
49
50 #define _PATH_KSYMS "/dev/ksyms"
51
52 #include <sys/rbtree.h>
53
54 static bool filter_by_pid;
55 static pid_t target_pid;
56 static bool per_symbol;
57
58 struct addr {
59 struct rb_node node;
60 uint64_t addr; /* address */
61 uint32_t pid; /* process id */
62 uint32_t lwpid; /* lwp id */
63 uint32_t cpuid; /* cpu id */
64 bool in_kernel; /* if addr is in the kernel address space */
65 unsigned int nsamples; /* number of samples taken for the address */
66 };
67
68 static rb_tree_t addrtree;
69
70 struct sym {
71 char *name;
72 uint64_t value;
73 uint64_t size;
74 };
75
76 static struct sym **syms = NULL;
77 static size_t nsyms = 0;
78
79 static int
80 compare_value(const void *p1, const void *p2)
81 {
82 const struct sym *s1 = *(const struct sym * const *)p1;
83 const struct sym *s2 = *(const struct sym * const *)p2;
84
85 if (s1->value > s2->value) {
86 return -1;
87 } else if (s1->value < s2->value) {
88 return 1;
89 }
90 /*
91 * to produce a stable result, it's better not to return 0
92 * even for __strong_alias.
93 */
94 if (s1->size > s2->size) {
95 return -1;
96 } else if (s1->size < s2->size) {
97 return 1;
98 }
99 return strcmp(s1->name, s2->name);
100 }
101
102 static void
103 ksymload(void)
104 {
105 Elf *e;
106 Elf_Scn *s;
107 GElf_Shdr sh_store;
108 GElf_Shdr *sh;
109 Elf_Data *d;
110 int fd;
111 size_t size, i;
112
113 fd = open(_PATH_KSYMS, O_RDONLY);
114 if (fd == -1) {
115 err(EXIT_FAILURE, "open");
116 }
117 if (elf_version(EV_CURRENT) == EV_NONE) {
118 goto elffail;
119 }
120 e = elf_begin(fd, ELF_C_READ, NULL);
121 if (e == NULL) {
122 goto elffail;
123 }
124 for (s = elf_nextscn(e, NULL); s != NULL; s = elf_nextscn(e, s)) {
125 sh = gelf_getshdr(s, &sh_store);
126 if (sh == NULL) {
127 goto elffail;
128 }
129 if (sh->sh_type == SHT_SYMTAB) {
130 break;
131 }
132 }
133 if (s == NULL) {
134 errx(EXIT_FAILURE, "no symtab");
135 }
136 d = elf_getdata(s, NULL);
137 if (d == NULL) {
138 goto elffail;
139 }
140 assert(sh->sh_size == d->d_size);
141 size = sh->sh_size / sh->sh_entsize;
142 for (i = 1; i < size; i++) {
143 GElf_Sym st_store;
144 GElf_Sym *st;
145 struct sym *sym;
146
147 st = gelf_getsym(d, (int)i, &st_store);
148 if (st == NULL) {
149 goto elffail;
150 }
151 if (ELF_ST_TYPE(st->st_info) != STT_FUNC) {
152 continue;
153 }
154 sym = emalloc(sizeof(*sym));
155 sym->name = estrdup(elf_strptr(e, sh->sh_link, st->st_name));
156 sym->value = (uint64_t)st->st_value;
157 sym->size = st->st_size;
158 nsyms++;
159 syms = erealloc(syms, sizeof(*syms) * nsyms);
160 syms[nsyms - 1] = sym;
161 }
162 qsort(syms, nsyms, sizeof(*syms), compare_value);
163 return;
164 elffail:
165 errx(EXIT_FAILURE, "libelf: %s", elf_errmsg(elf_errno()));
166 }
167
168 static const char *
169 ksymlookup(uint64_t value, uint64_t *offset)
170 {
171 size_t hi;
172 size_t lo;
173 size_t i;
174
175 /*
176 * try to find the smallest i for which syms[i]->value <= value.
177 * syms[] is ordered by syms[]->value in the descending order.
178 */
179
180 hi = nsyms - 1;
181 lo = 0;
182 while (lo < hi) {
183 const size_t mid = (lo + hi) / 2;
184 const struct sym *sym = syms[mid];
185
186 assert(syms[lo]->value >= sym->value);
187 assert(sym->value >= syms[hi]->value);
188 if (sym->value <= value) {
189 hi = mid;
190 continue;
191 }
192 lo = mid + 1;
193 }
194 assert(lo == nsyms - 1 || syms[lo]->value <= value);
195 assert(lo == 0 || syms[lo - 1]->value > value);
196 for (i = lo; i < nsyms; i++) {
197 const struct sym *sym = syms[i];
198
199 if (sym->value <= value &&
200 (sym->size == 0 || value - sym->value <= sym->size )) {
201 *offset = value - sym->value;
202 return sym->name;
203 }
204 if (sym->size != 0 && sym->value + sym->size < value) {
205 break;
206 }
207 }
208 return NULL;
209 }
210
211 static signed int
212 addrtree_compare_key(void *ctx, const void *n1, const void *keyp)
213 {
214 const struct addr *a1 = n1;
215 const struct addr *a2 = (const struct addr *)keyp;
216
217 if (a1->addr > a2->addr) {
218 return 1;
219 } else if (a1->addr < a2->addr) {
220 return -1;
221 }
222 if (a1->pid > a2->pid) {
223 return -1;
224 } else if (a1->pid < a2->pid) {
225 return 1;
226 }
227 if (a1->lwpid > a2->lwpid) {
228 return -1;
229 } else if (a1->lwpid < a2->lwpid) {
230 return 1;
231 }
232 if (a1->cpuid > a2->cpuid) {
233 return -1;
234 } else if (a1->cpuid < a2->cpuid) {
235 return 1;
236 }
237 if (a1->in_kernel > a2->in_kernel) {
238 return -1;
239 } else if (a1->in_kernel < a2->in_kernel) {
240 return 1;
241 }
242 return 0;
243 }
244
245 static signed int
246 addrtree_compare_nodes(void *ctx, const void *n1, const void *n2)
247 {
248 const struct addr *a2 = n2;
249
250 return addrtree_compare_key(ctx, n1, a2);
251 }
252
253 static const rb_tree_ops_t addrtree_ops = {
254 .rbto_compare_nodes = addrtree_compare_nodes,
255 .rbto_compare_key = addrtree_compare_key,
256 };
257
258 static int
259 compare_nsamples(const void *p1, const void *p2)
260 {
261 const struct addr *a1 = *(const struct addr * const *)p1;
262 const struct addr *a2 = *(const struct addr * const *)p2;
263
264 if (a1->nsamples > a2->nsamples) {
265 return -1;
266 } else if (a1->nsamples < a2->nsamples) {
267 return 1;
268 }
269 return 0;
270 }
271
272 void
273 tprof_analyze(int argc, char **argv)
274 {
275 struct addr *a;
276 struct addr **l;
277 struct addr **p;
278 size_t naddrs, i;
279 int ch;
280 bool distinguish_processes = true;
281 bool distinguish_cpus = true;
282 bool distinguish_lwps = true;
283 bool kernel_only = false;
284 extern char *optarg;
285 extern int optind;
286
287 while ((ch = getopt(argc, argv, "CkLPp:s")) != -1) {
288 uintmax_t val;
289 char *ep;
290
291 switch (ch) {
292 case 'C': /* don't distinguish cpus */
293 distinguish_cpus = false;
294 break;
295 case 'k': /* kernel only */
296 kernel_only = true;
297 break;
298 case 'L': /* don't distinguish lwps */
299 distinguish_lwps = false;
300 break;
301 case 'p': /* only for the process for the given pid */
302 errno = 0;
303 val = strtoumax(optarg, &ep, 10);
304 if (optarg[0] == 0 || *ep != 0 ||
305 val > INT32_MAX) {
306 errx(EXIT_FAILURE, "invalid p option");
307 }
308 target_pid = val;
309 filter_by_pid = true;
310 break;
311 case 'P': /* don't distinguish processes */
312 distinguish_processes = false;
313 break;
314 case 's': /* per symbol */
315 per_symbol = true;
316 break;
317 default:
318 exit(EXIT_FAILURE);
319 }
320 }
321 argc -= optind;
322 argv += optind;
323
324 ksymload();
325 rb_tree_init(&addrtree, &addrtree_ops);
326
327 /*
328 * read and count samples.
329 */
330
331 naddrs = 0;
332 while (/*CONSTCOND*/true) {
333 struct addr *o;
334 tprof_sample_t sample;
335 size_t n = fread(&sample, sizeof(sample), 1, stdin);
336 bool in_kernel;
337
338 if (n == 0) {
339 if (feof(stdin)) {
340 break;
341 }
342 if (ferror(stdin)) {
343 err(EXIT_FAILURE, "fread");
344 }
345 }
346 if (filter_by_pid && (pid_t)sample.s_pid != target_pid) {
347 continue;
348 }
349 in_kernel = (sample.s_flags & TPROF_SAMPLE_INKERNEL) != 0;
350 if (kernel_only && !in_kernel) {
351 continue;
352 }
353 a = emalloc(sizeof(*a));
354 a->addr = (uint64_t)sample.s_pc;
355 if (distinguish_processes) {
356 a->pid = sample.s_pid;
357 } else {
358 a->pid = 0;
359 }
360 if (distinguish_lwps) {
361 a->lwpid = sample.s_lwpid;
362 } else {
363 a->lwpid = 0;
364 }
365 if (distinguish_cpus) {
366 a->cpuid = sample.s_cpuid;
367 } else {
368 a->cpuid = 0;
369 }
370 a->in_kernel = in_kernel;
371 if (per_symbol) {
372 const char *name;
373 uint64_t offset;
374
375 name = ksymlookup(a->addr, &offset);
376 if (name != NULL) {
377 a->addr -= offset;
378 }
379 }
380 a->nsamples = 1;
381 o = rb_tree_insert_node(&addrtree, a);
382 if (o != a) {
383 assert(a->addr == o->addr);
384 assert(a->pid == o->pid);
385 assert(a->lwpid == o->lwpid);
386 assert(a->cpuid == o->cpuid);
387 assert(a->in_kernel == o->in_kernel);
388 free(a);
389 o->nsamples++;
390 } else {
391 naddrs++;
392 }
393 }
394
395 /*
396 * sort samples by addresses.
397 */
398
399 l = emalloc(naddrs * sizeof(*l));
400 p = l;
401 RB_TREE_FOREACH(a, &addrtree) {
402 *p++ = a;
403 }
404 assert(l + naddrs == p);
405 qsort(l, naddrs, sizeof(*l), compare_nsamples);
406
407 /*
408 * print addresses and number of samples, preferably with
409 * resolved symbol names.
410 */
411
412 for (i = 0; i < naddrs; i++) {
413 const char *name;
414 char buf[100];
415 uint64_t offset;
416
417 a = l[i];
418 if (a->in_kernel) {
419 name = ksymlookup(a->addr, &offset);
420 } else {
421 name = NULL;
422 }
423 if (name == NULL) {
424 (void)snprintf(buf, sizeof(buf), "<%016" PRIx64 ">",
425 a->addr);
426 name = buf;
427 } else if (offset != 0) {
428 (void)snprintf(buf, sizeof(buf), "%s+0x%" PRIx64, name,
429 offset);
430 name = buf;
431 }
432 printf("%8u %6" PRIu32 " %4" PRIu32 " %2" PRIu32 " %u %016"
433 PRIx64 " %s\n",
434 a->nsamples, a->pid, a->lwpid, a->cpuid, a->in_kernel,
435 a->addr, name);
436 }
437 }
438