aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/util
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util')
-rw-r--r--tools/perf/util/annotate.c11
-rw-r--r--tools/perf/util/annotate.h9
-rw-r--r--tools/perf/util/color.c2
-rw-r--r--tools/perf/util/evlist.c6
-rw-r--r--tools/perf/util/evlist.h4
-rw-r--r--tools/perf/util/evsel.c1
-rw-r--r--tools/perf/util/header.c1231
-rw-r--r--tools/perf/util/header.h29
-rw-r--r--tools/perf/util/hist.c447
-rw-r--r--tools/perf/util/hist.h61
-rw-r--r--tools/perf/util/map.c102
-rw-r--r--tools/perf/util/map.h42
-rw-r--r--tools/perf/util/probe-event.c4
-rw-r--r--tools/perf/util/session.c19
-rw-r--r--tools/perf/util/session.h1
-rw-r--r--tools/perf/util/sort.c4
-rw-r--r--tools/perf/util/sort.h2
-rw-r--r--tools/perf/util/symbol.c3
-rw-r--r--tools/perf/util/symbol.h5
-rw-r--r--tools/perf/util/top.c141
-rw-r--r--tools/perf/util/top.h39
-rw-r--r--tools/perf/util/trace-event-info.c112
-rw-r--r--tools/perf/util/trace-event.h13
-rw-r--r--tools/perf/util/ui/browser.c386
-rw-r--r--tools/perf/util/ui/browser.h12
-rw-r--r--tools/perf/util/ui/browsers/annotate.c201
-rw-r--r--tools/perf/util/ui/browsers/hists.c297
-rw-r--r--tools/perf/util/ui/browsers/map.c6
-rw-r--r--tools/perf/util/ui/browsers/top.c212
-rw-r--r--tools/perf/util/ui/helpline.h3
-rw-r--r--tools/perf/util/ui/keysyms.h25
-rw-r--r--tools/perf/util/ui/libslang.h2
-rw-r--r--tools/perf/util/ui/setup.c38
33 files changed, 2552 insertions, 918 deletions
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index e01af2b1a469..bc8f4773d4d8 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -16,6 +16,8 @@
16#include "annotate.h" 16#include "annotate.h"
17#include <pthread.h> 17#include <pthread.h>
18 18
19const char *disassembler_style;
20
19int symbol__annotate_init(struct map *map __used, struct symbol *sym) 21int symbol__annotate_init(struct map *map __used, struct symbol *sym)
20{ 22{
21 struct annotation *notes = symbol__annotation(sym); 23 struct annotation *notes = symbol__annotation(sym);
@@ -323,10 +325,15 @@ fallback:
323 dso, dso->long_name, sym, sym->name); 325 dso, dso->long_name, sym, sym->name);
324 326
325 snprintf(command, sizeof(command), 327 snprintf(command, sizeof(command),
326 "objdump --start-address=0x%016" PRIx64 328 "objdump %s%s --start-address=0x%016" PRIx64
327 " --stop-address=0x%016" PRIx64 " -dS -C %s|grep -v %s|expand", 329 " --stop-address=0x%016" PRIx64
330 " -d %s %s -C %s|grep -v %s|expand",
331 disassembler_style ? "-M " : "",
332 disassembler_style ? disassembler_style : "",
328 map__rip_2objdump(map, sym->start), 333 map__rip_2objdump(map, sym->start),
329 map__rip_2objdump(map, sym->end), 334 map__rip_2objdump(map, sym->end),
335 symbol_conf.annotate_asm_raw ? "" : "--no-show-raw",
336 symbol_conf.annotate_src ? "-S" : "",
330 symfs_filename, filename); 337 symfs_filename, filename);
331 338
332 pr_debug("Executing: %s\n", command); 339 pr_debug("Executing: %s\n", command);
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h
index c2c286896801..d9072523d342 100644
--- a/tools/perf/util/annotate.h
+++ b/tools/perf/util/annotate.h
@@ -91,13 +91,18 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx,
91#ifdef NO_NEWT_SUPPORT 91#ifdef NO_NEWT_SUPPORT
92static inline int symbol__tui_annotate(struct symbol *sym __used, 92static inline int symbol__tui_annotate(struct symbol *sym __used,
93 struct map *map __used, 93 struct map *map __used,
94 int evidx __used, int refresh __used) 94 int evidx __used,
95 void(*timer)(void *arg) __used,
96 void *arg __used, int delay_secs __used)
95{ 97{
96 return 0; 98 return 0;
97} 99}
98#else 100#else
99int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, 101int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
100 int refresh); 102 int nr_events, void(*timer)(void *arg), void *arg,
103 int delay_secs);
101#endif 104#endif
102 105
106extern const char *disassembler_style;
107
103#endif /* __PERF_ANNOTATE_H */ 108#endif /* __PERF_ANNOTATE_H */
diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c
index e191eb9a667f..521c38a79190 100644
--- a/tools/perf/util/color.c
+++ b/tools/perf/util/color.c
@@ -200,7 +200,7 @@ static int __color_vfprintf(FILE *fp, const char *color, const char *fmt,
200 * Auto-detect: 200 * Auto-detect:
201 */ 201 */
202 if (perf_use_color_default < 0) { 202 if (perf_use_color_default < 0) {
203 if (isatty(1) || pager_in_use()) 203 if (isatty(fileno(fp)) || pager_in_use())
204 perf_use_color_default = 1; 204 perf_use_color_default = 1;
205 else 205 else
206 perf_use_color_default = 0; 206 perf_use_color_default = 0;
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
index 72e9f4886b6d..2f6bc89027da 100644
--- a/tools/perf/util/evlist.c
+++ b/tools/perf/util/evlist.c
@@ -533,3 +533,9 @@ bool perf_evlist__sample_id_all(const struct perf_evlist *evlist)
533 first = list_entry(evlist->entries.next, struct perf_evsel, node); 533 first = list_entry(evlist->entries.next, struct perf_evsel, node);
534 return first->attr.sample_id_all; 534 return first->attr.sample_id_all;
535} 535}
536
537void perf_evlist__set_selected(struct perf_evlist *evlist,
538 struct perf_evsel *evsel)
539{
540 evlist->selected = evsel;
541}
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h
index f34915002745..6be71fc57794 100644
--- a/tools/perf/util/evlist.h
+++ b/tools/perf/util/evlist.h
@@ -25,6 +25,7 @@ struct perf_evlist {
25 struct pollfd *pollfd; 25 struct pollfd *pollfd;
26 struct thread_map *threads; 26 struct thread_map *threads;
27 struct cpu_map *cpus; 27 struct cpu_map *cpus;
28 struct perf_evsel *selected;
28}; 29};
29 30
30struct perf_evsel; 31struct perf_evsel;
@@ -56,6 +57,9 @@ void perf_evlist__munmap(struct perf_evlist *evlist);
56void perf_evlist__disable(struct perf_evlist *evlist); 57void perf_evlist__disable(struct perf_evlist *evlist);
57void perf_evlist__enable(struct perf_evlist *evlist); 58void perf_evlist__enable(struct perf_evlist *evlist);
58 59
60void perf_evlist__set_selected(struct perf_evlist *evlist,
61 struct perf_evsel *evsel);
62
59static inline void perf_evlist__set_maps(struct perf_evlist *evlist, 63static inline void perf_evlist__set_maps(struct perf_evlist *evlist,
60 struct cpu_map *cpus, 64 struct cpu_map *cpus,
61 struct thread_map *threads) 65 struct thread_map *threads)
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index e389815078d3..b46f6e4bff3c 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -39,6 +39,7 @@ void perf_evsel__init(struct perf_evsel *evsel,
39 evsel->idx = idx; 39 evsel->idx = idx;
40 evsel->attr = *attr; 40 evsel->attr = *attr;
41 INIT_LIST_HEAD(&evsel->node); 41 INIT_LIST_HEAD(&evsel->node);
42 hists__init(&evsel->hists);
42} 43}
43 44
44struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx) 45struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx)
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index b6c1ad123ca9..76c0b2c49eb8 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -7,6 +7,7 @@
7#include <stdlib.h> 7#include <stdlib.h>
8#include <linux/list.h> 8#include <linux/list.h>
9#include <linux/kernel.h> 9#include <linux/kernel.h>
10#include <sys/utsname.h>
10 11
11#include "evlist.h" 12#include "evlist.h"
12#include "evsel.h" 13#include "evsel.h"
@@ -17,12 +18,19 @@
17#include "session.h" 18#include "session.h"
18#include "symbol.h" 19#include "symbol.h"
19#include "debug.h" 20#include "debug.h"
21#include "cpumap.h"
20 22
21static bool no_buildid_cache = false; 23static bool no_buildid_cache = false;
22 24
23static int event_count; 25static int event_count;
24static struct perf_trace_event_type *events; 26static struct perf_trace_event_type *events;
25 27
28static u32 header_argc;
29static const char **header_argv;
30
31static int dsos__write_buildid_table(struct perf_header *header, int fd);
32static int perf_session__cache_build_ids(struct perf_session *session);
33
26int perf_header__push_event(u64 id, const char *name) 34int perf_header__push_event(u64 id, const char *name)
27{ 35{
28 if (strlen(name) > MAX_EVENT_NAME) 36 if (strlen(name) > MAX_EVENT_NAME)
@@ -110,6 +118,1020 @@ static int write_padded(int fd, const void *bf, size_t count,
110 return err; 118 return err;
111} 119}
112 120
121static int do_write_string(int fd, const char *str)
122{
123 u32 len, olen;
124 int ret;
125
126 olen = strlen(str) + 1;
127 len = ALIGN(olen, NAME_ALIGN);
128
129 /* write len, incl. \0 */
130 ret = do_write(fd, &len, sizeof(len));
131 if (ret < 0)
132 return ret;
133
134 return write_padded(fd, str, olen, len);
135}
136
137static char *do_read_string(int fd, struct perf_header *ph)
138{
139 ssize_t sz, ret;
140 u32 len;
141 char *buf;
142
143 sz = read(fd, &len, sizeof(len));
144 if (sz < (ssize_t)sizeof(len))
145 return NULL;
146
147 if (ph->needs_swap)
148 len = bswap_32(len);
149
150 buf = malloc(len);
151 if (!buf)
152 return NULL;
153
154 ret = read(fd, buf, len);
155 if (ret == (ssize_t)len) {
156 /*
157 * strings are padded by zeroes
158 * thus the actual strlen of buf
159 * may be less than len
160 */
161 return buf;
162 }
163
164 free(buf);
165 return NULL;
166}
167
168int
169perf_header__set_cmdline(int argc, const char **argv)
170{
171 int i;
172
173 header_argc = (u32)argc;
174
175 /* do not include NULL termination */
176 header_argv = calloc(argc, sizeof(char *));
177 if (!header_argv)
178 return -ENOMEM;
179
180 /*
181 * must copy argv contents because it gets moved
182 * around during option parsing
183 */
184 for (i = 0; i < argc ; i++)
185 header_argv[i] = argv[i];
186
187 return 0;
188}
189
190static int write_trace_info(int fd, struct perf_header *h __used,
191 struct perf_evlist *evlist)
192{
193 return read_tracing_data(fd, &evlist->entries);
194}
195
196
197static int write_build_id(int fd, struct perf_header *h,
198 struct perf_evlist *evlist __used)
199{
200 struct perf_session *session;
201 int err;
202
203 session = container_of(h, struct perf_session, header);
204
205 err = dsos__write_buildid_table(h, fd);
206 if (err < 0) {
207 pr_debug("failed to write buildid table\n");
208 return err;
209 }
210 if (!no_buildid_cache)
211 perf_session__cache_build_ids(session);
212
213 return 0;
214}
215
216static int write_hostname(int fd, struct perf_header *h __used,
217 struct perf_evlist *evlist __used)
218{
219 struct utsname uts;
220 int ret;
221
222 ret = uname(&uts);
223 if (ret < 0)
224 return -1;
225
226 return do_write_string(fd, uts.nodename);
227}
228
229static int write_osrelease(int fd, struct perf_header *h __used,
230 struct perf_evlist *evlist __used)
231{
232 struct utsname uts;
233 int ret;
234
235 ret = uname(&uts);
236 if (ret < 0)
237 return -1;
238
239 return do_write_string(fd, uts.release);
240}
241
242static int write_arch(int fd, struct perf_header *h __used,
243 struct perf_evlist *evlist __used)
244{
245 struct utsname uts;
246 int ret;
247
248 ret = uname(&uts);
249 if (ret < 0)
250 return -1;
251
252 return do_write_string(fd, uts.machine);
253}
254
255static int write_version(int fd, struct perf_header *h __used,
256 struct perf_evlist *evlist __used)
257{
258 return do_write_string(fd, perf_version_string);
259}
260
261static int write_cpudesc(int fd, struct perf_header *h __used,
262 struct perf_evlist *evlist __used)
263{
264#ifndef CPUINFO_PROC
265#define CPUINFO_PROC NULL
266#endif
267 FILE *file;
268 char *buf = NULL;
269 char *s, *p;
270 const char *search = CPUINFO_PROC;
271 size_t len = 0;
272 int ret = -1;
273
274 if (!search)
275 return -1;
276
277 file = fopen("/proc/cpuinfo", "r");
278 if (!file)
279 return -1;
280
281 while (getline(&buf, &len, file) > 0) {
282 ret = strncmp(buf, search, strlen(search));
283 if (!ret)
284 break;
285 }
286
287 if (ret)
288 goto done;
289
290 s = buf;
291
292 p = strchr(buf, ':');
293 if (p && *(p+1) == ' ' && *(p+2))
294 s = p + 2;
295 p = strchr(s, '\n');
296 if (p)
297 *p = '\0';
298
299 /* squash extra space characters (branding string) */
300 p = s;
301 while (*p) {
302 if (isspace(*p)) {
303 char *r = p + 1;
304 char *q = r;
305 *p = ' ';
306 while (*q && isspace(*q))
307 q++;
308 if (q != (p+1))
309 while ((*r++ = *q++));
310 }
311 p++;
312 }
313 ret = do_write_string(fd, s);
314done:
315 free(buf);
316 fclose(file);
317 return ret;
318}
319
320static int write_nrcpus(int fd, struct perf_header *h __used,
321 struct perf_evlist *evlist __used)
322{
323 long nr;
324 u32 nrc, nra;
325 int ret;
326
327 nr = sysconf(_SC_NPROCESSORS_CONF);
328 if (nr < 0)
329 return -1;
330
331 nrc = (u32)(nr & UINT_MAX);
332
333 nr = sysconf(_SC_NPROCESSORS_ONLN);
334 if (nr < 0)
335 return -1;
336
337 nra = (u32)(nr & UINT_MAX);
338
339 ret = do_write(fd, &nrc, sizeof(nrc));
340 if (ret < 0)
341 return ret;
342
343 return do_write(fd, &nra, sizeof(nra));
344}
345
346static int write_event_desc(int fd, struct perf_header *h __used,
347 struct perf_evlist *evlist)
348{
349 struct perf_evsel *attr;
350 u32 nre = 0, nri, sz;
351 int ret;
352
353 list_for_each_entry(attr, &evlist->entries, node)
354 nre++;
355
356 /*
357 * write number of events
358 */
359 ret = do_write(fd, &nre, sizeof(nre));
360 if (ret < 0)
361 return ret;
362
363 /*
364 * size of perf_event_attr struct
365 */
366 sz = (u32)sizeof(attr->attr);
367 ret = do_write(fd, &sz, sizeof(sz));
368 if (ret < 0)
369 return ret;
370
371 list_for_each_entry(attr, &evlist->entries, node) {
372
373 ret = do_write(fd, &attr->attr, sz);
374 if (ret < 0)
375 return ret;
376 /*
377 * write number of unique id per event
378 * there is one id per instance of an event
379 *
380 * copy into an nri to be independent of the
381 * type of ids,
382 */
383 nri = attr->ids;
384 ret = do_write(fd, &nri, sizeof(nri));
385 if (ret < 0)
386 return ret;
387
388 /*
389 * write event string as passed on cmdline
390 */
391 ret = do_write_string(fd, attr->name);
392 if (ret < 0)
393 return ret;
394 /*
395 * write unique ids for this event
396 */
397 ret = do_write(fd, attr->id, attr->ids * sizeof(u64));
398 if (ret < 0)
399 return ret;
400 }
401 return 0;
402}
403
404static int write_cmdline(int fd, struct perf_header *h __used,
405 struct perf_evlist *evlist __used)
406{
407 char buf[MAXPATHLEN];
408 char proc[32];
409 u32 i, n;
410 int ret;
411
412 /*
413 * actual atual path to perf binary
414 */
415 sprintf(proc, "/proc/%d/exe", getpid());
416 ret = readlink(proc, buf, sizeof(buf));
417 if (ret <= 0)
418 return -1;
419
420 /* readlink() does not add null termination */
421 buf[ret] = '\0';
422
423 /* account for binary path */
424 n = header_argc + 1;
425
426 ret = do_write(fd, &n, sizeof(n));
427 if (ret < 0)
428 return ret;
429
430 ret = do_write_string(fd, buf);
431 if (ret < 0)
432 return ret;
433
434 for (i = 0 ; i < header_argc; i++) {
435 ret = do_write_string(fd, header_argv[i]);
436 if (ret < 0)
437 return ret;
438 }
439 return 0;
440}
441
442#define CORE_SIB_FMT \
443 "/sys/devices/system/cpu/cpu%d/topology/core_siblings_list"
444#define THRD_SIB_FMT \
445 "/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list"
446
447struct cpu_topo {
448 u32 core_sib;
449 u32 thread_sib;
450 char **core_siblings;
451 char **thread_siblings;
452};
453
454static int build_cpu_topo(struct cpu_topo *tp, int cpu)
455{
456 FILE *fp;
457 char filename[MAXPATHLEN];
458 char *buf = NULL, *p;
459 size_t len = 0;
460 u32 i = 0;
461 int ret = -1;
462
463 sprintf(filename, CORE_SIB_FMT, cpu);
464 fp = fopen(filename, "r");
465 if (!fp)
466 return -1;
467
468 if (getline(&buf, &len, fp) <= 0)
469 goto done;
470
471 fclose(fp);
472
473 p = strchr(buf, '\n');
474 if (p)
475 *p = '\0';
476
477 for (i = 0; i < tp->core_sib; i++) {
478 if (!strcmp(buf, tp->core_siblings[i]))
479 break;
480 }
481 if (i == tp->core_sib) {
482 tp->core_siblings[i] = buf;
483 tp->core_sib++;
484 buf = NULL;
485 len = 0;
486 }
487
488 sprintf(filename, THRD_SIB_FMT, cpu);
489 fp = fopen(filename, "r");
490 if (!fp)
491 goto done;
492
493 if (getline(&buf, &len, fp) <= 0)
494 goto done;
495
496 p = strchr(buf, '\n');
497 if (p)
498 *p = '\0';
499
500 for (i = 0; i < tp->thread_sib; i++) {
501 if (!strcmp(buf, tp->thread_siblings[i]))
502 break;
503 }
504 if (i == tp->thread_sib) {
505 tp->thread_siblings[i] = buf;
506 tp->thread_sib++;
507 buf = NULL;
508 }
509 ret = 0;
510done:
511 if(fp)
512 fclose(fp);
513 free(buf);
514 return ret;
515}
516
517static void free_cpu_topo(struct cpu_topo *tp)
518{
519 u32 i;
520
521 if (!tp)
522 return;
523
524 for (i = 0 ; i < tp->core_sib; i++)
525 free(tp->core_siblings[i]);
526
527 for (i = 0 ; i < tp->thread_sib; i++)
528 free(tp->thread_siblings[i]);
529
530 free(tp);
531}
532
533static struct cpu_topo *build_cpu_topology(void)
534{
535 struct cpu_topo *tp;
536 void *addr;
537 u32 nr, i;
538 size_t sz;
539 long ncpus;
540 int ret = -1;
541
542 ncpus = sysconf(_SC_NPROCESSORS_CONF);
543 if (ncpus < 0)
544 return NULL;
545
546 nr = (u32)(ncpus & UINT_MAX);
547
548 sz = nr * sizeof(char *);
549
550 addr = calloc(1, sizeof(*tp) + 2 * sz);
551 if (!addr)
552 return NULL;
553
554 tp = addr;
555
556 addr += sizeof(*tp);
557 tp->core_siblings = addr;
558 addr += sz;
559 tp->thread_siblings = addr;
560
561 for (i = 0; i < nr; i++) {
562 ret = build_cpu_topo(tp, i);
563 if (ret < 0)
564 break;
565 }
566 if (ret) {
567 free_cpu_topo(tp);
568 tp = NULL;
569 }
570 return tp;
571}
572
573static int write_cpu_topology(int fd, struct perf_header *h __used,
574 struct perf_evlist *evlist __used)
575{
576 struct cpu_topo *tp;
577 u32 i;
578 int ret;
579
580 tp = build_cpu_topology();
581 if (!tp)
582 return -1;
583
584 ret = do_write(fd, &tp->core_sib, sizeof(tp->core_sib));
585 if (ret < 0)
586 goto done;
587
588 for (i = 0; i < tp->core_sib; i++) {
589 ret = do_write_string(fd, tp->core_siblings[i]);
590 if (ret < 0)
591 goto done;
592 }
593 ret = do_write(fd, &tp->thread_sib, sizeof(tp->thread_sib));
594 if (ret < 0)
595 goto done;
596
597 for (i = 0; i < tp->thread_sib; i++) {
598 ret = do_write_string(fd, tp->thread_siblings[i]);
599 if (ret < 0)
600 break;
601 }
602done:
603 free_cpu_topo(tp);
604 return ret;
605}
606
607
608
609static int write_total_mem(int fd, struct perf_header *h __used,
610 struct perf_evlist *evlist __used)
611{
612 char *buf = NULL;
613 FILE *fp;
614 size_t len = 0;
615 int ret = -1, n;
616 uint64_t mem;
617
618 fp = fopen("/proc/meminfo", "r");
619 if (!fp)
620 return -1;
621
622 while (getline(&buf, &len, fp) > 0) {
623 ret = strncmp(buf, "MemTotal:", 9);
624 if (!ret)
625 break;
626 }
627 if (!ret) {
628 n = sscanf(buf, "%*s %"PRIu64, &mem);
629 if (n == 1)
630 ret = do_write(fd, &mem, sizeof(mem));
631 }
632 free(buf);
633 fclose(fp);
634 return ret;
635}
636
637static int write_topo_node(int fd, int node)
638{
639 char str[MAXPATHLEN];
640 char field[32];
641 char *buf = NULL, *p;
642 size_t len = 0;
643 FILE *fp;
644 u64 mem_total, mem_free, mem;
645 int ret = -1;
646
647 sprintf(str, "/sys/devices/system/node/node%d/meminfo", node);
648 fp = fopen(str, "r");
649 if (!fp)
650 return -1;
651
652 while (getline(&buf, &len, fp) > 0) {
653 /* skip over invalid lines */
654 if (!strchr(buf, ':'))
655 continue;
656 if (sscanf(buf, "%*s %*d %s %"PRIu64, field, &mem) != 2)
657 goto done;
658 if (!strcmp(field, "MemTotal:"))
659 mem_total = mem;
660 if (!strcmp(field, "MemFree:"))
661 mem_free = mem;
662 }
663
664 fclose(fp);
665
666 ret = do_write(fd, &mem_total, sizeof(u64));
667 if (ret)
668 goto done;
669
670 ret = do_write(fd, &mem_free, sizeof(u64));
671 if (ret)
672 goto done;
673
674 ret = -1;
675 sprintf(str, "/sys/devices/system/node/node%d/cpulist", node);
676
677 fp = fopen(str, "r");
678 if (!fp)
679 goto done;
680
681 if (getline(&buf, &len, fp) <= 0)
682 goto done;
683
684 p = strchr(buf, '\n');
685 if (p)
686 *p = '\0';
687
688 ret = do_write_string(fd, buf);
689done:
690 free(buf);
691 fclose(fp);
692 return ret;
693}
694
695static int write_numa_topology(int fd, struct perf_header *h __used,
696 struct perf_evlist *evlist __used)
697{
698 char *buf = NULL;
699 size_t len = 0;
700 FILE *fp;
701 struct cpu_map *node_map = NULL;
702 char *c;
703 u32 nr, i, j;
704 int ret = -1;
705
706 fp = fopen("/sys/devices/system/node/online", "r");
707 if (!fp)
708 return -1;
709
710 if (getline(&buf, &len, fp) <= 0)
711 goto done;
712
713 c = strchr(buf, '\n');
714 if (c)
715 *c = '\0';
716
717 node_map = cpu_map__new(buf);
718 if (!node_map)
719 goto done;
720
721 nr = (u32)node_map->nr;
722
723 ret = do_write(fd, &nr, sizeof(nr));
724 if (ret < 0)
725 goto done;
726
727 for (i = 0; i < nr; i++) {
728 j = (u32)node_map->map[i];
729 ret = do_write(fd, &j, sizeof(j));
730 if (ret < 0)
731 break;
732
733 ret = write_topo_node(fd, i);
734 if (ret < 0)
735 break;
736 }
737done:
738 free(buf);
739 fclose(fp);
740 free(node_map);
741 return ret;
742}
743
744/*
745 * default get_cpuid(): nothing gets recorded
746 * actual implementation must be in arch/$(ARCH)/util/header.c
747 */
748int __attribute__((weak)) get_cpuid(char *buffer __used, size_t sz __used)
749{
750 return -1;
751}
752
753static int write_cpuid(int fd, struct perf_header *h __used,
754 struct perf_evlist *evlist __used)
755{
756 char buffer[64];
757 int ret;
758
759 ret = get_cpuid(buffer, sizeof(buffer));
760 if (!ret)
761 goto write_it;
762
763 return -1;
764write_it:
765 return do_write_string(fd, buffer);
766}
767
768static void print_hostname(struct perf_header *ph, int fd, FILE *fp)
769{
770 char *str = do_read_string(fd, ph);
771 fprintf(fp, "# hostname : %s\n", str);
772 free(str);
773}
774
775static void print_osrelease(struct perf_header *ph, int fd, FILE *fp)
776{
777 char *str = do_read_string(fd, ph);
778 fprintf(fp, "# os release : %s\n", str);
779 free(str);
780}
781
782static void print_arch(struct perf_header *ph, int fd, FILE *fp)
783{
784 char *str = do_read_string(fd, ph);
785 fprintf(fp, "# arch : %s\n", str);
786 free(str);
787}
788
789static void print_cpudesc(struct perf_header *ph, int fd, FILE *fp)
790{
791 char *str = do_read_string(fd, ph);
792 fprintf(fp, "# cpudesc : %s\n", str);
793 free(str);
794}
795
796static void print_nrcpus(struct perf_header *ph, int fd, FILE *fp)
797{
798 ssize_t ret;
799 u32 nr;
800
801 ret = read(fd, &nr, sizeof(nr));
802 if (ret != (ssize_t)sizeof(nr))
803 nr = -1; /* interpreted as error */
804
805 if (ph->needs_swap)
806 nr = bswap_32(nr);
807
808 fprintf(fp, "# nrcpus online : %u\n", nr);
809
810 ret = read(fd, &nr, sizeof(nr));
811 if (ret != (ssize_t)sizeof(nr))
812 nr = -1; /* interpreted as error */
813
814 if (ph->needs_swap)
815 nr = bswap_32(nr);
816
817 fprintf(fp, "# nrcpus avail : %u\n", nr);
818}
819
820static void print_version(struct perf_header *ph, int fd, FILE *fp)
821{
822 char *str = do_read_string(fd, ph);
823 fprintf(fp, "# perf version : %s\n", str);
824 free(str);
825}
826
827static void print_cmdline(struct perf_header *ph, int fd, FILE *fp)
828{
829 ssize_t ret;
830 char *str;
831 u32 nr, i;
832
833 ret = read(fd, &nr, sizeof(nr));
834 if (ret != (ssize_t)sizeof(nr))
835 return;
836
837 if (ph->needs_swap)
838 nr = bswap_32(nr);
839
840 fprintf(fp, "# cmdline : ");
841
842 for (i = 0; i < nr; i++) {
843 str = do_read_string(fd, ph);
844 fprintf(fp, "%s ", str);
845 free(str);
846 }
847 fputc('\n', fp);
848}
849
850static void print_cpu_topology(struct perf_header *ph, int fd, FILE *fp)
851{
852 ssize_t ret;
853 u32 nr, i;
854 char *str;
855
856 ret = read(fd, &nr, sizeof(nr));
857 if (ret != (ssize_t)sizeof(nr))
858 return;
859
860 if (ph->needs_swap)
861 nr = bswap_32(nr);
862
863 for (i = 0; i < nr; i++) {
864 str = do_read_string(fd, ph);
865 fprintf(fp, "# sibling cores : %s\n", str);
866 free(str);
867 }
868
869 ret = read(fd, &nr, sizeof(nr));
870 if (ret != (ssize_t)sizeof(nr))
871 return;
872
873 if (ph->needs_swap)
874 nr = bswap_32(nr);
875
876 for (i = 0; i < nr; i++) {
877 str = do_read_string(fd, ph);
878 fprintf(fp, "# sibling threads : %s\n", str);
879 free(str);
880 }
881}
882
883static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
884{
885 struct perf_event_attr attr;
886 uint64_t id;
887 void *buf = NULL;
888 char *str;
889 u32 nre, sz, nr, i, j, msz;
890 int ret;
891
892 /* number of events */
893 ret = read(fd, &nre, sizeof(nre));
894 if (ret != (ssize_t)sizeof(nre))
895 goto error;
896
897 if (ph->needs_swap)
898 nre = bswap_32(nre);
899
900 ret = read(fd, &sz, sizeof(sz));
901 if (ret != (ssize_t)sizeof(sz))
902 goto error;
903
904 if (ph->needs_swap)
905 sz = bswap_32(sz);
906
907 /*
908 * ensure it is at least to our ABI rev
909 */
910 if (sz < (u32)sizeof(attr))
911 goto error;
912
913 memset(&attr, 0, sizeof(attr));
914
915 /* read entire region to sync up to next field */
916 buf = malloc(sz);
917 if (!buf)
918 goto error;
919
920 msz = sizeof(attr);
921 if (sz < msz)
922 msz = sz;
923
924 for (i = 0 ; i < nre; i++) {
925
926 ret = read(fd, buf, sz);
927 if (ret != (ssize_t)sz)
928 goto error;
929
930 if (ph->needs_swap)
931 perf_event__attr_swap(buf);
932
933 memcpy(&attr, buf, msz);
934
935 ret = read(fd, &nr, sizeof(nr));
936 if (ret != (ssize_t)sizeof(nr))
937 goto error;
938
939 if (ph->needs_swap)
940 nr = bswap_32(nr);
941
942 str = do_read_string(fd, ph);
943 fprintf(fp, "# event : name = %s, ", str);
944 free(str);
945
946 fprintf(fp, "type = %d, config = 0x%"PRIx64
947 ", config1 = 0x%"PRIx64", config2 = 0x%"PRIx64,
948 attr.type,
949 (u64)attr.config,
950 (u64)attr.config1,
951 (u64)attr.config2);
952
953 fprintf(fp, ", excl_usr = %d, excl_kern = %d",
954 attr.exclude_user,
955 attr.exclude_kernel);
956
957 if (nr)
958 fprintf(fp, ", id = {");
959
960 for (j = 0 ; j < nr; j++) {
961 ret = read(fd, &id, sizeof(id));
962 if (ret != (ssize_t)sizeof(id))
963 goto error;
964
965 if (ph->needs_swap)
966 id = bswap_64(id);
967
968 if (j)
969 fputc(',', fp);
970
971 fprintf(fp, " %"PRIu64, id);
972 }
973 if (nr && j == nr)
974 fprintf(fp, " }");
975 fputc('\n', fp);
976 }
977 free(buf);
978 return;
979error:
980 fprintf(fp, "# event desc: not available or unable to read\n");
981}
982
983static void print_total_mem(struct perf_header *h __used, int fd, FILE *fp)
984{
985 uint64_t mem;
986 ssize_t ret;
987
988 ret = read(fd, &mem, sizeof(mem));
989 if (ret != sizeof(mem))
990 goto error;
991
992 if (h->needs_swap)
993 mem = bswap_64(mem);
994
995 fprintf(fp, "# total memory : %"PRIu64" kB\n", mem);
996 return;
997error:
998 fprintf(fp, "# total memory : unknown\n");
999}
1000
1001static void print_numa_topology(struct perf_header *h __used, int fd, FILE *fp)
1002{
1003 ssize_t ret;
1004 u32 nr, c, i;
1005 char *str;
1006 uint64_t mem_total, mem_free;
1007
1008 /* nr nodes */
1009 ret = read(fd, &nr, sizeof(nr));
1010 if (ret != (ssize_t)sizeof(nr))
1011 goto error;
1012
1013 if (h->needs_swap)
1014 nr = bswap_32(nr);
1015
1016 for (i = 0; i < nr; i++) {
1017
1018 /* node number */
1019 ret = read(fd, &c, sizeof(c));
1020 if (ret != (ssize_t)sizeof(c))
1021 goto error;
1022
1023 if (h->needs_swap)
1024 c = bswap_32(c);
1025
1026 ret = read(fd, &mem_total, sizeof(u64));
1027 if (ret != sizeof(u64))
1028 goto error;
1029
1030 ret = read(fd, &mem_free, sizeof(u64));
1031 if (ret != sizeof(u64))
1032 goto error;
1033
1034 if (h->needs_swap) {
1035 mem_total = bswap_64(mem_total);
1036 mem_free = bswap_64(mem_free);
1037 }
1038
1039 fprintf(fp, "# node%u meminfo : total = %"PRIu64" kB,"
1040 " free = %"PRIu64" kB\n",
1041 c,
1042 mem_total,
1043 mem_free);
1044
1045 str = do_read_string(fd, h);
1046 fprintf(fp, "# node%u cpu list : %s\n", c, str);
1047 free(str);
1048 }
1049 return;
1050error:
1051 fprintf(fp, "# numa topology : not available\n");
1052}
1053
1054static void print_cpuid(struct perf_header *ph, int fd, FILE *fp)
1055{
1056 char *str = do_read_string(fd, ph);
1057 fprintf(fp, "# cpuid : %s\n", str);
1058 free(str);
1059}
1060
1061struct feature_ops {
1062 int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist);
1063 void (*print)(struct perf_header *h, int fd, FILE *fp);
1064 const char *name;
1065 bool full_only;
1066};
1067
1068#define FEAT_OPA(n, w, p) \
1069 [n] = { .name = #n, .write = w, .print = p }
1070#define FEAT_OPF(n, w, p) \
1071 [n] = { .name = #n, .write = w, .print = p, .full_only = true }
1072
1073static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
1074 FEAT_OPA(HEADER_TRACE_INFO, write_trace_info, NULL),
1075 FEAT_OPA(HEADER_BUILD_ID, write_build_id, NULL),
1076 FEAT_OPA(HEADER_HOSTNAME, write_hostname, print_hostname),
1077 FEAT_OPA(HEADER_OSRELEASE, write_osrelease, print_osrelease),
1078 FEAT_OPA(HEADER_VERSION, write_version, print_version),
1079 FEAT_OPA(HEADER_ARCH, write_arch, print_arch),
1080 FEAT_OPA(HEADER_NRCPUS, write_nrcpus, print_nrcpus),
1081 FEAT_OPA(HEADER_CPUDESC, write_cpudesc, print_cpudesc),
1082 FEAT_OPA(HEADER_CPUID, write_cpuid, print_cpuid),
1083 FEAT_OPA(HEADER_TOTAL_MEM, write_total_mem, print_total_mem),
1084 FEAT_OPA(HEADER_EVENT_DESC, write_event_desc, print_event_desc),
1085 FEAT_OPA(HEADER_CMDLINE, write_cmdline, print_cmdline),
1086 FEAT_OPF(HEADER_CPU_TOPOLOGY, write_cpu_topology, print_cpu_topology),
1087 FEAT_OPF(HEADER_NUMA_TOPOLOGY, write_numa_topology, print_numa_topology),
1088};
1089
1090struct header_print_data {
1091 FILE *fp;
1092 bool full; /* extended list of headers */
1093};
1094
1095static int perf_file_section__fprintf_info(struct perf_file_section *section,
1096 struct perf_header *ph,
1097 int feat, int fd, void *data)
1098{
1099 struct header_print_data *hd = data;
1100
1101 if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) {
1102 pr_debug("Failed to lseek to %" PRIu64 " offset for feature "
1103 "%d, continuing...\n", section->offset, feat);
1104 return 0;
1105 }
1106 if (feat < HEADER_TRACE_INFO || feat >= HEADER_LAST_FEATURE) {
1107 pr_warning("unknown feature %d\n", feat);
1108 return -1;
1109 }
1110 if (!feat_ops[feat].print)
1111 return 0;
1112
1113 if (!feat_ops[feat].full_only || hd->full)
1114 feat_ops[feat].print(ph, fd, hd->fp);
1115 else
1116 fprintf(hd->fp, "# %s info available, use -I to display\n",
1117 feat_ops[feat].name);
1118
1119 return 0;
1120}
1121
1122int perf_header__fprintf_info(struct perf_session *session, FILE *fp, bool full)
1123{
1124 struct header_print_data hd;
1125 struct perf_header *header = &session->header;
1126 int fd = session->fd;
1127 hd.fp = fp;
1128 hd.full = full;
1129
1130 perf_header__process_sections(header, fd, &hd,
1131 perf_file_section__fprintf_info);
1132 return 0;
1133}
1134
113#define dsos__for_each_with_build_id(pos, head) \ 1135#define dsos__for_each_with_build_id(pos, head) \
114 list_for_each_entry(pos, head, node) \ 1136 list_for_each_entry(pos, head, node) \
115 if (!pos->has_build_id) \ 1137 if (!pos->has_build_id) \
@@ -267,7 +1289,7 @@ int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir)
267 if (access(linkname, F_OK)) 1289 if (access(linkname, F_OK))
268 goto out_free; 1290 goto out_free;
269 1291
270 if (readlink(linkname, filename, size) < 0) 1292 if (readlink(linkname, filename, size - 1) < 0)
271 goto out_free; 1293 goto out_free;
272 1294
273 if (unlink(linkname)) 1295 if (unlink(linkname))
@@ -356,15 +1378,41 @@ static bool perf_session__read_build_ids(struct perf_session *session, bool with
356 return ret; 1378 return ret;
357} 1379}
358 1380
1381static int do_write_feat(int fd, struct perf_header *h, int type,
1382 struct perf_file_section **p,
1383 struct perf_evlist *evlist)
1384{
1385 int err;
1386 int ret = 0;
1387
1388 if (perf_header__has_feat(h, type)) {
1389
1390 (*p)->offset = lseek(fd, 0, SEEK_CUR);
1391
1392 err = feat_ops[type].write(fd, h, evlist);
1393 if (err < 0) {
1394 pr_debug("failed to write feature %d\n", type);
1395
1396 /* undo anything written */
1397 lseek(fd, (*p)->offset, SEEK_SET);
1398
1399 return -1;
1400 }
1401 (*p)->size = lseek(fd, 0, SEEK_CUR) - (*p)->offset;
1402 (*p)++;
1403 }
1404 return ret;
1405}
1406
359static int perf_header__adds_write(struct perf_header *header, 1407static int perf_header__adds_write(struct perf_header *header,
360 struct perf_evlist *evlist, int fd) 1408 struct perf_evlist *evlist, int fd)
361{ 1409{
362 int nr_sections; 1410 int nr_sections;
363 struct perf_session *session; 1411 struct perf_session *session;
364 struct perf_file_section *feat_sec; 1412 struct perf_file_section *feat_sec, *p;
365 int sec_size; 1413 int sec_size;
366 u64 sec_start; 1414 u64 sec_start;
367 int idx = 0, err; 1415 int err;
368 1416
369 session = container_of(header, struct perf_session, header); 1417 session = container_of(header, struct perf_session, header);
370 1418
@@ -376,7 +1424,7 @@ static int perf_header__adds_write(struct perf_header *header,
376 if (!nr_sections) 1424 if (!nr_sections)
377 return 0; 1425 return 0;
378 1426
379 feat_sec = calloc(sizeof(*feat_sec), nr_sections); 1427 feat_sec = p = calloc(sizeof(*feat_sec), nr_sections);
380 if (feat_sec == NULL) 1428 if (feat_sec == NULL)
381 return -ENOMEM; 1429 return -ENOMEM;
382 1430
@@ -385,36 +1433,69 @@ static int perf_header__adds_write(struct perf_header *header,
385 sec_start = header->data_offset + header->data_size; 1433 sec_start = header->data_offset + header->data_size;
386 lseek(fd, sec_start + sec_size, SEEK_SET); 1434 lseek(fd, sec_start + sec_size, SEEK_SET);
387 1435
388 if (perf_header__has_feat(header, HEADER_TRACE_INFO)) { 1436 err = do_write_feat(fd, header, HEADER_TRACE_INFO, &p, evlist);
389 struct perf_file_section *trace_sec; 1437 if (err)
390 1438 goto out_free;
391 trace_sec = &feat_sec[idx++];
392 1439
393 /* Write trace info */ 1440 err = do_write_feat(fd, header, HEADER_BUILD_ID, &p, evlist);
394 trace_sec->offset = lseek(fd, 0, SEEK_CUR); 1441 if (err) {
395 read_tracing_data(fd, &evlist->entries); 1442 perf_header__clear_feat(header, HEADER_BUILD_ID);
396 trace_sec->size = lseek(fd, 0, SEEK_CUR) - trace_sec->offset; 1443 goto out_free;
397 } 1444 }
398 1445
399 if (perf_header__has_feat(header, HEADER_BUILD_ID)) { 1446 err = do_write_feat(fd, header, HEADER_HOSTNAME, &p, evlist);
400 struct perf_file_section *buildid_sec; 1447 if (err)
1448 perf_header__clear_feat(header, HEADER_HOSTNAME);
401 1449
402 buildid_sec = &feat_sec[idx++]; 1450 err = do_write_feat(fd, header, HEADER_OSRELEASE, &p, evlist);
1451 if (err)
1452 perf_header__clear_feat(header, HEADER_OSRELEASE);
403 1453
404 /* Write build-ids */ 1454 err = do_write_feat(fd, header, HEADER_VERSION, &p, evlist);
405 buildid_sec->offset = lseek(fd, 0, SEEK_CUR); 1455 if (err)
406 err = dsos__write_buildid_table(header, fd); 1456 perf_header__clear_feat(header, HEADER_VERSION);
407 if (err < 0) { 1457
408 pr_debug("failed to write buildid table\n"); 1458 err = do_write_feat(fd, header, HEADER_ARCH, &p, evlist);
409 goto out_free; 1459 if (err)
410 } 1460 perf_header__clear_feat(header, HEADER_ARCH);
411 buildid_sec->size = lseek(fd, 0, SEEK_CUR) - 1461
412 buildid_sec->offset; 1462 err = do_write_feat(fd, header, HEADER_NRCPUS, &p, evlist);
413 if (!no_buildid_cache) 1463 if (err)
414 perf_session__cache_build_ids(session); 1464 perf_header__clear_feat(header, HEADER_NRCPUS);
415 } 1465
1466 err = do_write_feat(fd, header, HEADER_CPUDESC, &p, evlist);
1467 if (err)
1468 perf_header__clear_feat(header, HEADER_CPUDESC);
1469
1470 err = do_write_feat(fd, header, HEADER_CPUID, &p, evlist);
1471 if (err)
1472 perf_header__clear_feat(header, HEADER_CPUID);
1473
1474 err = do_write_feat(fd, header, HEADER_TOTAL_MEM, &p, evlist);
1475 if (err)
1476 perf_header__clear_feat(header, HEADER_TOTAL_MEM);
1477
1478 err = do_write_feat(fd, header, HEADER_CMDLINE, &p, evlist);
1479 if (err)
1480 perf_header__clear_feat(header, HEADER_CMDLINE);
1481
1482 err = do_write_feat(fd, header, HEADER_EVENT_DESC, &p, evlist);
1483 if (err)
1484 perf_header__clear_feat(header, HEADER_EVENT_DESC);
1485
1486 err = do_write_feat(fd, header, HEADER_CPU_TOPOLOGY, &p, evlist);
1487 if (err)
1488 perf_header__clear_feat(header, HEADER_CPU_TOPOLOGY);
1489
1490 err = do_write_feat(fd, header, HEADER_NUMA_TOPOLOGY, &p, evlist);
1491 if (err)
1492 perf_header__clear_feat(header, HEADER_NUMA_TOPOLOGY);
416 1493
417 lseek(fd, sec_start, SEEK_SET); 1494 lseek(fd, sec_start, SEEK_SET);
1495 /*
1496 * may write more than needed due to dropped feature, but
1497 * this is okay, reader will skip the mising entries
1498 */
418 err = do_write(fd, feat_sec, sec_size); 1499 err = do_write(fd, feat_sec, sec_size);
419 if (err < 0) 1500 if (err < 0)
420 pr_debug("failed to write feature section\n"); 1501 pr_debug("failed to write feature section\n");
@@ -554,9 +1635,10 @@ static int perf_header__getbuffer64(struct perf_header *header,
554} 1635}
555 1636
556int perf_header__process_sections(struct perf_header *header, int fd, 1637int perf_header__process_sections(struct perf_header *header, int fd,
1638 void *data,
557 int (*process)(struct perf_file_section *section, 1639 int (*process)(struct perf_file_section *section,
558 struct perf_header *ph, 1640 struct perf_header *ph,
559 int feat, int fd)) 1641 int feat, int fd, void *data))
560{ 1642{
561 struct perf_file_section *feat_sec; 1643 struct perf_file_section *feat_sec;
562 int nr_sections; 1644 int nr_sections;
@@ -584,7 +1666,7 @@ int perf_header__process_sections(struct perf_header *header, int fd,
584 if (perf_header__has_feat(header, feat)) { 1666 if (perf_header__has_feat(header, feat)) {
585 struct perf_file_section *sec = &feat_sec[idx++]; 1667 struct perf_file_section *sec = &feat_sec[idx++];
586 1668
587 err = process(sec, header, feat, fd); 1669 err = process(sec, header, feat, fd, data);
588 if (err < 0) 1670 if (err < 0)
589 break; 1671 break;
590 } 1672 }
@@ -621,21 +1703,41 @@ int perf_file_header__read(struct perf_file_header *header,
621 bitmap_zero(header->adds_features, HEADER_FEAT_BITS); 1703 bitmap_zero(header->adds_features, HEADER_FEAT_BITS);
622 else 1704 else
623 return -1; 1705 return -1;
1706 } else if (ph->needs_swap) {
1707 unsigned int i;
1708 /*
1709 * feature bitmap is declared as an array of unsigned longs --
1710 * not good since its size can differ between the host that
1711 * generated the data file and the host analyzing the file.
1712 *
1713 * We need to handle endianness, but we don't know the size of
1714 * the unsigned long where the file was generated. Take a best
1715 * guess at determining it: try 64-bit swap first (ie., file
1716 * created on a 64-bit host), and check if the hostname feature
1717 * bit is set (this feature bit is forced on as of fbe96f2).
1718 * If the bit is not, undo the 64-bit swap and try a 32-bit
1719 * swap. If the hostname bit is still not set (e.g., older data
1720 * file), punt and fallback to the original behavior --
1721 * clearing all feature bits and setting buildid.
1722 */
1723 for (i = 0; i < BITS_TO_LONGS(HEADER_FEAT_BITS); ++i)
1724 header->adds_features[i] = bswap_64(header->adds_features[i]);
1725
1726 if (!test_bit(HEADER_HOSTNAME, header->adds_features)) {
1727 for (i = 0; i < BITS_TO_LONGS(HEADER_FEAT_BITS); ++i) {
1728 header->adds_features[i] = bswap_64(header->adds_features[i]);
1729 header->adds_features[i] = bswap_32(header->adds_features[i]);
1730 }
1731 }
1732
1733 if (!test_bit(HEADER_HOSTNAME, header->adds_features)) {
1734 bitmap_zero(header->adds_features, HEADER_FEAT_BITS);
1735 set_bit(HEADER_BUILD_ID, header->adds_features);
1736 }
624 } 1737 }
625 1738
626 memcpy(&ph->adds_features, &header->adds_features, 1739 memcpy(&ph->adds_features, &header->adds_features,
627 sizeof(ph->adds_features)); 1740 sizeof(ph->adds_features));
628 /*
629 * FIXME: hack that assumes that if we need swap the perf.data file
630 * may be coming from an arch with a different word-size, ergo different
631 * DEFINE_BITMAP format, investigate more later, but for now its mostly
632 * safe to assume that we have a build-id section. Trace files probably
633 * have several other issues in this realm anyway...
634 */
635 if (ph->needs_swap) {
636 memset(&ph->adds_features, 0, sizeof(ph->adds_features));
637 perf_header__set_feat(ph, HEADER_BUILD_ID);
638 }
639 1741
640 ph->event_offset = header->event_types.offset; 1742 ph->event_offset = header->event_types.offset;
641 ph->event_size = header->event_types.size; 1743 ph->event_size = header->event_types.size;
@@ -796,7 +1898,7 @@ out:
796 1898
797static int perf_file_section__process(struct perf_file_section *section, 1899static int perf_file_section__process(struct perf_file_section *section,
798 struct perf_header *ph, 1900 struct perf_header *ph,
799 int feat, int fd) 1901 int feat, int fd, void *data __used)
800{ 1902{
801 if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) { 1903 if (lseek(fd, section->offset, SEEK_SET) == (off_t)-1) {
802 pr_debug("Failed to lseek to %" PRIu64 " offset for feature " 1904 pr_debug("Failed to lseek to %" PRIu64 " offset for feature "
@@ -813,6 +1915,21 @@ static int perf_file_section__process(struct perf_file_section *section,
813 if (perf_header__read_build_ids(ph, fd, section->offset, section->size)) 1915 if (perf_header__read_build_ids(ph, fd, section->offset, section->size))
814 pr_debug("Failed to read buildids, continuing...\n"); 1916 pr_debug("Failed to read buildids, continuing...\n");
815 break; 1917 break;
1918
1919 case HEADER_HOSTNAME:
1920 case HEADER_OSRELEASE:
1921 case HEADER_VERSION:
1922 case HEADER_ARCH:
1923 case HEADER_NRCPUS:
1924 case HEADER_CPUDESC:
1925 case HEADER_CPUID:
1926 case HEADER_TOTAL_MEM:
1927 case HEADER_CMDLINE:
1928 case HEADER_EVENT_DESC:
1929 case HEADER_CPU_TOPOLOGY:
1930 case HEADER_NUMA_TOPOLOGY:
1931 break;
1932
816 default: 1933 default:
817 pr_debug("unknown feature %d, continuing...\n", feat); 1934 pr_debug("unknown feature %d, continuing...\n", feat);
818 } 1935 }
@@ -935,7 +2052,8 @@ int perf_session__read_header(struct perf_session *session, int fd)
935 event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type); 2052 event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type);
936 } 2053 }
937 2054
938 perf_header__process_sections(header, fd, perf_file_section__process); 2055 perf_header__process_sections(header, fd, NULL,
2056 perf_file_section__process);
939 2057
940 lseek(fd, header->data_offset, SEEK_SET); 2058 lseek(fd, header->data_offset, SEEK_SET);
941 2059
@@ -1100,15 +2218,29 @@ int perf_event__synthesize_tracing_data(int fd, struct perf_evlist *evlist,
1100 struct perf_session *session __unused) 2218 struct perf_session *session __unused)
1101{ 2219{
1102 union perf_event ev; 2220 union perf_event ev;
2221 struct tracing_data *tdata;
1103 ssize_t size = 0, aligned_size = 0, padding; 2222 ssize_t size = 0, aligned_size = 0, padding;
1104 int err __used = 0; 2223 int err __used = 0;
1105 2224
2225 /*
2226 * We are going to store the size of the data followed
2227 * by the data contents. Since the fd descriptor is a pipe,
2228 * we cannot seek back to store the size of the data once
2229 * we know it. Instead we:
2230 *
2231 * - write the tracing data to the temp file
2232 * - get/write the data size to pipe
2233 * - write the tracing data from the temp file
2234 * to the pipe
2235 */
2236 tdata = tracing_data_get(&evlist->entries, fd, true);
2237 if (!tdata)
2238 return -1;
2239
1106 memset(&ev, 0, sizeof(ev)); 2240 memset(&ev, 0, sizeof(ev));
1107 2241
1108 ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA; 2242 ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA;
1109 size = read_tracing_data_size(fd, &evlist->entries); 2243 size = tdata->size;
1110 if (size <= 0)
1111 return size;
1112 aligned_size = ALIGN(size, sizeof(u64)); 2244 aligned_size = ALIGN(size, sizeof(u64));
1113 padding = aligned_size - size; 2245 padding = aligned_size - size;
1114 ev.tracing_data.header.size = sizeof(ev.tracing_data); 2246 ev.tracing_data.header.size = sizeof(ev.tracing_data);
@@ -1116,7 +2248,12 @@ int perf_event__synthesize_tracing_data(int fd, struct perf_evlist *evlist,
1116 2248
1117 process(&ev, NULL, session); 2249 process(&ev, NULL, session);
1118 2250
1119 err = read_tracing_data(fd, &evlist->entries); 2251 /*
2252 * The put function will copy all the tracing data
2253 * stored in temp file to the pipe.
2254 */
2255 tracing_data_put(tdata);
2256
1120 write_padded(fd, NULL, 0, padding); 2257 write_padded(fd, NULL, 0, padding);
1121 2258
1122 return aligned_size; 2259 return aligned_size;
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
index 1886256768a1..3d5a742f4a2a 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -12,6 +12,20 @@
12enum { 12enum {
13 HEADER_TRACE_INFO = 1, 13 HEADER_TRACE_INFO = 1,
14 HEADER_BUILD_ID, 14 HEADER_BUILD_ID,
15
16 HEADER_HOSTNAME,
17 HEADER_OSRELEASE,
18 HEADER_VERSION,
19 HEADER_ARCH,
20 HEADER_NRCPUS,
21 HEADER_CPUDESC,
22 HEADER_CPUID,
23 HEADER_TOTAL_MEM,
24 HEADER_CMDLINE,
25 HEADER_EVENT_DESC,
26 HEADER_CPU_TOPOLOGY,
27 HEADER_NUMA_TOPOLOGY,
28
15 HEADER_LAST_FEATURE, 29 HEADER_LAST_FEATURE,
16}; 30};
17 31
@@ -68,10 +82,15 @@ void perf_header__set_feat(struct perf_header *header, int feat);
68void perf_header__clear_feat(struct perf_header *header, int feat); 82void perf_header__clear_feat(struct perf_header *header, int feat);
69bool perf_header__has_feat(const struct perf_header *header, int feat); 83bool perf_header__has_feat(const struct perf_header *header, int feat);
70 84
85int perf_header__set_cmdline(int argc, const char **argv);
86
71int perf_header__process_sections(struct perf_header *header, int fd, 87int perf_header__process_sections(struct perf_header *header, int fd,
88 void *data,
72 int (*process)(struct perf_file_section *section, 89 int (*process)(struct perf_file_section *section,
73 struct perf_header *ph, 90 struct perf_header *ph,
74 int feat, int fd)); 91 int feat, int fd, void *data));
92
93int perf_header__fprintf_info(struct perf_session *s, FILE *fp, bool full);
75 94
76int build_id_cache__add_s(const char *sbuild_id, const char *debugdir, 95int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
77 const char *name, bool is_kallsyms); 96 const char *name, bool is_kallsyms);
@@ -104,4 +123,10 @@ int perf_event__synthesize_build_id(struct dso *pos, u16 misc,
104 struct perf_session *session); 123 struct perf_session *session);
105int perf_event__process_build_id(union perf_event *event, 124int perf_event__process_build_id(union perf_event *event,
106 struct perf_session *session); 125 struct perf_session *session);
126
127/*
128 * arch specific callback
129 */
130int get_cpuid(char *buffer, size_t sz);
131
107#endif /* __PERF_HEADER_H */ 132#endif /* __PERF_HEADER_H */
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index 677e1da6bb3e..f6a993963a1e 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -6,6 +6,11 @@
6#include "sort.h" 6#include "sort.h"
7#include <math.h> 7#include <math.h>
8 8
9static bool hists__filter_entry_by_dso(struct hists *hists,
10 struct hist_entry *he);
11static bool hists__filter_entry_by_thread(struct hists *hists,
12 struct hist_entry *he);
13
9enum hist_filter { 14enum hist_filter {
10 HIST_FILTER__DSO, 15 HIST_FILTER__DSO,
11 HIST_FILTER__THREAD, 16 HIST_FILTER__THREAD,
@@ -18,56 +23,56 @@ struct callchain_param callchain_param = {
18 .order = ORDER_CALLEE 23 .order = ORDER_CALLEE
19}; 24};
20 25
21u16 hists__col_len(struct hists *self, enum hist_column col) 26u16 hists__col_len(struct hists *hists, enum hist_column col)
22{ 27{
23 return self->col_len[col]; 28 return hists->col_len[col];
24} 29}
25 30
26void hists__set_col_len(struct hists *self, enum hist_column col, u16 len) 31void hists__set_col_len(struct hists *hists, enum hist_column col, u16 len)
27{ 32{
28 self->col_len[col] = len; 33 hists->col_len[col] = len;
29} 34}
30 35
31bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len) 36bool hists__new_col_len(struct hists *hists, enum hist_column col, u16 len)
32{ 37{
33 if (len > hists__col_len(self, col)) { 38 if (len > hists__col_len(hists, col)) {
34 hists__set_col_len(self, col, len); 39 hists__set_col_len(hists, col, len);
35 return true; 40 return true;
36 } 41 }
37 return false; 42 return false;
38} 43}
39 44
40static void hists__reset_col_len(struct hists *self) 45static void hists__reset_col_len(struct hists *hists)
41{ 46{
42 enum hist_column col; 47 enum hist_column col;
43 48
44 for (col = 0; col < HISTC_NR_COLS; ++col) 49 for (col = 0; col < HISTC_NR_COLS; ++col)
45 hists__set_col_len(self, col, 0); 50 hists__set_col_len(hists, col, 0);
46} 51}
47 52
48static void hists__calc_col_len(struct hists *self, struct hist_entry *h) 53static void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
49{ 54{
50 u16 len; 55 u16 len;
51 56
52 if (h->ms.sym) 57 if (h->ms.sym)
53 hists__new_col_len(self, HISTC_SYMBOL, h->ms.sym->namelen); 58 hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen);
54 else { 59 else {
55 const unsigned int unresolved_col_width = BITS_PER_LONG / 4; 60 const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
56 61
57 if (hists__col_len(self, HISTC_DSO) < unresolved_col_width && 62 if (hists__col_len(hists, HISTC_DSO) < unresolved_col_width &&
58 !symbol_conf.col_width_list_str && !symbol_conf.field_sep && 63 !symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
59 !symbol_conf.dso_list) 64 !symbol_conf.dso_list)
60 hists__set_col_len(self, HISTC_DSO, 65 hists__set_col_len(hists, HISTC_DSO,
61 unresolved_col_width); 66 unresolved_col_width);
62 } 67 }
63 68
64 len = thread__comm_len(h->thread); 69 len = thread__comm_len(h->thread);
65 if (hists__new_col_len(self, HISTC_COMM, len)) 70 if (hists__new_col_len(hists, HISTC_COMM, len))
66 hists__set_col_len(self, HISTC_THREAD, len + 6); 71 hists__set_col_len(hists, HISTC_THREAD, len + 6);
67 72
68 if (h->ms.map) { 73 if (h->ms.map) {
69 len = dso__name_len(h->ms.map->dso); 74 len = dso__name_len(h->ms.map->dso);
70 hists__new_col_len(self, HISTC_DSO, len); 75 hists__new_col_len(hists, HISTC_DSO, len);
71 } 76 }
72} 77}
73 78
@@ -92,6 +97,67 @@ static void hist_entry__add_cpumode_period(struct hist_entry *self,
92 } 97 }
93} 98}
94 99
100static void hist_entry__decay(struct hist_entry *he)
101{
102 he->period = (he->period * 7) / 8;
103 he->nr_events = (he->nr_events * 7) / 8;
104}
105
106static bool hists__decay_entry(struct hists *hists, struct hist_entry *he)
107{
108 u64 prev_period = he->period;
109
110 if (prev_period == 0)
111 return true;
112
113 hist_entry__decay(he);
114
115 if (!he->filtered)
116 hists->stats.total_period -= prev_period - he->period;
117
118 return he->period == 0;
119}
120
121static void __hists__decay_entries(struct hists *hists, bool zap_user,
122 bool zap_kernel, bool threaded)
123{
124 struct rb_node *next = rb_first(&hists->entries);
125 struct hist_entry *n;
126
127 while (next) {
128 n = rb_entry(next, struct hist_entry, rb_node);
129 next = rb_next(&n->rb_node);
130 /*
131 * We may be annotating this, for instance, so keep it here in
132 * case some it gets new samples, we'll eventually free it when
133 * the user stops browsing and it agains gets fully decayed.
134 */
135 if (((zap_user && n->level == '.') ||
136 (zap_kernel && n->level != '.') ||
137 hists__decay_entry(hists, n)) &&
138 !n->used) {
139 rb_erase(&n->rb_node, &hists->entries);
140
141 if (sort__need_collapse || threaded)
142 rb_erase(&n->rb_node_in, &hists->entries_collapsed);
143
144 hist_entry__free(n);
145 --hists->nr_entries;
146 }
147 }
148}
149
150void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel)
151{
152 return __hists__decay_entries(hists, zap_user, zap_kernel, false);
153}
154
155void hists__decay_entries_threaded(struct hists *hists,
156 bool zap_user, bool zap_kernel)
157{
158 return __hists__decay_entries(hists, zap_user, zap_kernel, true);
159}
160
95/* 161/*
96 * histogram, sorted on item, collects periods 162 * histogram, sorted on item, collects periods
97 */ 163 */
@@ -113,11 +179,12 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template)
113 return self; 179 return self;
114} 180}
115 181
116static void hists__inc_nr_entries(struct hists *self, struct hist_entry *h) 182static void hists__inc_nr_entries(struct hists *hists, struct hist_entry *h)
117{ 183{
118 if (!h->filtered) { 184 if (!h->filtered) {
119 hists__calc_col_len(self, h); 185 hists__calc_col_len(hists, h);
120 ++self->nr_entries; 186 ++hists->nr_entries;
187 hists->stats.total_period += h->period;
121 } 188 }
122} 189}
123 190
@@ -128,11 +195,11 @@ static u8 symbol__parent_filter(const struct symbol *parent)
128 return 0; 195 return 0;
129} 196}
130 197
131struct hist_entry *__hists__add_entry(struct hists *self, 198struct hist_entry *__hists__add_entry(struct hists *hists,
132 struct addr_location *al, 199 struct addr_location *al,
133 struct symbol *sym_parent, u64 period) 200 struct symbol *sym_parent, u64 period)
134{ 201{
135 struct rb_node **p = &self->entries.rb_node; 202 struct rb_node **p;
136 struct rb_node *parent = NULL; 203 struct rb_node *parent = NULL;
137 struct hist_entry *he; 204 struct hist_entry *he;
138 struct hist_entry entry = { 205 struct hist_entry entry = {
@@ -150,9 +217,13 @@ struct hist_entry *__hists__add_entry(struct hists *self,
150 }; 217 };
151 int cmp; 218 int cmp;
152 219
220 pthread_mutex_lock(&hists->lock);
221
222 p = &hists->entries_in->rb_node;
223
153 while (*p != NULL) { 224 while (*p != NULL) {
154 parent = *p; 225 parent = *p;
155 he = rb_entry(parent, struct hist_entry, rb_node); 226 he = rb_entry(parent, struct hist_entry, rb_node_in);
156 227
157 cmp = hist_entry__cmp(&entry, he); 228 cmp = hist_entry__cmp(&entry, he);
158 229
@@ -170,12 +241,14 @@ struct hist_entry *__hists__add_entry(struct hists *self,
170 241
171 he = hist_entry__new(&entry); 242 he = hist_entry__new(&entry);
172 if (!he) 243 if (!he)
173 return NULL; 244 goto out_unlock;
174 rb_link_node(&he->rb_node, parent, p); 245
175 rb_insert_color(&he->rb_node, &self->entries); 246 rb_link_node(&he->rb_node_in, parent, p);
176 hists__inc_nr_entries(self, he); 247 rb_insert_color(&he->rb_node_in, hists->entries_in);
177out: 248out:
178 hist_entry__add_cpumode_period(he, al->cpumode, period); 249 hist_entry__add_cpumode_period(he, al->cpumode, period);
250out_unlock:
251 pthread_mutex_unlock(&hists->lock);
179 return he; 252 return he;
180} 253}
181 254
@@ -222,7 +295,7 @@ void hist_entry__free(struct hist_entry *he)
222 * collapse the histogram 295 * collapse the histogram
223 */ 296 */
224 297
225static bool hists__collapse_insert_entry(struct hists *self, 298static bool hists__collapse_insert_entry(struct hists *hists,
226 struct rb_root *root, 299 struct rb_root *root,
227 struct hist_entry *he) 300 struct hist_entry *he)
228{ 301{
@@ -233,15 +306,16 @@ static bool hists__collapse_insert_entry(struct hists *self,
233 306
234 while (*p != NULL) { 307 while (*p != NULL) {
235 parent = *p; 308 parent = *p;
236 iter = rb_entry(parent, struct hist_entry, rb_node); 309 iter = rb_entry(parent, struct hist_entry, rb_node_in);
237 310
238 cmp = hist_entry__collapse(iter, he); 311 cmp = hist_entry__collapse(iter, he);
239 312
240 if (!cmp) { 313 if (!cmp) {
241 iter->period += he->period; 314 iter->period += he->period;
315 iter->nr_events += he->nr_events;
242 if (symbol_conf.use_callchain) { 316 if (symbol_conf.use_callchain) {
243 callchain_cursor_reset(&self->callchain_cursor); 317 callchain_cursor_reset(&hists->callchain_cursor);
244 callchain_merge(&self->callchain_cursor, iter->callchain, 318 callchain_merge(&hists->callchain_cursor, iter->callchain,
245 he->callchain); 319 he->callchain);
246 } 320 }
247 hist_entry__free(he); 321 hist_entry__free(he);
@@ -254,35 +328,70 @@ static bool hists__collapse_insert_entry(struct hists *self,
254 p = &(*p)->rb_right; 328 p = &(*p)->rb_right;
255 } 329 }
256 330
257 rb_link_node(&he->rb_node, parent, p); 331 rb_link_node(&he->rb_node_in, parent, p);
258 rb_insert_color(&he->rb_node, root); 332 rb_insert_color(&he->rb_node_in, root);
259 return true; 333 return true;
260} 334}
261 335
262void hists__collapse_resort(struct hists *self) 336static struct rb_root *hists__get_rotate_entries_in(struct hists *hists)
337{
338 struct rb_root *root;
339
340 pthread_mutex_lock(&hists->lock);
341
342 root = hists->entries_in;
343 if (++hists->entries_in > &hists->entries_in_array[1])
344 hists->entries_in = &hists->entries_in_array[0];
345
346 pthread_mutex_unlock(&hists->lock);
347
348 return root;
349}
350
351static void hists__apply_filters(struct hists *hists, struct hist_entry *he)
352{
353 hists__filter_entry_by_dso(hists, he);
354 hists__filter_entry_by_thread(hists, he);
355}
356
357static void __hists__collapse_resort(struct hists *hists, bool threaded)
263{ 358{
264 struct rb_root tmp; 359 struct rb_root *root;
265 struct rb_node *next; 360 struct rb_node *next;
266 struct hist_entry *n; 361 struct hist_entry *n;
267 362
268 if (!sort__need_collapse) 363 if (!sort__need_collapse && !threaded)
269 return; 364 return;
270 365
271 tmp = RB_ROOT; 366 root = hists__get_rotate_entries_in(hists);
272 next = rb_first(&self->entries); 367 next = rb_first(root);
273 self->nr_entries = 0; 368 hists->stats.total_period = 0;
274 hists__reset_col_len(self);
275 369
276 while (next) { 370 while (next) {
277 n = rb_entry(next, struct hist_entry, rb_node); 371 n = rb_entry(next, struct hist_entry, rb_node_in);
278 next = rb_next(&n->rb_node); 372 next = rb_next(&n->rb_node_in);
279 373
280 rb_erase(&n->rb_node, &self->entries); 374 rb_erase(&n->rb_node_in, root);
281 if (hists__collapse_insert_entry(self, &tmp, n)) 375 if (hists__collapse_insert_entry(hists, &hists->entries_collapsed, n)) {
282 hists__inc_nr_entries(self, n); 376 /*
377 * If it wasn't combined with one of the entries already
378 * collapsed, we need to apply the filters that may have
379 * been set by, say, the hist_browser.
380 */
381 hists__apply_filters(hists, n);
382 hists__inc_nr_entries(hists, n);
383 }
283 } 384 }
385}
284 386
285 self->entries = tmp; 387void hists__collapse_resort(struct hists *hists)
388{
389 return __hists__collapse_resort(hists, false);
390}
391
392void hists__collapse_resort_threaded(struct hists *hists)
393{
394 return __hists__collapse_resort(hists, true);
286} 395}
287 396
288/* 397/*
@@ -315,31 +424,43 @@ static void __hists__insert_output_entry(struct rb_root *entries,
315 rb_insert_color(&he->rb_node, entries); 424 rb_insert_color(&he->rb_node, entries);
316} 425}
317 426
318void hists__output_resort(struct hists *self) 427static void __hists__output_resort(struct hists *hists, bool threaded)
319{ 428{
320 struct rb_root tmp; 429 struct rb_root *root;
321 struct rb_node *next; 430 struct rb_node *next;
322 struct hist_entry *n; 431 struct hist_entry *n;
323 u64 min_callchain_hits; 432 u64 min_callchain_hits;
324 433
325 min_callchain_hits = self->stats.total_period * (callchain_param.min_percent / 100); 434 min_callchain_hits = hists->stats.total_period * (callchain_param.min_percent / 100);
435
436 if (sort__need_collapse || threaded)
437 root = &hists->entries_collapsed;
438 else
439 root = hists->entries_in;
326 440
327 tmp = RB_ROOT; 441 next = rb_first(root);
328 next = rb_first(&self->entries); 442 hists->entries = RB_ROOT;
329 443
330 self->nr_entries = 0; 444 hists->nr_entries = 0;
331 hists__reset_col_len(self); 445 hists__reset_col_len(hists);
332 446
333 while (next) { 447 while (next) {
334 n = rb_entry(next, struct hist_entry, rb_node); 448 n = rb_entry(next, struct hist_entry, rb_node_in);
335 next = rb_next(&n->rb_node); 449 next = rb_next(&n->rb_node_in);
336 450
337 rb_erase(&n->rb_node, &self->entries); 451 __hists__insert_output_entry(&hists->entries, n, min_callchain_hits);
338 __hists__insert_output_entry(&tmp, n, min_callchain_hits); 452 hists__inc_nr_entries(hists, n);
339 hists__inc_nr_entries(self, n);
340 } 453 }
454}
341 455
342 self->entries = tmp; 456void hists__output_resort(struct hists *hists)
457{
458 return __hists__output_resort(hists, false);
459}
460
461void hists__output_resort_threaded(struct hists *hists)
462{
463 return __hists__output_resort(hists, true);
343} 464}
344 465
345static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) 466static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
@@ -594,12 +715,27 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self,
594 return ret; 715 return ret;
595} 716}
596 717
597int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, 718void hists__output_recalc_col_len(struct hists *hists, int max_rows)
598 struct hists *hists, struct hists *pair_hists, 719{
599 bool show_displacement, long displacement, 720 struct rb_node *next = rb_first(&hists->entries);
600 bool color, u64 session_total) 721 struct hist_entry *n;
722 int row = 0;
723
724 hists__reset_col_len(hists);
725
726 while (next && row++ < max_rows) {
727 n = rb_entry(next, struct hist_entry, rb_node);
728 if (!n->filtered)
729 hists__calc_col_len(hists, n);
730 next = rb_next(&n->rb_node);
731 }
732}
733
734static int hist_entry__pcnt_snprintf(struct hist_entry *self, char *s,
735 size_t size, struct hists *pair_hists,
736 bool show_displacement, long displacement,
737 bool color, u64 session_total)
601{ 738{
602 struct sort_entry *se;
603 u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us; 739 u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us;
604 u64 nr_events; 740 u64 nr_events;
605 const char *sep = symbol_conf.field_sep; 741 const char *sep = symbol_conf.field_sep;
@@ -664,6 +800,13 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size,
664 ret += snprintf(s + ret, size - ret, "%11" PRIu64, nr_events); 800 ret += snprintf(s + ret, size - ret, "%11" PRIu64, nr_events);
665 } 801 }
666 802
803 if (symbol_conf.show_total_period) {
804 if (sep)
805 ret += snprintf(s + ret, size - ret, "%c%" PRIu64, *sep, period);
806 else
807 ret += snprintf(s + ret, size - ret, " %12" PRIu64, period);
808 }
809
667 if (pair_hists) { 810 if (pair_hists) {
668 char bf[32]; 811 char bf[32];
669 double old_percent = 0, new_percent = 0, diff; 812 double old_percent = 0, new_percent = 0, diff;
@@ -698,26 +841,42 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size,
698 } 841 }
699 } 842 }
700 843
844 return ret;
845}
846
847int hist_entry__snprintf(struct hist_entry *he, char *s, size_t size,
848 struct hists *hists)
849{
850 const char *sep = symbol_conf.field_sep;
851 struct sort_entry *se;
852 int ret = 0;
853
701 list_for_each_entry(se, &hist_entry__sort_list, list) { 854 list_for_each_entry(se, &hist_entry__sort_list, list) {
702 if (se->elide) 855 if (se->elide)
703 continue; 856 continue;
704 857
705 ret += snprintf(s + ret, size - ret, "%s", sep ?: " "); 858 ret += snprintf(s + ret, size - ret, "%s", sep ?: " ");
706 ret += se->se_snprintf(self, s + ret, size - ret, 859 ret += se->se_snprintf(he, s + ret, size - ret,
707 hists__col_len(hists, se->se_width_idx)); 860 hists__col_len(hists, se->se_width_idx));
708 } 861 }
709 862
710 return ret; 863 return ret;
711} 864}
712 865
713int hist_entry__fprintf(struct hist_entry *self, struct hists *hists, 866int hist_entry__fprintf(struct hist_entry *he, size_t size, struct hists *hists,
714 struct hists *pair_hists, bool show_displacement, 867 struct hists *pair_hists, bool show_displacement,
715 long displacement, FILE *fp, u64 session_total) 868 long displacement, FILE *fp, u64 session_total)
716{ 869{
717 char bf[512]; 870 char bf[512];
718 hist_entry__snprintf(self, bf, sizeof(bf), hists, pair_hists, 871 int ret;
719 show_displacement, displacement, 872
720 true, session_total); 873 if (size == 0 || size > sizeof(bf))
874 size = sizeof(bf);
875
876 ret = hist_entry__pcnt_snprintf(he, bf, size, pair_hists,
877 show_displacement, displacement,
878 true, session_total);
879 hist_entry__snprintf(he, bf + ret, size - ret, hists);
721 return fprintf(fp, "%s\n", bf); 880 return fprintf(fp, "%s\n", bf);
722} 881}
723 882
@@ -738,8 +897,9 @@ static size_t hist_entry__fprintf_callchain(struct hist_entry *self,
738 left_margin); 897 left_margin);
739} 898}
740 899
741size_t hists__fprintf(struct hists *self, struct hists *pair, 900size_t hists__fprintf(struct hists *hists, struct hists *pair,
742 bool show_displacement, FILE *fp) 901 bool show_displacement, bool show_header, int max_rows,
902 int max_cols, FILE *fp)
743{ 903{
744 struct sort_entry *se; 904 struct sort_entry *se;
745 struct rb_node *nd; 905 struct rb_node *nd;
@@ -749,9 +909,13 @@ size_t hists__fprintf(struct hists *self, struct hists *pair,
749 unsigned int width; 909 unsigned int width;
750 const char *sep = symbol_conf.field_sep; 910 const char *sep = symbol_conf.field_sep;
751 const char *col_width = symbol_conf.col_width_list_str; 911 const char *col_width = symbol_conf.col_width_list_str;
912 int nr_rows = 0;
752 913
753 init_rem_hits(); 914 init_rem_hits();
754 915
916 if (!show_header)
917 goto print_entries;
918
755 fprintf(fp, "# %s", pair ? "Baseline" : "Overhead"); 919 fprintf(fp, "# %s", pair ? "Baseline" : "Overhead");
756 920
757 if (symbol_conf.show_nr_samples) { 921 if (symbol_conf.show_nr_samples) {
@@ -761,6 +925,13 @@ size_t hists__fprintf(struct hists *self, struct hists *pair,
761 fputs(" Samples ", fp); 925 fputs(" Samples ", fp);
762 } 926 }
763 927
928 if (symbol_conf.show_total_period) {
929 if (sep)
930 ret += fprintf(fp, "%cPeriod", *sep);
931 else
932 ret += fprintf(fp, " Period ");
933 }
934
764 if (symbol_conf.show_cpu_utilization) { 935 if (symbol_conf.show_cpu_utilization) {
765 if (sep) { 936 if (sep) {
766 ret += fprintf(fp, "%csys", *sep); 937 ret += fprintf(fp, "%csys", *sep);
@@ -803,18 +974,21 @@ size_t hists__fprintf(struct hists *self, struct hists *pair,
803 width = strlen(se->se_header); 974 width = strlen(se->se_header);
804 if (symbol_conf.col_width_list_str) { 975 if (symbol_conf.col_width_list_str) {
805 if (col_width) { 976 if (col_width) {
806 hists__set_col_len(self, se->se_width_idx, 977 hists__set_col_len(hists, se->se_width_idx,
807 atoi(col_width)); 978 atoi(col_width));
808 col_width = strchr(col_width, ','); 979 col_width = strchr(col_width, ',');
809 if (col_width) 980 if (col_width)
810 ++col_width; 981 ++col_width;
811 } 982 }
812 } 983 }
813 if (!hists__new_col_len(self, se->se_width_idx, width)) 984 if (!hists__new_col_len(hists, se->se_width_idx, width))
814 width = hists__col_len(self, se->se_width_idx); 985 width = hists__col_len(hists, se->se_width_idx);
815 fprintf(fp, " %*s", width, se->se_header); 986 fprintf(fp, " %*s", width, se->se_header);
816 } 987 }
988
817 fprintf(fp, "\n"); 989 fprintf(fp, "\n");
990 if (max_rows && ++nr_rows >= max_rows)
991 goto out;
818 992
819 if (sep) 993 if (sep)
820 goto print_entries; 994 goto print_entries;
@@ -822,6 +996,8 @@ size_t hists__fprintf(struct hists *self, struct hists *pair,
822 fprintf(fp, "# ........"); 996 fprintf(fp, "# ........");
823 if (symbol_conf.show_nr_samples) 997 if (symbol_conf.show_nr_samples)
824 fprintf(fp, " .........."); 998 fprintf(fp, " ..........");
999 if (symbol_conf.show_total_period)
1000 fprintf(fp, " ............");
825 if (pair) { 1001 if (pair) {
826 fprintf(fp, " .........."); 1002 fprintf(fp, " ..........");
827 if (show_displacement) 1003 if (show_displacement)
@@ -834,17 +1010,23 @@ size_t hists__fprintf(struct hists *self, struct hists *pair,
834 continue; 1010 continue;
835 1011
836 fprintf(fp, " "); 1012 fprintf(fp, " ");
837 width = hists__col_len(self, se->se_width_idx); 1013 width = hists__col_len(hists, se->se_width_idx);
838 if (width == 0) 1014 if (width == 0)
839 width = strlen(se->se_header); 1015 width = strlen(se->se_header);
840 for (i = 0; i < width; i++) 1016 for (i = 0; i < width; i++)
841 fprintf(fp, "."); 1017 fprintf(fp, ".");
842 } 1018 }
843 1019
844 fprintf(fp, "\n#\n"); 1020 fprintf(fp, "\n");
1021 if (max_rows && ++nr_rows >= max_rows)
1022 goto out;
1023
1024 fprintf(fp, "#\n");
1025 if (max_rows && ++nr_rows >= max_rows)
1026 goto out;
845 1027
846print_entries: 1028print_entries:
847 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { 1029 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
848 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 1030 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
849 1031
850 if (h->filtered) 1032 if (h->filtered)
@@ -858,19 +1040,22 @@ print_entries:
858 displacement = 0; 1040 displacement = 0;
859 ++position; 1041 ++position;
860 } 1042 }
861 ret += hist_entry__fprintf(h, self, pair, show_displacement, 1043 ret += hist_entry__fprintf(h, max_cols, hists, pair, show_displacement,
862 displacement, fp, self->stats.total_period); 1044 displacement, fp, hists->stats.total_period);
863 1045
864 if (symbol_conf.use_callchain) 1046 if (symbol_conf.use_callchain)
865 ret += hist_entry__fprintf_callchain(h, self, fp, 1047 ret += hist_entry__fprintf_callchain(h, hists, fp,
866 self->stats.total_period); 1048 hists->stats.total_period);
1049 if (max_rows && ++nr_rows >= max_rows)
1050 goto out;
1051
867 if (h->ms.map == NULL && verbose > 1) { 1052 if (h->ms.map == NULL && verbose > 1) {
868 __map_groups__fprintf_maps(&h->thread->mg, 1053 __map_groups__fprintf_maps(&h->thread->mg,
869 MAP__FUNCTION, verbose, fp); 1054 MAP__FUNCTION, verbose, fp);
870 fprintf(fp, "%.10s end\n", graph_dotted_line); 1055 fprintf(fp, "%.10s end\n", graph_dotted_line);
871 } 1056 }
872 } 1057 }
873 1058out:
874 free(rem_sq_bracket); 1059 free(rem_sq_bracket);
875 1060
876 return ret; 1061 return ret;
@@ -879,7 +1064,7 @@ print_entries:
879/* 1064/*
880 * See hists__fprintf to match the column widths 1065 * See hists__fprintf to match the column widths
881 */ 1066 */
882unsigned int hists__sort_list_width(struct hists *self) 1067unsigned int hists__sort_list_width(struct hists *hists)
883{ 1068{
884 struct sort_entry *se; 1069 struct sort_entry *se;
885 int ret = 9; /* total % */ 1070 int ret = 9; /* total % */
@@ -896,9 +1081,12 @@ unsigned int hists__sort_list_width(struct hists *self)
896 if (symbol_conf.show_nr_samples) 1081 if (symbol_conf.show_nr_samples)
897 ret += 11; 1082 ret += 11;
898 1083
1084 if (symbol_conf.show_total_period)
1085 ret += 13;
1086
899 list_for_each_entry(se, &hist_entry__sort_list, list) 1087 list_for_each_entry(se, &hist_entry__sort_list, list)
900 if (!se->elide) 1088 if (!se->elide)
901 ret += 2 + hists__col_len(self, se->se_width_idx); 1089 ret += 2 + hists__col_len(hists, se->se_width_idx);
902 1090
903 if (verbose) /* Addr + origin */ 1091 if (verbose) /* Addr + origin */
904 ret += 3 + BITS_PER_LONG / 4; 1092 ret += 3 + BITS_PER_LONG / 4;
@@ -906,63 +1094,84 @@ unsigned int hists__sort_list_width(struct hists *self)
906 return ret; 1094 return ret;
907} 1095}
908 1096
909static void hists__remove_entry_filter(struct hists *self, struct hist_entry *h, 1097static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h,
910 enum hist_filter filter) 1098 enum hist_filter filter)
911{ 1099{
912 h->filtered &= ~(1 << filter); 1100 h->filtered &= ~(1 << filter);
913 if (h->filtered) 1101 if (h->filtered)
914 return; 1102 return;
915 1103
916 ++self->nr_entries; 1104 ++hists->nr_entries;
917 if (h->ms.unfolded) 1105 if (h->ms.unfolded)
918 self->nr_entries += h->nr_rows; 1106 hists->nr_entries += h->nr_rows;
919 h->row_offset = 0; 1107 h->row_offset = 0;
920 self->stats.total_period += h->period; 1108 hists->stats.total_period += h->period;
921 self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; 1109 hists->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events;
922 1110
923 hists__calc_col_len(self, h); 1111 hists__calc_col_len(hists, h);
924} 1112}
925 1113
926void hists__filter_by_dso(struct hists *self, const struct dso *dso) 1114
1115static bool hists__filter_entry_by_dso(struct hists *hists,
1116 struct hist_entry *he)
1117{
1118 if (hists->dso_filter != NULL &&
1119 (he->ms.map == NULL || he->ms.map->dso != hists->dso_filter)) {
1120 he->filtered |= (1 << HIST_FILTER__DSO);
1121 return true;
1122 }
1123
1124 return false;
1125}
1126
1127void hists__filter_by_dso(struct hists *hists)
927{ 1128{
928 struct rb_node *nd; 1129 struct rb_node *nd;
929 1130
930 self->nr_entries = self->stats.total_period = 0; 1131 hists->nr_entries = hists->stats.total_period = 0;
931 self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; 1132 hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0;
932 hists__reset_col_len(self); 1133 hists__reset_col_len(hists);
933 1134
934 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { 1135 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
935 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 1136 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
936 1137
937 if (symbol_conf.exclude_other && !h->parent) 1138 if (symbol_conf.exclude_other && !h->parent)
938 continue; 1139 continue;
939 1140
940 if (dso != NULL && (h->ms.map == NULL || h->ms.map->dso != dso)) { 1141 if (hists__filter_entry_by_dso(hists, h))
941 h->filtered |= (1 << HIST_FILTER__DSO);
942 continue; 1142 continue;
943 }
944 1143
945 hists__remove_entry_filter(self, h, HIST_FILTER__DSO); 1144 hists__remove_entry_filter(hists, h, HIST_FILTER__DSO);
946 } 1145 }
947} 1146}
948 1147
949void hists__filter_by_thread(struct hists *self, const struct thread *thread) 1148static bool hists__filter_entry_by_thread(struct hists *hists,
1149 struct hist_entry *he)
1150{
1151 if (hists->thread_filter != NULL &&
1152 he->thread != hists->thread_filter) {
1153 he->filtered |= (1 << HIST_FILTER__THREAD);
1154 return true;
1155 }
1156
1157 return false;
1158}
1159
1160void hists__filter_by_thread(struct hists *hists)
950{ 1161{
951 struct rb_node *nd; 1162 struct rb_node *nd;
952 1163
953 self->nr_entries = self->stats.total_period = 0; 1164 hists->nr_entries = hists->stats.total_period = 0;
954 self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; 1165 hists->stats.nr_events[PERF_RECORD_SAMPLE] = 0;
955 hists__reset_col_len(self); 1166 hists__reset_col_len(hists);
956 1167
957 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { 1168 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
958 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 1169 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
959 1170
960 if (thread != NULL && h->thread != thread) { 1171 if (hists__filter_entry_by_thread(hists, h))
961 h->filtered |= (1 << HIST_FILTER__THREAD);
962 continue; 1172 continue;
963 }
964 1173
965 hists__remove_entry_filter(self, h, HIST_FILTER__THREAD); 1174 hists__remove_entry_filter(hists, h, HIST_FILTER__THREAD);
966 } 1175 }
967} 1176}
968 1177
@@ -976,13 +1185,13 @@ int hist_entry__annotate(struct hist_entry *he, size_t privsize)
976 return symbol__annotate(he->ms.sym, he->ms.map, privsize); 1185 return symbol__annotate(he->ms.sym, he->ms.map, privsize);
977} 1186}
978 1187
979void hists__inc_nr_events(struct hists *self, u32 type) 1188void hists__inc_nr_events(struct hists *hists, u32 type)
980{ 1189{
981 ++self->stats.nr_events[0]; 1190 ++hists->stats.nr_events[0];
982 ++self->stats.nr_events[type]; 1191 ++hists->stats.nr_events[type];
983} 1192}
984 1193
985size_t hists__fprintf_nr_events(struct hists *self, FILE *fp) 1194size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp)
986{ 1195{
987 int i; 1196 int i;
988 size_t ret = 0; 1197 size_t ret = 0;
@@ -990,7 +1199,7 @@ size_t hists__fprintf_nr_events(struct hists *self, FILE *fp)
990 for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) { 1199 for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) {
991 const char *name; 1200 const char *name;
992 1201
993 if (self->stats.nr_events[i] == 0) 1202 if (hists->stats.nr_events[i] == 0)
994 continue; 1203 continue;
995 1204
996 name = perf_event__name(i); 1205 name = perf_event__name(i);
@@ -998,8 +1207,18 @@ size_t hists__fprintf_nr_events(struct hists *self, FILE *fp)
998 continue; 1207 continue;
999 1208
1000 ret += fprintf(fp, "%16s events: %10d\n", name, 1209 ret += fprintf(fp, "%16s events: %10d\n", name,
1001 self->stats.nr_events[i]); 1210 hists->stats.nr_events[i]);
1002 } 1211 }
1003 1212
1004 return ret; 1213 return ret;
1005} 1214}
1215
1216void hists__init(struct hists *hists)
1217{
1218 memset(hists, 0, sizeof(*hists));
1219 hists->entries_in_array[0] = hists->entries_in_array[1] = RB_ROOT;
1220 hists->entries_in = &hists->entries_in_array[0];
1221 hists->entries_collapsed = RB_ROOT;
1222 hists->entries = RB_ROOT;
1223 pthread_mutex_init(&hists->lock, NULL);
1224}
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index 3beb97c4d822..ff93ddc91c5c 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -2,6 +2,7 @@
2#define __PERF_HIST_H 2#define __PERF_HIST_H
3 3
4#include <linux/types.h> 4#include <linux/types.h>
5#include <pthread.h>
5#include "callchain.h" 6#include "callchain.h"
6 7
7extern struct callchain_param callchain_param; 8extern struct callchain_param callchain_param;
@@ -42,9 +43,18 @@ enum hist_column {
42 HISTC_NR_COLS, /* Last entry */ 43 HISTC_NR_COLS, /* Last entry */
43}; 44};
44 45
46struct thread;
47struct dso;
48
45struct hists { 49struct hists {
50 struct rb_root entries_in_array[2];
51 struct rb_root *entries_in;
46 struct rb_root entries; 52 struct rb_root entries;
53 struct rb_root entries_collapsed;
47 u64 nr_entries; 54 u64 nr_entries;
55 const struct thread *thread_filter;
56 const struct dso *dso_filter;
57 pthread_mutex_t lock;
48 struct events_stats stats; 58 struct events_stats stats;
49 u64 event_stream; 59 u64 event_stream;
50 u16 col_len[HISTC_NR_COLS]; 60 u16 col_len[HISTC_NR_COLS];
@@ -52,34 +62,42 @@ struct hists {
52 struct callchain_cursor callchain_cursor; 62 struct callchain_cursor callchain_cursor;
53}; 63};
54 64
65void hists__init(struct hists *hists);
66
55struct hist_entry *__hists__add_entry(struct hists *self, 67struct hist_entry *__hists__add_entry(struct hists *self,
56 struct addr_location *al, 68 struct addr_location *al,
57 struct symbol *parent, u64 period); 69 struct symbol *parent, u64 period);
58extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); 70extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *);
59extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); 71extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *);
60int hist_entry__fprintf(struct hist_entry *self, struct hists *hists, 72int hist_entry__fprintf(struct hist_entry *he, size_t size, struct hists *hists,
61 struct hists *pair_hists, bool show_displacement, 73 struct hists *pair_hists, bool show_displacement,
62 long displacement, FILE *fp, u64 total); 74 long displacement, FILE *fp, u64 session_total);
63int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size, 75int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size,
64 struct hists *hists, struct hists *pair_hists, 76 struct hists *hists);
65 bool show_displacement, long displacement,
66 bool color, u64 total);
67void hist_entry__free(struct hist_entry *); 77void hist_entry__free(struct hist_entry *);
68 78
69void hists__output_resort(struct hists *self); 79void hists__output_resort(struct hists *self);
80void hists__output_resort_threaded(struct hists *hists);
70void hists__collapse_resort(struct hists *self); 81void hists__collapse_resort(struct hists *self);
82void hists__collapse_resort_threaded(struct hists *hists);
83
84void hists__decay_entries(struct hists *hists, bool zap_user, bool zap_kernel);
85void hists__decay_entries_threaded(struct hists *hists, bool zap_user,
86 bool zap_kernel);
87void hists__output_recalc_col_len(struct hists *hists, int max_rows);
71 88
72void hists__inc_nr_events(struct hists *self, u32 type); 89void hists__inc_nr_events(struct hists *self, u32 type);
73size_t hists__fprintf_nr_events(struct hists *self, FILE *fp); 90size_t hists__fprintf_nr_events(struct hists *self, FILE *fp);
74 91
75size_t hists__fprintf(struct hists *self, struct hists *pair, 92size_t hists__fprintf(struct hists *self, struct hists *pair,
76 bool show_displacement, FILE *fp); 93 bool show_displacement, bool show_header,
94 int max_rows, int max_cols, FILE *fp);
77 95
78int hist_entry__inc_addr_samples(struct hist_entry *self, int evidx, u64 addr); 96int hist_entry__inc_addr_samples(struct hist_entry *self, int evidx, u64 addr);
79int hist_entry__annotate(struct hist_entry *self, size_t privsize); 97int hist_entry__annotate(struct hist_entry *self, size_t privsize);
80 98
81void hists__filter_by_dso(struct hists *self, const struct dso *dso); 99void hists__filter_by_dso(struct hists *hists);
82void hists__filter_by_thread(struct hists *self, const struct thread *thread); 100void hists__filter_by_thread(struct hists *hists);
83 101
84u16 hists__col_len(struct hists *self, enum hist_column col); 102u16 hists__col_len(struct hists *self, enum hist_column col);
85void hists__set_col_len(struct hists *self, enum hist_column col, u16 len); 103void hists__set_col_len(struct hists *self, enum hist_column col, u16 len);
@@ -90,26 +108,33 @@ struct perf_evlist;
90#ifdef NO_NEWT_SUPPORT 108#ifdef NO_NEWT_SUPPORT
91static inline 109static inline
92int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __used, 110int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __used,
93 const char *help __used) 111 const char *help __used,
112 void(*timer)(void *arg) __used,
113 void *arg __used,
114 int refresh __used)
94{ 115{
95 return 0; 116 return 0;
96} 117}
97 118
98static inline int hist_entry__tui_annotate(struct hist_entry *self __used, 119static inline int hist_entry__tui_annotate(struct hist_entry *self __used,
99 int evidx __used) 120 int evidx __used,
121 int nr_events __used,
122 void(*timer)(void *arg) __used,
123 void *arg __used,
124 int delay_secs __used)
100{ 125{
101 return 0; 126 return 0;
102} 127}
103#define KEY_LEFT -1 128#define K_LEFT -1
104#define KEY_RIGHT -2 129#define K_RIGHT -2
105#else 130#else
106#include <newt.h> 131#include "ui/keysyms.h"
107int hist_entry__tui_annotate(struct hist_entry *self, int evidx); 132int hist_entry__tui_annotate(struct hist_entry *he, int evidx, int nr_events,
108 133 void(*timer)(void *arg), void *arg, int delay_secs);
109#define KEY_LEFT NEWT_KEY_LEFT
110#define KEY_RIGHT NEWT_KEY_RIGHT
111 134
112int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help); 135int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
136 void(*timer)(void *arg), void *arg,
137 int refresh);
113#endif 138#endif
114 139
115unsigned int hists__sort_list_width(struct hists *self); 140unsigned int hists__sort_list_width(struct hists *self);
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index a16ecab5229d..78284b13e808 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -18,6 +18,13 @@ static inline int is_anon_memory(const char *filename)
18 return strcmp(filename, "//anon") == 0; 18 return strcmp(filename, "//anon") == 0;
19} 19}
20 20
21static inline int is_no_dso_memory(const char *filename)
22{
23 return !strcmp(filename, "[stack]") ||
24 !strcmp(filename, "[vdso]") ||
25 !strcmp(filename, "[heap]");
26}
27
21void map__init(struct map *self, enum map_type type, 28void map__init(struct map *self, enum map_type type,
22 u64 start, u64 end, u64 pgoff, struct dso *dso) 29 u64 start, u64 end, u64 pgoff, struct dso *dso)
23{ 30{
@@ -42,9 +49,10 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
42 if (self != NULL) { 49 if (self != NULL) {
43 char newfilename[PATH_MAX]; 50 char newfilename[PATH_MAX];
44 struct dso *dso; 51 struct dso *dso;
45 int anon; 52 int anon, no_dso;
46 53
47 anon = is_anon_memory(filename); 54 anon = is_anon_memory(filename);
55 no_dso = is_no_dso_memory(filename);
48 56
49 if (anon) { 57 if (anon) {
50 snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid); 58 snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid);
@@ -57,12 +65,16 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
57 65
58 map__init(self, type, start, start + len, pgoff, dso); 66 map__init(self, type, start, start + len, pgoff, dso);
59 67
60 if (anon) { 68 if (anon || no_dso) {
61set_identity:
62 self->map_ip = self->unmap_ip = identity__map_ip; 69 self->map_ip = self->unmap_ip = identity__map_ip;
63 } else if (strcmp(filename, "[vdso]") == 0) { 70
64 dso__set_loaded(dso, self->type); 71 /*
65 goto set_identity; 72 * Set memory without DSO as loaded. All map__find_*
73 * functions still return NULL, and we avoid the
74 * unnecessary map__load warning.
75 */
76 if (no_dso)
77 dso__set_loaded(dso, self->type);
66 } 78 }
67 } 79 }
68 return self; 80 return self;
@@ -127,8 +139,8 @@ int map__load(struct map *self, symbol_filter_t filter)
127 139
128 if (len > sizeof(DSO__DELETED) && 140 if (len > sizeof(DSO__DELETED) &&
129 strcmp(name + real_len + 1, DSO__DELETED) == 0) { 141 strcmp(name + real_len + 1, DSO__DELETED) == 0) {
130 pr_warning("%.*s was updated, restart the long " 142 pr_warning("%.*s was updated (is prelink enabled?). "
131 "running apps that use it!\n", 143 "Restart the long running apps that use it!\n",
132 (int)real_len, name); 144 (int)real_len, name);
133 } else { 145 } else {
134 pr_warning("no symbols found in %s, maybe install " 146 pr_warning("no symbols found in %s, maybe install "
@@ -220,55 +232,55 @@ u64 map__objdump_2ip(struct map *map, u64 addr)
220 return ip; 232 return ip;
221} 233}
222 234
223void map_groups__init(struct map_groups *self) 235void map_groups__init(struct map_groups *mg)
224{ 236{
225 int i; 237 int i;
226 for (i = 0; i < MAP__NR_TYPES; ++i) { 238 for (i = 0; i < MAP__NR_TYPES; ++i) {
227 self->maps[i] = RB_ROOT; 239 mg->maps[i] = RB_ROOT;
228 INIT_LIST_HEAD(&self->removed_maps[i]); 240 INIT_LIST_HEAD(&mg->removed_maps[i]);
229 } 241 }
230 self->machine = NULL; 242 mg->machine = NULL;
231} 243}
232 244
233static void maps__delete(struct rb_root *self) 245static void maps__delete(struct rb_root *maps)
234{ 246{
235 struct rb_node *next = rb_first(self); 247 struct rb_node *next = rb_first(maps);
236 248
237 while (next) { 249 while (next) {
238 struct map *pos = rb_entry(next, struct map, rb_node); 250 struct map *pos = rb_entry(next, struct map, rb_node);
239 251
240 next = rb_next(&pos->rb_node); 252 next = rb_next(&pos->rb_node);
241 rb_erase(&pos->rb_node, self); 253 rb_erase(&pos->rb_node, maps);
242 map__delete(pos); 254 map__delete(pos);
243 } 255 }
244} 256}
245 257
246static void maps__delete_removed(struct list_head *self) 258static void maps__delete_removed(struct list_head *maps)
247{ 259{
248 struct map *pos, *n; 260 struct map *pos, *n;
249 261
250 list_for_each_entry_safe(pos, n, self, node) { 262 list_for_each_entry_safe(pos, n, maps, node) {
251 list_del(&pos->node); 263 list_del(&pos->node);
252 map__delete(pos); 264 map__delete(pos);
253 } 265 }
254} 266}
255 267
256void map_groups__exit(struct map_groups *self) 268void map_groups__exit(struct map_groups *mg)
257{ 269{
258 int i; 270 int i;
259 271
260 for (i = 0; i < MAP__NR_TYPES; ++i) { 272 for (i = 0; i < MAP__NR_TYPES; ++i) {
261 maps__delete(&self->maps[i]); 273 maps__delete(&mg->maps[i]);
262 maps__delete_removed(&self->removed_maps[i]); 274 maps__delete_removed(&mg->removed_maps[i]);
263 } 275 }
264} 276}
265 277
266void map_groups__flush(struct map_groups *self) 278void map_groups__flush(struct map_groups *mg)
267{ 279{
268 int type; 280 int type;
269 281
270 for (type = 0; type < MAP__NR_TYPES; type++) { 282 for (type = 0; type < MAP__NR_TYPES; type++) {
271 struct rb_root *root = &self->maps[type]; 283 struct rb_root *root = &mg->maps[type];
272 struct rb_node *next = rb_first(root); 284 struct rb_node *next = rb_first(root);
273 285
274 while (next) { 286 while (next) {
@@ -280,17 +292,17 @@ void map_groups__flush(struct map_groups *self)
280 * instance in some hist_entry instances, so 292 * instance in some hist_entry instances, so
281 * just move them to a separate list. 293 * just move them to a separate list.
282 */ 294 */
283 list_add_tail(&pos->node, &self->removed_maps[pos->type]); 295 list_add_tail(&pos->node, &mg->removed_maps[pos->type]);
284 } 296 }
285 } 297 }
286} 298}
287 299
288struct symbol *map_groups__find_symbol(struct map_groups *self, 300struct symbol *map_groups__find_symbol(struct map_groups *mg,
289 enum map_type type, u64 addr, 301 enum map_type type, u64 addr,
290 struct map **mapp, 302 struct map **mapp,
291 symbol_filter_t filter) 303 symbol_filter_t filter)
292{ 304{
293 struct map *map = map_groups__find(self, type, addr); 305 struct map *map = map_groups__find(mg, type, addr);
294 306
295 if (map != NULL) { 307 if (map != NULL) {
296 if (mapp != NULL) 308 if (mapp != NULL)
@@ -301,7 +313,7 @@ struct symbol *map_groups__find_symbol(struct map_groups *self,
301 return NULL; 313 return NULL;
302} 314}
303 315
304struct symbol *map_groups__find_symbol_by_name(struct map_groups *self, 316struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg,
305 enum map_type type, 317 enum map_type type,
306 const char *name, 318 const char *name,
307 struct map **mapp, 319 struct map **mapp,
@@ -309,7 +321,7 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *self,
309{ 321{
310 struct rb_node *nd; 322 struct rb_node *nd;
311 323
312 for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { 324 for (nd = rb_first(&mg->maps[type]); nd; nd = rb_next(nd)) {
313 struct map *pos = rb_entry(nd, struct map, rb_node); 325 struct map *pos = rb_entry(nd, struct map, rb_node);
314 struct symbol *sym = map__find_symbol_by_name(pos, name, filter); 326 struct symbol *sym = map__find_symbol_by_name(pos, name, filter);
315 327
@@ -323,13 +335,13 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *self,
323 return NULL; 335 return NULL;
324} 336}
325 337
326size_t __map_groups__fprintf_maps(struct map_groups *self, 338size_t __map_groups__fprintf_maps(struct map_groups *mg,
327 enum map_type type, int verbose, FILE *fp) 339 enum map_type type, int verbose, FILE *fp)
328{ 340{
329 size_t printed = fprintf(fp, "%s:\n", map_type__name[type]); 341 size_t printed = fprintf(fp, "%s:\n", map_type__name[type]);
330 struct rb_node *nd; 342 struct rb_node *nd;
331 343
332 for (nd = rb_first(&self->maps[type]); nd; nd = rb_next(nd)) { 344 for (nd = rb_first(&mg->maps[type]); nd; nd = rb_next(nd)) {
333 struct map *pos = rb_entry(nd, struct map, rb_node); 345 struct map *pos = rb_entry(nd, struct map, rb_node);
334 printed += fprintf(fp, "Map:"); 346 printed += fprintf(fp, "Map:");
335 printed += map__fprintf(pos, fp); 347 printed += map__fprintf(pos, fp);
@@ -342,22 +354,22 @@ size_t __map_groups__fprintf_maps(struct map_groups *self,
342 return printed; 354 return printed;
343} 355}
344 356
345size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp) 357size_t map_groups__fprintf_maps(struct map_groups *mg, int verbose, FILE *fp)
346{ 358{
347 size_t printed = 0, i; 359 size_t printed = 0, i;
348 for (i = 0; i < MAP__NR_TYPES; ++i) 360 for (i = 0; i < MAP__NR_TYPES; ++i)
349 printed += __map_groups__fprintf_maps(self, i, verbose, fp); 361 printed += __map_groups__fprintf_maps(mg, i, verbose, fp);
350 return printed; 362 return printed;
351} 363}
352 364
353static size_t __map_groups__fprintf_removed_maps(struct map_groups *self, 365static size_t __map_groups__fprintf_removed_maps(struct map_groups *mg,
354 enum map_type type, 366 enum map_type type,
355 int verbose, FILE *fp) 367 int verbose, FILE *fp)
356{ 368{
357 struct map *pos; 369 struct map *pos;
358 size_t printed = 0; 370 size_t printed = 0;
359 371
360 list_for_each_entry(pos, &self->removed_maps[type], node) { 372 list_for_each_entry(pos, &mg->removed_maps[type], node) {
361 printed += fprintf(fp, "Map:"); 373 printed += fprintf(fp, "Map:");
362 printed += map__fprintf(pos, fp); 374 printed += map__fprintf(pos, fp);
363 if (verbose > 1) { 375 if (verbose > 1) {
@@ -368,26 +380,26 @@ static size_t __map_groups__fprintf_removed_maps(struct map_groups *self,
368 return printed; 380 return printed;
369} 381}
370 382
371static size_t map_groups__fprintf_removed_maps(struct map_groups *self, 383static size_t map_groups__fprintf_removed_maps(struct map_groups *mg,
372 int verbose, FILE *fp) 384 int verbose, FILE *fp)
373{ 385{
374 size_t printed = 0, i; 386 size_t printed = 0, i;
375 for (i = 0; i < MAP__NR_TYPES; ++i) 387 for (i = 0; i < MAP__NR_TYPES; ++i)
376 printed += __map_groups__fprintf_removed_maps(self, i, verbose, fp); 388 printed += __map_groups__fprintf_removed_maps(mg, i, verbose, fp);
377 return printed; 389 return printed;
378} 390}
379 391
380size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp) 392size_t map_groups__fprintf(struct map_groups *mg, int verbose, FILE *fp)
381{ 393{
382 size_t printed = map_groups__fprintf_maps(self, verbose, fp); 394 size_t printed = map_groups__fprintf_maps(mg, verbose, fp);
383 printed += fprintf(fp, "Removed maps:\n"); 395 printed += fprintf(fp, "Removed maps:\n");
384 return printed + map_groups__fprintf_removed_maps(self, verbose, fp); 396 return printed + map_groups__fprintf_removed_maps(mg, verbose, fp);
385} 397}
386 398
387int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, 399int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map,
388 int verbose, FILE *fp) 400 int verbose, FILE *fp)
389{ 401{
390 struct rb_root *root = &self->maps[map->type]; 402 struct rb_root *root = &mg->maps[map->type];
391 struct rb_node *next = rb_first(root); 403 struct rb_node *next = rb_first(root);
392 int err = 0; 404 int err = 0;
393 405
@@ -418,7 +430,7 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map,
418 } 430 }
419 431
420 before->end = map->start - 1; 432 before->end = map->start - 1;
421 map_groups__insert(self, before); 433 map_groups__insert(mg, before);
422 if (verbose >= 2) 434 if (verbose >= 2)
423 map__fprintf(before, fp); 435 map__fprintf(before, fp);
424 } 436 }
@@ -432,7 +444,7 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map,
432 } 444 }
433 445
434 after->start = map->end + 1; 446 after->start = map->end + 1;
435 map_groups__insert(self, after); 447 map_groups__insert(mg, after);
436 if (verbose >= 2) 448 if (verbose >= 2)
437 map__fprintf(after, fp); 449 map__fprintf(after, fp);
438 } 450 }
@@ -441,7 +453,7 @@ move_map:
441 * If we have references, just move them to a separate list. 453 * If we have references, just move them to a separate list.
442 */ 454 */
443 if (pos->referenced) 455 if (pos->referenced)
444 list_add_tail(&pos->node, &self->removed_maps[map->type]); 456 list_add_tail(&pos->node, &mg->removed_maps[map->type]);
445 else 457 else
446 map__delete(pos); 458 map__delete(pos);
447 459
@@ -455,7 +467,7 @@ move_map:
455/* 467/*
456 * XXX This should not really _copy_ te maps, but refcount them. 468 * XXX This should not really _copy_ te maps, but refcount them.
457 */ 469 */
458int map_groups__clone(struct map_groups *self, 470int map_groups__clone(struct map_groups *mg,
459 struct map_groups *parent, enum map_type type) 471 struct map_groups *parent, enum map_type type)
460{ 472{
461 struct rb_node *nd; 473 struct rb_node *nd;
@@ -464,7 +476,7 @@ int map_groups__clone(struct map_groups *self,
464 struct map *new = map__clone(map); 476 struct map *new = map__clone(map);
465 if (new == NULL) 477 if (new == NULL)
466 return -ENOMEM; 478 return -ENOMEM;
467 map_groups__insert(self, new); 479 map_groups__insert(mg, new);
468 } 480 }
469 return 0; 481 return 0;
470} 482}
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index b397c0383728..890d85545d0f 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -123,17 +123,17 @@ void map__fixup_end(struct map *self);
123 123
124void map__reloc_vmlinux(struct map *self); 124void map__reloc_vmlinux(struct map *self);
125 125
126size_t __map_groups__fprintf_maps(struct map_groups *self, 126size_t __map_groups__fprintf_maps(struct map_groups *mg,
127 enum map_type type, int verbose, FILE *fp); 127 enum map_type type, int verbose, FILE *fp);
128void maps__insert(struct rb_root *maps, struct map *map); 128void maps__insert(struct rb_root *maps, struct map *map);
129void maps__remove(struct rb_root *self, struct map *map); 129void maps__remove(struct rb_root *maps, struct map *map);
130struct map *maps__find(struct rb_root *maps, u64 addr); 130struct map *maps__find(struct rb_root *maps, u64 addr);
131void map_groups__init(struct map_groups *self); 131void map_groups__init(struct map_groups *mg);
132void map_groups__exit(struct map_groups *self); 132void map_groups__exit(struct map_groups *mg);
133int map_groups__clone(struct map_groups *self, 133int map_groups__clone(struct map_groups *mg,
134 struct map_groups *parent, enum map_type type); 134 struct map_groups *parent, enum map_type type);
135size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp); 135size_t map_groups__fprintf(struct map_groups *mg, int verbose, FILE *fp);
136size_t map_groups__fprintf_maps(struct map_groups *self, int verbose, FILE *fp); 136size_t map_groups__fprintf_maps(struct map_groups *mg, int verbose, FILE *fp);
137 137
138typedef void (*machine__process_t)(struct machine *self, void *data); 138typedef void (*machine__process_t)(struct machine *self, void *data);
139 139
@@ -162,29 +162,29 @@ static inline bool machine__is_host(struct machine *self)
162 return self ? self->pid == HOST_KERNEL_ID : false; 162 return self ? self->pid == HOST_KERNEL_ID : false;
163} 163}
164 164
165static inline void map_groups__insert(struct map_groups *self, struct map *map) 165static inline void map_groups__insert(struct map_groups *mg, struct map *map)
166{ 166{
167 maps__insert(&self->maps[map->type], map); 167 maps__insert(&mg->maps[map->type], map);
168 map->groups = self; 168 map->groups = mg;
169} 169}
170 170
171static inline void map_groups__remove(struct map_groups *self, struct map *map) 171static inline void map_groups__remove(struct map_groups *mg, struct map *map)
172{ 172{
173 maps__remove(&self->maps[map->type], map); 173 maps__remove(&mg->maps[map->type], map);
174} 174}
175 175
176static inline struct map *map_groups__find(struct map_groups *self, 176static inline struct map *map_groups__find(struct map_groups *mg,
177 enum map_type type, u64 addr) 177 enum map_type type, u64 addr)
178{ 178{
179 return maps__find(&self->maps[type], addr); 179 return maps__find(&mg->maps[type], addr);
180} 180}
181 181
182struct symbol *map_groups__find_symbol(struct map_groups *self, 182struct symbol *map_groups__find_symbol(struct map_groups *mg,
183 enum map_type type, u64 addr, 183 enum map_type type, u64 addr,
184 struct map **mapp, 184 struct map **mapp,
185 symbol_filter_t filter); 185 symbol_filter_t filter);
186 186
187struct symbol *map_groups__find_symbol_by_name(struct map_groups *self, 187struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg,
188 enum map_type type, 188 enum map_type type,
189 const char *name, 189 const char *name,
190 struct map **mapp, 190 struct map **mapp,
@@ -208,11 +208,11 @@ struct symbol *machine__find_kernel_function(struct machine *self, u64 addr,
208} 208}
209 209
210static inline 210static inline
211struct symbol *map_groups__find_function_by_name(struct map_groups *self, 211struct symbol *map_groups__find_function_by_name(struct map_groups *mg,
212 const char *name, struct map **mapp, 212 const char *name, struct map **mapp,
213 symbol_filter_t filter) 213 symbol_filter_t filter)
214{ 214{
215 return map_groups__find_symbol_by_name(self, MAP__FUNCTION, name, mapp, filter); 215 return map_groups__find_symbol_by_name(mg, MAP__FUNCTION, name, mapp, filter);
216} 216}
217 217
218static inline 218static inline
@@ -225,13 +225,13 @@ struct symbol *machine__find_kernel_function_by_name(struct machine *self,
225 filter); 225 filter);
226} 226}
227 227
228int map_groups__fixup_overlappings(struct map_groups *self, struct map *map, 228int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map,
229 int verbose, FILE *fp); 229 int verbose, FILE *fp);
230 230
231struct map *map_groups__find_by_name(struct map_groups *self, 231struct map *map_groups__find_by_name(struct map_groups *mg,
232 enum map_type type, const char *name); 232 enum map_type type, const char *name);
233struct map *machine__new_module(struct machine *self, u64 start, const char *filename); 233struct map *machine__new_module(struct machine *self, u64 start, const char *filename);
234 234
235void map_groups__flush(struct map_groups *self); 235void map_groups__flush(struct map_groups *mg);
236 236
237#endif /* __PERF_MAP_H */ 237#endif /* __PERF_MAP_H */
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 1c7bfa5fe0a8..eb25900e2211 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -1956,8 +1956,10 @@ static int __del_trace_probe_event(int fd, struct str_node *ent)
1956 1956
1957 pr_debug("Writing event: %s\n", buf); 1957 pr_debug("Writing event: %s\n", buf);
1958 ret = write(fd, buf, strlen(buf)); 1958 ret = write(fd, buf, strlen(buf));
1959 if (ret < 0) 1959 if (ret < 0) {
1960 ret = -errno;
1960 goto error; 1961 goto error;
1962 }
1961 1963
1962 printf("Remove event: %s\n", ent->s); 1964 printf("Remove event: %s\n", ent->s);
1963 return 0; 1965 return 0;
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 72458d9da5b1..20e011c99a94 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -1326,3 +1326,22 @@ int perf_session__cpu_bitmap(struct perf_session *session,
1326 1326
1327 return 0; 1327 return 0;
1328} 1328}
1329
1330void perf_session__fprintf_info(struct perf_session *session, FILE *fp,
1331 bool full)
1332{
1333 struct stat st;
1334 int ret;
1335
1336 if (session == NULL || fp == NULL)
1337 return;
1338
1339 ret = fstat(session->fd, &st);
1340 if (ret == -1)
1341 return;
1342
1343 fprintf(fp, "# ========\n");
1344 fprintf(fp, "# captured on: %s", ctime(&st.st_ctime));
1345 perf_header__fprintf_info(session, fp, full);
1346 fprintf(fp, "# ========\n#\n");
1347}
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index 974d0cbee5e9..514b06d41f05 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -177,4 +177,5 @@ void perf_session__print_ip(union perf_event *event,
177int perf_session__cpu_bitmap(struct perf_session *session, 177int perf_session__cpu_bitmap(struct perf_session *session,
178 const char *cpu_list, unsigned long *cpu_bitmap); 178 const char *cpu_list, unsigned long *cpu_bitmap);
179 179
180void perf_session__fprintf_info(struct perf_session *s, FILE *fp, bool full);
180#endif /* __PERF_SESSION_H */ 181#endif /* __PERF_SESSION_H */
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index 1ee8f1e40f18..16da30d8d765 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -177,7 +177,9 @@ static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
177 BITS_PER_LONG / 4, self->ip, o); 177 BITS_PER_LONG / 4, self->ip, o);
178 } 178 }
179 179
180 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level); 180 if (!sort_dso.elide)
181 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level);
182
181 if (self->ms.sym) 183 if (self->ms.sym)
182 ret += repsep_snprintf(bf + ret, size - ret, "%s", 184 ret += repsep_snprintf(bf + ret, size - ret, "%s",
183 self->ms.sym->name); 185 self->ms.sym->name);
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index 77d0388ad415..3f67ae395752 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -45,6 +45,7 @@ extern enum sort_type sort__first_dimension;
45 * @nr_rows - rows expanded in callchain, recalculated on folding/unfolding 45 * @nr_rows - rows expanded in callchain, recalculated on folding/unfolding
46 */ 46 */
47struct hist_entry { 47struct hist_entry {
48 struct rb_node rb_node_in;
48 struct rb_node rb_node; 49 struct rb_node rb_node;
49 u64 period; 50 u64 period;
50 u64 period_sys; 51 u64 period_sys;
@@ -63,6 +64,7 @@ struct hist_entry {
63 64
64 bool init_have_children; 65 bool init_have_children;
65 char level; 66 char level;
67 bool used;
66 u8 filtered; 68 u8 filtered;
67 struct symbol *parent; 69 struct symbol *parent;
68 union { 70 union {
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 40eeaf07725b..632b50c7bc26 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -24,7 +24,7 @@
24#include <sys/utsname.h> 24#include <sys/utsname.h>
25 25
26#ifndef KSYM_NAME_LEN 26#ifndef KSYM_NAME_LEN
27#define KSYM_NAME_LEN 128 27#define KSYM_NAME_LEN 256
28#endif 28#endif
29 29
30#ifndef NT_GNU_BUILD_ID 30#ifndef NT_GNU_BUILD_ID
@@ -46,6 +46,7 @@ struct symbol_conf symbol_conf = {
46 .exclude_other = true, 46 .exclude_other = true,
47 .use_modules = true, 47 .use_modules = true,
48 .try_vmlinux_path = true, 48 .try_vmlinux_path = true,
49 .annotate_src = true,
49 .symfs = "", 50 .symfs = "",
50}; 51};
51 52
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 4f377d92e75a..29f8d742e92f 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -72,11 +72,14 @@ struct symbol_conf {
72 use_modules, 72 use_modules,
73 sort_by_name, 73 sort_by_name,
74 show_nr_samples, 74 show_nr_samples,
75 show_total_period,
75 use_callchain, 76 use_callchain,
76 exclude_other, 77 exclude_other,
77 show_cpu_utilization, 78 show_cpu_utilization,
78 initialized, 79 initialized,
79 kptr_restrict; 80 kptr_restrict,
81 annotate_asm_raw,
82 annotate_src;
80 const char *vmlinux_name, 83 const char *vmlinux_name,
81 *kallsyms_name, 84 *kallsyms_name,
82 *source_prefix, 85 *source_prefix,
diff --git a/tools/perf/util/top.c b/tools/perf/util/top.c
index a11f60735a18..500471dffa4f 100644
--- a/tools/perf/util/top.c
+++ b/tools/perf/util/top.c
@@ -15,52 +15,6 @@
15#include "top.h" 15#include "top.h"
16#include <inttypes.h> 16#include <inttypes.h>
17 17
18/*
19 * Ordering weight: count-1 * count-2 * ... / count-n
20 */
21static double sym_weight(const struct sym_entry *sym, struct perf_top *top)
22{
23 double weight = sym->snap_count;
24 int counter;
25
26 if (!top->display_weighted)
27 return weight;
28
29 for (counter = 1; counter < top->evlist->nr_entries - 1; counter++)
30 weight *= sym->count[counter];
31
32 weight /= (sym->count[counter] + 1);
33
34 return weight;
35}
36
37static void perf_top__remove_active_sym(struct perf_top *top, struct sym_entry *syme)
38{
39 pthread_mutex_lock(&top->active_symbols_lock);
40 list_del_init(&syme->node);
41 pthread_mutex_unlock(&top->active_symbols_lock);
42}
43
44static void rb_insert_active_sym(struct rb_root *tree, struct sym_entry *se)
45{
46 struct rb_node **p = &tree->rb_node;
47 struct rb_node *parent = NULL;
48 struct sym_entry *iter;
49
50 while (*p != NULL) {
51 parent = *p;
52 iter = rb_entry(parent, struct sym_entry, rb_node);
53
54 if (se->weight > iter->weight)
55 p = &(*p)->rb_left;
56 else
57 p = &(*p)->rb_right;
58 }
59
60 rb_link_node(&se->rb_node, parent, p);
61 rb_insert_color(&se->rb_node, tree);
62}
63
64#define SNPRINTF(buf, size, fmt, args...) \ 18#define SNPRINTF(buf, size, fmt, args...) \
65({ \ 19({ \
66 size_t r = snprintf(buf, size, fmt, ## args); \ 20 size_t r = snprintf(buf, size, fmt, ## args); \
@@ -69,7 +23,6 @@ static void rb_insert_active_sym(struct rb_root *tree, struct sym_entry *se)
69 23
70size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) 24size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size)
71{ 25{
72 struct perf_evsel *counter;
73 float samples_per_sec = top->samples / top->delay_secs; 26 float samples_per_sec = top->samples / top->delay_secs;
74 float ksamples_per_sec = top->kernel_samples / top->delay_secs; 27 float ksamples_per_sec = top->kernel_samples / top->delay_secs;
75 float esamples_percent = (100.0 * top->exact_samples) / top->samples; 28 float esamples_percent = (100.0 * top->exact_samples) / top->samples;
@@ -104,7 +57,7 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size)
104 esamples_percent); 57 esamples_percent);
105 } 58 }
106 59
107 if (top->evlist->nr_entries == 1 || !top->display_weighted) { 60 if (top->evlist->nr_entries == 1) {
108 struct perf_evsel *first; 61 struct perf_evsel *first;
109 first = list_entry(top->evlist->entries.next, struct perf_evsel, node); 62 first = list_entry(top->evlist->entries.next, struct perf_evsel, node);
110 ret += SNPRINTF(bf + ret, size - ret, "%" PRIu64 "%s ", 63 ret += SNPRINTF(bf + ret, size - ret, "%" PRIu64 "%s ",
@@ -112,27 +65,7 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size)
112 top->freq ? "Hz" : ""); 65 top->freq ? "Hz" : "");
113 } 66 }
114 67
115 if (!top->display_weighted) { 68 ret += SNPRINTF(bf + ret, size - ret, "%s", event_name(top->sym_evsel));
116 ret += SNPRINTF(bf + ret, size - ret, "%s",
117 event_name(top->sym_evsel));
118 } else {
119 /*
120 * Don't let events eat all the space. Leaving 30 bytes
121 * for the rest should be enough.
122 */
123 size_t last_pos = size - 30;
124
125 list_for_each_entry(counter, &top->evlist->entries, node) {
126 ret += SNPRINTF(bf + ret, size - ret, "%s%s",
127 counter->idx ? "/" : "",
128 event_name(counter));
129 if (ret > last_pos) {
130 sprintf(bf + last_pos - 3, "..");
131 ret = last_pos - 1;
132 break;
133 }
134 }
135 }
136 69
137 ret += SNPRINTF(bf + ret, size - ret, "], "); 70 ret += SNPRINTF(bf + ret, size - ret, "], ");
138 71
@@ -166,73 +99,3 @@ void perf_top__reset_sample_counters(struct perf_top *top)
166 top->exact_samples = top->guest_kernel_samples = 99 top->exact_samples = top->guest_kernel_samples =
167 top->guest_us_samples = 0; 100 top->guest_us_samples = 0;
168} 101}
169
170float perf_top__decay_samples(struct perf_top *top, struct rb_root *root)
171{
172 struct sym_entry *syme, *n;
173 float sum_ksamples = 0.0;
174 int snap = !top->display_weighted ? top->sym_evsel->idx : 0, j;
175
176 /* Sort the active symbols */
177 pthread_mutex_lock(&top->active_symbols_lock);
178 syme = list_entry(top->active_symbols.next, struct sym_entry, node);
179 pthread_mutex_unlock(&top->active_symbols_lock);
180
181 top->rb_entries = 0;
182 list_for_each_entry_safe_from(syme, n, &top->active_symbols, node) {
183 syme->snap_count = syme->count[snap];
184 if (syme->snap_count != 0) {
185
186 if ((top->hide_user_symbols &&
187 syme->map->dso->kernel == DSO_TYPE_USER) ||
188 (top->hide_kernel_symbols &&
189 syme->map->dso->kernel == DSO_TYPE_KERNEL)) {
190 perf_top__remove_active_sym(top, syme);
191 continue;
192 }
193 syme->weight = sym_weight(syme, top);
194
195 if ((int)syme->snap_count >= top->count_filter) {
196 rb_insert_active_sym(root, syme);
197 ++top->rb_entries;
198 }
199 sum_ksamples += syme->snap_count;
200
201 for (j = 0; j < top->evlist->nr_entries; j++)
202 syme->count[j] = top->zero ? 0 : syme->count[j] * 7 / 8;
203 } else
204 perf_top__remove_active_sym(top, syme);
205 }
206
207 return sum_ksamples;
208}
209
210/*
211 * Find the longest symbol name that will be displayed
212 */
213void perf_top__find_widths(struct perf_top *top, struct rb_root *root,
214 int *dso_width, int *dso_short_width, int *sym_width)
215{
216 struct rb_node *nd;
217 int printed = 0;
218
219 *sym_width = *dso_width = *dso_short_width = 0;
220
221 for (nd = rb_first(root); nd; nd = rb_next(nd)) {
222 struct sym_entry *syme = rb_entry(nd, struct sym_entry, rb_node);
223 struct symbol *sym = sym_entry__symbol(syme);
224
225 if (++printed > top->print_entries ||
226 (int)syme->snap_count < top->count_filter)
227 continue;
228
229 if (syme->map->dso->long_name_len > *dso_width)
230 *dso_width = syme->map->dso->long_name_len;
231
232 if (syme->map->dso->short_name_len > *dso_short_width)
233 *dso_short_width = syme->map->dso->short_name_len;
234
235 if (sym->namelen > *sym_width)
236 *sym_width = sym->namelen;
237 }
238}
diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h
index bfbf95bcc603..01d1057f3074 100644
--- a/tools/perf/util/top.h
+++ b/tools/perf/util/top.h
@@ -4,26 +4,10 @@
4#include "types.h" 4#include "types.h"
5#include "../perf.h" 5#include "../perf.h"
6#include <stddef.h> 6#include <stddef.h>
7#include <pthread.h>
8#include <linux/list.h>
9#include <linux/rbtree.h>
10 7
11struct perf_evlist; 8struct perf_evlist;
12struct perf_evsel; 9struct perf_evsel;
13 10struct perf_session;
14struct sym_entry {
15 struct rb_node rb_node;
16 struct list_head node;
17 unsigned long snap_count;
18 double weight;
19 struct map *map;
20 unsigned long count[0];
21};
22
23static inline struct symbol *sym_entry__symbol(struct sym_entry *self)
24{
25 return ((void *)self) + symbol_conf.priv_size;
26}
27 11
28struct perf_top { 12struct perf_top {
29 struct perf_evlist *evlist; 13 struct perf_evlist *evlist;
@@ -31,34 +15,21 @@ struct perf_top {
31 * Symbols will be added here in perf_event__process_sample and will 15 * Symbols will be added here in perf_event__process_sample and will
32 * get out after decayed. 16 * get out after decayed.
33 */ 17 */
34 struct list_head active_symbols;
35 pthread_mutex_t active_symbols_lock;
36 pthread_cond_t active_symbols_cond;
37 u64 samples; 18 u64 samples;
38 u64 kernel_samples, us_samples; 19 u64 kernel_samples, us_samples;
39 u64 exact_samples; 20 u64 exact_samples;
40 u64 guest_us_samples, guest_kernel_samples; 21 u64 guest_us_samples, guest_kernel_samples;
22 u64 total_lost_warned;
41 int print_entries, count_filter, delay_secs; 23 int print_entries, count_filter, delay_secs;
42 int display_weighted, freq, rb_entries; 24 int freq;
43 pid_t target_pid, target_tid; 25 pid_t target_pid, target_tid;
44 bool hide_kernel_symbols, hide_user_symbols, zero; 26 bool hide_kernel_symbols, hide_user_symbols, zero;
45 const char *cpu_list; 27 const char *cpu_list;
46 struct sym_entry *sym_filter_entry; 28 struct hist_entry *sym_filter_entry;
47 struct perf_evsel *sym_evsel; 29 struct perf_evsel *sym_evsel;
30 struct perf_session *session;
48}; 31};
49 32
50size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size); 33size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size);
51void perf_top__reset_sample_counters(struct perf_top *top); 34void perf_top__reset_sample_counters(struct perf_top *top);
52float perf_top__decay_samples(struct perf_top *top, struct rb_root *root);
53void perf_top__find_widths(struct perf_top *top, struct rb_root *root,
54 int *dso_width, int *dso_short_width, int *sym_width);
55
56#ifdef NO_NEWT_SUPPORT
57static inline int perf_top__tui_browser(struct perf_top *top __used)
58{
59 return 0;
60}
61#else
62int perf_top__tui_browser(struct perf_top *top);
63#endif
64#endif /* __PERF_TOP_H */ 35#endif /* __PERF_TOP_H */
diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c
index 3403f814ad72..2d530cf74f43 100644
--- a/tools/perf/util/trace-event-info.c
+++ b/tools/perf/util/trace-event-info.c
@@ -196,7 +196,8 @@ static void record_file(const char *file, size_t hdr_sz)
196 die("Can't read '%s'", file); 196 die("Can't read '%s'", file);
197 197
198 /* put in zeros for file size, then fill true size later */ 198 /* put in zeros for file size, then fill true size later */
199 write_or_die(&size, hdr_sz); 199 if (hdr_sz)
200 write_or_die(&size, hdr_sz);
200 201
201 do { 202 do {
202 r = read(fd, buf, BUFSIZ); 203 r = read(fd, buf, BUFSIZ);
@@ -212,7 +213,7 @@ static void record_file(const char *file, size_t hdr_sz)
212 if (bigendian()) 213 if (bigendian())
213 sizep += sizeof(u64) - hdr_sz; 214 sizep += sizeof(u64) - hdr_sz;
214 215
215 if (pwrite(output_fd, sizep, hdr_sz, hdr_pos) < 0) 216 if (hdr_sz && pwrite(output_fd, sizep, hdr_sz, hdr_pos) < 0)
216 die("writing to %s", output_file); 217 die("writing to %s", output_file);
217} 218}
218 219
@@ -428,6 +429,19 @@ get_tracepoints_path(struct list_head *pattrs)
428 return nr_tracepoints > 0 ? path.next : NULL; 429 return nr_tracepoints > 0 ? path.next : NULL;
429} 430}
430 431
432static void
433put_tracepoints_path(struct tracepoint_path *tps)
434{
435 while (tps) {
436 struct tracepoint_path *t = tps;
437
438 tps = tps->next;
439 free(t->name);
440 free(t->system);
441 free(t);
442 }
443}
444
431bool have_tracepoints(struct list_head *pattrs) 445bool have_tracepoints(struct list_head *pattrs)
432{ 446{
433 struct perf_evsel *pos; 447 struct perf_evsel *pos;
@@ -439,19 +453,11 @@ bool have_tracepoints(struct list_head *pattrs)
439 return false; 453 return false;
440} 454}
441 455
442int read_tracing_data(int fd, struct list_head *pattrs) 456static void tracing_data_header(void)
443{ 457{
444 char buf[BUFSIZ]; 458 char buf[20];
445 struct tracepoint_path *tps = get_tracepoints_path(pattrs);
446
447 /*
448 * What? No tracepoints? No sense writing anything here, bail out.
449 */
450 if (tps == NULL)
451 return -1;
452
453 output_fd = fd;
454 459
460 /* just guessing this is someone's birthday.. ;) */
455 buf[0] = 23; 461 buf[0] = 23;
456 buf[1] = 8; 462 buf[1] = 8;
457 buf[2] = 68; 463 buf[2] = 68;
@@ -476,28 +482,86 @@ int read_tracing_data(int fd, struct list_head *pattrs)
476 /* save page_size */ 482 /* save page_size */
477 page_size = sysconf(_SC_PAGESIZE); 483 page_size = sysconf(_SC_PAGESIZE);
478 write_or_die(&page_size, 4); 484 write_or_die(&page_size, 4);
485}
486
487struct tracing_data *tracing_data_get(struct list_head *pattrs,
488 int fd, bool temp)
489{
490 struct tracepoint_path *tps;
491 struct tracing_data *tdata;
492
493 output_fd = fd;
494
495 tps = get_tracepoints_path(pattrs);
496 if (!tps)
497 return NULL;
479 498
499 tdata = malloc_or_die(sizeof(*tdata));
500 tdata->temp = temp;
501 tdata->size = 0;
502
503 if (temp) {
504 int temp_fd;
505
506 snprintf(tdata->temp_file, sizeof(tdata->temp_file),
507 "/tmp/perf-XXXXXX");
508 if (!mkstemp(tdata->temp_file))
509 die("Can't make temp file");
510
511 temp_fd = open(tdata->temp_file, O_RDWR);
512 if (temp_fd < 0)
513 die("Can't read '%s'", tdata->temp_file);
514
515 /*
516 * Set the temp file the default output, so all the
517 * tracing data are stored into it.
518 */
519 output_fd = temp_fd;
520 }
521
522 tracing_data_header();
480 read_header_files(); 523 read_header_files();
481 read_ftrace_files(tps); 524 read_ftrace_files(tps);
482 read_event_files(tps); 525 read_event_files(tps);
483 read_proc_kallsyms(); 526 read_proc_kallsyms();
484 read_ftrace_printk(); 527 read_ftrace_printk();
485 528
486 return 0; 529 /*
530 * All tracing data are stored by now, we can restore
531 * the default output file in case we used temp file.
532 */
533 if (temp) {
534 tdata->size = lseek(output_fd, 0, SEEK_CUR);
535 close(output_fd);
536 output_fd = fd;
537 }
538
539 put_tracepoints_path(tps);
540 return tdata;
487} 541}
488 542
489ssize_t read_tracing_data_size(int fd, struct list_head *pattrs) 543void tracing_data_put(struct tracing_data *tdata)
490{ 544{
491 ssize_t size; 545 if (tdata->temp) {
492 int err = 0; 546 record_file(tdata->temp_file, 0);
547 unlink(tdata->temp_file);
548 }
493 549
494 calc_data_size = 1; 550 free(tdata);
495 err = read_tracing_data(fd, pattrs); 551}
496 size = calc_data_size - 1;
497 calc_data_size = 0;
498 552
499 if (err < 0) 553int read_tracing_data(int fd, struct list_head *pattrs)
500 return err; 554{
555 struct tracing_data *tdata;
501 556
502 return size; 557 /*
558 * We work over the real file, so we can write data
559 * directly, no temp file is needed.
560 */
561 tdata = tracing_data_get(pattrs, fd, false);
562 if (!tdata)
563 return -ENOMEM;
564
565 tracing_data_put(tdata);
566 return 0;
503} 567}
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h
index f674dda3363b..a84100817649 100644
--- a/tools/perf/util/trace-event.h
+++ b/tools/perf/util/trace-event.h
@@ -263,7 +263,18 @@ void *raw_field_ptr(struct event *event, const char *name, void *data);
263unsigned long long eval_flag(const char *flag); 263unsigned long long eval_flag(const char *flag);
264 264
265int read_tracing_data(int fd, struct list_head *pattrs); 265int read_tracing_data(int fd, struct list_head *pattrs);
266ssize_t read_tracing_data_size(int fd, struct list_head *pattrs); 266
267struct tracing_data {
268 /* size is only valid if temp is 'true' */
269 ssize_t size;
270 bool temp;
271 char temp_file[50];
272};
273
274struct tracing_data *tracing_data_get(struct list_head *pattrs,
275 int fd, bool temp);
276void tracing_data_put(struct tracing_data *tdata);
277
267 278
268/* taken from kernel/trace/trace.h */ 279/* taken from kernel/trace/trace.h */
269enum trace_flag_type { 280enum trace_flag_type {
diff --git a/tools/perf/util/ui/browser.c b/tools/perf/util/ui/browser.c
index 611219f80680..5359f371d30a 100644
--- a/tools/perf/util/ui/browser.c
+++ b/tools/perf/util/ui/browser.c
@@ -1,4 +1,8 @@
1#include "../util.h"
2#include "../cache.h"
3#include "../../perf.h"
1#include "libslang.h" 4#include "libslang.h"
5#include <newt.h>
2#include "ui.h" 6#include "ui.h"
3#include <linux/compiler.h> 7#include <linux/compiler.h>
4#include <linux/list.h> 8#include <linux/list.h>
@@ -7,13 +11,13 @@
7#include <sys/ttydefaults.h> 11#include <sys/ttydefaults.h>
8#include "browser.h" 12#include "browser.h"
9#include "helpline.h" 13#include "helpline.h"
14#include "keysyms.h"
10#include "../color.h" 15#include "../color.h"
11#include "../util.h"
12#include <stdio.h>
13 16
14static int ui_browser__percent_color(double percent, bool current) 17static int ui_browser__percent_color(struct ui_browser *browser,
18 double percent, bool current)
15{ 19{
16 if (current) 20 if (current && (!browser->use_navkeypressed || browser->navkeypressed))
17 return HE_COLORSET_SELECTED; 21 return HE_COLORSET_SELECTED;
18 if (percent >= MIN_RED) 22 if (percent >= MIN_RED)
19 return HE_COLORSET_TOP; 23 return HE_COLORSET_TOP;
@@ -30,7 +34,7 @@ void ui_browser__set_color(struct ui_browser *self __used, int color)
30void ui_browser__set_percent_color(struct ui_browser *self, 34void ui_browser__set_percent_color(struct ui_browser *self,
31 double percent, bool current) 35 double percent, bool current)
32{ 36{
33 int color = ui_browser__percent_color(percent, current); 37 int color = ui_browser__percent_color(self, percent, current);
34 ui_browser__set_color(self, color); 38 ui_browser__set_color(self, color);
35} 39}
36 40
@@ -39,31 +43,62 @@ void ui_browser__gotorc(struct ui_browser *self, int y, int x)
39 SLsmg_gotorc(self->y + y, self->x + x); 43 SLsmg_gotorc(self->y + y, self->x + x);
40} 44}
41 45
46static struct list_head *
47ui_browser__list_head_filter_entries(struct ui_browser *browser,
48 struct list_head *pos)
49{
50 do {
51 if (!browser->filter || !browser->filter(browser, pos))
52 return pos;
53 pos = pos->next;
54 } while (pos != browser->entries);
55
56 return NULL;
57}
58
59static struct list_head *
60ui_browser__list_head_filter_prev_entries(struct ui_browser *browser,
61 struct list_head *pos)
62{
63 do {
64 if (!browser->filter || !browser->filter(browser, pos))
65 return pos;
66 pos = pos->prev;
67 } while (pos != browser->entries);
68
69 return NULL;
70}
71
42void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence) 72void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence)
43{ 73{
44 struct list_head *head = self->entries; 74 struct list_head *head = self->entries;
45 struct list_head *pos; 75 struct list_head *pos;
46 76
77 if (self->nr_entries == 0)
78 return;
79
47 switch (whence) { 80 switch (whence) {
48 case SEEK_SET: 81 case SEEK_SET:
49 pos = head->next; 82 pos = ui_browser__list_head_filter_entries(self, head->next);
50 break; 83 break;
51 case SEEK_CUR: 84 case SEEK_CUR:
52 pos = self->top; 85 pos = self->top;
53 break; 86 break;
54 case SEEK_END: 87 case SEEK_END:
55 pos = head->prev; 88 pos = ui_browser__list_head_filter_prev_entries(self, head->prev);
56 break; 89 break;
57 default: 90 default:
58 return; 91 return;
59 } 92 }
60 93
94 assert(pos != NULL);
95
61 if (offset > 0) { 96 if (offset > 0) {
62 while (offset-- != 0) 97 while (offset-- != 0)
63 pos = pos->next; 98 pos = ui_browser__list_head_filter_entries(self, pos->next);
64 } else { 99 } else {
65 while (offset++ != 0) 100 while (offset++ != 0)
66 pos = pos->prev; 101 pos = ui_browser__list_head_filter_prev_entries(self, pos->prev);
67 } 102 }
68 103
69 self->top = pos; 104 self->top = pos;
@@ -127,11 +162,8 @@ bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row)
127 162
128void ui_browser__refresh_dimensions(struct ui_browser *self) 163void ui_browser__refresh_dimensions(struct ui_browser *self)
129{ 164{
130 int cols, rows; 165 self->width = SLtt_Screen_Cols - 1;
131 newtGetScreenSize(&cols, &rows); 166 self->height = SLtt_Screen_Rows - 2;
132
133 self->width = cols - 1;
134 self->height = rows - 2;
135 self->y = 1; 167 self->y = 1;
136 self->x = 0; 168 self->x = 0;
137} 169}
@@ -142,26 +174,11 @@ void ui_browser__reset_index(struct ui_browser *self)
142 self->seek(self, 0, SEEK_SET); 174 self->seek(self, 0, SEEK_SET);
143} 175}
144 176
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) 177void __ui_browser__show_title(struct ui_browser *browser, const char *title)
161{ 178{
162 SLsmg_gotorc(0, 0); 179 SLsmg_gotorc(0, 0);
163 ui_browser__set_color(browser, NEWT_COLORSET_ROOT); 180 ui_browser__set_color(browser, NEWT_COLORSET_ROOT);
164 slsmg_write_nstring(title, browser->width); 181 slsmg_write_nstring(title, browser->width + 1);
165} 182}
166 183
167void ui_browser__show_title(struct ui_browser *browser, const char *title) 184void ui_browser__show_title(struct ui_browser *browser, const char *title)
@@ -174,78 +191,189 @@ void ui_browser__show_title(struct ui_browser *browser, const char *title)
174int ui_browser__show(struct ui_browser *self, const char *title, 191int ui_browser__show(struct ui_browser *self, const char *title,
175 const char *helpline, ...) 192 const char *helpline, ...)
176{ 193{
194 int err;
177 va_list ap; 195 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 196
185 ui_browser__refresh_dimensions(self); 197 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 198
196 pthread_mutex_lock(&ui__lock); 199 pthread_mutex_lock(&ui__lock);
197 __ui_browser__show_title(self, title); 200 __ui_browser__show_title(self, title);
198 201
199 ui_browser__add_exit_keys(self, keys); 202 self->title = title;
200 newtFormAddComponent(self->form, self->sb); 203 free(self->helpline);
204 self->helpline = NULL;
201 205
202 va_start(ap, helpline); 206 va_start(ap, helpline);
203 ui_helpline__vpush(helpline, ap); 207 err = vasprintf(&self->helpline, helpline, ap);
204 va_end(ap); 208 va_end(ap);
209 if (err > 0)
210 ui_helpline__push(self->helpline);
205 pthread_mutex_unlock(&ui__lock); 211 pthread_mutex_unlock(&ui__lock);
206 return 0; 212 return err ? 0 : -1;
207} 213}
208 214
209void ui_browser__hide(struct ui_browser *self) 215void ui_browser__hide(struct ui_browser *browser __used)
210{ 216{
211 pthread_mutex_lock(&ui__lock); 217 pthread_mutex_lock(&ui__lock);
212 newtFormDestroy(self->form);
213 self->form = NULL;
214 ui_helpline__pop(); 218 ui_helpline__pop();
215 pthread_mutex_unlock(&ui__lock); 219 pthread_mutex_unlock(&ui__lock);
216} 220}
217 221
218int ui_browser__refresh(struct ui_browser *self) 222static void ui_browser__scrollbar_set(struct ui_browser *browser)
223{
224 int height = browser->height, h = 0, pct = 0,
225 col = browser->width,
226 row = browser->y - 1;
227
228 if (browser->nr_entries > 1) {
229 pct = ((browser->index * (browser->height - 1)) /
230 (browser->nr_entries - 1));
231 }
232
233 while (h < height) {
234 ui_browser__gotorc(browser, row++, col);
235 SLsmg_set_char_set(1);
236 SLsmg_write_char(h == pct ? SLSMG_DIAMOND_CHAR : SLSMG_BOARD_CHAR);
237 SLsmg_set_char_set(0);
238 ++h;
239 }
240}
241
242static int __ui_browser__refresh(struct ui_browser *browser)
219{ 243{
220 int row; 244 int row;
245 int width = browser->width;
246
247 row = browser->refresh(browser);
248 ui_browser__set_color(browser, HE_COLORSET_NORMAL);
249
250 if (!browser->use_navkeypressed || browser->navkeypressed)
251 ui_browser__scrollbar_set(browser);
252 else
253 width += 1;
221 254
255 SLsmg_fill_region(browser->y + row, browser->x,
256 browser->height - row, width, ' ');
257
258 return 0;
259}
260
261int ui_browser__refresh(struct ui_browser *browser)
262{
222 pthread_mutex_lock(&ui__lock); 263 pthread_mutex_lock(&ui__lock);
223 newtScrollbarSet(self->sb, self->index, self->nr_entries - 1); 264 __ui_browser__refresh(browser);
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); 265 pthread_mutex_unlock(&ui__lock);
229 266
230 return 0; 267 return 0;
231} 268}
232 269
233int ui_browser__run(struct ui_browser *self) 270/*
271 * Here we're updating nr_entries _after_ we started browsing, i.e. we have to
272 * forget about any reference to any entry in the underlying data structure,
273 * that is why we do a SEEK_SET. Think about 'perf top' in the hists browser
274 * after an output_resort and hist decay.
275 */
276void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries)
234{ 277{
235 struct newtExitStruct es; 278 off_t offset = nr_entries - browser->nr_entries;
279
280 browser->nr_entries = nr_entries;
236 281
237 if (ui_browser__refresh(self) < 0) 282 if (offset < 0) {
238 return -1; 283 if (browser->top_idx < (u64)-offset)
284 offset = -browser->top_idx;
285
286 browser->index += offset;
287 browser->top_idx += offset;
288 }
289
290 browser->top = NULL;
291 browser->seek(browser, browser->top_idx, SEEK_SET);
292}
293
294static int ui__getch(int delay_secs)
295{
296 struct timeval timeout, *ptimeout = delay_secs ? &timeout : NULL;
297 fd_set read_set;
298 int err, key;
299
300 FD_ZERO(&read_set);
301 FD_SET(0, &read_set);
302
303 if (delay_secs) {
304 timeout.tv_sec = delay_secs;
305 timeout.tv_usec = 0;
306 }
307
308 err = select(1, &read_set, NULL, NULL, ptimeout);
309
310 if (err == 0)
311 return K_TIMER;
312
313 if (err == -1) {
314 if (errno == EINTR)
315 return K_RESIZE;
316 return K_ERROR;
317 }
318
319 key = SLang_getkey();
320 if (key != K_ESC)
321 return key;
322
323 FD_ZERO(&read_set);
324 FD_SET(0, &read_set);
325 timeout.tv_sec = 0;
326 timeout.tv_usec = 20;
327 err = select(1, &read_set, NULL, NULL, &timeout);
328 if (err == 0)
329 return K_ESC;
330
331 SLang_ungetkey(key);
332 return SLkp_getkey();
333}
334
335int ui_browser__run(struct ui_browser *self, int delay_secs)
336{
337 int err, key;
338
339 pthread__unblock_sigwinch();
239 340
240 while (1) { 341 while (1) {
241 off_t offset; 342 off_t offset;
242 343
243 newtFormRun(self->form, &es); 344 pthread_mutex_lock(&ui__lock);
244 345 err = __ui_browser__refresh(self);
245 if (es.reason != NEWT_EXIT_HOTKEY) 346 SLsmg_refresh();
347 pthread_mutex_unlock(&ui__lock);
348 if (err < 0)
246 break; 349 break;
247 switch (es.u.key) { 350
248 case NEWT_KEY_DOWN: 351 key = ui__getch(delay_secs);
352
353 if (key == K_RESIZE) {
354 pthread_mutex_lock(&ui__lock);
355 SLtt_get_screen_size();
356 SLsmg_reinit_smg();
357 pthread_mutex_unlock(&ui__lock);
358 ui_browser__refresh_dimensions(self);
359 __ui_browser__show_title(self, self->title);
360 ui_helpline__puts(self->helpline);
361 continue;
362 }
363
364 if (self->use_navkeypressed && !self->navkeypressed) {
365 if (key == K_DOWN || key == K_UP ||
366 key == K_PGDN || key == K_PGUP ||
367 key == K_HOME || key == K_END ||
368 key == ' ') {
369 self->navkeypressed = true;
370 continue;
371 } else
372 return key;
373 }
374
375 switch (key) {
376 case K_DOWN:
249 if (self->index == self->nr_entries - 1) 377 if (self->index == self->nr_entries - 1)
250 break; 378 break;
251 ++self->index; 379 ++self->index;
@@ -254,7 +382,7 @@ int ui_browser__run(struct ui_browser *self)
254 self->seek(self, +1, SEEK_CUR); 382 self->seek(self, +1, SEEK_CUR);
255 } 383 }
256 break; 384 break;
257 case NEWT_KEY_UP: 385 case K_UP:
258 if (self->index == 0) 386 if (self->index == 0)
259 break; 387 break;
260 --self->index; 388 --self->index;
@@ -263,7 +391,7 @@ int ui_browser__run(struct ui_browser *self)
263 self->seek(self, -1, SEEK_CUR); 391 self->seek(self, -1, SEEK_CUR);
264 } 392 }
265 break; 393 break;
266 case NEWT_KEY_PGDN: 394 case K_PGDN:
267 case ' ': 395 case ' ':
268 if (self->top_idx + self->height > self->nr_entries - 1) 396 if (self->top_idx + self->height > self->nr_entries - 1)
269 break; 397 break;
@@ -275,7 +403,7 @@ int ui_browser__run(struct ui_browser *self)
275 self->top_idx += offset; 403 self->top_idx += offset;
276 self->seek(self, +offset, SEEK_CUR); 404 self->seek(self, +offset, SEEK_CUR);
277 break; 405 break;
278 case NEWT_KEY_PGUP: 406 case K_PGUP:
279 if (self->top_idx == 0) 407 if (self->top_idx == 0)
280 break; 408 break;
281 409
@@ -288,10 +416,10 @@ int ui_browser__run(struct ui_browser *self)
288 self->top_idx -= offset; 416 self->top_idx -= offset;
289 self->seek(self, -offset, SEEK_CUR); 417 self->seek(self, -offset, SEEK_CUR);
290 break; 418 break;
291 case NEWT_KEY_HOME: 419 case K_HOME:
292 ui_browser__reset_index(self); 420 ui_browser__reset_index(self);
293 break; 421 break;
294 case NEWT_KEY_END: 422 case K_END:
295 offset = self->height - 1; 423 offset = self->height - 1;
296 if (offset >= self->nr_entries) 424 if (offset >= self->nr_entries)
297 offset = self->nr_entries - 1; 425 offset = self->nr_entries - 1;
@@ -301,10 +429,8 @@ int ui_browser__run(struct ui_browser *self)
301 self->seek(self, -offset, SEEK_END); 429 self->seek(self, -offset, SEEK_END);
302 break; 430 break;
303 default: 431 default:
304 return es.u.key; 432 return key;
305 } 433 }
306 if (ui_browser__refresh(self) < 0)
307 return -1;
308 } 434 }
309 return -1; 435 return -1;
310} 436}
@@ -316,41 +442,105 @@ unsigned int ui_browser__list_head_refresh(struct ui_browser *self)
316 int row = 0; 442 int row = 0;
317 443
318 if (self->top == NULL || self->top == self->entries) 444 if (self->top == NULL || self->top == self->entries)
319 self->top = head->next; 445 self->top = ui_browser__list_head_filter_entries(self, head->next);
320 446
321 pos = self->top; 447 pos = self->top;
322 448
323 list_for_each_from(pos, head) { 449 list_for_each_from(pos, head) {
324 ui_browser__gotorc(self, row, 0); 450 if (!self->filter || !self->filter(self, pos)) {
325 self->write(self, pos, row); 451 ui_browser__gotorc(self, row, 0);
326 if (++row == self->height) 452 self->write(self, pos, row);
327 break; 453 if (++row == self->height)
454 break;
455 }
328 } 456 }
329 457
330 return row; 458 return row;
331} 459}
332 460
333static struct newtPercentTreeColors { 461static struct ui_browser__colorset {
334 const char *topColorFg, *topColorBg; 462 const char *name, *fg, *bg;
335 const char *mediumColorFg, *mediumColorBg; 463 int colorset;
336 const char *normalColorFg, *normalColorBg; 464} ui_browser__colorsets[] = {
337 const char *selColorFg, *selColorBg; 465 {
338 const char *codeColorFg, *codeColorBg; 466 .colorset = HE_COLORSET_TOP,
339} defaultPercentTreeColors = { 467 .name = "top",
340 "red", "lightgray", 468 .fg = "red",
341 "green", "lightgray", 469 .bg = "default",
342 "black", "lightgray", 470 },
343 "lightgray", "magenta", 471 {
344 "blue", "lightgray", 472 .colorset = HE_COLORSET_MEDIUM,
473 .name = "medium",
474 .fg = "green",
475 .bg = "default",
476 },
477 {
478 .colorset = HE_COLORSET_NORMAL,
479 .name = "normal",
480 .fg = "default",
481 .bg = "default",
482 },
483 {
484 .colorset = HE_COLORSET_SELECTED,
485 .name = "selected",
486 .fg = "black",
487 .bg = "lightgray",
488 },
489 {
490 .colorset = HE_COLORSET_CODE,
491 .name = "code",
492 .fg = "blue",
493 .bg = "default",
494 },
495 {
496 .name = NULL,
497 }
345}; 498};
346 499
500
501static int ui_browser__color_config(const char *var, const char *value,
502 void *data __used)
503{
504 char *fg = NULL, *bg;
505 int i;
506
507 /* same dir for all commands */
508 if (prefixcmp(var, "colors.") != 0)
509 return 0;
510
511 for (i = 0; ui_browser__colorsets[i].name != NULL; ++i) {
512 const char *name = var + 7;
513
514 if (strcmp(ui_browser__colorsets[i].name, name) != 0)
515 continue;
516
517 fg = strdup(value);
518 if (fg == NULL)
519 break;
520
521 bg = strchr(fg, ',');
522 if (bg == NULL)
523 break;
524
525 *bg = '\0';
526 while (isspace(*++bg));
527 ui_browser__colorsets[i].bg = bg;
528 ui_browser__colorsets[i].fg = fg;
529 return 0;
530 }
531
532 free(fg);
533 return -1;
534}
535
347void ui_browser__init(void) 536void ui_browser__init(void)
348{ 537{
349 struct newtPercentTreeColors *c = &defaultPercentTreeColors; 538 int i = 0;
350 539
351 sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg); 540 perf_config(ui_browser__color_config, NULL);
352 sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg); 541
353 sltt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg); 542 while (ui_browser__colorsets[i].name) {
354 sltt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg); 543 struct ui_browser__colorset *c = &ui_browser__colorsets[i++];
355 sltt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg); 544 sltt_set_color(c->colorset, c->name, c->fg, c->bg);
545 }
356} 546}
diff --git a/tools/perf/util/ui/browser.h b/tools/perf/util/ui/browser.h
index fc63dda10910..a2c707d33c5e 100644
--- a/tools/perf/util/ui/browser.h
+++ b/tools/perf/util/ui/browser.h
@@ -2,7 +2,6 @@
2#define _PERF_UI_BROWSER_H_ 1 2#define _PERF_UI_BROWSER_H_ 1
3 3
4#include <stdbool.h> 4#include <stdbool.h>
5#include <newt.h>
6#include <sys/types.h> 5#include <sys/types.h>
7#include "../types.h" 6#include "../types.h"
8 7
@@ -13,15 +12,19 @@
13#define HE_COLORSET_CODE 54 12#define HE_COLORSET_CODE 54
14 13
15struct ui_browser { 14struct ui_browser {
16 newtComponent form, sb;
17 u64 index, top_idx; 15 u64 index, top_idx;
18 void *top, *entries; 16 void *top, *entries;
19 u16 y, x, width, height; 17 u16 y, x, width, height;
20 void *priv; 18 void *priv;
19 const char *title;
20 char *helpline;
21 unsigned int (*refresh)(struct ui_browser *self); 21 unsigned int (*refresh)(struct ui_browser *self);
22 void (*write)(struct ui_browser *self, void *entry, int row); 22 void (*write)(struct ui_browser *self, void *entry, int row);
23 void (*seek)(struct ui_browser *self, off_t offset, int whence); 23 void (*seek)(struct ui_browser *self, off_t offset, int whence);
24 bool (*filter)(struct ui_browser *self, void *entry);
24 u32 nr_entries; 25 u32 nr_entries;
26 bool navkeypressed;
27 bool use_navkeypressed;
25}; 28};
26 29
27void ui_browser__set_color(struct ui_browser *self, int color); 30void ui_browser__set_color(struct ui_browser *self, int color);
@@ -32,15 +35,14 @@ void ui_browser__refresh_dimensions(struct ui_browser *self);
32void ui_browser__reset_index(struct ui_browser *self); 35void ui_browser__reset_index(struct ui_browser *self);
33 36
34void ui_browser__gotorc(struct ui_browser *self, int y, int x); 37void 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);
38void ui_browser__show_title(struct ui_browser *browser, const char *title); 39void ui_browser__show_title(struct ui_browser *browser, const char *title);
39int ui_browser__show(struct ui_browser *self, const char *title, 40int ui_browser__show(struct ui_browser *self, const char *title,
40 const char *helpline, ...); 41 const char *helpline, ...);
41void ui_browser__hide(struct ui_browser *self); 42void ui_browser__hide(struct ui_browser *self);
42int ui_browser__refresh(struct ui_browser *self); 43int ui_browser__refresh(struct ui_browser *self);
43int ui_browser__run(struct ui_browser *self); 44int ui_browser__run(struct ui_browser *browser, int delay_secs);
45void ui_browser__update_nr_entries(struct ui_browser *browser, u32 nr_entries);
44 46
45void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence); 47void 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); 48unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self);
diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c
index 0229723aceb3..4e0cb7fea7d9 100644
--- a/tools/perf/util/ui/browsers/annotate.c
+++ b/tools/perf/util/ui/browsers/annotate.c
@@ -6,6 +6,7 @@
6#include "../../sort.h" 6#include "../../sort.h"
7#include "../../symbol.h" 7#include "../../symbol.h"
8#include <pthread.h> 8#include <pthread.h>
9#include <newt.h>
9 10
10static void ui__error_window(const char *fmt, ...) 11static void ui__error_window(const char *fmt, ...)
11{ 12{
@@ -20,12 +21,17 @@ struct annotate_browser {
20 struct ui_browser b; 21 struct ui_browser b;
21 struct rb_root entries; 22 struct rb_root entries;
22 struct rb_node *curr_hot; 23 struct rb_node *curr_hot;
24 struct objdump_line *selection;
25 int nr_asm_entries;
26 int nr_entries;
27 bool hide_src_code;
23}; 28};
24 29
25struct objdump_line_rb_node { 30struct objdump_line_rb_node {
26 struct rb_node rb_node; 31 struct rb_node rb_node;
27 double percent; 32 double percent;
28 u32 idx; 33 u32 idx;
34 int idx_asm;
29}; 35};
30 36
31static inline 37static inline
@@ -34,9 +40,22 @@ struct objdump_line_rb_node *objdump_line__rb(struct objdump_line *self)
34 return (struct objdump_line_rb_node *)(self + 1); 40 return (struct objdump_line_rb_node *)(self + 1);
35} 41}
36 42
43static bool objdump_line__filter(struct ui_browser *browser, void *entry)
44{
45 struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
46
47 if (ab->hide_src_code) {
48 struct objdump_line *ol = list_entry(entry, struct objdump_line, node);
49 return ol->offset == -1;
50 }
51
52 return false;
53}
54
37static void annotate_browser__write(struct ui_browser *self, void *entry, int row) 55static void annotate_browser__write(struct ui_browser *self, void *entry, int row)
38{ 56{
39 struct objdump_line *ol = rb_entry(entry, struct objdump_line, node); 57 struct annotate_browser *ab = container_of(self, struct annotate_browser, b);
58 struct objdump_line *ol = list_entry(entry, struct objdump_line, node);
40 bool current_entry = ui_browser__is_current_entry(self, row); 59 bool current_entry = ui_browser__is_current_entry(self, row);
41 int width = self->width; 60 int width = self->width;
42 61
@@ -51,6 +70,11 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro
51 70
52 SLsmg_write_char(':'); 71 SLsmg_write_char(':');
53 slsmg_write_nstring(" ", 8); 72 slsmg_write_nstring(" ", 8);
73
74 /* The scroll bar isn't being used */
75 if (!self->navkeypressed)
76 width += 1;
77
54 if (!*ol->line) 78 if (!*ol->line)
55 slsmg_write_nstring(" ", width - 18); 79 slsmg_write_nstring(" ", width - 18);
56 else 80 else
@@ -58,6 +82,8 @@ static void annotate_browser__write(struct ui_browser *self, void *entry, int ro
58 82
59 if (!current_entry) 83 if (!current_entry)
60 ui_browser__set_color(self, HE_COLORSET_CODE); 84 ui_browser__set_color(self, HE_COLORSET_CODE);
85 else
86 ab->selection = ol;
61} 87}
62 88
63static double objdump_line__calc_percent(struct objdump_line *self, 89static double objdump_line__calc_percent(struct objdump_line *self,
@@ -141,7 +167,8 @@ static void annotate_browser__set_top(struct annotate_browser *self,
141static void annotate_browser__calc_percent(struct annotate_browser *browser, 167static void annotate_browser__calc_percent(struct annotate_browser *browser,
142 int evidx) 168 int evidx)
143{ 169{
144 struct symbol *sym = browser->b.priv; 170 struct map_symbol *ms = browser->b.priv;
171 struct symbol *sym = ms->sym;
145 struct annotation *notes = symbol__annotation(sym); 172 struct annotation *notes = symbol__annotation(sym);
146 struct objdump_line *pos; 173 struct objdump_line *pos;
147 174
@@ -163,25 +190,60 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser,
163 browser->curr_hot = rb_last(&browser->entries); 190 browser->curr_hot = rb_last(&browser->entries);
164} 191}
165 192
193static bool annotate_browser__toggle_source(struct annotate_browser *browser)
194{
195 struct objdump_line *ol;
196 struct objdump_line_rb_node *olrb;
197 off_t offset = browser->b.index - browser->b.top_idx;
198
199 browser->b.seek(&browser->b, offset, SEEK_CUR);
200 ol = list_entry(browser->b.top, struct objdump_line, node);
201 olrb = objdump_line__rb(ol);
202
203 if (browser->hide_src_code) {
204 if (olrb->idx_asm < offset)
205 offset = olrb->idx;
206
207 browser->b.nr_entries = browser->nr_entries;
208 browser->hide_src_code = false;
209 browser->b.seek(&browser->b, -offset, SEEK_CUR);
210 browser->b.top_idx = olrb->idx - offset;
211 browser->b.index = olrb->idx;
212 } else {
213 if (olrb->idx_asm < 0) {
214 ui_helpline__puts("Only available for assembly lines.");
215 browser->b.seek(&browser->b, -offset, SEEK_CUR);
216 return false;
217 }
218
219 if (olrb->idx_asm < offset)
220 offset = olrb->idx_asm;
221
222 browser->b.nr_entries = browser->nr_asm_entries;
223 browser->hide_src_code = true;
224 browser->b.seek(&browser->b, -offset, SEEK_CUR);
225 browser->b.top_idx = olrb->idx_asm - offset;
226 browser->b.index = olrb->idx_asm;
227 }
228
229 return true;
230}
231
166static int annotate_browser__run(struct annotate_browser *self, int evidx, 232static int annotate_browser__run(struct annotate_browser *self, int evidx,
167 int refresh) 233 int nr_events, void(*timer)(void *arg),
234 void *arg, int delay_secs)
168{ 235{
169 struct rb_node *nd = NULL; 236 struct rb_node *nd = NULL;
170 struct symbol *sym = self->b.priv; 237 struct map_symbol *ms = self->b.priv;
171 /* 238 struct symbol *sym = ms->sym;
172 * RIGHT To allow builtin-annotate to cycle thru multiple symbols by 239 const char *help = "<-, ESC: exit, TAB/shift+TAB: cycle hottest lines, "
173 * examining the exit key for this function. 240 "H: Hottest, -> Line action, S -> Toggle source "
174 */ 241 "code view";
175 int exit_keys[] = { 'H', NEWT_KEY_TAB, NEWT_KEY_UNTAB,
176 NEWT_KEY_RIGHT, 0 };
177 int key; 242 int key;
178 243
179 if (ui_browser__show(&self->b, sym->name, 244 if (ui_browser__show(&self->b, sym->name, help) < 0)
180 "<-, -> or ESC: exit, TAB/shift+TAB: "
181 "cycle hottest lines, H: Hottest") < 0)
182 return -1; 245 return -1;
183 246
184 ui_browser__add_exit_keys(&self->b, exit_keys);
185 annotate_browser__calc_percent(self, evidx); 247 annotate_browser__calc_percent(self, evidx);
186 248
187 if (self->curr_hot) 249 if (self->curr_hot)
@@ -189,13 +251,10 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx,
189 251
190 nd = self->curr_hot; 252 nd = self->curr_hot;
191 253
192 if (refresh != 0)
193 newtFormSetTimer(self->b.form, refresh);
194
195 while (1) { 254 while (1) {
196 key = ui_browser__run(&self->b); 255 key = ui_browser__run(&self->b, delay_secs);
197 256
198 if (refresh != 0) { 257 if (delay_secs != 0) {
199 annotate_browser__calc_percent(self, evidx); 258 annotate_browser__calc_percent(self, evidx);
200 /* 259 /*
201 * Current line focus got out of the list of most active 260 * Current line focus got out of the list of most active
@@ -207,15 +266,14 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx,
207 } 266 }
208 267
209 switch (key) { 268 switch (key) {
210 case -1: 269 case K_TIMER:
211 /* 270 if (timer != NULL)
212 * FIXME we need to check if it was 271 timer(arg);
213 * es.reason == NEWT_EXIT_TIMER 272
214 */ 273 if (delay_secs != 0)
215 if (refresh != 0)
216 symbol__annotate_decay_histogram(sym, evidx); 274 symbol__annotate_decay_histogram(sym, evidx);
217 continue; 275 continue;
218 case NEWT_KEY_TAB: 276 case K_TAB:
219 if (nd != NULL) { 277 if (nd != NULL) {
220 nd = rb_prev(nd); 278 nd = rb_prev(nd);
221 if (nd == NULL) 279 if (nd == NULL)
@@ -223,7 +281,7 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx,
223 } else 281 } else
224 nd = self->curr_hot; 282 nd = self->curr_hot;
225 break; 283 break;
226 case NEWT_KEY_UNTAB: 284 case K_UNTAB:
227 if (nd != NULL) 285 if (nd != NULL)
228 nd = rb_next(nd); 286 nd = rb_next(nd);
229 if (nd == NULL) 287 if (nd == NULL)
@@ -234,8 +292,68 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx,
234 case 'H': 292 case 'H':
235 nd = self->curr_hot; 293 nd = self->curr_hot;
236 break; 294 break;
237 default: 295 case 'S':
296 if (annotate_browser__toggle_source(self))
297 ui_helpline__puts(help);
298 continue;
299 case K_ENTER:
300 case K_RIGHT:
301 if (self->selection == NULL) {
302 ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
303 continue;
304 }
305
306 if (self->selection->offset == -1) {
307 ui_helpline__puts("Actions are only available for assembly lines.");
308 continue;
309 } else {
310 char *s = strstr(self->selection->line, "callq ");
311 struct annotation *notes;
312 struct symbol *target;
313 u64 ip;
314
315 if (s == NULL) {
316 ui_helpline__puts("Actions are only available for the 'callq' instruction.");
317 continue;
318 }
319
320 s = strchr(s, ' ');
321 if (s++ == NULL) {
322 ui_helpline__puts("Invallid callq instruction.");
323 continue;
324 }
325
326 ip = strtoull(s, NULL, 16);
327 ip = ms->map->map_ip(ms->map, ip);
328 target = map__find_symbol(ms->map, ip, NULL);
329 if (target == NULL) {
330 ui_helpline__puts("The called function was not found.");
331 continue;
332 }
333
334 notes = symbol__annotation(target);
335 pthread_mutex_lock(&notes->lock);
336
337 if (notes->src == NULL &&
338 symbol__alloc_hist(target, nr_events) < 0) {
339 pthread_mutex_unlock(&notes->lock);
340 ui__warning("Not enough memory for annotating '%s' symbol!\n",
341 target->name);
342 continue;
343 }
344
345 pthread_mutex_unlock(&notes->lock);
346 symbol__tui_annotate(target, ms->map, evidx, nr_events,
347 timer, arg, delay_secs);
348 }
349 continue;
350 case K_LEFT:
351 case K_ESC:
352 case 'q':
353 case CTRL('c'):
238 goto out; 354 goto out;
355 default:
356 continue;
239 } 357 }
240 358
241 if (nd != NULL) 359 if (nd != NULL)
@@ -246,22 +364,31 @@ out:
246 return key; 364 return key;
247} 365}
248 366
249int hist_entry__tui_annotate(struct hist_entry *he, int evidx) 367int hist_entry__tui_annotate(struct hist_entry *he, int evidx, int nr_events,
368 void(*timer)(void *arg), void *arg, int delay_secs)
250{ 369{
251 return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, 0); 370 return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, nr_events,
371 timer, arg, delay_secs);
252} 372}
253 373
254int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx, 374int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
255 int refresh) 375 int nr_events, void(*timer)(void *arg), void *arg,
376 int delay_secs)
256{ 377{
257 struct objdump_line *pos, *n; 378 struct objdump_line *pos, *n;
258 struct annotation *notes; 379 struct annotation *notes;
380 struct map_symbol ms = {
381 .map = map,
382 .sym = sym,
383 };
259 struct annotate_browser browser = { 384 struct annotate_browser browser = {
260 .b = { 385 .b = {
261 .refresh = ui_browser__list_head_refresh, 386 .refresh = ui_browser__list_head_refresh,
262 .seek = ui_browser__list_head_seek, 387 .seek = ui_browser__list_head_seek,
263 .write = annotate_browser__write, 388 .write = annotate_browser__write,
264 .priv = sym, 389 .filter = objdump_line__filter,
390 .priv = &ms,
391 .use_navkeypressed = true,
265 }, 392 },
266 }; 393 };
267 int ret; 394 int ret;
@@ -288,12 +415,18 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
288 if (browser.b.width < line_len) 415 if (browser.b.width < line_len)
289 browser.b.width = line_len; 416 browser.b.width = line_len;
290 rbpos = objdump_line__rb(pos); 417 rbpos = objdump_line__rb(pos);
291 rbpos->idx = browser.b.nr_entries++; 418 rbpos->idx = browser.nr_entries++;
419 if (pos->offset != -1)
420 rbpos->idx_asm = browser.nr_asm_entries++;
421 else
422 rbpos->idx_asm = -1;
292 } 423 }
293 424
425 browser.b.nr_entries = browser.nr_entries;
294 browser.b.entries = &notes->src->source, 426 browser.b.entries = &notes->src->source,
295 browser.b.width += 18; /* Percentage */ 427 browser.b.width += 18; /* Percentage */
296 ret = annotate_browser__run(&browser, evidx, refresh); 428 ret = annotate_browser__run(&browser, evidx, nr_events,
429 timer, arg, delay_secs);
297 list_for_each_entry_safe(pos, n, &notes->src->source, node) { 430 list_for_each_entry_safe(pos, n, &notes->src->source, node) {
298 list_del(&pos->node); 431 list_del(&pos->node);
299 objdump_line__free(pos); 432 objdump_line__free(pos);
diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c
index 5d767c622dfc..4663dcb2a19b 100644
--- a/tools/perf/util/ui/browsers/hists.c
+++ b/tools/perf/util/ui/browsers/hists.c
@@ -24,8 +24,12 @@ struct hist_browser {
24 struct hists *hists; 24 struct hists *hists;
25 struct hist_entry *he_selection; 25 struct hist_entry *he_selection;
26 struct map_symbol *selection; 26 struct map_symbol *selection;
27 bool has_symbols;
27}; 28};
28 29
30static int hists__browser_title(struct hists *self, char *bf, size_t size,
31 const char *ev_name);
32
29static void hist_browser__refresh_dimensions(struct hist_browser *self) 33static void hist_browser__refresh_dimensions(struct hist_browser *self)
30{ 34{
31 /* 3 == +/- toggle symbol before actual hist_entry rendering */ 35 /* 3 == +/- toggle symbol before actual hist_entry rendering */
@@ -290,28 +294,34 @@ static void hist_browser__set_folding(struct hist_browser *self, bool unfold)
290 ui_browser__reset_index(&self->b); 294 ui_browser__reset_index(&self->b);
291} 295}
292 296
293static int hist_browser__run(struct hist_browser *self, const char *title) 297static int hist_browser__run(struct hist_browser *self, const char *ev_name,
298 void(*timer)(void *arg), void *arg, int delay_secs)
294{ 299{
295 int key; 300 int key;
296 int exit_keys[] = { 'a', '?', 'h', 'C', 'd', 'D', 'E', 't', 301 char title[160];
297 NEWT_KEY_ENTER, NEWT_KEY_RIGHT, NEWT_KEY_LEFT,
298 NEWT_KEY_TAB, NEWT_KEY_UNTAB, 0, };
299 302
300 self->b.entries = &self->hists->entries; 303 self->b.entries = &self->hists->entries;
301 self->b.nr_entries = self->hists->nr_entries; 304 self->b.nr_entries = self->hists->nr_entries;
302 305
303 hist_browser__refresh_dimensions(self); 306 hist_browser__refresh_dimensions(self);
307 hists__browser_title(self->hists, title, sizeof(title), ev_name);
304 308
305 if (ui_browser__show(&self->b, title, 309 if (ui_browser__show(&self->b, title,
306 "Press '?' for help on key bindings") < 0) 310 "Press '?' for help on key bindings") < 0)
307 return -1; 311 return -1;
308 312
309 ui_browser__add_exit_keys(&self->b, exit_keys);
310
311 while (1) { 313 while (1) {
312 key = ui_browser__run(&self->b); 314 key = ui_browser__run(&self->b, delay_secs);
313 315
314 switch (key) { 316 switch (key) {
317 case -1:
318 /* FIXME we need to check if it was es.reason == NEWT_EXIT_TIMER */
319 timer(arg);
320 ui_browser__update_nr_entries(&self->b, self->hists->nr_entries);
321 hists__browser_title(self->hists, title, sizeof(title),
322 ev_name);
323 ui_browser__show_title(&self->b, title);
324 continue;
315 case 'D': { /* Debug */ 325 case 'D': { /* Debug */
316 static int seq; 326 static int seq;
317 struct hist_entry *h = rb_entry(self->b.top, 327 struct hist_entry *h = rb_entry(self->b.top,
@@ -334,7 +344,7 @@ static int hist_browser__run(struct hist_browser *self, const char *title)
334 /* Expand the whole world. */ 344 /* Expand the whole world. */
335 hist_browser__set_folding(self, true); 345 hist_browser__set_folding(self, true);
336 break; 346 break;
337 case NEWT_KEY_ENTER: 347 case K_ENTER:
338 if (hist_browser__toggle_fold(self)) 348 if (hist_browser__toggle_fold(self))
339 break; 349 break;
340 /* fall thru */ 350 /* fall thru */
@@ -532,7 +542,7 @@ static int hist_browser__show_entry(struct hist_browser *self,
532 char s[256]; 542 char s[256];
533 double percent; 543 double percent;
534 int printed = 0; 544 int printed = 0;
535 int color, width = self->b.width; 545 int width = self->b.width - 6; /* The percentage */
536 char folded_sign = ' '; 546 char folded_sign = ' ';
537 bool current_entry = ui_browser__is_current_entry(&self->b, row); 547 bool current_entry = ui_browser__is_current_entry(&self->b, row);
538 off_t row_offset = entry->row_offset; 548 off_t row_offset = entry->row_offset;
@@ -548,26 +558,35 @@ static int hist_browser__show_entry(struct hist_browser *self,
548 } 558 }
549 559
550 if (row_offset == 0) { 560 if (row_offset == 0) {
551 hist_entry__snprintf(entry, s, sizeof(s), self->hists, NULL, false, 561 hist_entry__snprintf(entry, s, sizeof(s), self->hists);
552 0, false, self->hists->stats.total_period);
553 percent = (entry->period * 100.0) / self->hists->stats.total_period; 562 percent = (entry->period * 100.0) / self->hists->stats.total_period;
554 563
555 color = HE_COLORSET_SELECTED; 564 ui_browser__set_percent_color(&self->b, percent, current_entry);
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); 565 ui_browser__gotorc(&self->b, row, 0);
567 if (symbol_conf.use_callchain) { 566 if (symbol_conf.use_callchain) {
568 slsmg_printf("%c ", folded_sign); 567 slsmg_printf("%c ", folded_sign);
569 width -= 2; 568 width -= 2;
570 } 569 }
570
571 slsmg_printf(" %5.2f%%", percent);
572
573 /* The scroll bar isn't being used */
574 if (!self->b.navkeypressed)
575 width += 1;
576
577 if (!current_entry || !self->b.navkeypressed)
578 ui_browser__set_color(&self->b, HE_COLORSET_NORMAL);
579
580 if (symbol_conf.show_nr_samples) {
581 slsmg_printf(" %11u", entry->nr_events);
582 width -= 12;
583 }
584
585 if (symbol_conf.show_total_period) {
586 slsmg_printf(" %12" PRIu64, entry->period);
587 width -= 13;
588 }
589
571 slsmg_write_nstring(s, width); 590 slsmg_write_nstring(s, width);
572 ++row; 591 ++row;
573 ++printed; 592 ++printed;
@@ -585,14 +604,23 @@ static int hist_browser__show_entry(struct hist_browser *self,
585 return printed; 604 return printed;
586} 605}
587 606
607static void ui_browser__hists_init_top(struct ui_browser *browser)
608{
609 if (browser->top == NULL) {
610 struct hist_browser *hb;
611
612 hb = container_of(browser, struct hist_browser, b);
613 browser->top = rb_first(&hb->hists->entries);
614 }
615}
616
588static unsigned int hist_browser__refresh(struct ui_browser *self) 617static unsigned int hist_browser__refresh(struct ui_browser *self)
589{ 618{
590 unsigned row = 0; 619 unsigned row = 0;
591 struct rb_node *nd; 620 struct rb_node *nd;
592 struct hist_browser *hb = container_of(self, struct hist_browser, b); 621 struct hist_browser *hb = container_of(self, struct hist_browser, b);
593 622
594 if (self->top == NULL) 623 ui_browser__hists_init_top(self);
595 self->top = rb_first(&hb->hists->entries);
596 624
597 for (nd = self->top; nd; nd = rb_next(nd)) { 625 for (nd = self->top; nd; nd = rb_next(nd)) {
598 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 626 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
@@ -644,6 +672,8 @@ static void ui_browser__hists_seek(struct ui_browser *self,
644 if (self->nr_entries == 0) 672 if (self->nr_entries == 0)
645 return; 673 return;
646 674
675 ui_browser__hists_init_top(self);
676
647 switch (whence) { 677 switch (whence) {
648 case SEEK_SET: 678 case SEEK_SET:
649 nd = hists__filter_entries(rb_first(self->entries)); 679 nd = hists__filter_entries(rb_first(self->entries));
@@ -761,6 +791,8 @@ static struct hist_browser *hist_browser__new(struct hists *hists)
761 self->hists = hists; 791 self->hists = hists;
762 self->b.refresh = hist_browser__refresh; 792 self->b.refresh = hist_browser__refresh;
763 self->b.seek = ui_browser__hists_seek; 793 self->b.seek = ui_browser__hists_seek;
794 self->b.use_navkeypressed = true,
795 self->has_symbols = sort_sym.list.next != NULL;
764 } 796 }
765 797
766 return self; 798 return self;
@@ -782,11 +814,12 @@ static struct thread *hist_browser__selected_thread(struct hist_browser *self)
782} 814}
783 815
784static int hists__browser_title(struct hists *self, char *bf, size_t size, 816static int hists__browser_title(struct hists *self, char *bf, size_t size,
785 const char *ev_name, const struct dso *dso, 817 const char *ev_name)
786 const struct thread *thread)
787{ 818{
788 char unit; 819 char unit;
789 int printed; 820 int printed;
821 const struct dso *dso = self->dso_filter;
822 const struct thread *thread = self->thread_filter;
790 unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE]; 823 unsigned long nr_events = self->stats.nr_events[PERF_RECORD_SAMPLE];
791 824
792 nr_events = convert_unit(nr_events, &unit); 825 nr_events = convert_unit(nr_events, &unit);
@@ -803,16 +836,15 @@ static int hists__browser_title(struct hists *self, char *bf, size_t size,
803 return printed; 836 return printed;
804} 837}
805 838
806static int perf_evsel__hists_browse(struct perf_evsel *evsel, 839static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
807 const char *helpline, const char *ev_name, 840 const char *helpline, const char *ev_name,
808 bool left_exits) 841 bool left_exits,
842 void(*timer)(void *arg), void *arg,
843 int delay_secs)
809{ 844{
810 struct hists *self = &evsel->hists; 845 struct hists *self = &evsel->hists;
811 struct hist_browser *browser = hist_browser__new(self); 846 struct hist_browser *browser = hist_browser__new(self);
812 struct pstack *fstack; 847 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; 848 int key = -1;
817 849
818 if (browser == NULL) 850 if (browser == NULL)
@@ -824,8 +856,6 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel,
824 856
825 ui_helpline__push(helpline); 857 ui_helpline__push(helpline);
826 858
827 hists__browser_title(self, msg, sizeof(msg), ev_name,
828 dso_filter, thread_filter);
829 while (1) { 859 while (1) {
830 const struct thread *thread = NULL; 860 const struct thread *thread = NULL;
831 const struct dso *dso = NULL; 861 const struct dso *dso = NULL;
@@ -834,7 +864,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel,
834 annotate = -2, zoom_dso = -2, zoom_thread = -2, 864 annotate = -2, zoom_dso = -2, zoom_thread = -2,
835 browse_map = -2; 865 browse_map = -2;
836 866
837 key = hist_browser__run(browser, msg); 867 key = hist_browser__run(browser, ev_name, timer, arg, delay_secs);
838 868
839 if (browser->he_selection != NULL) { 869 if (browser->he_selection != NULL) {
840 thread = hist_browser__selected_thread(browser); 870 thread = hist_browser__selected_thread(browser);
@@ -842,14 +872,23 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel,
842 } 872 }
843 873
844 switch (key) { 874 switch (key) {
845 case NEWT_KEY_TAB: 875 case K_TAB:
846 case NEWT_KEY_UNTAB: 876 case K_UNTAB:
877 if (nr_events == 1)
878 continue;
847 /* 879 /*
848 * Exit the browser, let hists__browser_tree 880 * Exit the browser, let hists__browser_tree
849 * go to the next or previous 881 * go to the next or previous
850 */ 882 */
851 goto out_free_stack; 883 goto out_free_stack;
852 case 'a': 884 case 'a':
885 if (!browser->has_symbols) {
886 ui__warning(
887 "Annotation is only available for symbolic views, "
888 "include \"sym\" in --sort to use it.");
889 continue;
890 }
891
853 if (browser->selection == NULL || 892 if (browser->selection == NULL ||
854 browser->selection->sym == NULL || 893 browser->selection->sym == NULL ||
855 browser->selection->map->dso->annotate_warned) 894 browser->selection->map->dso->annotate_warned)
@@ -859,25 +898,29 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel,
859 goto zoom_dso; 898 goto zoom_dso;
860 case 't': 899 case 't':
861 goto zoom_thread; 900 goto zoom_thread;
862 case NEWT_KEY_F1: 901 case K_F1:
863 case 'h': 902 case 'h':
864 case '?': 903 case '?':
865 ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n" 904 ui__help_window("h/?/F1 Show this window\n"
866 "<- Zoom out\n" 905 "UP/DOWN/PGUP\n"
867 "a Annotate current symbol\n" 906 "PGDN/SPACE Navigate\n"
868 "h/?/F1 Show this window\n" 907 "q/ESC/CTRL+C Exit browser\n\n"
869 "C Collapse all callchains\n" 908 "For multiple event sessions:\n\n"
870 "E Expand all callchains\n" 909 "TAB/UNTAB Switch events\n\n"
871 "d Zoom into current DSO\n" 910 "For symbolic views (--sort has sym):\n\n"
872 "t Zoom into current Thread\n" 911 "-> Zoom into DSO/Threads & Annotate current symbol\n"
873 "TAB/UNTAB Switch events\n" 912 "<- Zoom out\n"
874 "q/CTRL+C Exit browser"); 913 "a Annotate current symbol\n"
914 "C Collapse all callchains\n"
915 "E Expand all callchains\n"
916 "d Zoom into current DSO\n"
917 "t Zoom into current Thread\n");
875 continue; 918 continue;
876 case NEWT_KEY_ENTER: 919 case K_ENTER:
877 case NEWT_KEY_RIGHT: 920 case K_RIGHT:
878 /* menu */ 921 /* menu */
879 break; 922 break;
880 case NEWT_KEY_LEFT: { 923 case K_LEFT: {
881 const void *top; 924 const void *top;
882 925
883 if (pstack__empty(fstack)) { 926 if (pstack__empty(fstack)) {
@@ -889,21 +932,27 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel,
889 continue; 932 continue;
890 } 933 }
891 top = pstack__pop(fstack); 934 top = pstack__pop(fstack);
892 if (top == &dso_filter) 935 if (top == &browser->hists->dso_filter)
893 goto zoom_out_dso; 936 goto zoom_out_dso;
894 if (top == &thread_filter) 937 if (top == &browser->hists->thread_filter)
895 goto zoom_out_thread; 938 goto zoom_out_thread;
896 continue; 939 continue;
897 } 940 }
898 case NEWT_KEY_ESCAPE: 941 case K_ESC:
899 if (!left_exits && 942 if (!left_exits &&
900 !ui__dialog_yesno("Do you really want to exit?")) 943 !ui__dialog_yesno("Do you really want to exit?"))
901 continue; 944 continue;
902 /* Fall thru */ 945 /* Fall thru */
903 default: 946 case 'q':
947 case CTRL('c'):
904 goto out_free_stack; 948 goto out_free_stack;
949 default:
950 continue;
905 } 951 }
906 952
953 if (!browser->has_symbols)
954 goto add_exit_option;
955
907 if (browser->selection != NULL && 956 if (browser->selection != NULL &&
908 browser->selection->sym != NULL && 957 browser->selection->sym != NULL &&
909 !browser->selection->map->dso->annotate_warned && 958 !browser->selection->map->dso->annotate_warned &&
@@ -913,14 +962,14 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel,
913 962
914 if (thread != NULL && 963 if (thread != NULL &&
915 asprintf(&options[nr_options], "Zoom %s %s(%d) thread", 964 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
916 (thread_filter ? "out of" : "into"), 965 (browser->hists->thread_filter ? "out of" : "into"),
917 (thread->comm_set ? thread->comm : ""), 966 (thread->comm_set ? thread->comm : ""),
918 thread->pid) > 0) 967 thread->pid) > 0)
919 zoom_thread = nr_options++; 968 zoom_thread = nr_options++;
920 969
921 if (dso != NULL && 970 if (dso != NULL &&
922 asprintf(&options[nr_options], "Zoom %s %s DSO", 971 asprintf(&options[nr_options], "Zoom %s %s DSO",
923 (dso_filter ? "out of" : "into"), 972 (browser->hists->dso_filter ? "out of" : "into"),
924 (dso->kernel ? "the Kernel" : dso->short_name)) > 0) 973 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
925 zoom_dso = nr_options++; 974 zoom_dso = nr_options++;
926 975
@@ -928,7 +977,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel,
928 browser->selection->map != NULL && 977 browser->selection->map != NULL &&
929 asprintf(&options[nr_options], "Browse map details") > 0) 978 asprintf(&options[nr_options], "Browse map details") > 0)
930 browse_map = nr_options++; 979 browse_map = nr_options++;
931 980add_exit_option:
932 options[nr_options++] = (char *)"Exit"; 981 options[nr_options++] = (char *)"Exit";
933 982
934 choice = ui__popup_menu(nr_options, options); 983 choice = ui__popup_menu(nr_options, options);
@@ -948,46 +997,52 @@ do_annotate:
948 he = hist_browser__selected_entry(browser); 997 he = hist_browser__selected_entry(browser);
949 if (he == NULL) 998 if (he == NULL)
950 continue; 999 continue;
951 1000 /*
952 hist_entry__tui_annotate(he, evsel->idx); 1001 * Don't let this be freed, say, by hists__decay_entry.
1002 */
1003 he->used = true;
1004 hist_entry__tui_annotate(he, evsel->idx, nr_events,
1005 timer, arg, delay_secs);
1006 he->used = false;
1007 ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
953 } else if (choice == browse_map) 1008 } else if (choice == browse_map)
954 map__browse(browser->selection->map); 1009 map__browse(browser->selection->map);
955 else if (choice == zoom_dso) { 1010 else if (choice == zoom_dso) {
956zoom_dso: 1011zoom_dso:
957 if (dso_filter) { 1012 if (browser->hists->dso_filter) {
958 pstack__remove(fstack, &dso_filter); 1013 pstack__remove(fstack, &browser->hists->dso_filter);
959zoom_out_dso: 1014zoom_out_dso:
960 ui_helpline__pop(); 1015 ui_helpline__pop();
961 dso_filter = NULL; 1016 browser->hists->dso_filter = NULL;
1017 sort_dso.elide = false;
962 } else { 1018 } else {
963 if (dso == NULL) 1019 if (dso == NULL)
964 continue; 1020 continue;
965 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"", 1021 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
966 dso->kernel ? "the Kernel" : dso->short_name); 1022 dso->kernel ? "the Kernel" : dso->short_name);
967 dso_filter = dso; 1023 browser->hists->dso_filter = dso;
968 pstack__push(fstack, &dso_filter); 1024 sort_dso.elide = true;
1025 pstack__push(fstack, &browser->hists->dso_filter);
969 } 1026 }
970 hists__filter_by_dso(self, dso_filter); 1027 hists__filter_by_dso(self);
971 hists__browser_title(self, msg, sizeof(msg), ev_name,
972 dso_filter, thread_filter);
973 hist_browser__reset(browser); 1028 hist_browser__reset(browser);
974 } else if (choice == zoom_thread) { 1029 } else if (choice == zoom_thread) {
975zoom_thread: 1030zoom_thread:
976 if (thread_filter) { 1031 if (browser->hists->thread_filter) {
977 pstack__remove(fstack, &thread_filter); 1032 pstack__remove(fstack, &browser->hists->thread_filter);
978zoom_out_thread: 1033zoom_out_thread:
979 ui_helpline__pop(); 1034 ui_helpline__pop();
980 thread_filter = NULL; 1035 browser->hists->thread_filter = NULL;
1036 sort_thread.elide = false;
981 } else { 1037 } else {
982 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"", 1038 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
983 thread->comm_set ? thread->comm : "", 1039 thread->comm_set ? thread->comm : "",
984 thread->pid); 1040 thread->pid);
985 thread_filter = thread; 1041 browser->hists->thread_filter = thread;
986 pstack__push(fstack, &thread_filter); 1042 sort_thread.elide = true;
1043 pstack__push(fstack, &browser->hists->thread_filter);
987 } 1044 }
988 hists__filter_by_thread(self, thread_filter); 1045 hists__filter_by_thread(self);
989 hists__browser_title(self, msg, sizeof(msg), ev_name,
990 dso_filter, thread_filter);
991 hist_browser__reset(browser); 1046 hist_browser__reset(browser);
992 } 1047 }
993 } 1048 }
@@ -1026,9 +1081,10 @@ static void perf_evsel_menu__write(struct ui_browser *browser,
1026 menu->selection = evsel; 1081 menu->selection = evsel;
1027} 1082}
1028 1083
1029static int perf_evsel_menu__run(struct perf_evsel_menu *menu, const char *help) 1084static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
1085 int nr_events, const char *help,
1086 void(*timer)(void *arg), void *arg, int delay_secs)
1030{ 1087{
1031 int exit_keys[] = { NEWT_KEY_ENTER, NEWT_KEY_RIGHT, 0, };
1032 struct perf_evlist *evlist = menu->b.priv; 1088 struct perf_evlist *evlist = menu->b.priv;
1033 struct perf_evsel *pos; 1089 struct perf_evsel *pos;
1034 const char *ev_name, *title = "Available samples"; 1090 const char *ev_name, *title = "Available samples";
@@ -1038,50 +1094,65 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu, const char *help)
1038 "ESC: exit, ENTER|->: Browse histograms") < 0) 1094 "ESC: exit, ENTER|->: Browse histograms") < 0)
1039 return -1; 1095 return -1;
1040 1096
1041 ui_browser__add_exit_keys(&menu->b, exit_keys);
1042
1043 while (1) { 1097 while (1) {
1044 key = ui_browser__run(&menu->b); 1098 key = ui_browser__run(&menu->b, delay_secs);
1045 1099
1046 switch (key) { 1100 switch (key) {
1047 case NEWT_KEY_RIGHT: 1101 case K_TIMER:
1048 case NEWT_KEY_ENTER: 1102 timer(arg);
1103 continue;
1104 case K_RIGHT:
1105 case K_ENTER:
1049 if (!menu->selection) 1106 if (!menu->selection)
1050 continue; 1107 continue;
1051 pos = menu->selection; 1108 pos = menu->selection;
1052browse_hists: 1109browse_hists:
1110 perf_evlist__set_selected(evlist, pos);
1111 /*
1112 * Give the calling tool a chance to populate the non
1113 * default evsel resorted hists tree.
1114 */
1115 if (timer)
1116 timer(arg);
1053 ev_name = event_name(pos); 1117 ev_name = event_name(pos);
1054 key = perf_evsel__hists_browse(pos, help, ev_name, true); 1118 key = perf_evsel__hists_browse(pos, nr_events, help,
1119 ev_name, true, timer,
1120 arg, delay_secs);
1055 ui_browser__show_title(&menu->b, title); 1121 ui_browser__show_title(&menu->b, title);
1056 break; 1122 switch (key) {
1057 case NEWT_KEY_LEFT: 1123 case K_TAB:
1124 if (pos->node.next == &evlist->entries)
1125 pos = list_entry(evlist->entries.next, struct perf_evsel, node);
1126 else
1127 pos = list_entry(pos->node.next, struct perf_evsel, node);
1128 goto browse_hists;
1129 case K_UNTAB:
1130 if (pos->node.prev == &evlist->entries)
1131 pos = list_entry(evlist->entries.prev, struct perf_evsel, node);
1132 else
1133 pos = list_entry(pos->node.prev, struct perf_evsel, node);
1134 goto browse_hists;
1135 case K_ESC:
1136 if (!ui__dialog_yesno("Do you really want to exit?"))
1137 continue;
1138 /* Fall thru */
1139 case 'q':
1140 case CTRL('c'):
1141 goto out;
1142 default:
1143 continue;
1144 }
1145 case K_LEFT:
1058 continue; 1146 continue;
1059 case NEWT_KEY_ESCAPE: 1147 case K_ESC:
1060 if (!ui__dialog_yesno("Do you really want to exit?")) 1148 if (!ui__dialog_yesno("Do you really want to exit?"))
1061 continue; 1149 continue;
1062 /* Fall thru */ 1150 /* 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': 1151 case 'q':
1081 case CTRL('c'): 1152 case CTRL('c'):
1082 goto out; 1153 goto out;
1083 default: 1154 default:
1084 break; 1155 continue;
1085 } 1156 }
1086 } 1157 }
1087 1158
@@ -1091,7 +1162,9 @@ out:
1091} 1162}
1092 1163
1093static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, 1164static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1094 const char *help) 1165 const char *help,
1166 void(*timer)(void *arg), void *arg,
1167 int delay_secs)
1095{ 1168{
1096 struct perf_evsel *pos; 1169 struct perf_evsel *pos;
1097 struct perf_evsel_menu menu = { 1170 struct perf_evsel_menu menu = {
@@ -1121,18 +1194,24 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1121 pos->name = strdup(ev_name); 1194 pos->name = strdup(ev_name);
1122 } 1195 }
1123 1196
1124 return perf_evsel_menu__run(&menu, help); 1197 return perf_evsel_menu__run(&menu, evlist->nr_entries, help, timer,
1198 arg, delay_secs);
1125} 1199}
1126 1200
1127int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help) 1201int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
1202 void(*timer)(void *arg), void *arg,
1203 int delay_secs)
1128{ 1204{
1129 1205
1130 if (evlist->nr_entries == 1) { 1206 if (evlist->nr_entries == 1) {
1131 struct perf_evsel *first = list_entry(evlist->entries.next, 1207 struct perf_evsel *first = list_entry(evlist->entries.next,
1132 struct perf_evsel, node); 1208 struct perf_evsel, node);
1133 const char *ev_name = event_name(first); 1209 const char *ev_name = event_name(first);
1134 return perf_evsel__hists_browse(first, help, ev_name, false); 1210 return perf_evsel__hists_browse(first, evlist->nr_entries, help,
1211 ev_name, false, timer, arg,
1212 delay_secs);
1135 } 1213 }
1136 1214
1137 return __perf_evlist__tui_browse_hists(evlist, help); 1215 return __perf_evlist__tui_browse_hists(evlist, help,
1216 timer, arg, delay_secs);
1138} 1217}
diff --git a/tools/perf/util/ui/browsers/map.c b/tools/perf/util/ui/browsers/map.c
index 8462bffe20bc..6905bcc8be2d 100644
--- a/tools/perf/util/ui/browsers/map.c
+++ b/tools/perf/util/ui/browsers/map.c
@@ -1,5 +1,6 @@
1#include "../libslang.h" 1#include "../libslang.h"
2#include <elf.h> 2#include <elf.h>
3#include <newt.h>
3#include <inttypes.h> 4#include <inttypes.h>
4#include <sys/ttydefaults.h> 5#include <sys/ttydefaults.h>
5#include <ctype.h> 6#include <ctype.h>
@@ -108,11 +109,8 @@ static int map_browser__run(struct map_browser *self)
108 verbose ? "" : "restart with -v to use") < 0) 109 verbose ? "" : "restart with -v to use") < 0)
109 return -1; 110 return -1;
110 111
111 if (verbose)
112 ui_browser__add_exit_key(&self->b, '/');
113
114 while (1) { 112 while (1) {
115 key = ui_browser__run(&self->b); 113 key = ui_browser__run(&self->b, 0);
116 114
117 if (verbose && key == '/') 115 if (verbose && key == '/')
118 map_browser__search(self); 116 map_browser__search(self);
diff --git a/tools/perf/util/ui/browsers/top.c b/tools/perf/util/ui/browsers/top.c
deleted file mode 100644
index 88403cf8396a..000000000000
--- a/tools/perf/util/ui/browsers/top.c
+++ /dev/null
@@ -1,212 +0,0 @@
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.h b/tools/perf/util/ui/helpline.h
index ab6028d0c401..fdcbc0270acd 100644
--- a/tools/perf/util/ui/helpline.h
+++ b/tools/perf/util/ui/helpline.h
@@ -1,6 +1,9 @@
1#ifndef _PERF_UI_HELPLINE_H_ 1#ifndef _PERF_UI_HELPLINE_H_
2#define _PERF_UI_HELPLINE_H_ 1 2#define _PERF_UI_HELPLINE_H_ 1
3 3
4#include <stdio.h>
5#include <stdarg.h>
6
4void ui_helpline__init(void); 7void ui_helpline__init(void);
5void ui_helpline__pop(void); 8void ui_helpline__pop(void);
6void ui_helpline__push(const char *msg); 9void ui_helpline__push(const char *msg);
diff --git a/tools/perf/util/ui/keysyms.h b/tools/perf/util/ui/keysyms.h
new file mode 100644
index 000000000000..3458b1985761
--- /dev/null
+++ b/tools/perf/util/ui/keysyms.h
@@ -0,0 +1,25 @@
1#ifndef _PERF_KEYSYMS_H_
2#define _PERF_KEYSYMS_H_ 1
3
4#include "libslang.h"
5
6#define K_DOWN SL_KEY_DOWN
7#define K_END SL_KEY_END
8#define K_ENTER '\r'
9#define K_ESC 033
10#define K_F1 SL_KEY_F(1)
11#define K_HOME SL_KEY_HOME
12#define K_LEFT SL_KEY_LEFT
13#define K_PGDN SL_KEY_NPAGE
14#define K_PGUP SL_KEY_PPAGE
15#define K_RIGHT SL_KEY_RIGHT
16#define K_TAB '\t'
17#define K_UNTAB SL_KEY_UNTAB
18#define K_UP SL_KEY_UP
19
20/* Not really keys */
21#define K_TIMER -1
22#define K_ERROR -2
23#define K_RESIZE -3
24
25#endif /* _PERF_KEYSYMS_H_ */
diff --git a/tools/perf/util/ui/libslang.h b/tools/perf/util/ui/libslang.h
index 2b63e1c9b181..4d54b6450f5b 100644
--- a/tools/perf/util/ui/libslang.h
+++ b/tools/perf/util/ui/libslang.h
@@ -24,4 +24,6 @@
24#define sltt_set_color SLtt_set_color 24#define sltt_set_color SLtt_set_color
25#endif 25#endif
26 26
27#define SL_KEY_UNTAB 0x1000
28
27#endif /* _PERF_UI_SLANG_H_ */ 29#endif /* _PERF_UI_SLANG_H_ */
diff --git a/tools/perf/util/ui/setup.c b/tools/perf/util/ui/setup.c
index ee46d671db59..1e6ba06980c4 100644
--- a/tools/perf/util/ui/setup.c
+++ b/tools/perf/util/ui/setup.c
@@ -7,6 +7,7 @@
7#include "browser.h" 7#include "browser.h"
8#include "helpline.h" 8#include "helpline.h"
9#include "ui.h" 9#include "ui.h"
10#include "libslang.h"
10 11
11pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER; 12pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER;
12 13
@@ -17,6 +18,33 @@ static void newt_suspend(void *d __used)
17 newtResume(); 18 newtResume();
18} 19}
19 20
21static int ui__init(void)
22{
23 int err = SLkp_init();
24
25 if (err < 0)
26 goto out;
27
28 SLkp_define_keysym((char *)"^(kB)", SL_KEY_UNTAB);
29out:
30 return err;
31}
32
33static void ui__exit(void)
34{
35 SLtt_set_cursor_visibility(1);
36 SLsmg_refresh();
37 SLsmg_reset_smg();
38 SLang_reset_tty();
39}
40
41static void ui__signal(int sig)
42{
43 ui__exit();
44 psignal(sig, "perf");
45 exit(0);
46}
47
20void setup_browser(bool fallback_to_pager) 48void setup_browser(bool fallback_to_pager)
21{ 49{
22 if (!isatty(1) || !use_browser || dump_trace) { 50 if (!isatty(1) || !use_browser || dump_trace) {
@@ -28,10 +56,16 @@ void setup_browser(bool fallback_to_pager)
28 56
29 use_browser = 1; 57 use_browser = 1;
30 newtInit(); 58 newtInit();
31 newtCls(); 59 ui__init();
32 newtSetSuspendCallback(newt_suspend, NULL); 60 newtSetSuspendCallback(newt_suspend, NULL);
33 ui_helpline__init(); 61 ui_helpline__init();
34 ui_browser__init(); 62 ui_browser__init();
63
64 signal(SIGSEGV, ui__signal);
65 signal(SIGFPE, ui__signal);
66 signal(SIGINT, ui__signal);
67 signal(SIGQUIT, ui__signal);
68 signal(SIGTERM, ui__signal);
35} 69}
36 70
37void exit_browser(bool wait_for_ok) 71void exit_browser(bool wait_for_ok)
@@ -41,6 +75,6 @@ void exit_browser(bool wait_for_ok)
41 char title[] = "Fatal Error", ok[] = "Ok"; 75 char title[] = "Fatal Error", ok[] = "Ok";
42 newtWinMessage(title, ok, ui_helpline__last_msg); 76 newtWinMessage(title, ok, ui_helpline__last_msg);
43 } 77 }
44 newtFinished(); 78 ui__exit();
45 } 79 }
46} 80}