aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorJonathan Herman <hermanjl@cs.unc.edu>2013-01-22 10:38:37 -0500
committerJonathan Herman <hermanjl@cs.unc.edu>2013-01-22 10:38:37 -0500
commitfcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch)
treea57612d1888735a2ec7972891b68c1ac5ec8faea /tools
parent8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff)
Added missing tegra files.HEADmaster
Diffstat (limited to 'tools')
-rw-r--r--tools/perf/builtin-test.c919
-rw-r--r--tools/perf/util/include/linux/module.h6
-rw-r--r--tools/perf/util/ui/browser.c356
-rw-r--r--tools/perf/util/ui/browser.h52
-rw-r--r--tools/perf/util/ui/browsers/annotate.c302
-rw-r--r--tools/perf/util/ui/browsers/hists.c1138
-rw-r--r--tools/perf/util/ui/browsers/map.c156
-rw-r--r--tools/perf/util/ui/browsers/map.h6
-rw-r--r--tools/perf/util/ui/browsers/top.c212
-rw-r--r--tools/perf/util/ui/helpline.c72
-rw-r--r--tools/perf/util/ui/helpline.h11
-rw-r--r--tools/perf/util/ui/libslang.h27
-rw-r--r--tools/perf/util/ui/progress.c60
-rw-r--r--tools/perf/util/ui/progress.h11
-rw-r--r--tools/perf/util/ui/setup.c46
-rw-r--r--tools/perf/util/ui/ui.h8
-rw-r--r--tools/perf/util/ui/util.c130
-rw-r--r--tools/perf/util/ui/util.h10
-rw-r--r--tools/slub/slabinfo.c1385
19 files changed, 4907 insertions, 0 deletions
diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c
new file mode 100644
index 00000000000..efe696f936e
--- /dev/null
+++ b/tools/perf/builtin-test.c
@@ -0,0 +1,919 @@
1/*
2 * builtin-test.c
3 *
4 * Builtin regression testing command: ever growing number of sanity tests
5 */
6#include "builtin.h"
7
8#include "util/cache.h"
9#include "util/debug.h"
10#include "util/evlist.h"
11#include "util/parse-options.h"
12#include "util/parse-events.h"
13#include "util/symbol.h"
14#include "util/thread_map.h"
15#include "../../include/linux/hw_breakpoint.h"
16
17static long page_size;
18
19static int vmlinux_matches_kallsyms_filter(struct map *map __used, struct symbol *sym)
20{
21 bool *visited = symbol__priv(sym);
22 *visited = true;
23 return 0;
24}
25
26static int test__vmlinux_matches_kallsyms(void)
27{
28 int err = -1;
29 struct rb_node *nd;
30 struct symbol *sym;
31 struct map *kallsyms_map, *vmlinux_map;
32 struct machine kallsyms, vmlinux;
33 enum map_type type = MAP__FUNCTION;
34 struct ref_reloc_sym ref_reloc_sym = { .name = "_stext", };
35
36 /*
37 * Step 1:
38 *
39 * Init the machines that will hold kernel, modules obtained from
40 * both vmlinux + .ko files and from /proc/kallsyms split by modules.
41 */
42 machine__init(&kallsyms, "", HOST_KERNEL_ID);
43 machine__init(&vmlinux, "", HOST_KERNEL_ID);
44
45 /*
46 * Step 2:
47 *
48 * Create the kernel maps for kallsyms and the DSO where we will then
49 * load /proc/kallsyms. Also create the modules maps from /proc/modules
50 * and find the .ko files that match them in /lib/modules/`uname -r`/.
51 */
52 if (machine__create_kernel_maps(&kallsyms) < 0) {
53 pr_debug("machine__create_kernel_maps ");
54 return -1;
55 }
56
57 /*
58 * Step 3:
59 *
60 * Load and split /proc/kallsyms into multiple maps, one per module.
61 */
62 if (machine__load_kallsyms(&kallsyms, "/proc/kallsyms", type, NULL) <= 0) {
63 pr_debug("dso__load_kallsyms ");
64 goto out;
65 }
66
67 /*
68 * Step 4:
69 *
70 * kallsyms will be internally on demand sorted by name so that we can
71 * find the reference relocation * symbol, i.e. the symbol we will use
72 * to see if the running kernel was relocated by checking if it has the
73 * same value in the vmlinux file we load.
74 */
75 kallsyms_map = machine__kernel_map(&kallsyms, type);
76
77 sym = map__find_symbol_by_name(kallsyms_map, ref_reloc_sym.name, NULL);
78 if (sym == NULL) {
79 pr_debug("dso__find_symbol_by_name ");
80 goto out;
81 }
82
83 ref_reloc_sym.addr = sym->start;
84
85 /*
86 * Step 5:
87 *
88 * Now repeat step 2, this time for the vmlinux file we'll auto-locate.
89 */
90 if (machine__create_kernel_maps(&vmlinux) < 0) {
91 pr_debug("machine__create_kernel_maps ");
92 goto out;
93 }
94
95 vmlinux_map = machine__kernel_map(&vmlinux, type);
96 map__kmap(vmlinux_map)->ref_reloc_sym = &ref_reloc_sym;
97
98 /*
99 * Step 6:
100 *
101 * Locate a vmlinux file in the vmlinux path that has a buildid that
102 * matches the one of the running kernel.
103 *
104 * While doing that look if we find the ref reloc symbol, if we find it
105 * we'll have its ref_reloc_symbol.unrelocated_addr and then
106 * maps__reloc_vmlinux will notice and set proper ->[un]map_ip routines
107 * to fixup the symbols.
108 */
109 if (machine__load_vmlinux_path(&vmlinux, type,
110 vmlinux_matches_kallsyms_filter) <= 0) {
111 pr_debug("machine__load_vmlinux_path ");
112 goto out;
113 }
114
115 err = 0;
116 /*
117 * Step 7:
118 *
119 * Now look at the symbols in the vmlinux DSO and check if we find all of them
120 * in the kallsyms dso. For the ones that are in both, check its names and
121 * end addresses too.
122 */
123 for (nd = rb_first(&vmlinux_map->dso->symbols[type]); nd; nd = rb_next(nd)) {
124 struct symbol *pair, *first_pair;
125 bool backwards = true;
126
127 sym = rb_entry(nd, struct symbol, rb_node);
128
129 if (sym->start == sym->end)
130 continue;
131
132 first_pair = machine__find_kernel_symbol(&kallsyms, type, sym->start, NULL, NULL);
133 pair = first_pair;
134
135 if (pair && pair->start == sym->start) {
136next_pair:
137 if (strcmp(sym->name, pair->name) == 0) {
138 /*
139 * kallsyms don't have the symbol end, so we
140 * set that by using the next symbol start - 1,
141 * in some cases we get this up to a page
142 * wrong, trace_kmalloc when I was developing
143 * this code was one such example, 2106 bytes
144 * off the real size. More than that and we
145 * _really_ have a problem.
146 */
147 s64 skew = sym->end - pair->end;
148 if (llabs(skew) < page_size)
149 continue;
150
151 pr_debug("%#" PRIx64 ": diff end addr for %s v: %#" PRIx64 " k: %#" PRIx64 "\n",
152 sym->start, sym->name, sym->end, pair->end);
153 } else {
154 struct rb_node *nnd;
155detour:
156 nnd = backwards ? rb_prev(&pair->rb_node) :
157 rb_next(&pair->rb_node);
158 if (nnd) {
159 struct symbol *next = rb_entry(nnd, struct symbol, rb_node);
160
161 if (next->start == sym->start) {
162 pair = next;
163 goto next_pair;
164 }
165 }
166
167 if (backwards) {
168 backwards = false;
169 pair = first_pair;
170 goto detour;
171 }
172
173 pr_debug("%#" PRIx64 ": diff name v: %s k: %s\n",
174 sym->start, sym->name, pair->name);
175 }
176 } else
177 pr_debug("%#" PRIx64 ": %s not on kallsyms\n", sym->start, sym->name);
178
179 err = -1;
180 }
181
182 if (!verbose)
183 goto out;
184
185 pr_info("Maps only in vmlinux:\n");
186
187 for (nd = rb_first(&vmlinux.kmaps.maps[type]); nd; nd = rb_next(nd)) {
188 struct map *pos = rb_entry(nd, struct map, rb_node), *pair;
189 /*
190 * If it is the kernel, kallsyms is always "[kernel.kallsyms]", while
191 * the kernel will have the path for the vmlinux file being used,
192 * so use the short name, less descriptive but the same ("[kernel]" in
193 * both cases.
194 */
195 pair = map_groups__find_by_name(&kallsyms.kmaps, type,
196 (pos->dso->kernel ?
197 pos->dso->short_name :
198 pos->dso->name));
199 if (pair)
200 pair->priv = 1;
201 else
202 map__fprintf(pos, stderr);
203 }
204
205 pr_info("Maps in vmlinux with a different name in kallsyms:\n");
206
207 for (nd = rb_first(&vmlinux.kmaps.maps[type]); nd; nd = rb_next(nd)) {
208 struct map *pos = rb_entry(nd, struct map, rb_node), *pair;
209
210 pair = map_groups__find(&kallsyms.kmaps, type, pos->start);
211 if (pair == NULL || pair->priv)
212 continue;
213
214 if (pair->start == pos->start) {
215 pair->priv = 1;
216 pr_info(" %" PRIx64 "-%" PRIx64 " %" PRIx64 " %s in kallsyms as",
217 pos->start, pos->end, pos->pgoff, pos->dso->name);
218 if (pos->pgoff != pair->pgoff || pos->end != pair->end)
219 pr_info(": \n*%" PRIx64 "-%" PRIx64 " %" PRIx64 "",
220 pair->start, pair->end, pair->pgoff);
221 pr_info(" %s\n", pair->dso->name);
222 pair->priv = 1;
223 }
224 }
225
226 pr_info("Maps only in kallsyms:\n");
227
228 for (nd = rb_first(&kallsyms.kmaps.maps[type]);
229 nd; nd = rb_next(nd)) {
230 struct map *pos = rb_entry(nd, struct map, rb_node);
231
232 if (!pos->priv)
233 map__fprintf(pos, stderr);
234 }
235out:
236 return err;
237}
238
239#include "util/cpumap.h"
240#include "util/evsel.h"
241#include <sys/types.h>
242
243static int trace_event__id(const char *evname)
244{
245 char *filename;
246 int err = -1, fd;
247
248 if (asprintf(&filename,
249 "%s/syscalls/%s/id",
250 debugfs_path, evname) < 0)
251 return -1;
252
253 fd = open(filename, O_RDONLY);
254 if (fd >= 0) {
255 char id[16];
256 if (read(fd, id, sizeof(id)) > 0)
257 err = atoi(id);
258 close(fd);
259 }
260
261 free(filename);
262 return err;
263}
264
265static int test__open_syscall_event(void)
266{
267 int err = -1, fd;
268 struct thread_map *threads;
269 struct perf_evsel *evsel;
270 struct perf_event_attr attr;
271 unsigned int nr_open_calls = 111, i;
272 int id = trace_event__id("sys_enter_open");
273
274 if (id < 0) {
275 pr_debug("is debugfs mounted on /sys/kernel/debug?\n");
276 return -1;
277 }
278
279 threads = thread_map__new(-1, getpid());
280 if (threads == NULL) {
281 pr_debug("thread_map__new\n");
282 return -1;
283 }
284
285 memset(&attr, 0, sizeof(attr));
286 attr.type = PERF_TYPE_TRACEPOINT;
287 attr.config = id;
288 evsel = perf_evsel__new(&attr, 0);
289 if (evsel == NULL) {
290 pr_debug("perf_evsel__new\n");
291 goto out_thread_map_delete;
292 }
293
294 if (perf_evsel__open_per_thread(evsel, threads, false) < 0) {
295 pr_debug("failed to open counter: %s, "
296 "tweak /proc/sys/kernel/perf_event_paranoid?\n",
297 strerror(errno));
298 goto out_evsel_delete;
299 }
300
301 for (i = 0; i < nr_open_calls; ++i) {
302 fd = open("/etc/passwd", O_RDONLY);
303 close(fd);
304 }
305
306 if (perf_evsel__read_on_cpu(evsel, 0, 0) < 0) {
307 pr_debug("perf_evsel__read_on_cpu\n");
308 goto out_close_fd;
309 }
310
311 if (evsel->counts->cpu[0].val != nr_open_calls) {
312 pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls, got %" PRIu64 "\n",
313 nr_open_calls, evsel->counts->cpu[0].val);
314 goto out_close_fd;
315 }
316
317 err = 0;
318out_close_fd:
319 perf_evsel__close_fd(evsel, 1, threads->nr);
320out_evsel_delete:
321 perf_evsel__delete(evsel);
322out_thread_map_delete:
323 thread_map__delete(threads);
324 return err;
325}
326
327#include <sched.h>
328
329static int test__open_syscall_event_on_all_cpus(void)
330{
331 int err = -1, fd, cpu;
332 struct thread_map *threads;
333 struct cpu_map *cpus;
334 struct perf_evsel *evsel;
335 struct perf_event_attr attr;
336 unsigned int nr_open_calls = 111, i;
337 cpu_set_t cpu_set;
338 int id = trace_event__id("sys_enter_open");
339
340 if (id < 0) {
341 pr_debug("is debugfs mounted on /sys/kernel/debug?\n");
342 return -1;
343 }
344
345 threads = thread_map__new(-1, getpid());
346 if (threads == NULL) {
347 pr_debug("thread_map__new\n");
348 return -1;
349 }
350
351 cpus = cpu_map__new(NULL);
352 if (cpus == NULL) {
353 pr_debug("cpu_map__new\n");
354 goto out_thread_map_delete;
355 }
356
357
358 CPU_ZERO(&cpu_set);
359
360 memset(&attr, 0, sizeof(attr));
361 attr.type = PERF_TYPE_TRACEPOINT;
362 attr.config = id;
363 evsel = perf_evsel__new(&attr, 0);
364 if (evsel == NULL) {
365 pr_debug("perf_evsel__new\n");
366 goto out_thread_map_delete;
367 }
368
369 if (perf_evsel__open(evsel, cpus, threads, false) < 0) {
370 pr_debug("failed to open counter: %s, "
371 "tweak /proc/sys/kernel/perf_event_paranoid?\n",
372 strerror(errno));
373 goto out_evsel_delete;
374 }
375
376 for (cpu = 0; cpu < cpus->nr; ++cpu) {
377 unsigned int ncalls = nr_open_calls + cpu;
378 /*
379 * XXX eventually lift this restriction in a way that
380 * keeps perf building on older glibc installations
381 * without CPU_ALLOC. 1024 cpus in 2010 still seems
382 * a reasonable upper limit tho :-)
383 */
384 if (cpus->map[cpu] >= CPU_SETSIZE) {
385 pr_debug("Ignoring CPU %d\n", cpus->map[cpu]);
386 continue;
387 }
388
389 CPU_SET(cpus->map[cpu], &cpu_set);
390 if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0) {
391 pr_debug("sched_setaffinity() failed on CPU %d: %s ",
392 cpus->map[cpu],
393 strerror(errno));
394 goto out_close_fd;
395 }
396 for (i = 0; i < ncalls; ++i) {
397 fd = open("/etc/passwd", O_RDONLY);
398 close(fd);
399 }
400 CPU_CLR(cpus->map[cpu], &cpu_set);
401 }
402
403 /*
404 * Here we need to explicitely preallocate the counts, as if
405 * we use the auto allocation it will allocate just for 1 cpu,
406 * as we start by cpu 0.
407 */
408 if (perf_evsel__alloc_counts(evsel, cpus->nr) < 0) {
409 pr_debug("perf_evsel__alloc_counts(ncpus=%d)\n", cpus->nr);
410 goto out_close_fd;
411 }
412
413 err = 0;
414
415 for (cpu = 0; cpu < cpus->nr; ++cpu) {
416 unsigned int expected;
417
418 if (cpus->map[cpu] >= CPU_SETSIZE)
419 continue;
420
421 if (perf_evsel__read_on_cpu(evsel, cpu, 0) < 0) {
422 pr_debug("perf_evsel__read_on_cpu\n");
423 err = -1;
424 break;
425 }
426
427 expected = nr_open_calls + cpu;
428 if (evsel->counts->cpu[cpu].val != expected) {
429 pr_debug("perf_evsel__read_on_cpu: expected to intercept %d calls on cpu %d, got %" PRIu64 "\n",
430 expected, cpus->map[cpu], evsel->counts->cpu[cpu].val);
431 err = -1;
432 }
433 }
434
435out_close_fd:
436 perf_evsel__close_fd(evsel, 1, threads->nr);
437out_evsel_delete:
438 perf_evsel__delete(evsel);
439out_thread_map_delete:
440 thread_map__delete(threads);
441 return err;
442}
443
444/*
445 * This test will generate random numbers of calls to some getpid syscalls,
446 * then establish an mmap for a group of events that are created to monitor
447 * the syscalls.
448 *
449 * It will receive the events, using mmap, use its PERF_SAMPLE_ID generated
450 * sample.id field to map back to its respective perf_evsel instance.
451 *
452 * Then it checks if the number of syscalls reported as perf events by
453 * the kernel corresponds to the number of syscalls made.
454 */
455static int test__basic_mmap(void)
456{
457 int err = -1;
458 union perf_event *event;
459 struct thread_map *threads;
460 struct cpu_map *cpus;
461 struct perf_evlist *evlist;
462 struct perf_event_attr attr = {
463 .type = PERF_TYPE_TRACEPOINT,
464 .read_format = PERF_FORMAT_ID,
465 .sample_type = PERF_SAMPLE_ID,
466 .watermark = 0,
467 };
468 cpu_set_t cpu_set;
469 const char *syscall_names[] = { "getsid", "getppid", "getpgrp",
470 "getpgid", };
471 pid_t (*syscalls[])(void) = { (void *)getsid, getppid, getpgrp,
472 (void*)getpgid };
473#define nsyscalls ARRAY_SIZE(syscall_names)
474 int ids[nsyscalls];
475 unsigned int nr_events[nsyscalls],
476 expected_nr_events[nsyscalls], i, j;
477 struct perf_evsel *evsels[nsyscalls], *evsel;
478 int sample_size = __perf_evsel__sample_size(attr.sample_type);
479
480 for (i = 0; i < nsyscalls; ++i) {
481 char name[64];
482
483 snprintf(name, sizeof(name), "sys_enter_%s", syscall_names[i]);
484 ids[i] = trace_event__id(name);
485 if (ids[i] < 0) {
486 pr_debug("Is debugfs mounted on /sys/kernel/debug?\n");
487 return -1;
488 }
489 nr_events[i] = 0;
490 expected_nr_events[i] = random() % 257;
491 }
492
493 threads = thread_map__new(-1, getpid());
494 if (threads == NULL) {
495 pr_debug("thread_map__new\n");
496 return -1;
497 }
498
499 cpus = cpu_map__new(NULL);
500 if (cpus == NULL) {
501 pr_debug("cpu_map__new\n");
502 goto out_free_threads;
503 }
504
505 CPU_ZERO(&cpu_set);
506 CPU_SET(cpus->map[0], &cpu_set);
507 sched_setaffinity(0, sizeof(cpu_set), &cpu_set);
508 if (sched_setaffinity(0, sizeof(cpu_set), &cpu_set) < 0) {
509 pr_debug("sched_setaffinity() failed on CPU %d: %s ",
510 cpus->map[0], strerror(errno));
511 goto out_free_cpus;
512 }
513
514 evlist = perf_evlist__new(cpus, threads);
515 if (evlist == NULL) {
516 pr_debug("perf_evlist__new\n");
517 goto out_free_cpus;
518 }
519
520 /* anonymous union fields, can't be initialized above */
521 attr.wakeup_events = 1;
522 attr.sample_period = 1;
523
524 for (i = 0; i < nsyscalls; ++i) {
525 attr.config = ids[i];
526 evsels[i] = perf_evsel__new(&attr, i);
527 if (evsels[i] == NULL) {
528 pr_debug("perf_evsel__new\n");
529 goto out_free_evlist;
530 }
531
532 perf_evlist__add(evlist, evsels[i]);
533
534 if (perf_evsel__open(evsels[i], cpus, threads, false) < 0) {
535 pr_debug("failed to open counter: %s, "
536 "tweak /proc/sys/kernel/perf_event_paranoid?\n",
537 strerror(errno));
538 goto out_close_fd;
539 }
540 }
541
542 if (perf_evlist__mmap(evlist, 128, true) < 0) {
543 pr_debug("failed to mmap events: %d (%s)\n", errno,
544 strerror(errno));
545 goto out_close_fd;
546 }
547
548 for (i = 0; i < nsyscalls; ++i)
549 for (j = 0; j < expected_nr_events[i]; ++j) {
550 int foo = syscalls[i]();
551 ++foo;
552 }
553
554 while ((event = perf_evlist__mmap_read(evlist, 0)) != NULL) {
555 struct perf_sample sample;
556
557 if (event->header.type != PERF_RECORD_SAMPLE) {
558 pr_debug("unexpected %s event\n",
559 perf_event__name(event->header.type));
560 goto out_munmap;
561 }
562
563 err = perf_event__parse_sample(event, attr.sample_type, sample_size,
564 false, &sample, false);
565 if (err) {
566 pr_err("Can't parse sample, err = %d\n", err);
567 goto out_munmap;
568 }
569
570 evsel = perf_evlist__id2evsel(evlist, sample.id);
571 if (evsel == NULL) {
572 pr_debug("event with id %" PRIu64
573 " doesn't map to an evsel\n", sample.id);
574 goto out_munmap;
575 }
576 nr_events[evsel->idx]++;
577 }
578
579 list_for_each_entry(evsel, &evlist->entries, node) {
580 if (nr_events[evsel->idx] != expected_nr_events[evsel->idx]) {
581 pr_debug("expected %d %s events, got %d\n",
582 expected_nr_events[evsel->idx],
583 event_name(evsel), nr_events[evsel->idx]);
584 goto out_munmap;
585 }
586 }
587
588 err = 0;
589out_munmap:
590 perf_evlist__munmap(evlist);
591out_close_fd:
592 for (i = 0; i < nsyscalls; ++i)
593 perf_evsel__close_fd(evsels[i], 1, threads->nr);
594out_free_evlist:
595 perf_evlist__delete(evlist);
596out_free_cpus:
597 cpu_map__delete(cpus);
598out_free_threads:
599 thread_map__delete(threads);
600 return err;
601#undef nsyscalls
602}
603
604#define TEST_ASSERT_VAL(text, cond) \
605do { \
606 if (!cond) { \
607 pr_debug("FAILED %s:%d %s\n", __FILE__, __LINE__, text); \
608 return -1; \
609 } \
610} while (0)
611
612static int test__checkevent_tracepoint(struct perf_evlist *evlist)
613{
614 struct perf_evsel *evsel = list_entry(evlist->entries.next,
615 struct perf_evsel, node);
616
617 TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
618 TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type);
619 TEST_ASSERT_VAL("wrong sample_type",
620 (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | PERF_SAMPLE_CPU) ==
621 evsel->attr.sample_type);
622 TEST_ASSERT_VAL("wrong sample_period", 1 == evsel->attr.sample_period);
623 return 0;
624}
625
626static int test__checkevent_tracepoint_multi(struct perf_evlist *evlist)
627{
628 struct perf_evsel *evsel;
629
630 TEST_ASSERT_VAL("wrong number of entries", evlist->nr_entries > 1);
631
632 list_for_each_entry(evsel, &evlist->entries, node) {
633 TEST_ASSERT_VAL("wrong type",
634 PERF_TYPE_TRACEPOINT == evsel->attr.type);
635 TEST_ASSERT_VAL("wrong sample_type",
636 (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | PERF_SAMPLE_CPU)
637 == evsel->attr.sample_type);
638 TEST_ASSERT_VAL("wrong sample_period",
639 1 == evsel->attr.sample_period);
640 }
641 return 0;
642}
643
644static int test__checkevent_raw(struct perf_evlist *evlist)
645{
646 struct perf_evsel *evsel = list_entry(evlist->entries.next,
647 struct perf_evsel, node);
648
649 TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
650 TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
651 TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config);
652 return 0;
653}
654
655static int test__checkevent_numeric(struct perf_evlist *evlist)
656{
657 struct perf_evsel *evsel = list_entry(evlist->entries.next,
658 struct perf_evsel, node);
659
660 TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
661 TEST_ASSERT_VAL("wrong type", 1 == evsel->attr.type);
662 TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config);
663 return 0;
664}
665
666static int test__checkevent_symbolic_name(struct perf_evlist *evlist)
667{
668 struct perf_evsel *evsel = list_entry(evlist->entries.next,
669 struct perf_evsel, node);
670
671 TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
672 TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
673 TEST_ASSERT_VAL("wrong config",
674 PERF_COUNT_HW_INSTRUCTIONS == evsel->attr.config);
675 return 0;
676}
677
678static int test__checkevent_symbolic_alias(struct perf_evlist *evlist)
679{
680 struct perf_evsel *evsel = list_entry(evlist->entries.next,
681 struct perf_evsel, node);
682
683 TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
684 TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->attr.type);
685 TEST_ASSERT_VAL("wrong config",
686 PERF_COUNT_SW_PAGE_FAULTS == evsel->attr.config);
687 return 0;
688}
689
690static int test__checkevent_genhw(struct perf_evlist *evlist)
691{
692 struct perf_evsel *evsel = list_entry(evlist->entries.next,
693 struct perf_evsel, node);
694
695 TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
696 TEST_ASSERT_VAL("wrong type", PERF_TYPE_HW_CACHE == evsel->attr.type);
697 TEST_ASSERT_VAL("wrong config", (1 << 16) == evsel->attr.config);
698 return 0;
699}
700
701static int test__checkevent_breakpoint(struct perf_evlist *evlist)
702{
703 struct perf_evsel *evsel = list_entry(evlist->entries.next,
704 struct perf_evsel, node);
705
706 TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
707 TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->attr.type);
708 TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config);
709 TEST_ASSERT_VAL("wrong bp_type", (HW_BREAKPOINT_R | HW_BREAKPOINT_W) ==
710 evsel->attr.bp_type);
711 TEST_ASSERT_VAL("wrong bp_len", HW_BREAKPOINT_LEN_4 ==
712 evsel->attr.bp_len);
713 return 0;
714}
715
716static int test__checkevent_breakpoint_x(struct perf_evlist *evlist)
717{
718 struct perf_evsel *evsel = list_entry(evlist->entries.next,
719 struct perf_evsel, node);
720
721 TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
722 TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->attr.type);
723 TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config);
724 TEST_ASSERT_VAL("wrong bp_type",
725 HW_BREAKPOINT_X == evsel->attr.bp_type);
726 TEST_ASSERT_VAL("wrong bp_len", sizeof(long) == evsel->attr.bp_len);
727 return 0;
728}
729
730static int test__checkevent_breakpoint_r(struct perf_evlist *evlist)
731{
732 struct perf_evsel *evsel = list_entry(evlist->entries.next,
733 struct perf_evsel, node);
734
735 TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
736 TEST_ASSERT_VAL("wrong type",
737 PERF_TYPE_BREAKPOINT == evsel->attr.type);
738 TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config);
739 TEST_ASSERT_VAL("wrong bp_type",
740 HW_BREAKPOINT_R == evsel->attr.bp_type);
741 TEST_ASSERT_VAL("wrong bp_len",
742 HW_BREAKPOINT_LEN_4 == evsel->attr.bp_len);
743 return 0;
744}
745
746static int test__checkevent_breakpoint_w(struct perf_evlist *evlist)
747{
748 struct perf_evsel *evsel = list_entry(evlist->entries.next,
749 struct perf_evsel, node);
750
751 TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
752 TEST_ASSERT_VAL("wrong type",
753 PERF_TYPE_BREAKPOINT == evsel->attr.type);
754 TEST_ASSERT_VAL("wrong config", 0 == evsel->attr.config);
755 TEST_ASSERT_VAL("wrong bp_type",
756 HW_BREAKPOINT_W == evsel->attr.bp_type);
757 TEST_ASSERT_VAL("wrong bp_len",
758 HW_BREAKPOINT_LEN_4 == evsel->attr.bp_len);
759 return 0;
760}
761
762static struct test__event_st {
763 const char *name;
764 __u32 type;
765 int (*check)(struct perf_evlist *evlist);
766} test__events[] = {
767 {
768 .name = "syscalls:sys_enter_open",
769 .check = test__checkevent_tracepoint,
770 },
771 {
772 .name = "syscalls:*",
773 .check = test__checkevent_tracepoint_multi,
774 },
775 {
776 .name = "r1",
777 .check = test__checkevent_raw,
778 },
779 {
780 .name = "1:1",
781 .check = test__checkevent_numeric,
782 },
783 {
784 .name = "instructions",
785 .check = test__checkevent_symbolic_name,
786 },
787 {
788 .name = "faults",
789 .check = test__checkevent_symbolic_alias,
790 },
791 {
792 .name = "L1-dcache-load-miss",
793 .check = test__checkevent_genhw,
794 },
795 {
796 .name = "mem:0",
797 .check = test__checkevent_breakpoint,
798 },
799 {
800 .name = "mem:0:x",
801 .check = test__checkevent_breakpoint_x,
802 },
803 {
804 .name = "mem:0:r",
805 .check = test__checkevent_breakpoint_r,
806 },
807 {
808 .name = "mem:0:w",
809 .check = test__checkevent_breakpoint_w,
810 },
811};
812
813#define TEST__EVENTS_CNT (sizeof(test__events) / sizeof(struct test__event_st))
814
815static int test__parse_events(void)
816{
817 struct perf_evlist *evlist;
818 u_int i;
819 int ret = 0;
820
821 for (i = 0; i < TEST__EVENTS_CNT; i++) {
822 struct test__event_st *e = &test__events[i];
823
824 evlist = perf_evlist__new(NULL, NULL);
825 if (evlist == NULL)
826 break;
827
828 ret = parse_events(evlist, e->name, 0);
829 if (ret) {
830 pr_debug("failed to parse event '%s', err %d\n",
831 e->name, ret);
832 break;
833 }
834
835 ret = e->check(evlist);
836 if (ret)
837 break;
838
839 perf_evlist__delete(evlist);
840 }
841
842 return ret;
843}
844static struct test {
845 const char *desc;
846 int (*func)(void);
847} tests[] = {
848 {
849 .desc = "vmlinux symtab matches kallsyms",
850 .func = test__vmlinux_matches_kallsyms,
851 },
852 {
853 .desc = "detect open syscall event",
854 .func = test__open_syscall_event,
855 },
856 {
857 .desc = "detect open syscall event on all cpus",
858 .func = test__open_syscall_event_on_all_cpus,
859 },
860 {
861 .desc = "read samples using the mmap interface",
862 .func = test__basic_mmap,
863 },
864 {
865 .desc = "parse events tests",
866 .func = test__parse_events,
867 },
868 {
869 .func = NULL,
870 },
871};
872
873static int __cmd_test(void)
874{
875 int i = 0;
876
877 page_size = sysconf(_SC_PAGE_SIZE);
878
879 while (tests[i].func) {
880 int err;
881 pr_info("%2d: %s:", i + 1, tests[i].desc);
882 pr_debug("\n--- start ---\n");
883 err = tests[i].func();
884 pr_debug("---- end ----\n%s:", tests[i].desc);
885 pr_info(" %s\n", err ? "FAILED!\n" : "Ok");
886 ++i;
887 }
888
889 return 0;
890}
891
892static const char * const test_usage[] = {
893 "perf test [<options>]",
894 NULL,
895};
896
897static const struct option test_options[] = {
898 OPT_INTEGER('v', "verbose", &verbose,
899 "be more verbose (show symbol address, etc)"),
900 OPT_END()
901};
902
903int cmd_test(int argc, const char **argv, const char *prefix __used)
904{
905 argc = parse_options(argc, argv, test_options, test_usage, 0);
906 if (argc)
907 usage_with_options(test_usage, test_options);
908
909 symbol_conf.priv_size = sizeof(int);
910 symbol_conf.sort_by_name = true;
911 symbol_conf.try_vmlinux_path = true;
912
913 if (symbol__init() < 0)
914 return -1;
915
916 setup_pager();
917
918 return __cmd_test();
919}
diff --git a/tools/perf/util/include/linux/module.h b/tools/perf/util/include/linux/module.h
new file mode 100644
index 00000000000..b43e2dc21e0
--- /dev/null
+++ b/tools/perf/util/include/linux/module.h
@@ -0,0 +1,6 @@
1#ifndef PERF_LINUX_MODULE_H
2#define PERF_LINUX_MODULE_H
3
4#define EXPORT_SYMBOL(name)
5
6#endif
diff --git a/tools/perf/util/ui/browser.c b/tools/perf/util/ui/browser.c
new file mode 100644
index 00000000000..611219f8068
--- /dev/null
+++ b/tools/perf/util/ui/browser.c
@@ -0,0 +1,356 @@
1#include "libslang.h"
2#include "ui.h"
3#include <linux/compiler.h>
4#include <linux/list.h>
5#include <linux/rbtree.h>
6#include <stdlib.h>
7#include <sys/ttydefaults.h>
8#include "browser.h"
9#include "helpline.h"
10#include "../color.h"
11#include "../util.h"
12#include <stdio.h>
13
14static int ui_browser__percent_color(double percent, bool current)
15{
16 if (current)
17 return HE_COLORSET_SELECTED;
18 if (percent >= MIN_RED)
19 return HE_COLORSET_TOP;
20 if (percent >= MIN_GREEN)
21 return HE_COLORSET_MEDIUM;
22 return HE_COLORSET_NORMAL;
23}
24
25void ui_browser__set_color(struct ui_browser *self __used, int color)
26{
27 SLsmg_set_color(color);
28}
29
30void ui_browser__set_percent_color(struct ui_browser *self,
31 double percent, bool current)
32{
33 int color = ui_browser__percent_color(percent, current);
34 ui_browser__set_color(self, color);
35}
36
37void ui_browser__gotorc(struct ui_browser *self, int y, int x)
38{
39 SLsmg_gotorc(self->y + y, self->x + x);
40}
41
42void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence)
43{
44 struct list_head *head = self->entries;
45 struct list_head *pos;
46
47 switch (whence) {
48 case SEEK_SET:
49 pos = head->next;
50 break;
51 case SEEK_CUR:
52 pos = self->top;
53 break;
54 case SEEK_END:
55 pos = head->prev;
56 break;
57 default:
58 return;
59 }
60
61 if (offset > 0) {
62 while (offset-- != 0)
63 pos = pos->next;
64 } else {
65 while (offset++ != 0)
66 pos = pos->prev;
67 }
68
69 self->top = pos;
70}
71
72void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence)
73{
74 struct rb_root *root = self->entries;
75 struct rb_node *nd;
76
77 switch (whence) {
78 case SEEK_SET:
79 nd = rb_first(root);
80 break;
81 case SEEK_CUR:
82 nd = self->top;
83 break;
84 case SEEK_END:
85 nd = rb_last(root);
86 break;
87 default:
88 return;
89 }
90
91 if (offset > 0) {
92 while (offset-- != 0)
93 nd = rb_next(nd);
94 } else {
95 while (offset++ != 0)
96 nd = rb_prev(nd);
97 }
98
99 self->top = nd;
100}
101
102unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self)
103{
104 struct rb_node *nd;
105 int row = 0;
106
107 if (self->top == NULL)
108 self->top = rb_first(self->entries);
109
110 nd = self->top;
111
112 while (nd != NULL) {
113 ui_browser__gotorc(self, row, 0);
114 self->write(self, nd, row);
115 if (++row == self->height)
116 break;
117 nd = rb_next(nd);
118 }
119
120 return row;
121}
122
123bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row)
124{
125 return self->top_idx + row == self->index;
126}
127
128void ui_browser__refresh_dimensions(struct ui_browser *self)
129{
130 int cols, rows;
131 newtGetScreenSize(&cols, &rows);
132
133 self->width = cols - 1;
134 self->height = rows - 2;
135 self->y = 1;
136 self->x = 0;
137}
138
139void ui_browser__reset_index(struct ui_browser *self)
140{
141 self->index = self->top_idx = 0;
142 self->seek(self, 0, SEEK_SET);
143}
144
145void ui_browser__add_exit_key(struct ui_browser *self, int key)
146{
147 newtFormAddHotKey(self->form, key);
148}
149
150void ui_browser__add_exit_keys(struct ui_browser *self, int keys[])
151{
152 int i = 0;
153
154 while (keys[i] && i < 64) {
155 ui_browser__add_exit_key(self, keys[i]);
156 ++i;
157 }
158}
159
160void __ui_browser__show_title(struct ui_browser *browser, const char *title)
161{
162 SLsmg_gotorc(0, 0);
163 ui_browser__set_color(browser, NEWT_COLORSET_ROOT);
164 slsmg_write_nstring(title, browser->width);
165}
166
167void ui_browser__show_title(struct ui_browser *browser, const char *title)
168{
169 pthread_mutex_lock(&ui__lock);
170 __ui_browser__show_title(browser, title);
171 pthread_mutex_unlock(&ui__lock);
172}
173
174int ui_browser__show(struct ui_browser *self, const char *title,
175 const char *helpline, ...)
176{
177 va_list ap;
178 int keys[] = { NEWT_KEY_UP, NEWT_KEY_DOWN, NEWT_KEY_PGUP,
179 NEWT_KEY_PGDN, NEWT_KEY_HOME, NEWT_KEY_END, ' ',
180 NEWT_KEY_LEFT, NEWT_KEY_ESCAPE, 'q', CTRL('c'), 0 };
181
182 if (self->form != NULL)
183 newtFormDestroy(self->form);
184
185 ui_browser__refresh_dimensions(self);
186 self->form = newtForm(NULL, NULL, 0);
187 if (self->form == NULL)
188 return -1;
189
190 self->sb = newtVerticalScrollbar(self->width, 1, self->height,
191 HE_COLORSET_NORMAL,
192 HE_COLORSET_SELECTED);
193 if (self->sb == NULL)
194 return -1;
195
196 pthread_mutex_lock(&ui__lock);
197 __ui_browser__show_title(self, title);
198
199 ui_browser__add_exit_keys(self, keys);
200 newtFormAddComponent(self->form, self->sb);
201
202 va_start(ap, helpline);
203 ui_helpline__vpush(helpline, ap);
204 va_end(ap);
205 pthread_mutex_unlock(&ui__lock);
206 return 0;
207}
208
209void ui_browser__hide(struct ui_browser *self)
210{
211 pthread_mutex_lock(&ui__lock);
212 newtFormDestroy(self->form);
213 self->form = NULL;
214 ui_helpline__pop();
215 pthread_mutex_unlock(&ui__lock);
216}
217
218int ui_browser__refresh(struct ui_browser *self)
219{
220 int row;
221
222 pthread_mutex_lock(&ui__lock);
223 newtScrollbarSet(self->sb, self->index, self->nr_entries - 1);
224 row = self->refresh(self);
225 ui_browser__set_color(self, HE_COLORSET_NORMAL);
226 SLsmg_fill_region(self->y + row, self->x,
227 self->height - row, self->width, ' ');
228 pthread_mutex_unlock(&ui__lock);
229
230 return 0;
231}
232
233int ui_browser__run(struct ui_browser *self)
234{
235 struct newtExitStruct es;
236
237 if (ui_browser__refresh(self) < 0)
238 return -1;
239
240 while (1) {
241 off_t offset;
242
243 newtFormRun(self->form, &es);
244
245 if (es.reason != NEWT_EXIT_HOTKEY)
246 break;
247 switch (es.u.key) {
248 case NEWT_KEY_DOWN:
249 if (self->index == self->nr_entries - 1)
250 break;
251 ++self->index;
252 if (self->index == self->top_idx + self->height) {
253 ++self->top_idx;
254 self->seek(self, +1, SEEK_CUR);
255 }
256 break;
257 case NEWT_KEY_UP:
258 if (self->index == 0)
259 break;
260 --self->index;
261 if (self->index < self->top_idx) {
262 --self->top_idx;
263 self->seek(self, -1, SEEK_CUR);
264 }
265 break;
266 case NEWT_KEY_PGDN:
267 case ' ':
268 if (self->top_idx + self->height > self->nr_entries - 1)
269 break;
270
271 offset = self->height;
272 if (self->index + offset > self->nr_entries - 1)
273 offset = self->nr_entries - 1 - self->index;
274 self->index += offset;
275 self->top_idx += offset;
276 self->seek(self, +offset, SEEK_CUR);
277 break;
278 case NEWT_KEY_PGUP:
279 if (self->top_idx == 0)
280 break;
281
282 if (self->top_idx < self->height)
283 offset = self->top_idx;
284 else
285 offset = self->height;
286
287 self->index -= offset;
288 self->top_idx -= offset;
289 self->seek(self, -offset, SEEK_CUR);
290 break;
291 case NEWT_KEY_HOME:
292 ui_browser__reset_index(self);
293 break;
294 case NEWT_KEY_END:
295 offset = self->height - 1;
296 if (offset >= self->nr_entries)
297 offset = self->nr_entries - 1;
298
299 self->index = self->nr_entries - 1;
300 self->top_idx = self->index - offset;
301 self->seek(self, -offset, SEEK_END);
302 break;
303 default:
304 return es.u.key;
305 }
306 if (ui_browser__refresh(self) < 0)
307 return -1;
308 }
309 return -1;
310}
311
312unsigned int ui_browser__list_head_refresh(struct ui_browser *self)
313{
314 struct list_head *pos;
315 struct list_head *head = self->entries;
316 int row = 0;
317
318 if (self->top == NULL || self->top == self->entries)
319 self->top = head->next;
320
321 pos = self->top;
322
323 list_for_each_from(pos, head) {
324 ui_browser__gotorc(self, row, 0);
325 self->write(self, pos, row);
326 if (++row == self->height)
327 break;
328 }
329
330 return row;
331}
332
333static struct newtPercentTreeColors {
334 const char *topColorFg, *topColorBg;
335 const char *mediumColorFg, *mediumColorBg;
336 const char *normalColorFg, *normalColorBg;
337 const char *selColorFg, *selColorBg;
338 const char *codeColorFg, *codeColorBg;
339} defaultPercentTreeColors = {
340 "red", "lightgray",
341 "green", "lightgray",
342 "black", "lightgray",
343 "lightgray", "magenta",
344 "blue", "lightgray",
345};
346
347void ui_browser__init(void)
348{
349 struct newtPercentTreeColors *c = &defaultPercentTreeColors;
350
351 sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg);
352 sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg);
353 sltt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg);
354 sltt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg);
355 sltt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg);
356}
diff --git a/tools/perf/util/ui/browser.h b/tools/perf/util/ui/browser.h
new file mode 100644
index 00000000000..fc63dda1091
--- /dev/null
+++ b/tools/perf/util/ui/browser.h
@@ -0,0 +1,52 @@
1#ifndef _PERF_UI_BROWSER_H_
2#define _PERF_UI_BROWSER_H_ 1
3
4#include <stdbool.h>
5#include <newt.h>
6#include <sys/types.h>
7#include "../types.h"
8
9#define HE_COLORSET_TOP 50
10#define HE_COLORSET_MEDIUM 51
11#define HE_COLORSET_NORMAL 52
12#define HE_COLORSET_SELECTED 53
13#define HE_COLORSET_CODE 54
14
15struct ui_browser {
16 newtComponent form, sb;
17 u64 index, top_idx;
18 void *top, *entries;
19 u16 y, x, width, height;
20 void *priv;
21 unsigned int (*refresh)(struct ui_browser *self);
22 void (*write)(struct ui_browser *self, void *entry, int row);
23 void (*seek)(struct ui_browser *self, off_t offset, int whence);
24 u32 nr_entries;
25};
26
27void ui_browser__set_color(struct ui_browser *self, int color);
28void ui_browser__set_percent_color(struct ui_browser *self,
29 double percent, bool current);
30bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row);
31void ui_browser__refresh_dimensions(struct ui_browser *self);
32void ui_browser__reset_index(struct ui_browser *self);
33
34void ui_browser__gotorc(struct ui_browser *self, int y, int x);
35void ui_browser__add_exit_key(struct ui_browser *self, int key);
36void ui_browser__add_exit_keys(struct ui_browser *self, int keys[]);
37void __ui_browser__show_title(struct ui_browser *browser, const char *title);
38void ui_browser__show_title(struct ui_browser *browser, const char *title);
39int ui_browser__show(struct ui_browser *self, const char *title,
40 const char *helpline, ...);
41void ui_browser__hide(struct ui_browser *self);
42int ui_browser__refresh(struct ui_browser *self);
43int ui_browser__run(struct ui_browser *self);
44
45void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence);
46unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self);
47
48void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence);
49unsigned int ui_browser__list_head_refresh(struct ui_browser *self);
50
51void ui_browser__init(void);
52#endif /* _PERF_UI_BROWSER_H_ */
diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c
new file mode 100644
index 00000000000..0229723aceb
--- /dev/null
+++ b/tools/perf/util/ui/browsers/annotate.c
@@ -0,0 +1,302 @@
1#include "../browser.h"
2#include "../helpline.h"
3#include "../libslang.h"
4#include "../../annotate.h"
5#include "../../hist.h"
6#include "../../sort.h"
7#include "../../symbol.h"
8#include <pthread.h>
9
10static void ui__error_window(const char *fmt, ...)
11{
12 va_list ap;
13
14 va_start(ap, fmt);
15 newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap);
16 va_end(ap);
17}
18
19struct annotate_browser {
20 struct ui_browser b;
21 struct rb_root entries;
22 struct rb_node *curr_hot;
23};
24
25struct objdump_line_rb_node {
26 struct rb_node rb_node;
27 double percent;
28 u32 idx;
29};
30
31static inline
32struct objdump_line_rb_node *objdump_line__rb(struct objdump_line *self)
33{
34 return (struct objdump_line_rb_node *)(self + 1);
35}
36
37static void annotate_browser__write(struct ui_browser *self, void *entry, int row)
38{
39 struct objdump_line *ol = rb_entry(entry, struct objdump_line, node);
40 bool current_entry = ui_browser__is_current_entry(self, row);
41 int width = self->width;
42
43 if (ol->offset != -1) {
44 struct objdump_line_rb_node *olrb = objdump_line__rb(ol);
45 ui_browser__set_percent_color(self, olrb->percent, current_entry);
46 slsmg_printf(" %7.2f ", olrb->percent);
47 } else {
48 ui_browser__set_percent_color(self, 0, current_entry);
49 slsmg_write_nstring(" ", 9);
50 }
51
52 SLsmg_write_char(':');
53 slsmg_write_nstring(" ", 8);
54 if (!*ol->line)
55 slsmg_write_nstring(" ", width - 18);
56 else
57 slsmg_write_nstring(ol->line, width - 18);
58
59 if (!current_entry)
60 ui_browser__set_color(self, HE_COLORSET_CODE);
61}
62
63static double objdump_line__calc_percent(struct objdump_line *self,
64 struct symbol *sym, int evidx)
65{
66 double percent = 0.0;
67
68 if (self->offset != -1) {
69 int len = sym->end - sym->start;
70 unsigned int hits = 0;
71 struct annotation *notes = symbol__annotation(sym);
72 struct source_line *src_line = notes->src->lines;
73 struct sym_hist *h = annotation__histogram(notes, evidx);
74 s64 offset = self->offset;
75 struct objdump_line *next;
76
77 next = objdump__get_next_ip_line(&notes->src->source, self);
78 while (offset < (s64)len &&
79 (next == NULL || offset < next->offset)) {
80 if (src_line) {
81 percent += src_line[offset].percent;
82 } else
83 hits += h->addr[offset];
84
85 ++offset;
86 }
87 /*
88 * If the percentage wasn't already calculated in
89 * symbol__get_source_line, do it now:
90 */
91 if (src_line == NULL && h->sum)
92 percent = 100.0 * hits / h->sum;
93 }
94
95 return percent;
96}
97
98static void objdump__insert_line(struct rb_root *self,
99 struct objdump_line_rb_node *line)
100{
101 struct rb_node **p = &self->rb_node;
102 struct rb_node *parent = NULL;
103 struct objdump_line_rb_node *l;
104
105 while (*p != NULL) {
106 parent = *p;
107 l = rb_entry(parent, struct objdump_line_rb_node, rb_node);
108 if (line->percent < l->percent)
109 p = &(*p)->rb_left;
110 else
111 p = &(*p)->rb_right;
112 }
113 rb_link_node(&line->rb_node, parent, p);
114 rb_insert_color(&line->rb_node, self);
115}
116
117static void annotate_browser__set_top(struct annotate_browser *self,
118 struct rb_node *nd)
119{
120 struct objdump_line_rb_node *rbpos;
121 struct objdump_line *pos;
122 unsigned back;
123
124 ui_browser__refresh_dimensions(&self->b);
125 back = self->b.height / 2;
126 rbpos = rb_entry(nd, struct objdump_line_rb_node, rb_node);
127 pos = ((struct objdump_line *)rbpos) - 1;
128 self->b.top_idx = self->b.index = rbpos->idx;
129
130 while (self->b.top_idx != 0 && back != 0) {
131 pos = list_entry(pos->node.prev, struct objdump_line, node);
132
133 --self->b.top_idx;
134 --back;
135 }
136
137 self->b.top = pos;
138 self->curr_hot = nd;
139}
140
141static void annotate_browser__calc_percent(struct annotate_browser *browser,
142 int evidx)
143{
144 struct symbol *sym = browser->b.priv;
145 struct annotation *notes = symbol__annotation(sym);
146 struct objdump_line *pos;
147
148 browser->entries = RB_ROOT;
149
150 pthread_mutex_lock(&notes->lock);
151
152 list_for_each_entry(pos, &notes->src->source, node) {
153 struct objdump_line_rb_node *rbpos = objdump_line__rb(pos);
154 rbpos->percent = objdump_line__calc_percent(pos, sym, evidx);
155 if (rbpos->percent < 0.01) {
156 RB_CLEAR_NODE(&rbpos->rb_node);
157 continue;
158 }
159 objdump__insert_line(&browser->entries, rbpos);
160 }
161 pthread_mutex_unlock(&notes->lock);
162
163 browser->curr_hot = rb_last(&browser->entries);
164}
165
166static int annotate_browser__run(struct annotate_browser *self, int evidx,
167 int refresh)
168{
169 struct rb_node *nd = NULL;
170 struct symbol *sym = self->b.priv;
171 /*
172 * RIGHT To allow builtin-annotate to cycle thru multiple symbols by
173 * examining the exit key for this function.
174 */
175 int exit_keys[] = { 'H', NEWT_KEY_TAB, NEWT_KEY_UNTAB,
176 NEWT_KEY_RIGHT, 0 };
177 int key;
178
179 if (ui_browser__show(&self->b, sym->name,
180 "<-, -> or ESC: exit, TAB/shift+TAB: "
181 "cycle hottest lines, H: Hottest") < 0)
182 return -1;
183
184 ui_browser__add_exit_keys(&self->b, exit_keys);
185 annotate_browser__calc_percent(self, evidx);
186
187 if (self->curr_hot)
188 annotate_browser__set_top(self, self->curr_hot);
189
190 nd = self->curr_hot;
191
192 if (refresh != 0)
193 newtFormSetTimer(self->b.form, refresh);
194
195 while (1) {
196 key = ui_browser__run(&self->b);
197
198 if (refresh != 0) {
199 annotate_browser__calc_percent(self, evidx);
200 /*
201 * Current line focus got out of the list of most active
202 * lines, NULL it so that if TAB|UNTAB is pressed, we
203 * move to curr_hot (current hottest line).
204 */
205 if (nd != NULL && RB_EMPTY_NODE(nd))
206 nd = NULL;
207 }
208
209 switch (key) {
210 case -1:
211 /*
212 * FIXME we need to check if it was
213 * es.reason == NEWT_EXIT_TIMER
214 */
215 if (refresh != 0)
216 symbol__annotate_decay_histogram(sym, evidx);
217 continue;
218 case NEWT_KEY_TAB:
219 if (nd != NULL) {
220 nd = rb_prev(nd);
221 if (nd == NULL)
222 nd = rb_last(&self->entries);
223 } else
224 nd = self->curr_hot;
225 break;
226 case NEWT_KEY_UNTAB:
227 if (nd != NULL)
228 nd = rb_next(nd);
229 if (nd == NULL)
230 nd = rb_first(&self->entries);
231 else
232 nd = self->curr_hot;
233 break;
234 case 'H':
235 nd = self->curr_hot;
236 break;
237 default:
238 goto out;
239 }
240
241 if (nd != NULL)
242 annotate_browser__set_top(self, nd);
243 }
244out:
245 ui_browser__hide(&self->b);
246 return key;
247}
248
249int hist_entry__tui_annotate(struct hist_entry *he, int evidx)
250{
251 return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, 0);
252}
253
254int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
255 int refresh)
256{
257 struct objdump_line *pos, *n;
258 struct annotation *notes;
259 struct annotate_browser browser = {
260 .b = {
261 .refresh = ui_browser__list_head_refresh,
262 .seek = ui_browser__list_head_seek,
263 .write = annotate_browser__write,
264 .priv = sym,
265 },
266 };
267 int ret;
268
269 if (sym == NULL)
270 return -1;
271
272 if (map->dso->annotate_warned)
273 return -1;
274
275 if (symbol__annotate(sym, map, sizeof(struct objdump_line_rb_node)) < 0) {
276 ui__error_window(ui_helpline__last_msg);
277 return -1;
278 }
279
280 ui_helpline__push("Press <- or ESC to exit");
281
282 notes = symbol__annotation(sym);
283
284 list_for_each_entry(pos, &notes->src->source, node) {
285 struct objdump_line_rb_node *rbpos;
286 size_t line_len = strlen(pos->line);
287
288 if (browser.b.width < line_len)
289 browser.b.width = line_len;
290 rbpos = objdump_line__rb(pos);
291 rbpos->idx = browser.b.nr_entries++;
292 }
293
294 browser.b.entries = &notes->src->source,
295 browser.b.width += 18; /* Percentage */
296 ret = annotate_browser__run(&browser, evidx, refresh);
297 list_for_each_entry_safe(pos, n, &notes->src->source, node) {
298 list_del(&pos->node);
299 objdump_line__free(pos);
300 }
301 return ret;
302}
diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c
new file mode 100644
index 00000000000..5d767c622df
--- /dev/null
+++ b/tools/perf/util/ui/browsers/hists.c
@@ -0,0 +1,1138 @@
1#define _GNU_SOURCE
2#include <stdio.h>
3#undef _GNU_SOURCE
4#include "../libslang.h"
5#include <stdlib.h>
6#include <string.h>
7#include <newt.h>
8#include <linux/rbtree.h>
9
10#include "../../evsel.h"
11#include "../../evlist.h"
12#include "../../hist.h"
13#include "../../pstack.h"
14#include "../../sort.h"
15#include "../../util.h"
16
17#include "../browser.h"
18#include "../helpline.h"
19#include "../util.h"
20#include "map.h"
21
22struct hist_browser {
23 struct ui_browser b;
24 struct hists *hists;
25 struct hist_entry *he_selection;
26 struct map_symbol *selection;
27};
28
29static void hist_browser__refresh_dimensions(struct hist_browser *self)
30{
31 /* 3 == +/- toggle symbol before actual hist_entry rendering */
32 self->b.width = 3 + (hists__sort_list_width(self->hists) +
33 sizeof("[k]"));
34}
35
36static void hist_browser__reset(struct hist_browser *self)
37{
38 self->b.nr_entries = self->hists->nr_entries;
39 hist_browser__refresh_dimensions(self);
40 ui_browser__reset_index(&self->b);
41}
42
43static char tree__folded_sign(bool unfolded)
44{
45 return unfolded ? '-' : '+';
46}
47
48static char map_symbol__folded(const struct map_symbol *self)
49{
50 return self->has_children ? tree__folded_sign(self->unfolded) : ' ';
51}
52
53static char hist_entry__folded(const struct hist_entry *self)
54{
55 return map_symbol__folded(&self->ms);
56}
57
58static char callchain_list__folded(const struct callchain_list *self)
59{
60 return map_symbol__folded(&self->ms);
61}
62
63static void map_symbol__set_folding(struct map_symbol *self, bool unfold)
64{
65 self->unfolded = unfold ? self->has_children : false;
66}
67
68static int callchain_node__count_rows_rb_tree(struct callchain_node *self)
69{
70 int n = 0;
71 struct rb_node *nd;
72
73 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
74 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
75 struct callchain_list *chain;
76 char folded_sign = ' '; /* No children */
77
78 list_for_each_entry(chain, &child->val, list) {
79 ++n;
80 /* We need this because we may not have children */
81 folded_sign = callchain_list__folded(chain);
82 if (folded_sign == '+')
83 break;
84 }
85
86 if (folded_sign == '-') /* Have children and they're unfolded */
87 n += callchain_node__count_rows_rb_tree(child);
88 }
89
90 return n;
91}
92
93static int callchain_node__count_rows(struct callchain_node *node)
94{
95 struct callchain_list *chain;
96 bool unfolded = false;
97 int n = 0;
98
99 list_for_each_entry(chain, &node->val, list) {
100 ++n;
101 unfolded = chain->ms.unfolded;
102 }
103
104 if (unfolded)
105 n += callchain_node__count_rows_rb_tree(node);
106
107 return n;
108}
109
110static int callchain__count_rows(struct rb_root *chain)
111{
112 struct rb_node *nd;
113 int n = 0;
114
115 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
116 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
117 n += callchain_node__count_rows(node);
118 }
119
120 return n;
121}
122
123static bool map_symbol__toggle_fold(struct map_symbol *self)
124{
125 if (!self->has_children)
126 return false;
127
128 self->unfolded = !self->unfolded;
129 return true;
130}
131
132static void callchain_node__init_have_children_rb_tree(struct callchain_node *self)
133{
134 struct rb_node *nd = rb_first(&self->rb_root);
135
136 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
137 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
138 struct callchain_list *chain;
139 bool first = true;
140
141 list_for_each_entry(chain, &child->val, list) {
142 if (first) {
143 first = false;
144 chain->ms.has_children = chain->list.next != &child->val ||
145 !RB_EMPTY_ROOT(&child->rb_root);
146 } else
147 chain->ms.has_children = chain->list.next == &child->val &&
148 !RB_EMPTY_ROOT(&child->rb_root);
149 }
150
151 callchain_node__init_have_children_rb_tree(child);
152 }
153}
154
155static void callchain_node__init_have_children(struct callchain_node *self)
156{
157 struct callchain_list *chain;
158
159 list_for_each_entry(chain, &self->val, list)
160 chain->ms.has_children = !RB_EMPTY_ROOT(&self->rb_root);
161
162 callchain_node__init_have_children_rb_tree(self);
163}
164
165static void callchain__init_have_children(struct rb_root *self)
166{
167 struct rb_node *nd;
168
169 for (nd = rb_first(self); nd; nd = rb_next(nd)) {
170 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
171 callchain_node__init_have_children(node);
172 }
173}
174
175static void hist_entry__init_have_children(struct hist_entry *self)
176{
177 if (!self->init_have_children) {
178 self->ms.has_children = !RB_EMPTY_ROOT(&self->sorted_chain);
179 callchain__init_have_children(&self->sorted_chain);
180 self->init_have_children = true;
181 }
182}
183
184static bool hist_browser__toggle_fold(struct hist_browser *self)
185{
186 if (map_symbol__toggle_fold(self->selection)) {
187 struct hist_entry *he = self->he_selection;
188
189 hist_entry__init_have_children(he);
190 self->hists->nr_entries -= he->nr_rows;
191
192 if (he->ms.unfolded)
193 he->nr_rows = callchain__count_rows(&he->sorted_chain);
194 else
195 he->nr_rows = 0;
196 self->hists->nr_entries += he->nr_rows;
197 self->b.nr_entries = self->hists->nr_entries;
198
199 return true;
200 }
201
202 /* If it doesn't have children, no toggling performed */
203 return false;
204}
205
206static int callchain_node__set_folding_rb_tree(struct callchain_node *self, bool unfold)
207{
208 int n = 0;
209 struct rb_node *nd;
210
211 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
212 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
213 struct callchain_list *chain;
214 bool has_children = false;
215
216 list_for_each_entry(chain, &child->val, list) {
217 ++n;
218 map_symbol__set_folding(&chain->ms, unfold);
219 has_children = chain->ms.has_children;
220 }
221
222 if (has_children)
223 n += callchain_node__set_folding_rb_tree(child, unfold);
224 }
225
226 return n;
227}
228
229static int callchain_node__set_folding(struct callchain_node *node, bool unfold)
230{
231 struct callchain_list *chain;
232 bool has_children = false;
233 int n = 0;
234
235 list_for_each_entry(chain, &node->val, list) {
236 ++n;
237 map_symbol__set_folding(&chain->ms, unfold);
238 has_children = chain->ms.has_children;
239 }
240
241 if (has_children)
242 n += callchain_node__set_folding_rb_tree(node, unfold);
243
244 return n;
245}
246
247static int callchain__set_folding(struct rb_root *chain, bool unfold)
248{
249 struct rb_node *nd;
250 int n = 0;
251
252 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
253 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
254 n += callchain_node__set_folding(node, unfold);
255 }
256
257 return n;
258}
259
260static void hist_entry__set_folding(struct hist_entry *self, bool unfold)
261{
262 hist_entry__init_have_children(self);
263 map_symbol__set_folding(&self->ms, unfold);
264
265 if (self->ms.has_children) {
266 int n = callchain__set_folding(&self->sorted_chain, unfold);
267 self->nr_rows = unfold ? n : 0;
268 } else
269 self->nr_rows = 0;
270}
271
272static void hists__set_folding(struct hists *self, bool unfold)
273{
274 struct rb_node *nd;
275
276 self->nr_entries = 0;
277
278 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
279 struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
280 hist_entry__set_folding(he, unfold);
281 self->nr_entries += 1 + he->nr_rows;
282 }
283}
284
285static void hist_browser__set_folding(struct hist_browser *self, bool unfold)
286{
287 hists__set_folding(self->hists, unfold);
288 self->b.nr_entries = self->hists->nr_entries;
289 /* Go to the start, we may be way after valid entries after a collapse */
290 ui_browser__reset_index(&self->b);
291}
292
293static int hist_browser__run(struct hist_browser *self, const char *title)
294{
295 int key;
296 int exit_keys[] = { 'a', '?', 'h', 'C', 'd', 'D', 'E', 't',
297 NEWT_KEY_ENTER, NEWT_KEY_RIGHT, NEWT_KEY_LEFT,
298 NEWT_KEY_TAB, NEWT_KEY_UNTAB, 0, };
299
300 self->b.entries = &self->hists->entries;
301 self->b.nr_entries = self->hists->nr_entries;
302
303 hist_browser__refresh_dimensions(self);
304
305 if (ui_browser__show(&self->b, title,
306 "Press '?' for help on key bindings") < 0)
307 return -1;
308
309 ui_browser__add_exit_keys(&self->b, exit_keys);
310
311 while (1) {
312 key = ui_browser__run(&self->b);
313
314 switch (key) {
315 case 'D': { /* Debug */
316 static int seq;
317 struct hist_entry *h = rb_entry(self->b.top,
318 struct hist_entry, rb_node);
319 ui_helpline__pop();
320 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
321 seq++, self->b.nr_entries,
322 self->hists->nr_entries,
323 self->b.height,
324 self->b.index,
325 self->b.top_idx,
326 h->row_offset, h->nr_rows);
327 }
328 break;
329 case 'C':
330 /* Collapse the whole world. */
331 hist_browser__set_folding(self, false);
332 break;
333 case 'E':
334 /* Expand the whole world. */
335 hist_browser__set_folding(self, true);
336 break;
337 case NEWT_KEY_ENTER:
338 if (hist_browser__toggle_fold(self))
339 break;
340 /* fall thru */
341 default:
342 goto out;
343 }
344 }
345out:
346 ui_browser__hide(&self->b);
347 return key;
348}
349
350static char *callchain_list__sym_name(struct callchain_list *self,
351 char *bf, size_t bfsize)
352{
353 if (self->ms.sym)
354 return self->ms.sym->name;
355
356 snprintf(bf, bfsize, "%#" PRIx64, self->ip);
357 return bf;
358}
359
360#define LEVEL_OFFSET_STEP 3
361
362static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
363 struct callchain_node *chain_node,
364 u64 total, int level,
365 unsigned short row,
366 off_t *row_offset,
367 bool *is_current_entry)
368{
369 struct rb_node *node;
370 int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
371 u64 new_total, remaining;
372
373 if (callchain_param.mode == CHAIN_GRAPH_REL)
374 new_total = chain_node->children_hit;
375 else
376 new_total = total;
377
378 remaining = new_total;
379 node = rb_first(&chain_node->rb_root);
380 while (node) {
381 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
382 struct rb_node *next = rb_next(node);
383 u64 cumul = callchain_cumul_hits(child);
384 struct callchain_list *chain;
385 char folded_sign = ' ';
386 int first = true;
387 int extra_offset = 0;
388
389 remaining -= cumul;
390
391 list_for_each_entry(chain, &child->val, list) {
392 char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str;
393 const char *str;
394 int color;
395 bool was_first = first;
396
397 if (first)
398 first = false;
399 else
400 extra_offset = LEVEL_OFFSET_STEP;
401
402 folded_sign = callchain_list__folded(chain);
403 if (*row_offset != 0) {
404 --*row_offset;
405 goto do_next;
406 }
407
408 alloc_str = NULL;
409 str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
410 if (was_first) {
411 double percent = cumul * 100.0 / new_total;
412
413 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
414 str = "Not enough memory!";
415 else
416 str = alloc_str;
417 }
418
419 color = HE_COLORSET_NORMAL;
420 width = self->b.width - (offset + extra_offset + 2);
421 if (ui_browser__is_current_entry(&self->b, row)) {
422 self->selection = &chain->ms;
423 color = HE_COLORSET_SELECTED;
424 *is_current_entry = true;
425 }
426
427 ui_browser__set_color(&self->b, color);
428 ui_browser__gotorc(&self->b, row, 0);
429 slsmg_write_nstring(" ", offset + extra_offset);
430 slsmg_printf("%c ", folded_sign);
431 slsmg_write_nstring(str, width);
432 free(alloc_str);
433
434 if (++row == self->b.height)
435 goto out;
436do_next:
437 if (folded_sign == '+')
438 break;
439 }
440
441 if (folded_sign == '-') {
442 const int new_level = level + (extra_offset ? 2 : 1);
443 row += hist_browser__show_callchain_node_rb_tree(self, child, new_total,
444 new_level, row, row_offset,
445 is_current_entry);
446 }
447 if (row == self->b.height)
448 goto out;
449 node = next;
450 }
451out:
452 return row - first_row;
453}
454
455static int hist_browser__show_callchain_node(struct hist_browser *self,
456 struct callchain_node *node,
457 int level, unsigned short row,
458 off_t *row_offset,
459 bool *is_current_entry)
460{
461 struct callchain_list *chain;
462 int first_row = row,
463 offset = level * LEVEL_OFFSET_STEP,
464 width = self->b.width - offset;
465 char folded_sign = ' ';
466
467 list_for_each_entry(chain, &node->val, list) {
468 char ipstr[BITS_PER_LONG / 4 + 1], *s;
469 int color;
470
471 folded_sign = callchain_list__folded(chain);
472
473 if (*row_offset != 0) {
474 --*row_offset;
475 continue;
476 }
477
478 color = HE_COLORSET_NORMAL;
479 if (ui_browser__is_current_entry(&self->b, row)) {
480 self->selection = &chain->ms;
481 color = HE_COLORSET_SELECTED;
482 *is_current_entry = true;
483 }
484
485 s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
486 ui_browser__gotorc(&self->b, row, 0);
487 ui_browser__set_color(&self->b, color);
488 slsmg_write_nstring(" ", offset);
489 slsmg_printf("%c ", folded_sign);
490 slsmg_write_nstring(s, width - 2);
491
492 if (++row == self->b.height)
493 goto out;
494 }
495
496 if (folded_sign == '-')
497 row += hist_browser__show_callchain_node_rb_tree(self, node,
498 self->hists->stats.total_period,
499 level + 1, row,
500 row_offset,
501 is_current_entry);
502out:
503 return row - first_row;
504}
505
506static int hist_browser__show_callchain(struct hist_browser *self,
507 struct rb_root *chain,
508 int level, unsigned short row,
509 off_t *row_offset,
510 bool *is_current_entry)
511{
512 struct rb_node *nd;
513 int first_row = row;
514
515 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
516 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
517
518 row += hist_browser__show_callchain_node(self, node, level,
519 row, row_offset,
520 is_current_entry);
521 if (row == self->b.height)
522 break;
523 }
524
525 return row - first_row;
526}
527
528static int hist_browser__show_entry(struct hist_browser *self,
529 struct hist_entry *entry,
530 unsigned short row)
531{
532 char s[256];
533 double percent;
534 int printed = 0;
535 int color, width = self->b.width;
536 char folded_sign = ' ';
537 bool current_entry = ui_browser__is_current_entry(&self->b, row);
538 off_t row_offset = entry->row_offset;
539
540 if (current_entry) {
541 self->he_selection = entry;
542 self->selection = &entry->ms;
543 }
544
545 if (symbol_conf.use_callchain) {
546 hist_entry__init_have_children(entry);
547 folded_sign = hist_entry__folded(entry);
548 }
549
550 if (row_offset == 0) {
551 hist_entry__snprintf(entry, s, sizeof(s), self->hists, NULL, false,
552 0, false, self->hists->stats.total_period);
553 percent = (entry->period * 100.0) / self->hists->stats.total_period;
554
555 color = HE_COLORSET_SELECTED;
556 if (!current_entry) {
557 if (percent >= MIN_RED)
558 color = HE_COLORSET_TOP;
559 else if (percent >= MIN_GREEN)
560 color = HE_COLORSET_MEDIUM;
561 else
562 color = HE_COLORSET_NORMAL;
563 }
564
565 ui_browser__set_color(&self->b, color);
566 ui_browser__gotorc(&self->b, row, 0);
567 if (symbol_conf.use_callchain) {
568 slsmg_printf("%c ", folded_sign);
569 width -= 2;
570 }
571 slsmg_write_nstring(s, width);
572 ++row;
573 ++printed;
574 } else
575 --row_offset;
576
577 if (folded_sign == '-' && row != self->b.height) {
578 printed += hist_browser__show_callchain(self, &entry->sorted_chain,
579 1, row, &row_offset,
580 &current_entry);
581 if (current_entry)
582 self->he_selection = entry;
583 }
584
585 return printed;
586}
587
588static unsigned int hist_browser__refresh(struct ui_browser *self)
589{
590 unsigned row = 0;
591 struct rb_node *nd;
592 struct hist_browser *hb = container_of(self, struct hist_browser, b);
593
594 if (self->top == NULL)
595 self->top = rb_first(&hb->hists->entries);
596
597 for (nd = self->top; nd; nd = rb_next(nd)) {
598 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
599
600 if (h->filtered)
601 continue;
602
603 row += hist_browser__show_entry(hb, h, row);
604 if (row == self->height)
605 break;
606 }
607
608 return row;
609}
610
611static struct rb_node *hists__filter_entries(struct rb_node *nd)
612{
613 while (nd != NULL) {
614 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
615 if (!h->filtered)
616 return nd;
617
618 nd = rb_next(nd);
619 }
620
621 return NULL;
622}
623
624static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
625{
626 while (nd != NULL) {
627 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
628 if (!h->filtered)
629 return nd;
630
631 nd = rb_prev(nd);
632 }
633
634 return NULL;
635}
636
637static void ui_browser__hists_seek(struct ui_browser *self,
638 off_t offset, int whence)
639{
640 struct hist_entry *h;
641 struct rb_node *nd;
642 bool first = true;
643
644 if (self->nr_entries == 0)
645 return;
646
647 switch (whence) {
648 case SEEK_SET:
649 nd = hists__filter_entries(rb_first(self->entries));
650 break;
651 case SEEK_CUR:
652 nd = self->top;
653 goto do_offset;
654 case SEEK_END:
655 nd = hists__filter_prev_entries(rb_last(self->entries));
656 first = false;
657 break;
658 default:
659 return;
660 }
661
662 /*
663 * Moves not relative to the first visible entry invalidates its
664 * row_offset:
665 */
666 h = rb_entry(self->top, struct hist_entry, rb_node);
667 h->row_offset = 0;
668
669 /*
670 * Here we have to check if nd is expanded (+), if it is we can't go
671 * the next top level hist_entry, instead we must compute an offset of
672 * what _not_ to show and not change the first visible entry.
673 *
674 * This offset increments when we are going from top to bottom and
675 * decreases when we're going from bottom to top.
676 *
677 * As we don't have backpointers to the top level in the callchains
678 * structure, we need to always print the whole hist_entry callchain,
679 * skipping the first ones that are before the first visible entry
680 * and stop when we printed enough lines to fill the screen.
681 */
682do_offset:
683 if (offset > 0) {
684 do {
685 h = rb_entry(nd, struct hist_entry, rb_node);
686 if (h->ms.unfolded) {
687 u16 remaining = h->nr_rows - h->row_offset;
688 if (offset > remaining) {
689 offset -= remaining;
690 h->row_offset = 0;
691 } else {
692 h->row_offset += offset;
693 offset = 0;
694 self->top = nd;
695 break;
696 }
697 }
698 nd = hists__filter_entries(rb_next(nd));
699 if (nd == NULL)
700 break;
701 --offset;
702 self->top = nd;
703 } while (offset != 0);
704 } else if (offset < 0) {
705 while (1) {
706 h = rb_entry(nd, struct hist_entry, rb_node);
707 if (h->ms.unfolded) {
708 if (first) {
709 if (-offset > h->row_offset) {
710 offset += h->row_offset;
711 h->row_offset = 0;
712 } else {
713 h->row_offset += offset;
714 offset = 0;
715 self->top = nd;
716 break;
717 }
718 } else {
719 if (-offset > h->nr_rows) {
720 offset += h->nr_rows;
721 h->row_offset = 0;
722 } else {
723 h->row_offset = h->nr_rows + offset;
724 offset = 0;
725 self->top = nd;
726 break;
727 }
728 }
729 }
730
731 nd = hists__filter_prev_entries(rb_prev(nd));
732 if (nd == NULL)
733 break;
734 ++offset;
735 self->top = nd;
736 if (offset == 0) {
737 /*
738 * Last unfiltered hist_entry, check if it is
739 * unfolded, if it is then we should have
740 * row_offset at its last entry.
741 */
742 h = rb_entry(nd, struct hist_entry, rb_node);
743 if (h->ms.unfolded)
744 h->row_offset = h->nr_rows;
745 break;
746 }
747 first = false;
748 }
749 } else {
750 self->top = nd;
751 h = rb_entry(nd, struct hist_entry, rb_node);
752 h->row_offset = 0;
753 }
754}
755
756static struct hist_browser *hist_browser__new(struct hists *hists)
757{
758 struct hist_browser *self = zalloc(sizeof(*self));
759
760 if (self) {
761 self->hists = hists;
762 self->b.refresh = hist_browser__refresh;
763 self->b.seek = ui_browser__hists_seek;
764 }
765
766 return self;
767}
768
769static void hist_browser__delete(struct hist_browser *self)
770{
771 free(self);
772}
773
774static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
775{
776 return self->he_selection;
777}
778
779static struct thread *hist_browser__selected_thread(struct hist_browser *self)
780{
781 return self->he_selection->thread;
782}
783
784static int hists__browser_title(struct hists *self, char *bf, size_t size,
785 const char *ev_name, const struct dso *dso,
786 const struct thread *thread)
787{
788 char unit;
789 int printed;
790 unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE];
791
792 nr_events = convert_unit(nr_events, &unit);
793 printed = snprintf(bf, size, "Events: %lu%c %s", nr_events, unit, ev_name);
794
795 if (thread)
796 printed += snprintf(bf + printed, size - printed,
797 ", Thread: %s(%d)",
798 (thread->comm_set ? thread->comm : ""),
799 thread->pid);
800 if (dso)
801 printed += snprintf(bf + printed, size - printed,
802 ", DSO: %s", dso->short_name);
803 return printed;
804}
805
806static int perf_evsel__hists_browse(struct perf_evsel *evsel,
807 const char *helpline, const char *ev_name,
808 bool left_exits)
809{
810 struct hists *self = &evsel->hists;
811 struct hist_browser *browser = hist_browser__new(self);
812 struct pstack *fstack;
813 const struct thread *thread_filter = NULL;
814 const struct dso *dso_filter = NULL;
815 char msg[160];
816 int key = -1;
817
818 if (browser == NULL)
819 return -1;
820
821 fstack = pstack__new(2);
822 if (fstack == NULL)
823 goto out;
824
825 ui_helpline__push(helpline);
826
827 hists__browser_title(self, msg, sizeof(msg), ev_name,
828 dso_filter, thread_filter);
829 while (1) {
830 const struct thread *thread = NULL;
831 const struct dso *dso = NULL;
832 char *options[16];
833 int nr_options = 0, choice = 0, i,
834 annotate = -2, zoom_dso = -2, zoom_thread = -2,
835 browse_map = -2;
836
837 key = hist_browser__run(browser, msg);
838
839 if (browser->he_selection != NULL) {
840 thread = hist_browser__selected_thread(browser);
841 dso = browser->selection->map ? browser->selection->map->dso : NULL;
842 }
843
844 switch (key) {
845 case NEWT_KEY_TAB:
846 case NEWT_KEY_UNTAB:
847 /*
848 * Exit the browser, let hists__browser_tree
849 * go to the next or previous
850 */
851 goto out_free_stack;
852 case 'a':
853 if (browser->selection == NULL ||
854 browser->selection->sym == NULL ||
855 browser->selection->map->dso->annotate_warned)
856 continue;
857 goto do_annotate;
858 case 'd':
859 goto zoom_dso;
860 case 't':
861 goto zoom_thread;
862 case NEWT_KEY_F1:
863 case 'h':
864 case '?':
865 ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
866 "<- Zoom out\n"
867 "a Annotate current symbol\n"
868 "h/?/F1 Show this window\n"
869 "C Collapse all callchains\n"
870 "E Expand all callchains\n"
871 "d Zoom into current DSO\n"
872 "t Zoom into current Thread\n"
873 "TAB/UNTAB Switch events\n"
874 "q/CTRL+C Exit browser");
875 continue;
876 case NEWT_KEY_ENTER:
877 case NEWT_KEY_RIGHT:
878 /* menu */
879 break;
880 case NEWT_KEY_LEFT: {
881 const void *top;
882
883 if (pstack__empty(fstack)) {
884 /*
885 * Go back to the perf_evsel_menu__run or other user
886 */
887 if (left_exits)
888 goto out_free_stack;
889 continue;
890 }
891 top = pstack__pop(fstack);
892 if (top == &dso_filter)
893 goto zoom_out_dso;
894 if (top == &thread_filter)
895 goto zoom_out_thread;
896 continue;
897 }
898 case NEWT_KEY_ESCAPE:
899 if (!left_exits &&
900 !ui__dialog_yesno("Do you really want to exit?"))
901 continue;
902 /* Fall thru */
903 default:
904 goto out_free_stack;
905 }
906
907 if (browser->selection != NULL &&
908 browser->selection->sym != NULL &&
909 !browser->selection->map->dso->annotate_warned &&
910 asprintf(&options[nr_options], "Annotate %s",
911 browser->selection->sym->name) > 0)
912 annotate = nr_options++;
913
914 if (thread != NULL &&
915 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
916 (thread_filter ? "out of" : "into"),
917 (thread->comm_set ? thread->comm : ""),
918 thread->pid) > 0)
919 zoom_thread = nr_options++;
920
921 if (dso != NULL &&
922 asprintf(&options[nr_options], "Zoom %s %s DSO",
923 (dso_filter ? "out of" : "into"),
924 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
925 zoom_dso = nr_options++;
926
927 if (browser->selection != NULL &&
928 browser->selection->map != NULL &&
929 asprintf(&options[nr_options], "Browse map details") > 0)
930 browse_map = nr_options++;
931
932 options[nr_options++] = (char *)"Exit";
933
934 choice = ui__popup_menu(nr_options, options);
935
936 for (i = 0; i < nr_options - 1; ++i)
937 free(options[i]);
938
939 if (choice == nr_options - 1)
940 break;
941
942 if (choice == -1)
943 continue;
944
945 if (choice == annotate) {
946 struct hist_entry *he;
947do_annotate:
948 he = hist_browser__selected_entry(browser);
949 if (he == NULL)
950 continue;
951
952 hist_entry__tui_annotate(he, evsel->idx);
953 } else if (choice == browse_map)
954 map__browse(browser->selection->map);
955 else if (choice == zoom_dso) {
956zoom_dso:
957 if (dso_filter) {
958 pstack__remove(fstack, &dso_filter);
959zoom_out_dso:
960 ui_helpline__pop();
961 dso_filter = NULL;
962 } else {
963 if (dso == NULL)
964 continue;
965 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
966 dso->kernel ? "the Kernel" : dso->short_name);
967 dso_filter = dso;
968 pstack__push(fstack, &dso_filter);
969 }
970 hists__filter_by_dso(self, dso_filter);
971 hists__browser_title(self, msg, sizeof(msg), ev_name,
972 dso_filter, thread_filter);
973 hist_browser__reset(browser);
974 } else if (choice == zoom_thread) {
975zoom_thread:
976 if (thread_filter) {
977 pstack__remove(fstack, &thread_filter);
978zoom_out_thread:
979 ui_helpline__pop();
980 thread_filter = NULL;
981 } else {
982 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
983 thread->comm_set ? thread->comm : "",
984 thread->pid);
985 thread_filter = thread;
986 pstack__push(fstack, &thread_filter);
987 }
988 hists__filter_by_thread(self, thread_filter);
989 hists__browser_title(self, msg, sizeof(msg), ev_name,
990 dso_filter, thread_filter);
991 hist_browser__reset(browser);
992 }
993 }
994out_free_stack:
995 pstack__delete(fstack);
996out:
997 hist_browser__delete(browser);
998 return key;
999}
1000
1001struct perf_evsel_menu {
1002 struct ui_browser b;
1003 struct perf_evsel *selection;
1004};
1005
1006static void perf_evsel_menu__write(struct ui_browser *browser,
1007 void *entry, int row)
1008{
1009 struct perf_evsel_menu *menu = container_of(browser,
1010 struct perf_evsel_menu, b);
1011 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1012 bool current_entry = ui_browser__is_current_entry(browser, row);
1013 unsigned long nr_events = evsel->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1014 const char *ev_name = event_name(evsel);
1015 char bf[256], unit;
1016
1017 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1018 HE_COLORSET_NORMAL);
1019
1020 nr_events = convert_unit(nr_events, &unit);
1021 snprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1022 unit, unit == ' ' ? "" : " ", ev_name);
1023 slsmg_write_nstring(bf, browser->width);
1024
1025 if (current_entry)
1026 menu->selection = evsel;
1027}
1028
1029static int perf_evsel_menu__run(struct perf_evsel_menu *menu, const char *help)
1030{
1031 int exit_keys[] = { NEWT_KEY_ENTER, NEWT_KEY_RIGHT, 0, };
1032 struct perf_evlist *evlist = menu->b.priv;
1033 struct perf_evsel *pos;
1034 const char *ev_name, *title = "Available samples";
1035 int key;
1036
1037 if (ui_browser__show(&menu->b, title,
1038 "ESC: exit, ENTER|->: Browse histograms") < 0)
1039 return -1;
1040
1041 ui_browser__add_exit_keys(&menu->b, exit_keys);
1042
1043 while (1) {
1044 key = ui_browser__run(&menu->b);
1045
1046 switch (key) {
1047 case NEWT_KEY_RIGHT:
1048 case NEWT_KEY_ENTER:
1049 if (!menu->selection)
1050 continue;
1051 pos = menu->selection;
1052browse_hists:
1053 ev_name = event_name(pos);
1054 key = perf_evsel__hists_browse(pos, help, ev_name, true);
1055 ui_browser__show_title(&menu->b, title);
1056 break;
1057 case NEWT_KEY_LEFT:
1058 continue;
1059 case NEWT_KEY_ESCAPE:
1060 if (!ui__dialog_yesno("Do you really want to exit?"))
1061 continue;
1062 /* Fall thru */
1063 default:
1064 goto out;
1065 }
1066
1067 switch (key) {
1068 case NEWT_KEY_TAB:
1069 if (pos->node.next == &evlist->entries)
1070 pos = list_entry(evlist->entries.next, struct perf_evsel, node);
1071 else
1072 pos = list_entry(pos->node.next, struct perf_evsel, node);
1073 goto browse_hists;
1074 case NEWT_KEY_UNTAB:
1075 if (pos->node.prev == &evlist->entries)
1076 pos = list_entry(evlist->entries.prev, struct perf_evsel, node);
1077 else
1078 pos = list_entry(pos->node.prev, struct perf_evsel, node);
1079 goto browse_hists;
1080 case 'q':
1081 case CTRL('c'):
1082 goto out;
1083 default:
1084 break;
1085 }
1086 }
1087
1088out:
1089 ui_browser__hide(&menu->b);
1090 return key;
1091}
1092
1093static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1094 const char *help)
1095{
1096 struct perf_evsel *pos;
1097 struct perf_evsel_menu menu = {
1098 .b = {
1099 .entries = &evlist->entries,
1100 .refresh = ui_browser__list_head_refresh,
1101 .seek = ui_browser__list_head_seek,
1102 .write = perf_evsel_menu__write,
1103 .nr_entries = evlist->nr_entries,
1104 .priv = evlist,
1105 },
1106 };
1107
1108 ui_helpline__push("Press ESC to exit");
1109
1110 list_for_each_entry(pos, &evlist->entries, node) {
1111 const char *ev_name = event_name(pos);
1112 size_t line_len = strlen(ev_name) + 7;
1113
1114 if (menu.b.width < line_len)
1115 menu.b.width = line_len;
1116 /*
1117 * Cache the evsel name, tracepoints have a _high_ cost per
1118 * event_name() call.
1119 */
1120 if (pos->name == NULL)
1121 pos->name = strdup(ev_name);
1122 }
1123
1124 return perf_evsel_menu__run(&menu, help);
1125}
1126
1127int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help)
1128{
1129
1130 if (evlist->nr_entries == 1) {
1131 struct perf_evsel *first = list_entry(evlist->entries.next,
1132 struct perf_evsel, node);
1133 const char *ev_name = event_name(first);
1134 return perf_evsel__hists_browse(first, help, ev_name, false);
1135 }
1136
1137 return __perf_evlist__tui_browse_hists(evlist, help);
1138}
diff --git a/tools/perf/util/ui/browsers/map.c b/tools/perf/util/ui/browsers/map.c
new file mode 100644
index 00000000000..8462bffe20b
--- /dev/null
+++ b/tools/perf/util/ui/browsers/map.c
@@ -0,0 +1,156 @@
1#include "../libslang.h"
2#include <elf.h>
3#include <inttypes.h>
4#include <sys/ttydefaults.h>
5#include <ctype.h>
6#include <string.h>
7#include <linux/bitops.h>
8#include "../../debug.h"
9#include "../../symbol.h"
10#include "../browser.h"
11#include "../helpline.h"
12#include "map.h"
13
14static int ui_entry__read(const char *title, char *bf, size_t size, int width)
15{
16 struct newtExitStruct es;
17 newtComponent form, entry;
18 const char *result;
19 int err = -1;
20
21 newtCenteredWindow(width, 1, title);
22 form = newtForm(NULL, NULL, 0);
23 if (form == NULL)
24 return -1;
25
26 entry = newtEntry(0, 0, "0x", width, &result, NEWT_FLAG_SCROLL);
27 if (entry == NULL)
28 goto out_free_form;
29
30 newtFormAddComponent(form, entry);
31 newtFormAddHotKey(form, NEWT_KEY_ENTER);
32 newtFormAddHotKey(form, NEWT_KEY_ESCAPE);
33 newtFormAddHotKey(form, NEWT_KEY_LEFT);
34 newtFormAddHotKey(form, CTRL('c'));
35 newtFormRun(form, &es);
36
37 if (result != NULL) {
38 strncpy(bf, result, size);
39 err = 0;
40 }
41out_free_form:
42 newtPopWindow();
43 newtFormDestroy(form);
44 return err;
45}
46
47struct map_browser {
48 struct ui_browser b;
49 struct map *map;
50 u8 addrlen;
51};
52
53static void map_browser__write(struct ui_browser *self, void *nd, int row)
54{
55 struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
56 struct map_browser *mb = container_of(self, struct map_browser, b);
57 bool current_entry = ui_browser__is_current_entry(self, row);
58 int width;
59
60 ui_browser__set_percent_color(self, 0, current_entry);
61 slsmg_printf("%*" PRIx64 " %*" PRIx64 " %c ",
62 mb->addrlen, sym->start, mb->addrlen, sym->end,
63 sym->binding == STB_GLOBAL ? 'g' :
64 sym->binding == STB_LOCAL ? 'l' : 'w');
65 width = self->width - ((mb->addrlen * 2) + 4);
66 if (width > 0)
67 slsmg_write_nstring(sym->name, width);
68}
69
70/* FIXME uber-kludgy, see comment on cmd_report... */
71static u32 *symbol__browser_index(struct symbol *self)
72{
73 return ((void *)self) - sizeof(struct rb_node) - sizeof(u32);
74}
75
76static int map_browser__search(struct map_browser *self)
77{
78 char target[512];
79 struct symbol *sym;
80 int err = ui_entry__read("Search by name/addr", target, sizeof(target), 40);
81
82 if (err)
83 return err;
84
85 if (target[0] == '0' && tolower(target[1]) == 'x') {
86 u64 addr = strtoull(target, NULL, 16);
87 sym = map__find_symbol(self->map, addr, NULL);
88 } else
89 sym = map__find_symbol_by_name(self->map, target, NULL);
90
91 if (sym != NULL) {
92 u32 *idx = symbol__browser_index(sym);
93
94 self->b.top = &sym->rb_node;
95 self->b.index = self->b.top_idx = *idx;
96 } else
97 ui_helpline__fpush("%s not found!", target);
98
99 return 0;
100}
101
102static int map_browser__run(struct map_browser *self)
103{
104 int key;
105
106 if (ui_browser__show(&self->b, self->map->dso->long_name,
107 "Press <- or ESC to exit, %s / to search",
108 verbose ? "" : "restart with -v to use") < 0)
109 return -1;
110
111 if (verbose)
112 ui_browser__add_exit_key(&self->b, '/');
113
114 while (1) {
115 key = ui_browser__run(&self->b);
116
117 if (verbose && key == '/')
118 map_browser__search(self);
119 else
120 break;
121 }
122
123 ui_browser__hide(&self->b);
124 return key;
125}
126
127int map__browse(struct map *self)
128{
129 struct map_browser mb = {
130 .b = {
131 .entries = &self->dso->symbols[self->type],
132 .refresh = ui_browser__rb_tree_refresh,
133 .seek = ui_browser__rb_tree_seek,
134 .write = map_browser__write,
135 },
136 .map = self,
137 };
138 struct rb_node *nd;
139 char tmp[BITS_PER_LONG / 4];
140 u64 maxaddr = 0;
141
142 for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) {
143 struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
144
145 if (maxaddr < pos->end)
146 maxaddr = pos->end;
147 if (verbose) {
148 u32 *idx = symbol__browser_index(pos);
149 *idx = mb.b.nr_entries;
150 }
151 ++mb.b.nr_entries;
152 }
153
154 mb.addrlen = snprintf(tmp, sizeof(tmp), "%" PRIx64, maxaddr);
155 return map_browser__run(&mb);
156}
diff --git a/tools/perf/util/ui/browsers/map.h b/tools/perf/util/ui/browsers/map.h
new file mode 100644
index 00000000000..df8581a43e1
--- /dev/null
+++ b/tools/perf/util/ui/browsers/map.h
@@ -0,0 +1,6 @@
1#ifndef _PERF_UI_MAP_BROWSER_H_
2#define _PERF_UI_MAP_BROWSER_H_ 1
3struct map;
4
5int map__browse(struct map *self);
6#endif /* _PERF_UI_MAP_BROWSER_H_ */
diff --git a/tools/perf/util/ui/browsers/top.c b/tools/perf/util/ui/browsers/top.c
new file mode 100644
index 00000000000..88403cf8396
--- /dev/null
+++ b/tools/perf/util/ui/browsers/top.c
@@ -0,0 +1,212 @@
1/*
2 * Copyright (C) 2011, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com>
3 *
4 * Parts came from builtin-{top,stat,record}.c, see those files for further
5 * copyright notes.
6 *
7 * Released under the GPL v2. (and only v2, not any later version)
8 */
9#include "../browser.h"
10#include "../../annotate.h"
11#include "../helpline.h"
12#include "../libslang.h"
13#include "../util.h"
14#include "../../evlist.h"
15#include "../../hist.h"
16#include "../../sort.h"
17#include "../../symbol.h"
18#include "../../top.h"
19
20struct perf_top_browser {
21 struct ui_browser b;
22 struct rb_root root;
23 struct sym_entry *selection;
24 float sum_ksamples;
25 int dso_width;
26 int dso_short_width;
27 int sym_width;
28};
29
30static void perf_top_browser__write(struct ui_browser *browser, void *entry, int row)
31{
32 struct perf_top_browser *top_browser = container_of(browser, struct perf_top_browser, b);
33 struct sym_entry *syme = rb_entry(entry, struct sym_entry, rb_node);
34 bool current_entry = ui_browser__is_current_entry(browser, row);
35 struct symbol *symbol = sym_entry__symbol(syme);
36 struct perf_top *top = browser->priv;
37 int width = browser->width;
38 double pcnt;
39
40 pcnt = 100.0 - (100.0 * ((top_browser->sum_ksamples - syme->snap_count) /
41 top_browser->sum_ksamples));
42 ui_browser__set_percent_color(browser, pcnt, current_entry);
43
44 if (top->evlist->nr_entries == 1 || !top->display_weighted) {
45 slsmg_printf("%20.2f ", syme->weight);
46 width -= 24;
47 } else {
48 slsmg_printf("%9.1f %10ld ", syme->weight, syme->snap_count);
49 width -= 23;
50 }
51
52 slsmg_printf("%4.1f%%", pcnt);
53 width -= 7;
54
55 if (verbose) {
56 slsmg_printf(" %016" PRIx64, symbol->start);
57 width -= 17;
58 }
59
60 slsmg_printf(" %-*.*s ", top_browser->sym_width, top_browser->sym_width,
61 symbol->name);
62 width -= top_browser->sym_width;
63 slsmg_write_nstring(width >= syme->map->dso->long_name_len ?
64 syme->map->dso->long_name :
65 syme->map->dso->short_name, width);
66
67 if (current_entry)
68 top_browser->selection = syme;
69}
70
71static void perf_top_browser__update_rb_tree(struct perf_top_browser *browser)
72{
73 struct perf_top *top = browser->b.priv;
74 u64 top_idx = browser->b.top_idx;
75
76 browser->root = RB_ROOT;
77 browser->b.top = NULL;
78 browser->sum_ksamples = perf_top__decay_samples(top, &browser->root);
79 /*
80 * No active symbols
81 */
82 if (top->rb_entries == 0)
83 return;
84
85 perf_top__find_widths(top, &browser->root, &browser->dso_width,
86 &browser->dso_short_width,
87 &browser->sym_width);
88 if (browser->sym_width + browser->dso_width > browser->b.width - 29) {
89 browser->dso_width = browser->dso_short_width;
90 if (browser->sym_width + browser->dso_width > browser->b.width - 29)
91 browser->sym_width = browser->b.width - browser->dso_width - 29;
92 }
93
94 /*
95 * Adjust the ui_browser indexes since the entries in the browser->root
96 * rb_tree may have changed, then seek it from start, so that we get a
97 * possible new top of the screen.
98 */
99 browser->b.nr_entries = top->rb_entries;
100
101 if (top_idx >= browser->b.nr_entries) {
102 if (browser->b.height >= browser->b.nr_entries)
103 top_idx = browser->b.nr_entries - browser->b.height;
104 else
105 top_idx = 0;
106 }
107
108 if (browser->b.index >= top_idx + browser->b.height)
109 browser->b.index = top_idx + browser->b.index - browser->b.top_idx;
110
111 if (browser->b.index >= browser->b.nr_entries)
112 browser->b.index = browser->b.nr_entries - 1;
113
114 browser->b.top_idx = top_idx;
115 browser->b.seek(&browser->b, top_idx, SEEK_SET);
116}
117
118static void perf_top_browser__annotate(struct perf_top_browser *browser)
119{
120 struct sym_entry *syme = browser->selection;
121 struct symbol *sym = sym_entry__symbol(syme);
122 struct annotation *notes = symbol__annotation(sym);
123 struct perf_top *top = browser->b.priv;
124
125 if (notes->src != NULL)
126 goto do_annotation;
127
128 pthread_mutex_lock(&notes->lock);
129
130 top->sym_filter_entry = NULL;
131
132 if (symbol__alloc_hist(sym, top->evlist->nr_entries) < 0) {
133 pr_err("Not enough memory for annotating '%s' symbol!\n",
134 sym->name);
135 pthread_mutex_unlock(&notes->lock);
136 return;
137 }
138
139 top->sym_filter_entry = syme;
140
141 pthread_mutex_unlock(&notes->lock);
142do_annotation:
143 symbol__tui_annotate(sym, syme->map, 0, top->delay_secs * 1000);
144}
145
146static int perf_top_browser__run(struct perf_top_browser *browser)
147{
148 int key;
149 char title[160];
150 struct perf_top *top = browser->b.priv;
151 int delay_msecs = top->delay_secs * 1000;
152 int exit_keys[] = { 'a', NEWT_KEY_ENTER, NEWT_KEY_RIGHT, 0, };
153
154 perf_top_browser__update_rb_tree(browser);
155 perf_top__header_snprintf(top, title, sizeof(title));
156 perf_top__reset_sample_counters(top);
157
158 if (ui_browser__show(&browser->b, title,
159 "ESC: exit, ENTER|->|a: Live Annotate") < 0)
160 return -1;
161
162 newtFormSetTimer(browser->b.form, delay_msecs);
163 ui_browser__add_exit_keys(&browser->b, exit_keys);
164
165 while (1) {
166 key = ui_browser__run(&browser->b);
167
168 switch (key) {
169 case -1:
170 /* FIXME we need to check if it was es.reason == NEWT_EXIT_TIMER */
171 perf_top_browser__update_rb_tree(browser);
172 perf_top__header_snprintf(top, title, sizeof(title));
173 perf_top__reset_sample_counters(top);
174 ui_browser__set_color(&browser->b, NEWT_COLORSET_ROOT);
175 SLsmg_gotorc(0, 0);
176 slsmg_write_nstring(title, browser->b.width);
177 break;
178 case 'a':
179 case NEWT_KEY_RIGHT:
180 case NEWT_KEY_ENTER:
181 if (browser->selection)
182 perf_top_browser__annotate(browser);
183 break;
184 case NEWT_KEY_LEFT:
185 continue;
186 case NEWT_KEY_ESCAPE:
187 if (!ui__dialog_yesno("Do you really want to exit?"))
188 continue;
189 /* Fall thru */
190 default:
191 goto out;
192 }
193 }
194out:
195 ui_browser__hide(&browser->b);
196 return key;
197}
198
199int perf_top__tui_browser(struct perf_top *top)
200{
201 struct perf_top_browser browser = {
202 .b = {
203 .entries = &browser.root,
204 .refresh = ui_browser__rb_tree_refresh,
205 .seek = ui_browser__rb_tree_seek,
206 .write = perf_top_browser__write,
207 .priv = top,
208 },
209 };
210
211 return perf_top_browser__run(&browser);
212}
diff --git a/tools/perf/util/ui/helpline.c b/tools/perf/util/ui/helpline.c
new file mode 100644
index 00000000000..f36d2ff509e
--- /dev/null
+++ b/tools/perf/util/ui/helpline.c
@@ -0,0 +1,72 @@
1#define _GNU_SOURCE
2#include <stdio.h>
3#include <stdlib.h>
4#include <newt.h>
5
6#include "../debug.h"
7#include "helpline.h"
8#include "ui.h"
9
10void ui_helpline__pop(void)
11{
12 newtPopHelpLine();
13}
14
15void ui_helpline__push(const char *msg)
16{
17 newtPushHelpLine(msg);
18}
19
20void ui_helpline__vpush(const char *fmt, va_list ap)
21{
22 char *s;
23
24 if (vasprintf(&s, fmt, ap) < 0)
25 vfprintf(stderr, fmt, ap);
26 else {
27 ui_helpline__push(s);
28 free(s);
29 }
30}
31
32void ui_helpline__fpush(const char *fmt, ...)
33{
34 va_list ap;
35
36 va_start(ap, fmt);
37 ui_helpline__vpush(fmt, ap);
38 va_end(ap);
39}
40
41void ui_helpline__puts(const char *msg)
42{
43 ui_helpline__pop();
44 ui_helpline__push(msg);
45}
46
47void ui_helpline__init(void)
48{
49 ui_helpline__puts(" ");
50}
51
52char ui_helpline__last_msg[1024];
53
54int ui_helpline__show_help(const char *format, va_list ap)
55{
56 int ret;
57 static int backlog;
58
59 pthread_mutex_lock(&ui__lock);
60 ret = vsnprintf(ui_helpline__last_msg + backlog,
61 sizeof(ui_helpline__last_msg) - backlog, format, ap);
62 backlog += ret;
63
64 if (ui_helpline__last_msg[backlog - 1] == '\n') {
65 ui_helpline__puts(ui_helpline__last_msg);
66 newtRefresh();
67 backlog = 0;
68 }
69 pthread_mutex_unlock(&ui__lock);
70
71 return ret;
72}
diff --git a/tools/perf/util/ui/helpline.h b/tools/perf/util/ui/helpline.h
new file mode 100644
index 00000000000..ab6028d0c40
--- /dev/null
+++ b/tools/perf/util/ui/helpline.h
@@ -0,0 +1,11 @@
1#ifndef _PERF_UI_HELPLINE_H_
2#define _PERF_UI_HELPLINE_H_ 1
3
4void ui_helpline__init(void);
5void ui_helpline__pop(void);
6void ui_helpline__push(const char *msg);
7void ui_helpline__vpush(const char *fmt, va_list ap);
8void ui_helpline__fpush(const char *fmt, ...);
9void ui_helpline__puts(const char *msg);
10
11#endif /* _PERF_UI_HELPLINE_H_ */
diff --git a/tools/perf/util/ui/libslang.h b/tools/perf/util/ui/libslang.h
new file mode 100644
index 00000000000..2b63e1c9b18
--- /dev/null
+++ b/tools/perf/util/ui/libslang.h
@@ -0,0 +1,27 @@
1#ifndef _PERF_UI_SLANG_H_
2#define _PERF_UI_SLANG_H_ 1
3/*
4 * slang versions <= 2.0.6 have a "#if HAVE_LONG_LONG" that breaks
5 * the build if it isn't defined. Use the equivalent one that glibc
6 * has on features.h.
7 */
8#include <features.h>
9#ifndef HAVE_LONG_LONG
10#define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG
11#endif
12#include <slang.h>
13
14#if SLANG_VERSION < 20104
15#define slsmg_printf(msg, args...) \
16 SLsmg_printf((char *)(msg), ##args)
17#define slsmg_write_nstring(msg, len) \
18 SLsmg_write_nstring((char *)(msg), len)
19#define sltt_set_color(obj, name, fg, bg) \
20 SLtt_set_color(obj,(char *)(name), (char *)(fg), (char *)(bg))
21#else
22#define slsmg_printf SLsmg_printf
23#define slsmg_write_nstring SLsmg_write_nstring
24#define sltt_set_color SLtt_set_color
25#endif
26
27#endif /* _PERF_UI_SLANG_H_ */
diff --git a/tools/perf/util/ui/progress.c b/tools/perf/util/ui/progress.c
new file mode 100644
index 00000000000..d7fc399d36b
--- /dev/null
+++ b/tools/perf/util/ui/progress.c
@@ -0,0 +1,60 @@
1#include <stdlib.h>
2#include <newt.h>
3#include "../cache.h"
4#include "progress.h"
5
6struct ui_progress {
7 newtComponent form, scale;
8};
9
10struct ui_progress *ui_progress__new(const char *title, u64 total)
11{
12 struct ui_progress *self = malloc(sizeof(*self));
13
14 if (self != NULL) {
15 int cols;
16
17 if (use_browser <= 0)
18 return self;
19 newtGetScreenSize(&cols, NULL);
20 cols -= 4;
21 newtCenteredWindow(cols, 1, title);
22 self->form = newtForm(NULL, NULL, 0);
23 if (self->form == NULL)
24 goto out_free_self;
25 self->scale = newtScale(0, 0, cols, total);
26 if (self->scale == NULL)
27 goto out_free_form;
28 newtFormAddComponent(self->form, self->scale);
29 newtRefresh();
30 }
31
32 return self;
33
34out_free_form:
35 newtFormDestroy(self->form);
36out_free_self:
37 free(self);
38 return NULL;
39}
40
41void ui_progress__update(struct ui_progress *self, u64 curr)
42{
43 /*
44 * FIXME: We should have a per UI backend way of showing progress,
45 * stdio will just show a percentage as NN%, etc.
46 */
47 if (use_browser <= 0)
48 return;
49 newtScaleSet(self->scale, curr);
50 newtRefresh();
51}
52
53void ui_progress__delete(struct ui_progress *self)
54{
55 if (use_browser > 0) {
56 newtFormDestroy(self->form);
57 newtPopWindow();
58 }
59 free(self);
60}
diff --git a/tools/perf/util/ui/progress.h b/tools/perf/util/ui/progress.h
new file mode 100644
index 00000000000..a3820a0beb5
--- /dev/null
+++ b/tools/perf/util/ui/progress.h
@@ -0,0 +1,11 @@
1#ifndef _PERF_UI_PROGRESS_H_
2#define _PERF_UI_PROGRESS_H_ 1
3
4struct ui_progress;
5
6struct ui_progress *ui_progress__new(const char *title, u64 total);
7void ui_progress__delete(struct ui_progress *self);
8
9void ui_progress__update(struct ui_progress *self, u64 curr);
10
11#endif
diff --git a/tools/perf/util/ui/setup.c b/tools/perf/util/ui/setup.c
new file mode 100644
index 00000000000..ee46d671db5
--- /dev/null
+++ b/tools/perf/util/ui/setup.c
@@ -0,0 +1,46 @@
1#include <newt.h>
2#include <signal.h>
3#include <stdbool.h>
4
5#include "../cache.h"
6#include "../debug.h"
7#include "browser.h"
8#include "helpline.h"
9#include "ui.h"
10
11pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER;
12
13static void newt_suspend(void *d __used)
14{
15 newtSuspend();
16 raise(SIGTSTP);
17 newtResume();
18}
19
20void setup_browser(bool fallback_to_pager)
21{
22 if (!isatty(1) || !use_browser || dump_trace) {
23 use_browser = 0;
24 if (fallback_to_pager)
25 setup_pager();
26 return;
27 }
28
29 use_browser = 1;
30 newtInit();
31 newtCls();
32 newtSetSuspendCallback(newt_suspend, NULL);
33 ui_helpline__init();
34 ui_browser__init();
35}
36
37void exit_browser(bool wait_for_ok)
38{
39 if (use_browser > 0) {
40 if (wait_for_ok) {
41 char title[] = "Fatal Error", ok[] = "Ok";
42 newtWinMessage(title, ok, ui_helpline__last_msg);
43 }
44 newtFinished();
45 }
46}
diff --git a/tools/perf/util/ui/ui.h b/tools/perf/util/ui/ui.h
new file mode 100644
index 00000000000..d264e059c82
--- /dev/null
+++ b/tools/perf/util/ui/ui.h
@@ -0,0 +1,8 @@
1#ifndef _PERF_UI_H_
2#define _PERF_UI_H_ 1
3
4#include <pthread.h>
5
6extern pthread_mutex_t ui__lock;
7
8#endif /* _PERF_UI_H_ */
diff --git a/tools/perf/util/ui/util.c b/tools/perf/util/ui/util.c
new file mode 100644
index 00000000000..fdf1fc8f08b
--- /dev/null
+++ b/tools/perf/util/ui/util.c
@@ -0,0 +1,130 @@
1#include <newt.h>
2#include <signal.h>
3#include <stdio.h>
4#include <stdbool.h>
5#include <string.h>
6#include <sys/ttydefaults.h>
7
8#include "../cache.h"
9#include "../debug.h"
10#include "browser.h"
11#include "helpline.h"
12#include "ui.h"
13#include "util.h"
14
15static void newt_form__set_exit_keys(newtComponent self)
16{
17 newtFormAddHotKey(self, NEWT_KEY_LEFT);
18 newtFormAddHotKey(self, NEWT_KEY_ESCAPE);
19 newtFormAddHotKey(self, 'Q');
20 newtFormAddHotKey(self, 'q');
21 newtFormAddHotKey(self, CTRL('c'));
22}
23
24static newtComponent newt_form__new(void)
25{
26 newtComponent self = newtForm(NULL, NULL, 0);
27 if (self)
28 newt_form__set_exit_keys(self);
29 return self;
30}
31
32int ui__popup_menu(int argc, char * const argv[])
33{
34 struct newtExitStruct es;
35 int i, rc = -1, max_len = 5;
36 newtComponent listbox, form = newt_form__new();
37
38 if (form == NULL)
39 return -1;
40
41 listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT);
42 if (listbox == NULL)
43 goto out_destroy_form;
44
45 newtFormAddComponent(form, listbox);
46
47 for (i = 0; i < argc; ++i) {
48 int len = strlen(argv[i]);
49 if (len > max_len)
50 max_len = len;
51 if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i))
52 goto out_destroy_form;
53 }
54
55 newtCenteredWindow(max_len, argc, NULL);
56 newtFormRun(form, &es);
57 rc = newtListboxGetCurrent(listbox) - NULL;
58 if (es.reason == NEWT_EXIT_HOTKEY)
59 rc = -1;
60 newtPopWindow();
61out_destroy_form:
62 newtFormDestroy(form);
63 return rc;
64}
65
66int ui__help_window(const char *text)
67{
68 struct newtExitStruct es;
69 newtComponent tb, form = newt_form__new();
70 int rc = -1;
71 int max_len = 0, nr_lines = 0;
72 const char *t;
73
74 if (form == NULL)
75 return -1;
76
77 t = text;
78 while (1) {
79 const char *sep = strchr(t, '\n');
80 int len;
81
82 if (sep == NULL)
83 sep = strchr(t, '\0');
84 len = sep - t;
85 if (max_len < len)
86 max_len = len;
87 ++nr_lines;
88 if (*sep == '\0')
89 break;
90 t = sep + 1;
91 }
92
93 tb = newtTextbox(0, 0, max_len, nr_lines, 0);
94 if (tb == NULL)
95 goto out_destroy_form;
96
97 newtTextboxSetText(tb, text);
98 newtFormAddComponent(form, tb);
99 newtCenteredWindow(max_len, nr_lines, NULL);
100 newtFormRun(form, &es);
101 newtPopWindow();
102 rc = 0;
103out_destroy_form:
104 newtFormDestroy(form);
105 return rc;
106}
107
108static const char yes[] = "Yes", no[] = "No",
109 warning_str[] = "Warning!", ok[] = "Ok";
110
111bool ui__dialog_yesno(const char *msg)
112{
113 /* newtWinChoice should really be accepting const char pointers... */
114 return newtWinChoice(NULL, (char *)yes, (char *)no, (char *)msg) == 1;
115}
116
117void ui__warning(const char *format, ...)
118{
119 va_list args;
120
121 va_start(args, format);
122 if (use_browser > 0) {
123 pthread_mutex_lock(&ui__lock);
124 newtWinMessagev((char *)warning_str, (char *)ok,
125 (char *)format, args);
126 pthread_mutex_unlock(&ui__lock);
127 } else
128 vfprintf(stderr, format, args);
129 va_end(args);
130}
diff --git a/tools/perf/util/ui/util.h b/tools/perf/util/ui/util.h
new file mode 100644
index 00000000000..afcbc1d9953
--- /dev/null
+++ b/tools/perf/util/ui/util.h
@@ -0,0 +1,10 @@
1#ifndef _PERF_UI_UTIL_H_
2#define _PERF_UI_UTIL_H_ 1
3
4#include <stdbool.h>
5
6int ui__popup_menu(int argc, char * const argv[]);
7int ui__help_window(const char *text);
8bool ui__dialog_yesno(const char *msg);
9
10#endif /* _PERF_UI_UTIL_H_ */
diff --git a/tools/slub/slabinfo.c b/tools/slub/slabinfo.c
new file mode 100644
index 00000000000..868cc93f7ac
--- /dev/null
+++ b/tools/slub/slabinfo.c
@@ -0,0 +1,1385 @@
1/*
2 * Slabinfo: Tool to get reports about slabs
3 *
4 * (C) 2007 sgi, Christoph Lameter
5 * (C) 2011 Linux Foundation, Christoph Lameter
6 *
7 * Compile with:
8 *
9 * gcc -o slabinfo slabinfo.c
10 */
11#include <stdio.h>
12#include <stdlib.h>
13#include <sys/types.h>
14#include <dirent.h>
15#include <strings.h>
16#include <string.h>
17#include <unistd.h>
18#include <stdarg.h>
19#include <getopt.h>
20#include <regex.h>
21#include <errno.h>
22
23#define MAX_SLABS 500
24#define MAX_ALIASES 500
25#define MAX_NODES 1024
26
27struct slabinfo {
28 char *name;
29 int alias;
30 int refs;
31 int aliases, align, cache_dma, cpu_slabs, destroy_by_rcu;
32 int hwcache_align, object_size, objs_per_slab;
33 int sanity_checks, slab_size, store_user, trace;
34 int order, poison, reclaim_account, red_zone;
35 unsigned long partial, objects, slabs, objects_partial, objects_total;
36 unsigned long alloc_fastpath, alloc_slowpath;
37 unsigned long free_fastpath, free_slowpath;
38 unsigned long free_frozen, free_add_partial, free_remove_partial;
39 unsigned long alloc_from_partial, alloc_slab, free_slab, alloc_refill;
40 unsigned long cpuslab_flush, deactivate_full, deactivate_empty;
41 unsigned long deactivate_to_head, deactivate_to_tail;
42 unsigned long deactivate_remote_frees, order_fallback;
43 unsigned long cmpxchg_double_cpu_fail, cmpxchg_double_fail;
44 unsigned long alloc_node_mismatch, deactivate_bypass;
45 int numa[MAX_NODES];
46 int numa_partial[MAX_NODES];
47} slabinfo[MAX_SLABS];
48
49struct aliasinfo {
50 char *name;
51 char *ref;
52 struct slabinfo *slab;
53} aliasinfo[MAX_ALIASES];
54
55int slabs = 0;
56int actual_slabs = 0;
57int aliases = 0;
58int alias_targets = 0;
59int highest_node = 0;
60
61char buffer[4096];
62
63int show_empty = 0;
64int show_report = 0;
65int show_alias = 0;
66int show_slab = 0;
67int skip_zero = 1;
68int show_numa = 0;
69int show_track = 0;
70int show_first_alias = 0;
71int validate = 0;
72int shrink = 0;
73int show_inverted = 0;
74int show_single_ref = 0;
75int show_totals = 0;
76int sort_size = 0;
77int sort_active = 0;
78int set_debug = 0;
79int show_ops = 0;
80int show_activity = 0;
81
82/* Debug options */
83int sanity = 0;
84int redzone = 0;
85int poison = 0;
86int tracking = 0;
87int tracing = 0;
88
89int page_size;
90
91regex_t pattern;
92
93static void fatal(const char *x, ...)
94{
95 va_list ap;
96
97 va_start(ap, x);
98 vfprintf(stderr, x, ap);
99 va_end(ap);
100 exit(EXIT_FAILURE);
101}
102
103static void usage(void)
104{
105 printf("slabinfo 4/15/2011. (c) 2007 sgi/(c) 2011 Linux Foundation.\n\n"
106 "slabinfo [-ahnpvtsz] [-d debugopts] [slab-regexp]\n"
107 "-a|--aliases Show aliases\n"
108 "-A|--activity Most active slabs first\n"
109 "-d<options>|--debug=<options> Set/Clear Debug options\n"
110 "-D|--display-active Switch line format to activity\n"
111 "-e|--empty Show empty slabs\n"
112 "-f|--first-alias Show first alias\n"
113 "-h|--help Show usage information\n"
114 "-i|--inverted Inverted list\n"
115 "-l|--slabs Show slabs\n"
116 "-n|--numa Show NUMA information\n"
117 "-o|--ops Show kmem_cache_ops\n"
118 "-s|--shrink Shrink slabs\n"
119 "-r|--report Detailed report on single slabs\n"
120 "-S|--Size Sort by size\n"
121 "-t|--tracking Show alloc/free information\n"
122 "-T|--Totals Show summary information\n"
123 "-v|--validate Validate slabs\n"
124 "-z|--zero Include empty slabs\n"
125 "-1|--1ref Single reference\n"
126 "\nValid debug options (FZPUT may be combined)\n"
127 "a / A Switch on all debug options (=FZUP)\n"
128 "- Switch off all debug options\n"
129 "f / F Sanity Checks (SLAB_DEBUG_FREE)\n"
130 "z / Z Redzoning\n"
131 "p / P Poisoning\n"
132 "u / U Tracking\n"
133 "t / T Tracing\n"
134 );
135}
136
137static unsigned long read_obj(const char *name)
138{
139 FILE *f = fopen(name, "r");
140
141 if (!f)
142 buffer[0] = 0;
143 else {
144 if (!fgets(buffer, sizeof(buffer), f))
145 buffer[0] = 0;
146 fclose(f);
147 if (buffer[strlen(buffer)] == '\n')
148 buffer[strlen(buffer)] = 0;
149 }
150 return strlen(buffer);
151}
152
153
154/*
155 * Get the contents of an attribute
156 */
157static unsigned long get_obj(const char *name)
158{
159 if (!read_obj(name))
160 return 0;
161
162 return atol(buffer);
163}
164
165static unsigned long get_obj_and_str(const char *name, char **x)
166{
167 unsigned long result = 0;
168 char *p;
169
170 *x = NULL;
171
172 if (!read_obj(name)) {
173 x = NULL;
174 return 0;
175 }
176 result = strtoul(buffer, &p, 10);
177 while (*p == ' ')
178 p++;
179 if (*p)
180 *x = strdup(p);
181 return result;
182}
183
184static void set_obj(struct slabinfo *s, const char *name, int n)
185{
186 char x[100];
187 FILE *f;
188
189 snprintf(x, 100, "%s/%s", s->name, name);
190 f = fopen(x, "w");
191 if (!f)
192 fatal("Cannot write to %s\n", x);
193
194 fprintf(f, "%d\n", n);
195 fclose(f);
196}
197
198static unsigned long read_slab_obj(struct slabinfo *s, const char *name)
199{
200 char x[100];
201 FILE *f;
202 size_t l;
203
204 snprintf(x, 100, "%s/%s", s->name, name);
205 f = fopen(x, "r");
206 if (!f) {
207 buffer[0] = 0;
208 l = 0;
209 } else {
210 l = fread(buffer, 1, sizeof(buffer), f);
211 buffer[l] = 0;
212 fclose(f);
213 }
214 return l;
215}
216
217
218/*
219 * Put a size string together
220 */
221static int store_size(char *buffer, unsigned long value)
222{
223 unsigned long divisor = 1;
224 char trailer = 0;
225 int n;
226
227 if (value > 1000000000UL) {
228 divisor = 100000000UL;
229 trailer = 'G';
230 } else if (value > 1000000UL) {
231 divisor = 100000UL;
232 trailer = 'M';
233 } else if (value > 1000UL) {
234 divisor = 100;
235 trailer = 'K';
236 }
237
238 value /= divisor;
239 n = sprintf(buffer, "%ld",value);
240 if (trailer) {
241 buffer[n] = trailer;
242 n++;
243 buffer[n] = 0;
244 }
245 if (divisor != 1) {
246 memmove(buffer + n - 2, buffer + n - 3, 4);
247 buffer[n-2] = '.';
248 n++;
249 }
250 return n;
251}
252
253static void decode_numa_list(int *numa, char *t)
254{
255 int node;
256 int nr;
257
258 memset(numa, 0, MAX_NODES * sizeof(int));
259
260 if (!t)
261 return;
262
263 while (*t == 'N') {
264 t++;
265 node = strtoul(t, &t, 10);
266 if (*t == '=') {
267 t++;
268 nr = strtoul(t, &t, 10);
269 numa[node] = nr;
270 if (node > highest_node)
271 highest_node = node;
272 }
273 while (*t == ' ')
274 t++;
275 }
276}
277
278static void slab_validate(struct slabinfo *s)
279{
280 if (strcmp(s->name, "*") == 0)
281 return;
282
283 set_obj(s, "validate", 1);
284}
285
286static void slab_shrink(struct slabinfo *s)
287{
288 if (strcmp(s->name, "*") == 0)
289 return;
290
291 set_obj(s, "shrink", 1);
292}
293
294int line = 0;
295
296static void first_line(void)
297{
298 if (show_activity)
299 printf("Name Objects Alloc Free %%Fast Fallb O CmpX UL\n");
300 else
301 printf("Name Objects Objsize Space "
302 "Slabs/Part/Cpu O/S O %%Fr %%Ef Flg\n");
303}
304
305/*
306 * Find the shortest alias of a slab
307 */
308static struct aliasinfo *find_one_alias(struct slabinfo *find)
309{
310 struct aliasinfo *a;
311 struct aliasinfo *best = NULL;
312
313 for(a = aliasinfo;a < aliasinfo + aliases; a++) {
314 if (a->slab == find &&
315 (!best || strlen(best->name) < strlen(a->name))) {
316 best = a;
317 if (strncmp(a->name,"kmall", 5) == 0)
318 return best;
319 }
320 }
321 return best;
322}
323
324static unsigned long slab_size(struct slabinfo *s)
325{
326 return s->slabs * (page_size << s->order);
327}
328
329static unsigned long slab_activity(struct slabinfo *s)
330{
331 return s->alloc_fastpath + s->free_fastpath +
332 s->alloc_slowpath + s->free_slowpath;
333}
334
335static void slab_numa(struct slabinfo *s, int mode)
336{
337 int node;
338
339 if (strcmp(s->name, "*") == 0)
340 return;
341
342 if (!highest_node) {
343 printf("\n%s: No NUMA information available.\n", s->name);
344 return;
345 }
346
347 if (skip_zero && !s->slabs)
348 return;
349
350 if (!line) {
351 printf("\n%-21s:", mode ? "NUMA nodes" : "Slab");
352 for(node = 0; node <= highest_node; node++)
353 printf(" %4d", node);
354 printf("\n----------------------");
355 for(node = 0; node <= highest_node; node++)
356 printf("-----");
357 printf("\n");
358 }
359 printf("%-21s ", mode ? "All slabs" : s->name);
360 for(node = 0; node <= highest_node; node++) {
361 char b[20];
362
363 store_size(b, s->numa[node]);
364 printf(" %4s", b);
365 }
366 printf("\n");
367 if (mode) {
368 printf("%-21s ", "Partial slabs");
369 for(node = 0; node <= highest_node; node++) {
370 char b[20];
371
372 store_size(b, s->numa_partial[node]);
373 printf(" %4s", b);
374 }
375 printf("\n");
376 }
377 line++;
378}
379
380static void show_tracking(struct slabinfo *s)
381{
382 printf("\n%s: Kernel object allocation\n", s->name);
383 printf("-----------------------------------------------------------------------\n");
384 if (read_slab_obj(s, "alloc_calls"))
385 printf("%s", buffer);
386 else
387 printf("No Data\n");
388
389 printf("\n%s: Kernel object freeing\n", s->name);
390 printf("------------------------------------------------------------------------\n");
391 if (read_slab_obj(s, "free_calls"))
392 printf("%s", buffer);
393 else
394 printf("No Data\n");
395
396}
397
398static void ops(struct slabinfo *s)
399{
400 if (strcmp(s->name, "*") == 0)
401 return;
402
403 if (read_slab_obj(s, "ops")) {
404 printf("\n%s: kmem_cache operations\n", s->name);
405 printf("--------------------------------------------\n");
406 printf("%s", buffer);
407 } else
408 printf("\n%s has no kmem_cache operations\n", s->name);
409}
410
411static const char *onoff(int x)
412{
413 if (x)
414 return "On ";
415 return "Off";
416}
417
418static void slab_stats(struct slabinfo *s)
419{
420 unsigned long total_alloc;
421 unsigned long total_free;
422 unsigned long total;
423
424 if (!s->alloc_slab)
425 return;
426
427 total_alloc = s->alloc_fastpath + s->alloc_slowpath;
428 total_free = s->free_fastpath + s->free_slowpath;
429
430 if (!total_alloc)
431 return;
432
433 printf("\n");
434 printf("Slab Perf Counter Alloc Free %%Al %%Fr\n");
435 printf("--------------------------------------------------\n");
436 printf("Fastpath %8lu %8lu %3lu %3lu\n",
437 s->alloc_fastpath, s->free_fastpath,
438 s->alloc_fastpath * 100 / total_alloc,
439 s->free_fastpath * 100 / total_free);
440 printf("Slowpath %8lu %8lu %3lu %3lu\n",
441 total_alloc - s->alloc_fastpath, s->free_slowpath,
442 (total_alloc - s->alloc_fastpath) * 100 / total_alloc,
443 s->free_slowpath * 100 / total_free);
444 printf("Page Alloc %8lu %8lu %3lu %3lu\n",
445 s->alloc_slab, s->free_slab,
446 s->alloc_slab * 100 / total_alloc,
447 s->free_slab * 100 / total_free);
448 printf("Add partial %8lu %8lu %3lu %3lu\n",
449 s->deactivate_to_head + s->deactivate_to_tail,
450 s->free_add_partial,
451 (s->deactivate_to_head + s->deactivate_to_tail) * 100 / total_alloc,
452 s->free_add_partial * 100 / total_free);
453 printf("Remove partial %8lu %8lu %3lu %3lu\n",
454 s->alloc_from_partial, s->free_remove_partial,
455 s->alloc_from_partial * 100 / total_alloc,
456 s->free_remove_partial * 100 / total_free);
457
458 printf("RemoteObj/SlabFrozen %8lu %8lu %3lu %3lu\n",
459 s->deactivate_remote_frees, s->free_frozen,
460 s->deactivate_remote_frees * 100 / total_alloc,
461 s->free_frozen * 100 / total_free);
462
463 printf("Total %8lu %8lu\n\n", total_alloc, total_free);
464
465 if (s->cpuslab_flush)
466 printf("Flushes %8lu\n", s->cpuslab_flush);
467
468 total = s->deactivate_full + s->deactivate_empty +
469 s->deactivate_to_head + s->deactivate_to_tail + s->deactivate_bypass;
470
471 if (total) {
472 printf("\nSlab Deactivation Ocurrences %%\n");
473 printf("-------------------------------------------------\n");
474 printf("Slab full %7lu %3lu%%\n",
475 s->deactivate_full, (s->deactivate_full * 100) / total);
476 printf("Slab empty %7lu %3lu%%\n",
477 s->deactivate_empty, (s->deactivate_empty * 100) / total);
478 printf("Moved to head of partial list %7lu %3lu%%\n",
479 s->deactivate_to_head, (s->deactivate_to_head * 100) / total);
480 printf("Moved to tail of partial list %7lu %3lu%%\n",
481 s->deactivate_to_tail, (s->deactivate_to_tail * 100) / total);
482 printf("Deactivation bypass %7lu %3lu%%\n",
483 s->deactivate_bypass, (s->deactivate_bypass * 100) / total);
484 printf("Refilled from foreign frees %7lu %3lu%%\n",
485 s->alloc_refill, (s->alloc_refill * 100) / total);
486 printf("Node mismatch %7lu %3lu%%\n",
487 s->alloc_node_mismatch, (s->alloc_node_mismatch * 100) / total);
488 }
489
490 if (s->cmpxchg_double_fail || s->cmpxchg_double_cpu_fail)
491 printf("\nCmpxchg_double Looping\n------------------------\n");
492 printf("Locked Cmpxchg Double redos %lu\nUnlocked Cmpxchg Double redos %lu\n",
493 s->cmpxchg_double_fail, s->cmpxchg_double_cpu_fail);
494}
495
496static void report(struct slabinfo *s)
497{
498 if (strcmp(s->name, "*") == 0)
499 return;
500
501 printf("\nSlabcache: %-20s Aliases: %2d Order : %2d Objects: %lu\n",
502 s->name, s->aliases, s->order, s->objects);
503 if (s->hwcache_align)
504 printf("** Hardware cacheline aligned\n");
505 if (s->cache_dma)
506 printf("** Memory is allocated in a special DMA zone\n");
507 if (s->destroy_by_rcu)
508 printf("** Slabs are destroyed via RCU\n");
509 if (s->reclaim_account)
510 printf("** Reclaim accounting active\n");
511
512 printf("\nSizes (bytes) Slabs Debug Memory\n");
513 printf("------------------------------------------------------------------------\n");
514 printf("Object : %7d Total : %7ld Sanity Checks : %s Total: %7ld\n",
515 s->object_size, s->slabs, onoff(s->sanity_checks),
516 s->slabs * (page_size << s->order));
517 printf("SlabObj: %7d Full : %7ld Redzoning : %s Used : %7ld\n",
518 s->slab_size, s->slabs - s->partial - s->cpu_slabs,
519 onoff(s->red_zone), s->objects * s->object_size);
520 printf("SlabSiz: %7d Partial: %7ld Poisoning : %s Loss : %7ld\n",
521 page_size << s->order, s->partial, onoff(s->poison),
522 s->slabs * (page_size << s->order) - s->objects * s->object_size);
523 printf("Loss : %7d CpuSlab: %7d Tracking : %s Lalig: %7ld\n",
524 s->slab_size - s->object_size, s->cpu_slabs, onoff(s->store_user),
525 (s->slab_size - s->object_size) * s->objects);
526 printf("Align : %7d Objects: %7d Tracing : %s Lpadd: %7ld\n",
527 s->align, s->objs_per_slab, onoff(s->trace),
528 ((page_size << s->order) - s->objs_per_slab * s->slab_size) *
529 s->slabs);
530
531 ops(s);
532 show_tracking(s);
533 slab_numa(s, 1);
534 slab_stats(s);
535}
536
537static void slabcache(struct slabinfo *s)
538{
539 char size_str[20];
540 char dist_str[40];
541 char flags[20];
542 char *p = flags;
543
544 if (strcmp(s->name, "*") == 0)
545 return;
546
547 if (actual_slabs == 1) {
548 report(s);
549 return;
550 }
551
552 if (skip_zero && !show_empty && !s->slabs)
553 return;
554
555 if (show_empty && s->slabs)
556 return;
557
558 store_size(size_str, slab_size(s));
559 snprintf(dist_str, 40, "%lu/%lu/%d", s->slabs - s->cpu_slabs,
560 s->partial, s->cpu_slabs);
561
562 if (!line++)
563 first_line();
564
565 if (s->aliases)
566 *p++ = '*';
567 if (s->cache_dma)
568 *p++ = 'd';
569 if (s->hwcache_align)
570 *p++ = 'A';
571 if (s->poison)
572 *p++ = 'P';
573 if (s->reclaim_account)
574 *p++ = 'a';
575 if (s->red_zone)
576 *p++ = 'Z';
577 if (s->sanity_checks)
578 *p++ = 'F';
579 if (s->store_user)
580 *p++ = 'U';
581 if (s->trace)
582 *p++ = 'T';
583
584 *p = 0;
585 if (show_activity) {
586 unsigned long total_alloc;
587 unsigned long total_free;
588
589 total_alloc = s->alloc_fastpath + s->alloc_slowpath;
590 total_free = s->free_fastpath + s->free_slowpath;
591
592 printf("%-21s %8ld %10ld %10ld %3ld %3ld %5ld %1d %4ld %4ld\n",
593 s->name, s->objects,
594 total_alloc, total_free,
595 total_alloc ? (s->alloc_fastpath * 100 / total_alloc) : 0,
596 total_free ? (s->free_fastpath * 100 / total_free) : 0,
597 s->order_fallback, s->order, s->cmpxchg_double_fail,
598 s->cmpxchg_double_cpu_fail);
599 }
600 else
601 printf("%-21s %8ld %7d %8s %14s %4d %1d %3ld %3ld %s\n",
602 s->name, s->objects, s->object_size, size_str, dist_str,
603 s->objs_per_slab, s->order,
604 s->slabs ? (s->partial * 100) / s->slabs : 100,
605 s->slabs ? (s->objects * s->object_size * 100) /
606 (s->slabs * (page_size << s->order)) : 100,
607 flags);
608}
609
610/*
611 * Analyze debug options. Return false if something is amiss.
612 */
613static int debug_opt_scan(char *opt)
614{
615 if (!opt || !opt[0] || strcmp(opt, "-") == 0)
616 return 1;
617
618 if (strcasecmp(opt, "a") == 0) {
619 sanity = 1;
620 poison = 1;
621 redzone = 1;
622 tracking = 1;
623 return 1;
624 }
625
626 for ( ; *opt; opt++)
627 switch (*opt) {
628 case 'F' : case 'f':
629 if (sanity)
630 return 0;
631 sanity = 1;
632 break;
633 case 'P' : case 'p':
634 if (poison)
635 return 0;
636 poison = 1;
637 break;
638
639 case 'Z' : case 'z':
640 if (redzone)
641 return 0;
642 redzone = 1;
643 break;
644
645 case 'U' : case 'u':
646 if (tracking)
647 return 0;
648 tracking = 1;
649 break;
650
651 case 'T' : case 't':
652 if (tracing)
653 return 0;
654 tracing = 1;
655 break;
656 default:
657 return 0;
658 }
659 return 1;
660}
661
662static int slab_empty(struct slabinfo *s)
663{
664 if (s->objects > 0)
665 return 0;
666
667 /*
668 * We may still have slabs even if there are no objects. Shrinking will
669 * remove them.
670 */
671 if (s->slabs != 0)
672 set_obj(s, "shrink", 1);
673
674 return 1;
675}
676
677static void slab_debug(struct slabinfo *s)
678{
679 if (strcmp(s->name, "*") == 0)
680 return;
681
682 if (sanity && !s->sanity_checks) {
683 set_obj(s, "sanity", 1);
684 }
685 if (!sanity && s->sanity_checks) {
686 if (slab_empty(s))
687 set_obj(s, "sanity", 0);
688 else
689 fprintf(stderr, "%s not empty cannot disable sanity checks\n", s->name);
690 }
691 if (redzone && !s->red_zone) {
692 if (slab_empty(s))
693 set_obj(s, "red_zone", 1);
694 else
695 fprintf(stderr, "%s not empty cannot enable redzoning\n", s->name);
696 }
697 if (!redzone && s->red_zone) {
698 if (slab_empty(s))
699 set_obj(s, "red_zone", 0);
700 else
701 fprintf(stderr, "%s not empty cannot disable redzoning\n", s->name);
702 }
703 if (poison && !s->poison) {
704 if (slab_empty(s))
705 set_obj(s, "poison", 1);
706 else
707 fprintf(stderr, "%s not empty cannot enable poisoning\n", s->name);
708 }
709 if (!poison && s->poison) {
710 if (slab_empty(s))
711 set_obj(s, "poison", 0);
712 else
713 fprintf(stderr, "%s not empty cannot disable poisoning\n", s->name);
714 }
715 if (tracking && !s->store_user) {
716 if (slab_empty(s))
717 set_obj(s, "store_user", 1);
718 else
719 fprintf(stderr, "%s not empty cannot enable tracking\n", s->name);
720 }
721 if (!tracking && s->store_user) {
722 if (slab_empty(s))
723 set_obj(s, "store_user", 0);
724 else
725 fprintf(stderr, "%s not empty cannot disable tracking\n", s->name);
726 }
727 if (tracing && !s->trace) {
728 if (slabs == 1)
729 set_obj(s, "trace", 1);
730 else
731 fprintf(stderr, "%s can only enable trace for one slab at a time\n", s->name);
732 }
733 if (!tracing && s->trace)
734 set_obj(s, "trace", 1);
735}
736
737static void totals(void)
738{
739 struct slabinfo *s;
740
741 int used_slabs = 0;
742 char b1[20], b2[20], b3[20], b4[20];
743 unsigned long long max = 1ULL << 63;
744
745 /* Object size */
746 unsigned long long min_objsize = max, max_objsize = 0, avg_objsize;
747
748 /* Number of partial slabs in a slabcache */
749 unsigned long long min_partial = max, max_partial = 0,
750 avg_partial, total_partial = 0;
751
752 /* Number of slabs in a slab cache */
753 unsigned long long min_slabs = max, max_slabs = 0,
754 avg_slabs, total_slabs = 0;
755
756 /* Size of the whole slab */
757 unsigned long long min_size = max, max_size = 0,
758 avg_size, total_size = 0;
759
760 /* Bytes used for object storage in a slab */
761 unsigned long long min_used = max, max_used = 0,
762 avg_used, total_used = 0;
763
764 /* Waste: Bytes used for alignment and padding */
765 unsigned long long min_waste = max, max_waste = 0,
766 avg_waste, total_waste = 0;
767 /* Number of objects in a slab */
768 unsigned long long min_objects = max, max_objects = 0,
769 avg_objects, total_objects = 0;
770 /* Waste per object */
771 unsigned long long min_objwaste = max,
772 max_objwaste = 0, avg_objwaste,
773 total_objwaste = 0;
774
775 /* Memory per object */
776 unsigned long long min_memobj = max,
777 max_memobj = 0, avg_memobj,
778 total_objsize = 0;
779
780 /* Percentage of partial slabs per slab */
781 unsigned long min_ppart = 100, max_ppart = 0,
782 avg_ppart, total_ppart = 0;
783
784 /* Number of objects in partial slabs */
785 unsigned long min_partobj = max, max_partobj = 0,
786 avg_partobj, total_partobj = 0;
787
788 /* Percentage of partial objects of all objects in a slab */
789 unsigned long min_ppartobj = 100, max_ppartobj = 0,
790 avg_ppartobj, total_ppartobj = 0;
791
792
793 for (s = slabinfo; s < slabinfo + slabs; s++) {
794 unsigned long long size;
795 unsigned long used;
796 unsigned long long wasted;
797 unsigned long long objwaste;
798 unsigned long percentage_partial_slabs;
799 unsigned long percentage_partial_objs;
800
801 if (!s->slabs || !s->objects)
802 continue;
803
804 used_slabs++;
805
806 size = slab_size(s);
807 used = s->objects * s->object_size;
808 wasted = size - used;
809 objwaste = s->slab_size - s->object_size;
810
811 percentage_partial_slabs = s->partial * 100 / s->slabs;
812 if (percentage_partial_slabs > 100)
813 percentage_partial_slabs = 100;
814
815 percentage_partial_objs = s->objects_partial * 100
816 / s->objects;
817
818 if (percentage_partial_objs > 100)
819 percentage_partial_objs = 100;
820
821 if (s->object_size < min_objsize)
822 min_objsize = s->object_size;
823 if (s->partial < min_partial)
824 min_partial = s->partial;
825 if (s->slabs < min_slabs)
826 min_slabs = s->slabs;
827 if (size < min_size)
828 min_size = size;
829 if (wasted < min_waste)
830 min_waste = wasted;
831 if (objwaste < min_objwaste)
832 min_objwaste = objwaste;
833 if (s->objects < min_objects)
834 min_objects = s->objects;
835 if (used < min_used)
836 min_used = used;
837 if (s->objects_partial < min_partobj)
838 min_partobj = s->objects_partial;
839 if (percentage_partial_slabs < min_ppart)
840 min_ppart = percentage_partial_slabs;
841 if (percentage_partial_objs < min_ppartobj)
842 min_ppartobj = percentage_partial_objs;
843 if (s->slab_size < min_memobj)
844 min_memobj = s->slab_size;
845
846 if (s->object_size > max_objsize)
847 max_objsize = s->object_size;
848 if (s->partial > max_partial)
849 max_partial = s->partial;
850 if (s->slabs > max_slabs)
851 max_slabs = s->slabs;
852 if (size > max_size)
853 max_size = size;
854 if (wasted > max_waste)
855 max_waste = wasted;
856 if (objwaste > max_objwaste)
857 max_objwaste = objwaste;
858 if (s->objects > max_objects)
859 max_objects = s->objects;
860 if (used > max_used)
861 max_used = used;
862 if (s->objects_partial > max_partobj)
863 max_partobj = s->objects_partial;
864 if (percentage_partial_slabs > max_ppart)
865 max_ppart = percentage_partial_slabs;
866 if (percentage_partial_objs > max_ppartobj)
867 max_ppartobj = percentage_partial_objs;
868 if (s->slab_size > max_memobj)
869 max_memobj = s->slab_size;
870
871 total_partial += s->partial;
872 total_slabs += s->slabs;
873 total_size += size;
874 total_waste += wasted;
875
876 total_objects += s->objects;
877 total_used += used;
878 total_partobj += s->objects_partial;
879 total_ppart += percentage_partial_slabs;
880 total_ppartobj += percentage_partial_objs;
881
882 total_objwaste += s->objects * objwaste;
883 total_objsize += s->objects * s->slab_size;
884 }
885
886 if (!total_objects) {
887 printf("No objects\n");
888 return;
889 }
890 if (!used_slabs) {
891 printf("No slabs\n");
892 return;
893 }
894
895 /* Per slab averages */
896 avg_partial = total_partial / used_slabs;
897 avg_slabs = total_slabs / used_slabs;
898 avg_size = total_size / used_slabs;
899 avg_waste = total_waste / used_slabs;
900
901 avg_objects = total_objects / used_slabs;
902 avg_used = total_used / used_slabs;
903 avg_partobj = total_partobj / used_slabs;
904 avg_ppart = total_ppart / used_slabs;
905 avg_ppartobj = total_ppartobj / used_slabs;
906
907 /* Per object object sizes */
908 avg_objsize = total_used / total_objects;
909 avg_objwaste = total_objwaste / total_objects;
910 avg_partobj = total_partobj * 100 / total_objects;
911 avg_memobj = total_objsize / total_objects;
912
913 printf("Slabcache Totals\n");
914 printf("----------------\n");
915 printf("Slabcaches : %3d Aliases : %3d->%-3d Active: %3d\n",
916 slabs, aliases, alias_targets, used_slabs);
917
918 store_size(b1, total_size);store_size(b2, total_waste);
919 store_size(b3, total_waste * 100 / total_used);
920 printf("Memory used: %6s # Loss : %6s MRatio:%6s%%\n", b1, b2, b3);
921
922 store_size(b1, total_objects);store_size(b2, total_partobj);
923 store_size(b3, total_partobj * 100 / total_objects);
924 printf("# Objects : %6s # PartObj: %6s ORatio:%6s%%\n", b1, b2, b3);
925
926 printf("\n");
927 printf("Per Cache Average Min Max Total\n");
928 printf("---------------------------------------------------------\n");
929
930 store_size(b1, avg_objects);store_size(b2, min_objects);
931 store_size(b3, max_objects);store_size(b4, total_objects);
932 printf("#Objects %10s %10s %10s %10s\n",
933 b1, b2, b3, b4);
934
935 store_size(b1, avg_slabs);store_size(b2, min_slabs);
936 store_size(b3, max_slabs);store_size(b4, total_slabs);
937 printf("#Slabs %10s %10s %10s %10s\n",
938 b1, b2, b3, b4);
939
940 store_size(b1, avg_partial);store_size(b2, min_partial);
941 store_size(b3, max_partial);store_size(b4, total_partial);
942 printf("#PartSlab %10s %10s %10s %10s\n",
943 b1, b2, b3, b4);
944 store_size(b1, avg_ppart);store_size(b2, min_ppart);
945 store_size(b3, max_ppart);
946 store_size(b4, total_partial * 100 / total_slabs);
947 printf("%%PartSlab%10s%% %10s%% %10s%% %10s%%\n",
948 b1, b2, b3, b4);
949
950 store_size(b1, avg_partobj);store_size(b2, min_partobj);
951 store_size(b3, max_partobj);
952 store_size(b4, total_partobj);
953 printf("PartObjs %10s %10s %10s %10s\n",
954 b1, b2, b3, b4);
955
956 store_size(b1, avg_ppartobj);store_size(b2, min_ppartobj);
957 store_size(b3, max_ppartobj);
958 store_size(b4, total_partobj * 100 / total_objects);
959 printf("%% PartObj%10s%% %10s%% %10s%% %10s%%\n",
960 b1, b2, b3, b4);
961
962 store_size(b1, avg_size);store_size(b2, min_size);
963 store_size(b3, max_size);store_size(b4, total_size);
964 printf("Memory %10s %10s %10s %10s\n",
965 b1, b2, b3, b4);
966
967 store_size(b1, avg_used);store_size(b2, min_used);
968 store_size(b3, max_used);store_size(b4, total_used);
969 printf("Used %10s %10s %10s %10s\n",
970 b1, b2, b3, b4);
971
972 store_size(b1, avg_waste);store_size(b2, min_waste);
973 store_size(b3, max_waste);store_size(b4, total_waste);
974 printf("Loss %10s %10s %10s %10s\n",
975 b1, b2, b3, b4);
976
977 printf("\n");
978 printf("Per Object Average Min Max\n");
979 printf("---------------------------------------------\n");
980
981 store_size(b1, avg_memobj);store_size(b2, min_memobj);
982 store_size(b3, max_memobj);
983 printf("Memory %10s %10s %10s\n",
984 b1, b2, b3);
985 store_size(b1, avg_objsize);store_size(b2, min_objsize);
986 store_size(b3, max_objsize);
987 printf("User %10s %10s %10s\n",
988 b1, b2, b3);
989
990 store_size(b1, avg_objwaste);store_size(b2, min_objwaste);
991 store_size(b3, max_objwaste);
992 printf("Loss %10s %10s %10s\n",
993 b1, b2, b3);
994}
995
996static void sort_slabs(void)
997{
998 struct slabinfo *s1,*s2;
999
1000 for (s1 = slabinfo; s1 < slabinfo + slabs; s1++) {
1001 for (s2 = s1 + 1; s2 < slabinfo + slabs; s2++) {
1002 int result;
1003
1004 if (sort_size)
1005 result = slab_size(s1) < slab_size(s2);
1006 else if (sort_active)
1007 result = slab_activity(s1) < slab_activity(s2);
1008 else
1009 result = strcasecmp(s1->name, s2->name);
1010
1011 if (show_inverted)
1012 result = -result;
1013
1014 if (result > 0) {
1015 struct slabinfo t;
1016
1017 memcpy(&t, s1, sizeof(struct slabinfo));
1018 memcpy(s1, s2, sizeof(struct slabinfo));
1019 memcpy(s2, &t, sizeof(struct slabinfo));
1020 }
1021 }
1022 }
1023}
1024
1025static void sort_aliases(void)
1026{
1027 struct aliasinfo *a1,*a2;
1028
1029 for (a1 = aliasinfo; a1 < aliasinfo + aliases; a1++) {
1030 for (a2 = a1 + 1; a2 < aliasinfo + aliases; a2++) {
1031 char *n1, *n2;
1032
1033 n1 = a1->name;
1034 n2 = a2->name;
1035 if (show_alias && !show_inverted) {
1036 n1 = a1->ref;
1037 n2 = a2->ref;
1038 }
1039 if (strcasecmp(n1, n2) > 0) {
1040 struct aliasinfo t;
1041
1042 memcpy(&t, a1, sizeof(struct aliasinfo));
1043 memcpy(a1, a2, sizeof(struct aliasinfo));
1044 memcpy(a2, &t, sizeof(struct aliasinfo));
1045 }
1046 }
1047 }
1048}
1049
1050static void link_slabs(void)
1051{
1052 struct aliasinfo *a;
1053 struct slabinfo *s;
1054
1055 for (a = aliasinfo; a < aliasinfo + aliases; a++) {
1056
1057 for (s = slabinfo; s < slabinfo + slabs; s++)
1058 if (strcmp(a->ref, s->name) == 0) {
1059 a->slab = s;
1060 s->refs++;
1061 break;
1062 }
1063 if (s == slabinfo + slabs)
1064 fatal("Unresolved alias %s\n", a->ref);
1065 }
1066}
1067
1068static void alias(void)
1069{
1070 struct aliasinfo *a;
1071 char *active = NULL;
1072
1073 sort_aliases();
1074 link_slabs();
1075
1076 for(a = aliasinfo; a < aliasinfo + aliases; a++) {
1077
1078 if (!show_single_ref && a->slab->refs == 1)
1079 continue;
1080
1081 if (!show_inverted) {
1082 if (active) {
1083 if (strcmp(a->slab->name, active) == 0) {
1084 printf(" %s", a->name);
1085 continue;
1086 }
1087 }
1088 printf("\n%-12s <- %s", a->slab->name, a->name);
1089 active = a->slab->name;
1090 }
1091 else
1092 printf("%-20s -> %s\n", a->name, a->slab->name);
1093 }
1094 if (active)
1095 printf("\n");
1096}
1097
1098
1099static void rename_slabs(void)
1100{
1101 struct slabinfo *s;
1102 struct aliasinfo *a;
1103
1104 for (s = slabinfo; s < slabinfo + slabs; s++) {
1105 if (*s->name != ':')
1106 continue;
1107
1108 if (s->refs > 1 && !show_first_alias)
1109 continue;
1110
1111 a = find_one_alias(s);
1112
1113 if (a)
1114 s->name = a->name;
1115 else {
1116 s->name = "*";
1117 actual_slabs--;
1118 }
1119 }
1120}
1121
1122static int slab_mismatch(char *slab)
1123{
1124 return regexec(&pattern, slab, 0, NULL, 0);
1125}
1126
1127static void read_slab_dir(void)
1128{
1129 DIR *dir;
1130 struct dirent *de;
1131 struct slabinfo *slab = slabinfo;
1132 struct aliasinfo *alias = aliasinfo;
1133 char *p;
1134 char *t;
1135 int count;
1136
1137 if (chdir("/sys/kernel/slab") && chdir("/sys/slab"))
1138 fatal("SYSFS support for SLUB not active\n");
1139
1140 dir = opendir(".");
1141 while ((de = readdir(dir))) {
1142 if (de->d_name[0] == '.' ||
1143 (de->d_name[0] != ':' && slab_mismatch(de->d_name)))
1144 continue;
1145 switch (de->d_type) {
1146 case DT_LNK:
1147 alias->name = strdup(de->d_name);
1148 count = readlink(de->d_name, buffer, sizeof(buffer));
1149
1150 if (count < 0)
1151 fatal("Cannot read symlink %s\n", de->d_name);
1152
1153 buffer[count] = 0;
1154 p = buffer + count;
1155 while (p > buffer && p[-1] != '/')
1156 p--;
1157 alias->ref = strdup(p);
1158 alias++;
1159 break;
1160 case DT_DIR:
1161 if (chdir(de->d_name))
1162 fatal("Unable to access slab %s\n", slab->name);
1163 slab->name = strdup(de->d_name);
1164 slab->alias = 0;
1165 slab->refs = 0;
1166 slab->aliases = get_obj("aliases");
1167 slab->align = get_obj("align");
1168 slab->cache_dma = get_obj("cache_dma");
1169 slab->cpu_slabs = get_obj("cpu_slabs");
1170 slab->destroy_by_rcu = get_obj("destroy_by_rcu");
1171 slab->hwcache_align = get_obj("hwcache_align");
1172 slab->object_size = get_obj("object_size");
1173 slab->objects = get_obj("objects");
1174 slab->objects_partial = get_obj("objects_partial");
1175 slab->objects_total = get_obj("objects_total");
1176 slab->objs_per_slab = get_obj("objs_per_slab");
1177 slab->order = get_obj("order");
1178 slab->partial = get_obj("partial");
1179 slab->partial = get_obj_and_str("partial", &t);
1180 decode_numa_list(slab->numa_partial, t);
1181 free(t);
1182 slab->poison = get_obj("poison");
1183 slab->reclaim_account = get_obj("reclaim_account");
1184 slab->red_zone = get_obj("red_zone");
1185 slab->sanity_checks = get_obj("sanity_checks");
1186 slab->slab_size = get_obj("slab_size");
1187 slab->slabs = get_obj_and_str("slabs", &t);
1188 decode_numa_list(slab->numa, t);
1189 free(t);
1190 slab->store_user = get_obj("store_user");
1191 slab->trace = get_obj("trace");
1192 slab->alloc_fastpath = get_obj("alloc_fastpath");
1193 slab->alloc_slowpath = get_obj("alloc_slowpath");
1194 slab->free_fastpath = get_obj("free_fastpath");
1195 slab->free_slowpath = get_obj("free_slowpath");
1196 slab->free_frozen= get_obj("free_frozen");
1197 slab->free_add_partial = get_obj("free_add_partial");
1198 slab->free_remove_partial = get_obj("free_remove_partial");
1199 slab->alloc_from_partial = get_obj("alloc_from_partial");
1200 slab->alloc_slab = get_obj("alloc_slab");
1201 slab->alloc_refill = get_obj("alloc_refill");
1202 slab->free_slab = get_obj("free_slab");
1203 slab->cpuslab_flush = get_obj("cpuslab_flush");
1204 slab->deactivate_full = get_obj("deactivate_full");
1205 slab->deactivate_empty = get_obj("deactivate_empty");
1206 slab->deactivate_to_head = get_obj("deactivate_to_head");
1207 slab->deactivate_to_tail = get_obj("deactivate_to_tail");
1208 slab->deactivate_remote_frees = get_obj("deactivate_remote_frees");
1209 slab->order_fallback = get_obj("order_fallback");
1210 slab->cmpxchg_double_cpu_fail = get_obj("cmpxchg_double_cpu_fail");
1211 slab->cmpxchg_double_fail = get_obj("cmpxchg_double_fail");
1212 slab->alloc_node_mismatch = get_obj("alloc_node_mismatch");
1213 slab->deactivate_bypass = get_obj("deactivate_bypass");
1214 chdir("..");
1215 if (slab->name[0] == ':')
1216 alias_targets++;
1217 slab++;
1218 break;
1219 default :
1220 fatal("Unknown file type %lx\n", de->d_type);
1221 }
1222 }
1223 closedir(dir);
1224 slabs = slab - slabinfo;
1225 actual_slabs = slabs;
1226 aliases = alias - aliasinfo;
1227 if (slabs > MAX_SLABS)
1228 fatal("Too many slabs\n");
1229 if (aliases > MAX_ALIASES)
1230 fatal("Too many aliases\n");
1231}
1232
1233static void output_slabs(void)
1234{
1235 struct slabinfo *slab;
1236
1237 for (slab = slabinfo; slab < slabinfo + slabs; slab++) {
1238
1239 if (slab->alias)
1240 continue;
1241
1242
1243 if (show_numa)
1244 slab_numa(slab, 0);
1245 else if (show_track)
1246 show_tracking(slab);
1247 else if (validate)
1248 slab_validate(slab);
1249 else if (shrink)
1250 slab_shrink(slab);
1251 else if (set_debug)
1252 slab_debug(slab);
1253 else if (show_ops)
1254 ops(slab);
1255 else if (show_slab)
1256 slabcache(slab);
1257 else if (show_report)
1258 report(slab);
1259 }
1260}
1261
1262struct option opts[] = {
1263 { "aliases", 0, NULL, 'a' },
1264 { "activity", 0, NULL, 'A' },
1265 { "debug", 2, NULL, 'd' },
1266 { "display-activity", 0, NULL, 'D' },
1267 { "empty", 0, NULL, 'e' },
1268 { "first-alias", 0, NULL, 'f' },
1269 { "help", 0, NULL, 'h' },
1270 { "inverted", 0, NULL, 'i'},
1271 { "numa", 0, NULL, 'n' },
1272 { "ops", 0, NULL, 'o' },
1273 { "report", 0, NULL, 'r' },
1274 { "shrink", 0, NULL, 's' },
1275 { "slabs", 0, NULL, 'l' },
1276 { "track", 0, NULL, 't'},
1277 { "validate", 0, NULL, 'v' },
1278 { "zero", 0, NULL, 'z' },
1279 { "1ref", 0, NULL, '1'},
1280 { NULL, 0, NULL, 0 }
1281};
1282
1283int main(int argc, char *argv[])
1284{
1285 int c;
1286 int err;
1287 char *pattern_source;
1288
1289 page_size = getpagesize();
1290
1291 while ((c = getopt_long(argc, argv, "aAd::Defhil1noprstvzTS",
1292 opts, NULL)) != -1)
1293 switch (c) {
1294 case '1':
1295 show_single_ref = 1;
1296 break;
1297 case 'a':
1298 show_alias = 1;
1299 break;
1300 case 'A':
1301 sort_active = 1;
1302 break;
1303 case 'd':
1304 set_debug = 1;
1305 if (!debug_opt_scan(optarg))
1306 fatal("Invalid debug option '%s'\n", optarg);
1307 break;
1308 case 'D':
1309 show_activity = 1;
1310 break;
1311 case 'e':
1312 show_empty = 1;
1313 break;
1314 case 'f':
1315 show_first_alias = 1;
1316 break;
1317 case 'h':
1318 usage();
1319 return 0;
1320 case 'i':
1321 show_inverted = 1;
1322 break;
1323 case 'n':
1324 show_numa = 1;
1325 break;
1326 case 'o':
1327 show_ops = 1;
1328 break;
1329 case 'r':
1330 show_report = 1;
1331 break;
1332 case 's':
1333 shrink = 1;
1334 break;
1335 case 'l':
1336 show_slab = 1;
1337 break;
1338 case 't':
1339 show_track = 1;
1340 break;
1341 case 'v':
1342 validate = 1;
1343 break;
1344 case 'z':
1345 skip_zero = 0;
1346 break;
1347 case 'T':
1348 show_totals = 1;
1349 break;
1350 case 'S':
1351 sort_size = 1;
1352 break;
1353
1354 default:
1355 fatal("%s: Invalid option '%c'\n", argv[0], optopt);
1356
1357 }
1358
1359 if (!show_slab && !show_alias && !show_track && !show_report
1360 && !validate && !shrink && !set_debug && !show_ops)
1361 show_slab = 1;
1362
1363 if (argc > optind)
1364 pattern_source = argv[optind];
1365 else
1366 pattern_source = ".*";
1367
1368 err = regcomp(&pattern, pattern_source, REG_ICASE|REG_NOSUB);
1369 if (err)
1370 fatal("%s: Invalid pattern '%s' code %d\n",
1371 argv[0], pattern_source, err);
1372 read_slab_dir();
1373 if (show_alias)
1374 alias();
1375 else
1376 if (show_totals)
1377 totals();
1378 else {
1379 link_slabs();
1380 rename_slabs();
1381 sort_slabs();
1382 output_slabs();
1383 }
1384 return 0;
1385}