aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/util
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util')
-rwxr-xr-xtools/perf/util/PERF-VERSION-GEN17
-rw-r--r--tools/perf/util/build-id.c28
-rw-r--r--tools/perf/util/cache.h1
-rw-r--r--tools/perf/util/callchain.c37
-rw-r--r--tools/perf/util/callchain.h7
-rw-r--r--tools/perf/util/config.c64
-rw-r--r--tools/perf/util/cpumap.c57
-rw-r--r--tools/perf/util/cpumap.h2
-rw-r--r--tools/perf/util/debug.c12
-rw-r--r--tools/perf/util/debug.h9
-rw-r--r--tools/perf/util/event.c124
-rw-r--r--tools/perf/util/event.h6
-rw-r--r--tools/perf/util/header.c13
-rw-r--r--tools/perf/util/hist.c221
-rw-r--r--tools/perf/util/hist.h33
-rw-r--r--tools/perf/util/include/linux/list.h8
-rw-r--r--tools/perf/util/include/linux/types.h12
-rw-r--r--tools/perf/util/map.c116
-rw-r--r--tools/perf/util/map.h14
-rw-r--r--tools/perf/util/newt.c1167
-rw-r--r--tools/perf/util/parse-events.c11
-rw-r--r--tools/perf/util/probe-event.c282
-rw-r--r--tools/perf/util/probe-event.h29
-rw-r--r--tools/perf/util/probe-finder.c257
-rw-r--r--tools/perf/util/probe-finder.h10
-rw-r--r--tools/perf/util/pstack.h2
-rw-r--r--tools/perf/util/scripting-engines/trace-event-python.c50
-rw-r--r--tools/perf/util/session.c73
-rw-r--r--tools/perf/util/session.h2
-rw-r--r--tools/perf/util/sort.c46
-rw-r--r--tools/perf/util/sort.h22
-rw-r--r--tools/perf/util/symbol.c364
-rw-r--r--tools/perf/util/symbol.h19
-rw-r--r--tools/perf/util/thread.c18
-rw-r--r--tools/perf/util/thread.h7
-rw-r--r--tools/perf/util/ui/browser.c329
-rw-r--r--tools/perf/util/ui/browser.h46
-rw-r--r--tools/perf/util/ui/browsers/annotate.c240
-rw-r--r--tools/perf/util/ui/browsers/hists.c948
-rw-r--r--tools/perf/util/ui/browsers/map.c161
-rw-r--r--tools/perf/util/ui/browsers/map.h6
-rw-r--r--tools/perf/util/ui/helpline.c69
-rw-r--r--tools/perf/util/ui/helpline.h11
-rw-r--r--tools/perf/util/ui/libslang.h27
-rw-r--r--tools/perf/util/ui/progress.c60
-rw-r--r--tools/perf/util/ui/progress.h11
-rw-r--r--tools/perf/util/ui/setup.c42
-rw-r--r--tools/perf/util/ui/util.c114
-rw-r--r--tools/perf/util/ui/util.h10
-rw-r--r--tools/perf/util/util.h3
50 files changed, 3506 insertions, 1711 deletions
diff --git a/tools/perf/util/PERF-VERSION-GEN b/tools/perf/util/PERF-VERSION-GEN
index 49ece7921914..97d76562a1a0 100755
--- a/tools/perf/util/PERF-VERSION-GEN
+++ b/tools/perf/util/PERF-VERSION-GEN
@@ -5,17 +5,13 @@ if [ $# -eq 1 ] ; then
5fi 5fi
6 6
7GVF=${OUTPUT}PERF-VERSION-FILE 7GVF=${OUTPUT}PERF-VERSION-FILE
8DEF_VER=v0.0.2.PERF
9 8
10LF=' 9LF='
11' 10'
12 11
13# First see if there is a version file (included in release tarballs), 12# First check if there is a .git to get the version from git describe
14# then try git-describe, then default. 13# otherwise try to get the version from the kernel makefile
15if test -f version 14if test -d ../../.git -o -f ../../.git &&
16then
17 VN=$(cat version) || VN="$DEF_VER"
18elif test -d .git -o -f .git &&
19 VN=$(git describe --abbrev=4 HEAD 2>/dev/null) && 15 VN=$(git describe --abbrev=4 HEAD 2>/dev/null) &&
20 case "$VN" in 16 case "$VN" in
21 *$LF*) (exit 1) ;; 17 *$LF*) (exit 1) ;;
@@ -27,7 +23,12 @@ elif test -d .git -o -f .git &&
27then 23then
28 VN=$(echo "$VN" | sed -e 's/-/./g'); 24 VN=$(echo "$VN" | sed -e 's/-/./g');
29else 25else
30 VN="$DEF_VER" 26 eval `grep '^VERSION\s*=' ../../Makefile|tr -d ' '`
27 eval `grep '^PATCHLEVEL\s*=' ../../Makefile|tr -d ' '`
28 eval `grep '^SUBLEVEL\s*=' ../../Makefile|tr -d ' '`
29 eval `grep '^EXTRAVERSION\s*=' ../../Makefile|tr -d ' '`
30
31 VN="${VERSION}.${PATCHLEVEL}.${SUBLEVEL}${EXTRAVERSION}"
31fi 32fi
32 33
33VN=$(expr "$VN" : v*'\(.*\)') 34VN=$(expr "$VN" : v*'\(.*\)')
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index 70c5cf87d020..e437edb72417 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -12,6 +12,7 @@
12#include "event.h" 12#include "event.h"
13#include "symbol.h" 13#include "symbol.h"
14#include <linux/kernel.h> 14#include <linux/kernel.h>
15#include "debug.h"
15 16
16static int build_id__mark_dso_hit(event_t *event, struct perf_session *session) 17static int build_id__mark_dso_hit(event_t *event, struct perf_session *session)
17{ 18{
@@ -34,28 +35,43 @@ static int build_id__mark_dso_hit(event_t *event, struct perf_session *session)
34 return 0; 35 return 0;
35} 36}
36 37
38static int event__exit_del_thread(event_t *self, struct perf_session *session)
39{
40 struct thread *thread = perf_session__findnew(session, self->fork.tid);
41
42 dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid,
43 self->fork.ppid, self->fork.ptid);
44
45 if (thread) {
46 rb_erase(&thread->rb_node, &session->threads);
47 session->last_match = NULL;
48 thread__delete(thread);
49 }
50
51 return 0;
52}
53
37struct perf_event_ops build_id__mark_dso_hit_ops = { 54struct perf_event_ops build_id__mark_dso_hit_ops = {
38 .sample = build_id__mark_dso_hit, 55 .sample = build_id__mark_dso_hit,
39 .mmap = event__process_mmap, 56 .mmap = event__process_mmap,
40 .fork = event__process_task, 57 .fork = event__process_task,
58 .exit = event__exit_del_thread,
41}; 59};
42 60
43char *dso__build_id_filename(struct dso *self, char *bf, size_t size) 61char *dso__build_id_filename(struct dso *self, char *bf, size_t size)
44{ 62{
45 char build_id_hex[BUILD_ID_SIZE * 2 + 1]; 63 char build_id_hex[BUILD_ID_SIZE * 2 + 1];
46 const char *home;
47 64
48 if (!self->has_build_id) 65 if (!self->has_build_id)
49 return NULL; 66 return NULL;
50 67
51 build_id__sprintf(self->build_id, sizeof(self->build_id), build_id_hex); 68 build_id__sprintf(self->build_id, sizeof(self->build_id), build_id_hex);
52 home = getenv("HOME");
53 if (bf == NULL) { 69 if (bf == NULL) {
54 if (asprintf(&bf, "%s/%s/.build-id/%.2s/%s", home, 70 if (asprintf(&bf, "%s/.build-id/%.2s/%s", buildid_dir,
55 DEBUG_CACHE_DIR, build_id_hex, build_id_hex + 2) < 0) 71 build_id_hex, build_id_hex + 2) < 0)
56 return NULL; 72 return NULL;
57 } else 73 } else
58 snprintf(bf, size, "%s/%s/.build-id/%.2s/%s", home, 74 snprintf(bf, size, "%s/.build-id/%.2s/%s", buildid_dir,
59 DEBUG_CACHE_DIR, build_id_hex, build_id_hex + 2); 75 build_id_hex, build_id_hex + 2);
60 return bf; 76 return bf;
61} 77}
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h
index 65fe664fddf6..27e9ebe4076e 100644
--- a/tools/perf/util/cache.h
+++ b/tools/perf/util/cache.h
@@ -23,6 +23,7 @@ extern int perf_config(config_fn_t fn, void *);
23extern int perf_config_int(const char *, const char *); 23extern int perf_config_int(const char *, const char *);
24extern int perf_config_bool(const char *, const char *); 24extern int perf_config_bool(const char *, const char *);
25extern int config_error_nonbool(const char *); 25extern int config_error_nonbool(const char *);
26extern const char *perf_config_dirname(const char *, const char *);
26 27
27/* pager.c */ 28/* pager.c */
28extern void setup_pager(void); 29extern void setup_pager(void);
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index 62b69ad4aa73..f231f43424d2 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -18,7 +18,7 @@
18#include "util.h" 18#include "util.h"
19#include "callchain.h" 19#include "callchain.h"
20 20
21bool ip_callchain__valid(struct ip_callchain *chain, event_t *event) 21bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event)
22{ 22{
23 unsigned int chain_size = event->header.size; 23 unsigned int chain_size = event->header.size;
24 chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event; 24 chain_size -= (unsigned long)&event->ip.__more_data - (unsigned long)event;
@@ -230,7 +230,7 @@ fill_node(struct callchain_node *node, struct resolved_chain *chain, int start)
230 230
231static void 231static void
232add_child(struct callchain_node *parent, struct resolved_chain *chain, 232add_child(struct callchain_node *parent, struct resolved_chain *chain,
233 int start) 233 int start, u64 period)
234{ 234{
235 struct callchain_node *new; 235 struct callchain_node *new;
236 236
@@ -238,7 +238,7 @@ add_child(struct callchain_node *parent, struct resolved_chain *chain,
238 fill_node(new, chain, start); 238 fill_node(new, chain, start);
239 239
240 new->children_hit = 0; 240 new->children_hit = 0;
241 new->hit = 1; 241 new->hit = period;
242} 242}
243 243
244/* 244/*
@@ -248,7 +248,8 @@ add_child(struct callchain_node *parent, struct resolved_chain *chain,
248 */ 248 */
249static void 249static void
250split_add_child(struct callchain_node *parent, struct resolved_chain *chain, 250split_add_child(struct callchain_node *parent, struct resolved_chain *chain,
251 struct callchain_list *to_split, int idx_parents, int idx_local) 251 struct callchain_list *to_split, int idx_parents, int idx_local,
252 u64 period)
252{ 253{
253 struct callchain_node *new; 254 struct callchain_node *new;
254 struct list_head *old_tail; 255 struct list_head *old_tail;
@@ -275,41 +276,41 @@ split_add_child(struct callchain_node *parent, struct resolved_chain *chain,
275 /* create a new child for the new branch if any */ 276 /* create a new child for the new branch if any */
276 if (idx_total < chain->nr) { 277 if (idx_total < chain->nr) {
277 parent->hit = 0; 278 parent->hit = 0;
278 add_child(parent, chain, idx_total); 279 add_child(parent, chain, idx_total, period);
279 parent->children_hit++; 280 parent->children_hit += period;
280 } else { 281 } else {
281 parent->hit = 1; 282 parent->hit = period;
282 } 283 }
283} 284}
284 285
285static int 286static int
286__append_chain(struct callchain_node *root, struct resolved_chain *chain, 287__append_chain(struct callchain_node *root, struct resolved_chain *chain,
287 unsigned int start); 288 unsigned int start, u64 period);
288 289
289static void 290static void
290__append_chain_children(struct callchain_node *root, 291__append_chain_children(struct callchain_node *root,
291 struct resolved_chain *chain, 292 struct resolved_chain *chain,
292 unsigned int start) 293 unsigned int start, u64 period)
293{ 294{
294 struct callchain_node *rnode; 295 struct callchain_node *rnode;
295 296
296 /* lookup in childrens */ 297 /* lookup in childrens */
297 chain_for_each_child(rnode, root) { 298 chain_for_each_child(rnode, root) {
298 unsigned int ret = __append_chain(rnode, chain, start); 299 unsigned int ret = __append_chain(rnode, chain, start, period);
299 300
300 if (!ret) 301 if (!ret)
301 goto inc_children_hit; 302 goto inc_children_hit;
302 } 303 }
303 /* nothing in children, add to the current node */ 304 /* nothing in children, add to the current node */
304 add_child(root, chain, start); 305 add_child(root, chain, start, period);
305 306
306inc_children_hit: 307inc_children_hit:
307 root->children_hit++; 308 root->children_hit += period;
308} 309}
309 310
310static int 311static int
311__append_chain(struct callchain_node *root, struct resolved_chain *chain, 312__append_chain(struct callchain_node *root, struct resolved_chain *chain,
312 unsigned int start) 313 unsigned int start, u64 period)
313{ 314{
314 struct callchain_list *cnode; 315 struct callchain_list *cnode;
315 unsigned int i = start; 316 unsigned int i = start;
@@ -345,18 +346,18 @@ __append_chain(struct callchain_node *root, struct resolved_chain *chain,
345 346
346 /* we match only a part of the node. Split it and add the new chain */ 347 /* we match only a part of the node. Split it and add the new chain */
347 if (i - start < root->val_nr) { 348 if (i - start < root->val_nr) {
348 split_add_child(root, chain, cnode, start, i - start); 349 split_add_child(root, chain, cnode, start, i - start, period);
349 return 0; 350 return 0;
350 } 351 }
351 352
352 /* we match 100% of the path, increment the hit */ 353 /* we match 100% of the path, increment the hit */
353 if (i - start == root->val_nr && i == chain->nr) { 354 if (i - start == root->val_nr && i == chain->nr) {
354 root->hit++; 355 root->hit += period;
355 return 0; 356 return 0;
356 } 357 }
357 358
358 /* We match the node and still have a part remaining */ 359 /* We match the node and still have a part remaining */
359 __append_chain_children(root, chain, i); 360 __append_chain_children(root, chain, i, period);
360 361
361 return 0; 362 return 0;
362} 363}
@@ -380,7 +381,7 @@ static void filter_context(struct ip_callchain *old, struct resolved_chain *new,
380 381
381 382
382int append_chain(struct callchain_node *root, struct ip_callchain *chain, 383int append_chain(struct callchain_node *root, struct ip_callchain *chain,
383 struct map_symbol *syms) 384 struct map_symbol *syms, u64 period)
384{ 385{
385 struct resolved_chain *filtered; 386 struct resolved_chain *filtered;
386 387
@@ -397,7 +398,7 @@ int append_chain(struct callchain_node *root, struct ip_callchain *chain,
397 if (!filtered->nr) 398 if (!filtered->nr)
398 goto end; 399 goto end;
399 400
400 __append_chain_children(root, filtered, 0); 401 __append_chain_children(root, filtered, 0, period);
401end: 402end:
402 free(filtered); 403 free(filtered);
403 404
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h
index 1ca73e4a2723..624a96c636fd 100644
--- a/tools/perf/util/callchain.h
+++ b/tools/perf/util/callchain.h
@@ -49,6 +49,9 @@ static inline void callchain_init(struct callchain_node *node)
49 INIT_LIST_HEAD(&node->brothers); 49 INIT_LIST_HEAD(&node->brothers);
50 INIT_LIST_HEAD(&node->children); 50 INIT_LIST_HEAD(&node->children);
51 INIT_LIST_HEAD(&node->val); 51 INIT_LIST_HEAD(&node->val);
52
53 node->parent = NULL;
54 node->hit = 0;
52} 55}
53 56
54static inline u64 cumul_hits(struct callchain_node *node) 57static inline u64 cumul_hits(struct callchain_node *node)
@@ -58,7 +61,7 @@ static inline u64 cumul_hits(struct callchain_node *node)
58 61
59int register_callchain_param(struct callchain_param *param); 62int register_callchain_param(struct callchain_param *param);
60int append_chain(struct callchain_node *root, struct ip_callchain *chain, 63int append_chain(struct callchain_node *root, struct ip_callchain *chain,
61 struct map_symbol *syms); 64 struct map_symbol *syms, u64 period);
62 65
63bool ip_callchain__valid(struct ip_callchain *chain, event_t *event); 66bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event);
64#endif /* __PERF_CALLCHAIN_H */ 67#endif /* __PERF_CALLCHAIN_H */
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c
index dabe892d0e53..e02d78cae70f 100644
--- a/tools/perf/util/config.c
+++ b/tools/perf/util/config.c
@@ -11,6 +11,11 @@
11 11
12#define MAXNAME (256) 12#define MAXNAME (256)
13 13
14#define DEBUG_CACHE_DIR ".debug"
15
16
17char buildid_dir[MAXPATHLEN]; /* root dir for buildid, binary cache */
18
14static FILE *config_file; 19static FILE *config_file;
15static const char *config_file_name; 20static const char *config_file_name;
16static int config_linenr; 21static int config_linenr;
@@ -127,7 +132,7 @@ static int get_value(config_fn_t fn, void *data, char *name, unsigned int len)
127 break; 132 break;
128 if (!iskeychar(c)) 133 if (!iskeychar(c))
129 break; 134 break;
130 name[len++] = tolower(c); 135 name[len++] = c;
131 if (len >= MAXNAME) 136 if (len >= MAXNAME)
132 return -1; 137 return -1;
133 } 138 }
@@ -327,6 +332,13 @@ int perf_config_bool(const char *name, const char *value)
327 return !!perf_config_bool_or_int(name, value, &discard); 332 return !!perf_config_bool_or_int(name, value, &discard);
328} 333}
329 334
335const char *perf_config_dirname(const char *name, const char *value)
336{
337 if (!name)
338 return NULL;
339 return value;
340}
341
330static int perf_default_core_config(const char *var __used, const char *value __used) 342static int perf_default_core_config(const char *var __used, const char *value __used)
331{ 343{
332 /* Add other config variables here and to Documentation/config.txt. */ 344 /* Add other config variables here and to Documentation/config.txt. */
@@ -428,3 +440,53 @@ int config_error_nonbool(const char *var)
428{ 440{
429 return error("Missing value for '%s'", var); 441 return error("Missing value for '%s'", var);
430} 442}
443
444struct buildid_dir_config {
445 char *dir;
446};
447
448static int buildid_dir_command_config(const char *var, const char *value,
449 void *data)
450{
451 struct buildid_dir_config *c = data;
452 const char *v;
453
454 /* same dir for all commands */
455 if (!prefixcmp(var, "buildid.") && !strcmp(var + 8, "dir")) {
456 v = perf_config_dirname(var, value);
457 if (!v)
458 return -1;
459 strncpy(c->dir, v, MAXPATHLEN-1);
460 c->dir[MAXPATHLEN-1] = '\0';
461 }
462 return 0;
463}
464
465static void check_buildid_dir_config(void)
466{
467 struct buildid_dir_config c;
468 c.dir = buildid_dir;
469 perf_config(buildid_dir_command_config, &c);
470}
471
472void set_buildid_dir(void)
473{
474 buildid_dir[0] = '\0';
475
476 /* try config file */
477 check_buildid_dir_config();
478
479 /* default to $HOME/.debug */
480 if (buildid_dir[0] == '\0') {
481 char *v = getenv("HOME");
482 if (v) {
483 snprintf(buildid_dir, MAXPATHLEN-1, "%s/%s",
484 v, DEBUG_CACHE_DIR);
485 } else {
486 strncpy(buildid_dir, DEBUG_CACHE_DIR, MAXPATHLEN-1);
487 }
488 buildid_dir[MAXPATHLEN-1] = '\0';
489 }
490 /* for communicating with external commands */
491 setenv("PERF_BUILDID_DIR", buildid_dir, 1);
492}
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
index 4e01490e51e5..0f9b8d7a7d7e 100644
--- a/tools/perf/util/cpumap.c
+++ b/tools/perf/util/cpumap.c
@@ -20,7 +20,7 @@ static int default_cpu_map(void)
20 return nr_cpus; 20 return nr_cpus;
21} 21}
22 22
23int read_cpu_map(void) 23static int read_all_cpu_map(void)
24{ 24{
25 FILE *onlnf; 25 FILE *onlnf;
26 int nr_cpus = 0; 26 int nr_cpus = 0;
@@ -57,3 +57,58 @@ int read_cpu_map(void)
57 57
58 return default_cpu_map(); 58 return default_cpu_map();
59} 59}
60
61int read_cpu_map(const char *cpu_list)
62{
63 unsigned long start_cpu, end_cpu = 0;
64 char *p = NULL;
65 int i, nr_cpus = 0;
66
67 if (!cpu_list)
68 return read_all_cpu_map();
69
70 if (!isdigit(*cpu_list))
71 goto invalid;
72
73 while (isdigit(*cpu_list)) {
74 p = NULL;
75 start_cpu = strtoul(cpu_list, &p, 0);
76 if (start_cpu >= INT_MAX
77 || (*p != '\0' && *p != ',' && *p != '-'))
78 goto invalid;
79
80 if (*p == '-') {
81 cpu_list = ++p;
82 p = NULL;
83 end_cpu = strtoul(cpu_list, &p, 0);
84
85 if (end_cpu >= INT_MAX || (*p != '\0' && *p != ','))
86 goto invalid;
87
88 if (end_cpu < start_cpu)
89 goto invalid;
90 } else {
91 end_cpu = start_cpu;
92 }
93
94 for (; start_cpu <= end_cpu; start_cpu++) {
95 /* check for duplicates */
96 for (i = 0; i < nr_cpus; i++)
97 if (cpumap[i] == (int)start_cpu)
98 goto invalid;
99
100 assert(nr_cpus < MAX_NR_CPUS);
101 cpumap[nr_cpus++] = (int)start_cpu;
102 }
103 if (*p)
104 ++p;
105
106 cpu_list = p;
107 }
108 if (nr_cpus > 0)
109 return nr_cpus;
110
111 return default_cpu_map();
112invalid:
113 return -1;
114}
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
index 86c78bb33098..3e60f56e490e 100644
--- a/tools/perf/util/cpumap.h
+++ b/tools/perf/util/cpumap.h
@@ -1,7 +1,7 @@
1#ifndef __PERF_CPUMAP_H 1#ifndef __PERF_CPUMAP_H
2#define __PERF_CPUMAP_H 2#define __PERF_CPUMAP_H
3 3
4extern int read_cpu_map(void); 4extern int read_cpu_map(const char *cpu_list);
5extern int cpumap[]; 5extern int cpumap[];
6 6
7#endif /* __PERF_CPUMAP_H */ 7#endif /* __PERF_CPUMAP_H */
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c
index 6cddff2bc970..f9c7e3ad1aa7 100644
--- a/tools/perf/util/debug.c
+++ b/tools/perf/util/debug.c
@@ -23,7 +23,7 @@ int eprintf(int level, const char *fmt, ...)
23 if (verbose >= level) { 23 if (verbose >= level) {
24 va_start(args, fmt); 24 va_start(args, fmt);
25 if (use_browser > 0) 25 if (use_browser > 0)
26 ret = browser__show_help(fmt, args); 26 ret = ui_helpline__show_help(fmt, args);
27 else 27 else
28 ret = vfprintf(stderr, fmt, args); 28 ret = vfprintf(stderr, fmt, args);
29 va_end(args); 29 va_end(args);
@@ -86,12 +86,10 @@ void trace_event(event_t *event)
86 dump_printf_color(" ", color); 86 dump_printf_color(" ", color);
87 for (j = 0; j < 15-(i & 15); j++) 87 for (j = 0; j < 15-(i & 15); j++)
88 dump_printf_color(" ", color); 88 dump_printf_color(" ", color);
89 for (j = 0; j < (i & 15); j++) { 89 for (j = i & ~15; j <= i; j++) {
90 if (isprint(raw_event[i-15+j])) 90 dump_printf_color("%c", color,
91 dump_printf_color("%c", color, 91 isprint(raw_event[j]) ?
92 raw_event[i-15+j]); 92 raw_event[j] : '.');
93 else
94 dump_printf_color(".", color);
95 } 93 }
96 dump_printf_color("\n", color); 94 dump_printf_color("\n", color);
97 } 95 }
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h
index 047ac3324ebe..7a17ee061bcb 100644
--- a/tools/perf/util/debug.h
+++ b/tools/perf/util/debug.h
@@ -14,7 +14,7 @@ void trace_event(event_t *event);
14struct ui_progress; 14struct ui_progress;
15 15
16#ifdef NO_NEWT_SUPPORT 16#ifdef NO_NEWT_SUPPORT
17static inline int browser__show_help(const char *format __used, va_list ap __used) 17static inline int ui_helpline__show_help(const char *format __used, va_list ap __used)
18{ 18{
19 return 0; 19 return 0;
20} 20}
@@ -30,10 +30,9 @@ static inline void ui_progress__update(struct ui_progress *self __used,
30 30
31static inline void ui_progress__delete(struct ui_progress *self __used) {} 31static inline void ui_progress__delete(struct ui_progress *self __used) {}
32#else 32#else
33int browser__show_help(const char *format, va_list ap); 33extern char ui_helpline__last_msg[];
34struct ui_progress *ui_progress__new(const char *title, u64 total); 34int ui_helpline__show_help(const char *format, va_list ap);
35void ui_progress__update(struct ui_progress *self, u64 curr); 35#include "ui/progress.h"
36void ui_progress__delete(struct ui_progress *self);
37#endif 36#endif
38 37
39#endif /* __PERF_DEBUG_H */ 38#endif /* __PERF_DEBUG_H */
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index 50771b5813ee..dab9e754a281 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -151,7 +151,6 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid,
151 continue; 151 continue;
152 pbf += n + 3; 152 pbf += n + 3;
153 if (*pbf == 'x') { /* vm_exec */ 153 if (*pbf == 'x') { /* vm_exec */
154 u64 vm_pgoff;
155 char *execname = strchr(bf, '/'); 154 char *execname = strchr(bf, '/');
156 155
157 /* Catch VDSO */ 156 /* Catch VDSO */
@@ -162,12 +161,7 @@ static int event__synthesize_mmap_events(pid_t pid, pid_t tgid,
162 continue; 161 continue;
163 162
164 pbf += 3; 163 pbf += 3;
165 n = hex2u64(pbf, &vm_pgoff); 164 n = hex2u64(pbf, &ev.mmap.pgoff);
166 /* pgoff is in bytes, not pages */
167 if (n >= 0)
168 ev.mmap.pgoff = vm_pgoff << getpagesize();
169 else
170 ev.mmap.pgoff = 0;
171 165
172 size = strlen(execname); 166 size = strlen(execname);
173 execname[size - 1] = '\0'; /* Remove \n */ 167 execname[size - 1] = '\0'; /* Remove \n */
@@ -340,41 +334,41 @@ int event__synthesize_kernel_mmap(event__handler_t process,
340 return process(&ev, session); 334 return process(&ev, session);
341} 335}
342 336
343static void thread__comm_adjust(struct thread *self) 337static void thread__comm_adjust(struct thread *self, struct hists *hists)
344{ 338{
345 char *comm = self->comm; 339 char *comm = self->comm;
346 340
347 if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && 341 if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
348 (!symbol_conf.comm_list || 342 (!symbol_conf.comm_list ||
349 strlist__has_entry(symbol_conf.comm_list, comm))) { 343 strlist__has_entry(symbol_conf.comm_list, comm))) {
350 unsigned int slen = strlen(comm); 344 u16 slen = strlen(comm);
351 345
352 if (slen > comms__col_width) { 346 if (hists__new_col_len(hists, HISTC_COMM, slen))
353 comms__col_width = slen; 347 hists__set_col_len(hists, HISTC_THREAD, slen + 6);
354 threads__col_width = slen + 6;
355 }
356 } 348 }
357} 349}
358 350
359static int thread__set_comm_adjust(struct thread *self, const char *comm) 351static int thread__set_comm_adjust(struct thread *self, const char *comm,
352 struct hists *hists)
360{ 353{
361 int ret = thread__set_comm(self, comm); 354 int ret = thread__set_comm(self, comm);
362 355
363 if (ret) 356 if (ret)
364 return ret; 357 return ret;
365 358
366 thread__comm_adjust(self); 359 thread__comm_adjust(self, hists);
367 360
368 return 0; 361 return 0;
369} 362}
370 363
371int event__process_comm(event_t *self, struct perf_session *session) 364int event__process_comm(event_t *self, struct perf_session *session)
372{ 365{
373 struct thread *thread = perf_session__findnew(session, self->comm.pid); 366 struct thread *thread = perf_session__findnew(session, self->comm.tid);
374 367
375 dump_printf(": %s:%d\n", self->comm.comm, self->comm.pid); 368 dump_printf(": %s:%d\n", self->comm.comm, self->comm.tid);
376 369
377 if (thread == NULL || thread__set_comm_adjust(thread, self->comm.comm)) { 370 if (thread == NULL || thread__set_comm_adjust(thread, self->comm.comm,
371 &session->hists)) {
378 dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); 372 dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n");
379 return -1; 373 return -1;
380 } 374 }
@@ -456,6 +450,7 @@ static int event__process_kernel_mmap(event_t *self,
456 goto out_problem; 450 goto out_problem;
457 451
458 map->dso->short_name = name; 452 map->dso->short_name = name;
453 map->dso->sname_alloc = 1;
459 map->end = map->start + self->mmap.len; 454 map->end = map->start + self->mmap.len;
460 } else if (is_kernel_mmap) { 455 } else if (is_kernel_mmap) {
461 const char *symbol_name = (self->mmap.filename + 456 const char *symbol_name = (self->mmap.filename +
@@ -514,12 +509,13 @@ int event__process_mmap(event_t *self, struct perf_session *session)
514 if (machine == NULL) 509 if (machine == NULL)
515 goto out_problem; 510 goto out_problem;
516 thread = perf_session__findnew(session, self->mmap.pid); 511 thread = perf_session__findnew(session, self->mmap.pid);
512 if (thread == NULL)
513 goto out_problem;
517 map = map__new(&machine->user_dsos, self->mmap.start, 514 map = map__new(&machine->user_dsos, self->mmap.start,
518 self->mmap.len, self->mmap.pgoff, 515 self->mmap.len, self->mmap.pgoff,
519 self->mmap.pid, self->mmap.filename, 516 self->mmap.pid, self->mmap.filename,
520 MAP__FUNCTION, session->cwd, session->cwdlen); 517 MAP__FUNCTION);
521 518 if (map == NULL)
522 if (thread == NULL || map == NULL)
523 goto out_problem; 519 goto out_problem;
524 520
525 thread__insert_map(thread, map); 521 thread__insert_map(thread, map);
@@ -532,19 +528,16 @@ out_problem:
532 528
533int event__process_task(event_t *self, struct perf_session *session) 529int event__process_task(event_t *self, struct perf_session *session)
534{ 530{
535 struct thread *thread = perf_session__findnew(session, self->fork.pid); 531 struct thread *thread = perf_session__findnew(session, self->fork.tid);
536 struct thread *parent = perf_session__findnew(session, self->fork.ppid); 532 struct thread *parent = perf_session__findnew(session, self->fork.ptid);
537 533
538 dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid, 534 dump_printf("(%d:%d):(%d:%d)\n", self->fork.pid, self->fork.tid,
539 self->fork.ppid, self->fork.ptid); 535 self->fork.ppid, self->fork.ptid);
540 /*
541 * A thread clone will have the same PID for both parent and child.
542 */
543 if (thread == parent)
544 return 0;
545 536
546 if (self->header.type == PERF_RECORD_EXIT) 537 if (self->header.type == PERF_RECORD_EXIT) {
538 perf_session__remove_thread(session, thread);
547 return 0; 539 return 0;
540 }
548 541
549 if (thread == NULL || parent == NULL || 542 if (thread == NULL || parent == NULL ||
550 thread__fork(thread, parent) < 0) { 543 thread__fork(thread, parent) < 0) {
@@ -555,6 +548,26 @@ int event__process_task(event_t *self, struct perf_session *session)
555 return 0; 548 return 0;
556} 549}
557 550
551int event__process(event_t *event, struct perf_session *session)
552{
553 switch (event->header.type) {
554 case PERF_RECORD_COMM:
555 event__process_comm(event, session);
556 break;
557 case PERF_RECORD_MMAP:
558 event__process_mmap(event, session);
559 break;
560 case PERF_RECORD_FORK:
561 case PERF_RECORD_EXIT:
562 event__process_task(event, session);
563 break;
564 default:
565 break;
566 }
567
568 return 0;
569}
570
558void thread__find_addr_map(struct thread *self, 571void thread__find_addr_map(struct thread *self,
559 struct perf_session *session, u8 cpumode, 572 struct perf_session *session, u8 cpumode,
560 enum map_type type, pid_t pid, u64 addr, 573 enum map_type type, pid_t pid, u64 addr,
@@ -644,27 +657,49 @@ void thread__find_addr_location(struct thread *self,
644 al->sym = NULL; 657 al->sym = NULL;
645} 658}
646 659
647static void dso__calc_col_width(struct dso *self) 660static void dso__calc_col_width(struct dso *self, struct hists *hists)
648{ 661{
649 if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && 662 if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
650 (!symbol_conf.dso_list || 663 (!symbol_conf.dso_list ||
651 strlist__has_entry(symbol_conf.dso_list, self->name))) { 664 strlist__has_entry(symbol_conf.dso_list, self->name))) {
652 u16 slen = self->short_name_len; 665 u16 slen = dso__name_len(self);
653 if (verbose) 666 hists__new_col_len(hists, HISTC_DSO, slen);
654 slen = self->long_name_len;
655 if (dsos__col_width < slen)
656 dsos__col_width = slen;
657 } 667 }
658 668
659 self->slen_calculated = 1; 669 self->slen_calculated = 1;
660} 670}
661 671
662int event__preprocess_sample(const event_t *self, struct perf_session *session, 672int event__preprocess_sample(const event_t *self, struct perf_session *session,
663 struct addr_location *al, symbol_filter_t filter) 673 struct addr_location *al, struct sample_data *data,
674 symbol_filter_t filter)
664{ 675{
665 u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; 676 u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
666 struct thread *thread = perf_session__findnew(session, self->ip.pid); 677 struct thread *thread;
678
679 event__parse_sample(self, session->sample_type, data);
680
681 dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld cpu:%d\n",
682 self->header.misc, data->pid, data->tid, data->ip,
683 data->period, data->cpu);
667 684
685 if (session->sample_type & PERF_SAMPLE_CALLCHAIN) {
686 unsigned int i;
687
688 dump_printf("... chain: nr:%Lu\n", data->callchain->nr);
689
690 if (!ip_callchain__valid(data->callchain, self)) {
691 pr_debug("call-chain problem with event, "
692 "skipping it.\n");
693 goto out_filtered;
694 }
695
696 if (dump_trace) {
697 for (i = 0; i < data->callchain->nr; i++)
698 dump_printf("..... %2d: %016Lx\n",
699 i, data->callchain->ips[i]);
700 }
701 }
702 thread = perf_session__findnew(session, self->ip.pid);
668 if (thread == NULL) 703 if (thread == NULL)
669 return -1; 704 return -1;
670 705
@@ -690,6 +725,7 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session,
690 al->map ? al->map->dso->long_name : 725 al->map ? al->map->dso->long_name :
691 al->level == 'H' ? "[hypervisor]" : "<not found>"); 726 al->level == 'H' ? "[hypervisor]" : "<not found>");
692 al->sym = NULL; 727 al->sym = NULL;
728 al->cpu = data->cpu;
693 729
694 if (al->map) { 730 if (al->map) {
695 if (symbol_conf.dso_list && 731 if (symbol_conf.dso_list &&
@@ -706,16 +742,17 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session,
706 * sampled. 742 * sampled.
707 */ 743 */
708 if (!sort_dso.elide && !al->map->dso->slen_calculated) 744 if (!sort_dso.elide && !al->map->dso->slen_calculated)
709 dso__calc_col_width(al->map->dso); 745 dso__calc_col_width(al->map->dso, &session->hists);
710 746
711 al->sym = map__find_symbol(al->map, al->addr, filter); 747 al->sym = map__find_symbol(al->map, al->addr, filter);
712 } else { 748 } else {
713 const unsigned int unresolved_col_width = BITS_PER_LONG / 4; 749 const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
714 750
715 if (dsos__col_width < unresolved_col_width && 751 if (hists__col_len(&session->hists, HISTC_DSO) < unresolved_col_width &&
716 !symbol_conf.col_width_list_str && !symbol_conf.field_sep && 752 !symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
717 !symbol_conf.dso_list) 753 !symbol_conf.dso_list)
718 dsos__col_width = unresolved_col_width; 754 hists__set_col_len(&session->hists, HISTC_DSO,
755 unresolved_col_width);
719 } 756 }
720 757
721 if (symbol_conf.sym_list && al->sym && 758 if (symbol_conf.sym_list && al->sym &&
@@ -729,9 +766,9 @@ out_filtered:
729 return 0; 766 return 0;
730} 767}
731 768
732int event__parse_sample(event_t *event, u64 type, struct sample_data *data) 769int event__parse_sample(const event_t *event, u64 type, struct sample_data *data)
733{ 770{
734 u64 *array = event->sample.array; 771 const u64 *array = event->sample.array;
735 772
736 if (type & PERF_SAMPLE_IP) { 773 if (type & PERF_SAMPLE_IP) {
737 data->ip = event->ip.ip; 774 data->ip = event->ip.ip;
@@ -770,7 +807,8 @@ int event__parse_sample(event_t *event, u64 type, struct sample_data *data)
770 u32 *p = (u32 *)array; 807 u32 *p = (u32 *)array;
771 data->cpu = *p; 808 data->cpu = *p;
772 array++; 809 array++;
773 } 810 } else
811 data->cpu = -1;
774 812
775 if (type & PERF_SAMPLE_PERIOD) { 813 if (type & PERF_SAMPLE_PERIOD) {
776 data->period = *array; 814 data->period = *array;
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index 8577085db067..8e790dae7026 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -154,11 +154,13 @@ int event__process_comm(event_t *self, struct perf_session *session);
154int event__process_lost(event_t *self, struct perf_session *session); 154int event__process_lost(event_t *self, struct perf_session *session);
155int event__process_mmap(event_t *self, struct perf_session *session); 155int event__process_mmap(event_t *self, struct perf_session *session);
156int event__process_task(event_t *self, struct perf_session *session); 156int event__process_task(event_t *self, struct perf_session *session);
157int event__process(event_t *event, struct perf_session *session);
157 158
158struct addr_location; 159struct addr_location;
159int event__preprocess_sample(const event_t *self, struct perf_session *session, 160int event__preprocess_sample(const event_t *self, struct perf_session *session,
160 struct addr_location *al, symbol_filter_t filter); 161 struct addr_location *al, struct sample_data *data,
161int event__parse_sample(event_t *event, u64 type, struct sample_data *data); 162 symbol_filter_t filter);
163int event__parse_sample(const event_t *event, u64 type, struct sample_data *data);
162 164
163extern const char *event__name[]; 165extern const char *event__name[];
164 166
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 1f62435f96c2..d7e67b167ea3 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -16,6 +16,8 @@
16#include "symbol.h" 16#include "symbol.h"
17#include "debug.h" 17#include "debug.h"
18 18
19static bool no_buildid_cache = false;
20
19/* 21/*
20 * Create new perf.data header attribute: 22 * Create new perf.data header attribute:
21 */ 23 */
@@ -385,8 +387,7 @@ static int perf_session__cache_build_ids(struct perf_session *self)
385 int ret; 387 int ret;
386 char debugdir[PATH_MAX]; 388 char debugdir[PATH_MAX];
387 389
388 snprintf(debugdir, sizeof(debugdir), "%s/%s", getenv("HOME"), 390 snprintf(debugdir, sizeof(debugdir), "%s", buildid_dir);
389 DEBUG_CACHE_DIR);
390 391
391 if (mkdir(debugdir, 0755) != 0 && errno != EEXIST) 392 if (mkdir(debugdir, 0755) != 0 && errno != EEXIST)
392 return -1; 393 return -1;
@@ -471,7 +472,8 @@ static int perf_header__adds_write(struct perf_header *self, int fd)
471 } 472 }
472 buildid_sec->size = lseek(fd, 0, SEEK_CUR) - 473 buildid_sec->size = lseek(fd, 0, SEEK_CUR) -
473 buildid_sec->offset; 474 buildid_sec->offset;
474 perf_session__cache_build_ids(session); 475 if (!no_buildid_cache)
476 perf_session__cache_build_ids(session);
475 } 477 }
476 478
477 lseek(fd, sec_start, SEEK_SET); 479 lseek(fd, sec_start, SEEK_SET);
@@ -1190,3 +1192,8 @@ int event__process_build_id(event_t *self,
1190 session); 1192 session);
1191 return 0; 1193 return 0;
1192} 1194}
1195
1196void disable_buildid_cache(void)
1197{
1198 no_buildid_cache = true;
1199}
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index cbf7eae2ce09..be22ae6ef055 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -5,11 +5,61 @@
5#include "sort.h" 5#include "sort.h"
6#include <math.h> 6#include <math.h>
7 7
8enum hist_filter {
9 HIST_FILTER__DSO,
10 HIST_FILTER__THREAD,
11 HIST_FILTER__PARENT,
12};
13
8struct callchain_param callchain_param = { 14struct callchain_param callchain_param = {
9 .mode = CHAIN_GRAPH_REL, 15 .mode = CHAIN_GRAPH_REL,
10 .min_percent = 0.5 16 .min_percent = 0.5
11}; 17};
12 18
19u16 hists__col_len(struct hists *self, enum hist_column col)
20{
21 return self->col_len[col];
22}
23
24void hists__set_col_len(struct hists *self, enum hist_column col, u16 len)
25{
26 self->col_len[col] = len;
27}
28
29bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len)
30{
31 if (len > hists__col_len(self, col)) {
32 hists__set_col_len(self, col, len);
33 return true;
34 }
35 return false;
36}
37
38static void hists__reset_col_len(struct hists *self)
39{
40 enum hist_column col;
41
42 for (col = 0; col < HISTC_NR_COLS; ++col)
43 hists__set_col_len(self, col, 0);
44}
45
46static void hists__calc_col_len(struct hists *self, struct hist_entry *h)
47{
48 u16 len;
49
50 if (h->ms.sym)
51 hists__new_col_len(self, HISTC_SYMBOL, h->ms.sym->namelen);
52
53 len = thread__comm_len(h->thread);
54 if (hists__new_col_len(self, HISTC_COMM, len))
55 hists__set_col_len(self, HISTC_THREAD, len + 6);
56
57 if (h->ms.map) {
58 len = dso__name_len(h->ms.map->dso);
59 hists__new_col_len(self, HISTC_DSO, len);
60 }
61}
62
13static void hist_entry__add_cpumode_period(struct hist_entry *self, 63static void hist_entry__add_cpumode_period(struct hist_entry *self,
14 unsigned int cpumode, u64 period) 64 unsigned int cpumode, u64 period)
15{ 65{
@@ -43,6 +93,8 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template)
43 if (self != NULL) { 93 if (self != NULL) {
44 *self = *template; 94 *self = *template;
45 self->nr_events = 1; 95 self->nr_events = 1;
96 if (self->ms.map)
97 self->ms.map->referenced = true;
46 if (symbol_conf.use_callchain) 98 if (symbol_conf.use_callchain)
47 callchain_init(self->callchain); 99 callchain_init(self->callchain);
48 } 100 }
@@ -50,11 +102,19 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template)
50 return self; 102 return self;
51} 103}
52 104
53static void hists__inc_nr_entries(struct hists *self, struct hist_entry *entry) 105static void hists__inc_nr_entries(struct hists *self, struct hist_entry *h)
54{ 106{
55 if (entry->ms.sym && self->max_sym_namelen < entry->ms.sym->namelen) 107 if (!h->filtered) {
56 self->max_sym_namelen = entry->ms.sym->namelen; 108 hists__calc_col_len(self, h);
57 ++self->nr_entries; 109 ++self->nr_entries;
110 }
111}
112
113static u8 symbol__parent_filter(const struct symbol *parent)
114{
115 if (symbol_conf.exclude_other && parent == NULL)
116 return 1 << HIST_FILTER__PARENT;
117 return 0;
58} 118}
59 119
60struct hist_entry *__hists__add_entry(struct hists *self, 120struct hist_entry *__hists__add_entry(struct hists *self,
@@ -70,10 +130,12 @@ struct hist_entry *__hists__add_entry(struct hists *self,
70 .map = al->map, 130 .map = al->map,
71 .sym = al->sym, 131 .sym = al->sym,
72 }, 132 },
133 .cpu = al->cpu,
73 .ip = al->addr, 134 .ip = al->addr,
74 .level = al->level, 135 .level = al->level,
75 .period = period, 136 .period = period,
76 .parent = sym_parent, 137 .parent = sym_parent,
138 .filtered = symbol__parent_filter(sym_parent),
77 }; 139 };
78 int cmp; 140 int cmp;
79 141
@@ -191,7 +253,7 @@ void hists__collapse_resort(struct hists *self)
191 tmp = RB_ROOT; 253 tmp = RB_ROOT;
192 next = rb_first(&self->entries); 254 next = rb_first(&self->entries);
193 self->nr_entries = 0; 255 self->nr_entries = 0;
194 self->max_sym_namelen = 0; 256 hists__reset_col_len(self);
195 257
196 while (next) { 258 while (next) {
197 n = rb_entry(next, struct hist_entry, rb_node); 259 n = rb_entry(next, struct hist_entry, rb_node);
@@ -248,7 +310,7 @@ void hists__output_resort(struct hists *self)
248 next = rb_first(&self->entries); 310 next = rb_first(&self->entries);
249 311
250 self->nr_entries = 0; 312 self->nr_entries = 0;
251 self->max_sym_namelen = 0; 313 hists__reset_col_len(self);
252 314
253 while (next) { 315 while (next) {
254 n = rb_entry(next, struct hist_entry, rb_node); 316 n = rb_entry(next, struct hist_entry, rb_node);
@@ -515,8 +577,9 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self,
515} 577}
516 578
517int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, 579int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size,
518 struct hists *pair_hists, bool show_displacement, 580 struct hists *hists, struct hists *pair_hists,
519 long displacement, bool color, u64 session_total) 581 bool show_displacement, long displacement,
582 bool color, u64 session_total)
520{ 583{
521 struct sort_entry *se; 584 struct sort_entry *se;
522 u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us; 585 u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us;
@@ -620,24 +683,25 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size,
620 683
621 ret += snprintf(s + ret, size - ret, "%s", sep ?: " "); 684 ret += snprintf(s + ret, size - ret, "%s", sep ?: " ");
622 ret += se->se_snprintf(self, s + ret, size - ret, 685 ret += se->se_snprintf(self, s + ret, size - ret,
623 se->se_width ? *se->se_width : 0); 686 hists__col_len(hists, se->se_width_idx));
624 } 687 }
625 688
626 return ret; 689 return ret;
627} 690}
628 691
629int hist_entry__fprintf(struct hist_entry *self, struct hists *pair_hists, 692int hist_entry__fprintf(struct hist_entry *self, struct hists *hists,
630 bool show_displacement, long displacement, FILE *fp, 693 struct hists *pair_hists, bool show_displacement,
631 u64 session_total) 694 long displacement, FILE *fp, u64 session_total)
632{ 695{
633 char bf[512]; 696 char bf[512];
634 hist_entry__snprintf(self, bf, sizeof(bf), pair_hists, 697 hist_entry__snprintf(self, bf, sizeof(bf), hists, pair_hists,
635 show_displacement, displacement, 698 show_displacement, displacement,
636 true, session_total); 699 true, session_total);
637 return fprintf(fp, "%s\n", bf); 700 return fprintf(fp, "%s\n", bf);
638} 701}
639 702
640static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp, 703static size_t hist_entry__fprintf_callchain(struct hist_entry *self,
704 struct hists *hists, FILE *fp,
641 u64 session_total) 705 u64 session_total)
642{ 706{
643 int left_margin = 0; 707 int left_margin = 0;
@@ -645,7 +709,7 @@ static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp,
645 if (sort__first_dimension == SORT_COMM) { 709 if (sort__first_dimension == SORT_COMM) {
646 struct sort_entry *se = list_first_entry(&hist_entry__sort_list, 710 struct sort_entry *se = list_first_entry(&hist_entry__sort_list,
647 typeof(*se), list); 711 typeof(*se), list);
648 left_margin = se->se_width ? *se->se_width : 0; 712 left_margin = hists__col_len(hists, se->se_width_idx);
649 left_margin -= thread__comm_len(self->thread); 713 left_margin -= thread__comm_len(self->thread);
650 } 714 }
651 715
@@ -716,17 +780,17 @@ size_t hists__fprintf(struct hists *self, struct hists *pair,
716 continue; 780 continue;
717 } 781 }
718 width = strlen(se->se_header); 782 width = strlen(se->se_header);
719 if (se->se_width) { 783 if (symbol_conf.col_width_list_str) {
720 if (symbol_conf.col_width_list_str) { 784 if (col_width) {
721 if (col_width) { 785 hists__set_col_len(self, se->se_width_idx,
722 *se->se_width = atoi(col_width); 786 atoi(col_width));
723 col_width = strchr(col_width, ','); 787 col_width = strchr(col_width, ',');
724 if (col_width) 788 if (col_width)
725 ++col_width; 789 ++col_width;
726 }
727 } 790 }
728 width = *se->se_width = max(*se->se_width, width);
729 } 791 }
792 if (!hists__new_col_len(self, se->se_width_idx, width))
793 width = hists__col_len(self, se->se_width_idx);
730 fprintf(fp, " %*s", width, se->se_header); 794 fprintf(fp, " %*s", width, se->se_header);
731 } 795 }
732 fprintf(fp, "\n"); 796 fprintf(fp, "\n");
@@ -749,9 +813,8 @@ size_t hists__fprintf(struct hists *self, struct hists *pair,
749 continue; 813 continue;
750 814
751 fprintf(fp, " "); 815 fprintf(fp, " ");
752 if (se->se_width) 816 width = hists__col_len(self, se->se_width_idx);
753 width = *se->se_width; 817 if (width == 0)
754 else
755 width = strlen(se->se_header); 818 width = strlen(se->se_header);
756 for (i = 0; i < width; i++) 819 for (i = 0; i < width; i++)
757 fprintf(fp, "."); 820 fprintf(fp, ".");
@@ -771,12 +834,12 @@ print_entries:
771 displacement = 0; 834 displacement = 0;
772 ++position; 835 ++position;
773 } 836 }
774 ret += hist_entry__fprintf(h, pair, show_displacement, 837 ret += hist_entry__fprintf(h, self, pair, show_displacement,
775 displacement, fp, self->stats.total_period); 838 displacement, fp, self->stats.total_period);
776 839
777 if (symbol_conf.use_callchain) 840 if (symbol_conf.use_callchain)
778 ret += hist_entry__fprintf_callchain(h, fp, self->stats.total_period); 841 ret += hist_entry__fprintf_callchain(h, self, fp,
779 842 self->stats.total_period);
780 if (h->ms.map == NULL && verbose > 1) { 843 if (h->ms.map == NULL && verbose > 1) {
781 __map_groups__fprintf_maps(&h->thread->mg, 844 __map_groups__fprintf_maps(&h->thread->mg,
782 MAP__FUNCTION, verbose, fp); 845 MAP__FUNCTION, verbose, fp);
@@ -789,10 +852,52 @@ print_entries:
789 return ret; 852 return ret;
790} 853}
791 854
792enum hist_filter { 855/*
793 HIST_FILTER__DSO, 856 * See hists__fprintf to match the column widths
794 HIST_FILTER__THREAD, 857 */
795}; 858unsigned int hists__sort_list_width(struct hists *self)
859{
860 struct sort_entry *se;
861 int ret = 9; /* total % */
862
863 if (symbol_conf.show_cpu_utilization) {
864 ret += 7; /* count_sys % */
865 ret += 6; /* count_us % */
866 if (perf_guest) {
867 ret += 13; /* count_guest_sys % */
868 ret += 12; /* count_guest_us % */
869 }
870 }
871
872 if (symbol_conf.show_nr_samples)
873 ret += 11;
874
875 list_for_each_entry(se, &hist_entry__sort_list, list)
876 if (!se->elide)
877 ret += 2 + hists__col_len(self, se->se_width_idx);
878
879 if (verbose) /* Addr + origin */
880 ret += 3 + BITS_PER_LONG / 4;
881
882 return ret;
883}
884
885static void hists__remove_entry_filter(struct hists *self, struct hist_entry *h,
886 enum hist_filter filter)
887{
888 h->filtered &= ~(1 << filter);
889 if (h->filtered)
890 return;
891
892 ++self->nr_entries;
893 if (h->ms.unfolded)
894 self->nr_entries += h->nr_rows;
895 h->row_offset = 0;
896 self->stats.total_period += h->period;
897 self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events;
898
899 hists__calc_col_len(self, h);
900}
796 901
797void hists__filter_by_dso(struct hists *self, const struct dso *dso) 902void hists__filter_by_dso(struct hists *self, const struct dso *dso)
798{ 903{
@@ -800,7 +905,7 @@ void hists__filter_by_dso(struct hists *self, const struct dso *dso)
800 905
801 self->nr_entries = self->stats.total_period = 0; 906 self->nr_entries = self->stats.total_period = 0;
802 self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; 907 self->stats.nr_events[PERF_RECORD_SAMPLE] = 0;
803 self->max_sym_namelen = 0; 908 hists__reset_col_len(self);
804 909
805 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { 910 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
806 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 911 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
@@ -813,15 +918,7 @@ void hists__filter_by_dso(struct hists *self, const struct dso *dso)
813 continue; 918 continue;
814 } 919 }
815 920
816 h->filtered &= ~(1 << HIST_FILTER__DSO); 921 hists__remove_entry_filter(self, h, HIST_FILTER__DSO);
817 if (!h->filtered) {
818 ++self->nr_entries;
819 self->stats.total_period += h->period;
820 self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events;
821 if (h->ms.sym &&
822 self->max_sym_namelen < h->ms.sym->namelen)
823 self->max_sym_namelen = h->ms.sym->namelen;
824 }
825 } 922 }
826} 923}
827 924
@@ -831,7 +928,7 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread)
831 928
832 self->nr_entries = self->stats.total_period = 0; 929 self->nr_entries = self->stats.total_period = 0;
833 self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; 930 self->stats.nr_events[PERF_RECORD_SAMPLE] = 0;
834 self->max_sym_namelen = 0; 931 hists__reset_col_len(self);
835 932
836 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { 933 for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
837 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); 934 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
@@ -840,15 +937,8 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread)
840 h->filtered |= (1 << HIST_FILTER__THREAD); 937 h->filtered |= (1 << HIST_FILTER__THREAD);
841 continue; 938 continue;
842 } 939 }
843 h->filtered &= ~(1 << HIST_FILTER__THREAD); 940
844 if (!h->filtered) { 941 hists__remove_entry_filter(self, h, HIST_FILTER__THREAD);
845 ++self->nr_entries;
846 self->stats.total_period += h->period;
847 self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events;
848 if (h->ms.sym &&
849 self->max_sym_namelen < h->ms.sym->namelen)
850 self->max_sym_namelen = h->ms.sym->namelen;
851 }
852 } 942 }
853} 943}
854 944
@@ -893,9 +983,9 @@ int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip)
893 return 0; 983 return 0;
894} 984}
895 985
896static struct objdump_line *objdump_line__new(s64 offset, char *line) 986static struct objdump_line *objdump_line__new(s64 offset, char *line, size_t privsize)
897{ 987{
898 struct objdump_line *self = malloc(sizeof(*self)); 988 struct objdump_line *self = malloc(sizeof(*self) + privsize);
899 989
900 if (self != NULL) { 990 if (self != NULL) {
901 self->offset = offset; 991 self->offset = offset;
@@ -927,7 +1017,7 @@ struct objdump_line *objdump__get_next_ip_line(struct list_head *head,
927} 1017}
928 1018
929static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file, 1019static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file,
930 struct list_head *head) 1020 struct list_head *head, size_t privsize)
931{ 1021{
932 struct symbol *sym = self->ms.sym; 1022 struct symbol *sym = self->ms.sym;
933 struct objdump_line *objdump_line; 1023 struct objdump_line *objdump_line;
@@ -965,16 +1055,20 @@ static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file,
965 * Parse hexa addresses followed by ':' 1055 * Parse hexa addresses followed by ':'
966 */ 1056 */
967 line_ip = strtoull(tmp, &tmp2, 16); 1057 line_ip = strtoull(tmp, &tmp2, 16);
968 if (*tmp2 != ':') 1058 if (*tmp2 != ':' || tmp == tmp2 || tmp2[1] == '\0')
969 line_ip = -1; 1059 line_ip = -1;
970 } 1060 }
971 1061
972 if (line_ip != -1) { 1062 if (line_ip != -1) {
973 u64 start = map__rip_2objdump(self->ms.map, sym->start); 1063 u64 start = map__rip_2objdump(self->ms.map, sym->start),
1064 end = map__rip_2objdump(self->ms.map, sym->end);
1065
974 offset = line_ip - start; 1066 offset = line_ip - start;
1067 if (offset < 0 || (u64)line_ip > end)
1068 offset = -1;
975 } 1069 }
976 1070
977 objdump_line = objdump_line__new(offset, line); 1071 objdump_line = objdump_line__new(offset, line, privsize);
978 if (objdump_line == NULL) { 1072 if (objdump_line == NULL) {
979 free(line); 1073 free(line);
980 return -1; 1074 return -1;
@@ -984,7 +1078,8 @@ static int hist_entry__parse_objdump_line(struct hist_entry *self, FILE *file,
984 return 0; 1078 return 0;
985} 1079}
986 1080
987int hist_entry__annotate(struct hist_entry *self, struct list_head *head) 1081int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
1082 size_t privsize)
988{ 1083{
989 struct symbol *sym = self->ms.sym; 1084 struct symbol *sym = self->ms.sym;
990 struct map *map = self->ms.map; 1085 struct map *map = self->ms.map;
@@ -1037,7 +1132,7 @@ fallback:
1037 dso, dso->long_name, sym, sym->name); 1132 dso, dso->long_name, sym, sym->name);
1038 1133
1039 snprintf(command, sizeof(command), 1134 snprintf(command, sizeof(command),
1040 "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s|expand", 1135 "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS -C %s|grep -v %s|expand",
1041 map__rip_2objdump(map, sym->start), 1136 map__rip_2objdump(map, sym->start),
1042 map__rip_2objdump(map, sym->end), 1137 map__rip_2objdump(map, sym->end),
1043 filename, filename); 1138 filename, filename);
@@ -1049,7 +1144,7 @@ fallback:
1049 goto out_free_filename; 1144 goto out_free_filename;
1050 1145
1051 while (!feof(file)) 1146 while (!feof(file))
1052 if (hist_entry__parse_objdump_line(self, file, head) < 0) 1147 if (hist_entry__parse_objdump_line(self, file, head, privsize) < 0)
1053 break; 1148 break;
1054 1149
1055 pclose(file); 1150 pclose(file);
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index 83fa33a7b38b..587d375d3430 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -56,6 +56,16 @@ struct events_stats {
56 u32 nr_unknown_events; 56 u32 nr_unknown_events;
57}; 57};
58 58
59enum hist_column {
60 HISTC_SYMBOL,
61 HISTC_DSO,
62 HISTC_THREAD,
63 HISTC_COMM,
64 HISTC_PARENT,
65 HISTC_CPU,
66 HISTC_NR_COLS, /* Last entry */
67};
68
59struct hists { 69struct hists {
60 struct rb_node rb_node; 70 struct rb_node rb_node;
61 struct rb_root entries; 71 struct rb_root entries;
@@ -64,7 +74,7 @@ struct hists {
64 u64 config; 74 u64 config;
65 u64 event_stream; 75 u64 event_stream;
66 u32 type; 76 u32 type;
67 u32 max_sym_namelen; 77 u16 col_len[HISTC_NR_COLS];
68}; 78};
69 79
70struct hist_entry *__hists__add_entry(struct hists *self, 80struct hist_entry *__hists__add_entry(struct hists *self,
@@ -72,12 +82,13 @@ struct hist_entry *__hists__add_entry(struct hists *self,
72 struct symbol *parent, u64 period); 82 struct symbol *parent, u64 period);
73extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); 83extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *);
74extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); 84extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *);
75int hist_entry__fprintf(struct hist_entry *self, struct hists *pair_hists, 85int hist_entry__fprintf(struct hist_entry *self, struct hists *hists,
76 bool show_displacement, long displacement, FILE *fp, 86 struct hists *pair_hists, bool show_displacement,
77 u64 total); 87 long displacement, FILE *fp, u64 total);
78int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size, 88int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size,
79 struct hists *pair_hists, bool show_displacement, 89 struct hists *hists, struct hists *pair_hists,
80 long displacement, bool color, u64 total); 90 bool show_displacement, long displacement,
91 bool color, u64 total);
81void hist_entry__free(struct hist_entry *); 92void hist_entry__free(struct hist_entry *);
82 93
83void hists__output_resort(struct hists *self); 94void hists__output_resort(struct hists *self);
@@ -90,11 +101,16 @@ size_t hists__fprintf(struct hists *self, struct hists *pair,
90 bool show_displacement, FILE *fp); 101 bool show_displacement, FILE *fp);
91 102
92int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip); 103int hist_entry__inc_addr_samples(struct hist_entry *self, u64 ip);
93int hist_entry__annotate(struct hist_entry *self, struct list_head *head); 104int hist_entry__annotate(struct hist_entry *self, struct list_head *head,
105 size_t privsize);
94 106
95void hists__filter_by_dso(struct hists *self, const struct dso *dso); 107void hists__filter_by_dso(struct hists *self, const struct dso *dso);
96void hists__filter_by_thread(struct hists *self, const struct thread *thread); 108void hists__filter_by_thread(struct hists *self, const struct thread *thread);
97 109
110u16 hists__col_len(struct hists *self, enum hist_column col);
111void hists__set_col_len(struct hists *self, enum hist_column col, u16 len);
112bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len);
113
98#ifdef NO_NEWT_SUPPORT 114#ifdef NO_NEWT_SUPPORT
99static inline int hists__browse(struct hists *self __used, 115static inline int hists__browse(struct hists *self __used,
100 const char *helpline __used, 116 const char *helpline __used,
@@ -126,4 +142,7 @@ int hist_entry__tui_annotate(struct hist_entry *self);
126 142
127int hists__tui_browse_tree(struct rb_root *self, const char *help); 143int hists__tui_browse_tree(struct rb_root *self, const char *help);
128#endif 144#endif
145
146unsigned int hists__sort_list_width(struct hists *self);
147
129#endif /* __PERF_HIST_H */ 148#endif /* __PERF_HIST_H */
diff --git a/tools/perf/util/include/linux/list.h b/tools/perf/util/include/linux/list.h
index dbe4b814382a..f5ca26e53fbb 100644
--- a/tools/perf/util/include/linux/list.h
+++ b/tools/perf/util/include/linux/list.h
@@ -15,4 +15,12 @@ static inline void list_del_range(struct list_head *begin,
15 begin->prev->next = end->next; 15 begin->prev->next = end->next;
16 end->next->prev = begin->prev; 16 end->next->prev = begin->prev;
17} 17}
18
19/**
20 * list_for_each_from - iterate over a list from one of its nodes
21 * @pos: the &struct list_head to use as a loop cursor, from where to start
22 * @head: the head for your list.
23 */
24#define list_for_each_from(pos, head) \
25 for (; prefetch(pos->next), pos != (head); pos = pos->next)
18#endif 26#endif
diff --git a/tools/perf/util/include/linux/types.h b/tools/perf/util/include/linux/types.h
index 196862a81a21..12de3b8112f9 100644
--- a/tools/perf/util/include/linux/types.h
+++ b/tools/perf/util/include/linux/types.h
@@ -6,4 +6,16 @@
6#define DECLARE_BITMAP(name,bits) \ 6#define DECLARE_BITMAP(name,bits) \
7 unsigned long name[BITS_TO_LONGS(bits)] 7 unsigned long name[BITS_TO_LONGS(bits)]
8 8
9struct list_head {
10 struct list_head *next, *prev;
11};
12
13struct hlist_head {
14 struct hlist_node *first;
15};
16
17struct hlist_node {
18 struct hlist_node *next, **pprev;
19};
20
9#endif 21#endif
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index e672f2fef65b..3a7eb6ec0eec 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -17,16 +17,6 @@ static inline int is_anon_memory(const char *filename)
17 return strcmp(filename, "//anon") == 0; 17 return strcmp(filename, "//anon") == 0;
18} 18}
19 19
20static int strcommon(const char *pathname, char *cwd, int cwdlen)
21{
22 int n = 0;
23
24 while (n < cwdlen && pathname[n] == cwd[n])
25 ++n;
26
27 return n;
28}
29
30void map__init(struct map *self, enum map_type type, 20void map__init(struct map *self, enum map_type type,
31 u64 start, u64 end, u64 pgoff, struct dso *dso) 21 u64 start, u64 end, u64 pgoff, struct dso *dso)
32{ 22{
@@ -39,11 +29,12 @@ void map__init(struct map *self, enum map_type type,
39 self->unmap_ip = map__unmap_ip; 29 self->unmap_ip = map__unmap_ip;
40 RB_CLEAR_NODE(&self->rb_node); 30 RB_CLEAR_NODE(&self->rb_node);
41 self->groups = NULL; 31 self->groups = NULL;
32 self->referenced = false;
42} 33}
43 34
44struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, 35struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
45 u64 pgoff, u32 pid, char *filename, 36 u64 pgoff, u32 pid, char *filename,
46 enum map_type type, char *cwd, int cwdlen) 37 enum map_type type)
47{ 38{
48 struct map *self = malloc(sizeof(*self)); 39 struct map *self = malloc(sizeof(*self));
49 40
@@ -52,16 +43,6 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
52 struct dso *dso; 43 struct dso *dso;
53 int anon; 44 int anon;
54 45
55 if (cwd) {
56 int n = strcommon(filename, cwd, cwdlen);
57
58 if (n == cwdlen) {
59 snprintf(newfilename, sizeof(newfilename),
60 ".%s", filename + n);
61 filename = newfilename;
62 }
63 }
64
65 anon = is_anon_memory(filename); 46 anon = is_anon_memory(filename);
66 47
67 if (anon) { 48 if (anon) {
@@ -248,6 +229,39 @@ void map_groups__init(struct map_groups *self)
248 self->machine = NULL; 229 self->machine = NULL;
249} 230}
250 231
232static void maps__delete(struct rb_root *self)
233{
234 struct rb_node *next = rb_first(self);
235
236 while (next) {
237 struct map *pos = rb_entry(next, struct map, rb_node);
238
239 next = rb_next(&pos->rb_node);
240 rb_erase(&pos->rb_node, self);
241 map__delete(pos);
242 }
243}
244
245static void maps__delete_removed(struct list_head *self)
246{
247 struct map *pos, *n;
248
249 list_for_each_entry_safe(pos, n, self, node) {
250 list_del(&pos->node);
251 map__delete(pos);
252 }
253}
254
255void map_groups__exit(struct map_groups *self)
256{
257 int i;
258
259 for (i = 0; i < MAP__NR_TYPES; ++i) {
260 maps__delete(&self->maps[i]);
261 maps__delete_removed(&self->removed_maps[i]);
262 }
263}
264
251void map_groups__flush(struct map_groups *self) 265void map_groups__flush(struct map_groups *self)
252{ 266{
253 int type; 267 int type;
@@ -374,6 +388,7 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map,
374{ 388{
375 struct rb_root *root = &self->maps[map->type]; 389 struct rb_root *root = &self->maps[map->type];
376 struct rb_node *next = rb_first(root); 390 struct rb_node *next = rb_first(root);
391 int err = 0;
377 392
378 while (next) { 393 while (next) {
379 struct map *pos = rb_entry(next, struct map, rb_node); 394 struct map *pos = rb_entry(next, struct map, rb_node);
@@ -390,20 +405,16 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map,
390 405
391 rb_erase(&pos->rb_node, root); 406 rb_erase(&pos->rb_node, root);
392 /* 407 /*
393 * We may have references to this map, for instance in some
394 * hist_entry instances, so just move them to a separate
395 * list.
396 */
397 list_add_tail(&pos->node, &self->removed_maps[map->type]);
398 /*
399 * Now check if we need to create new maps for areas not 408 * Now check if we need to create new maps for areas not
400 * overlapped by the new map: 409 * overlapped by the new map:
401 */ 410 */
402 if (map->start > pos->start) { 411 if (map->start > pos->start) {
403 struct map *before = map__clone(pos); 412 struct map *before = map__clone(pos);
404 413
405 if (before == NULL) 414 if (before == NULL) {
406 return -ENOMEM; 415 err = -ENOMEM;
416 goto move_map;
417 }
407 418
408 before->end = map->start - 1; 419 before->end = map->start - 1;
409 map_groups__insert(self, before); 420 map_groups__insert(self, before);
@@ -414,14 +425,27 @@ int map_groups__fixup_overlappings(struct map_groups *self, struct map *map,
414 if (map->end < pos->end) { 425 if (map->end < pos->end) {
415 struct map *after = map__clone(pos); 426 struct map *after = map__clone(pos);
416 427
417 if (after == NULL) 428 if (after == NULL) {
418 return -ENOMEM; 429 err = -ENOMEM;
430 goto move_map;
431 }
419 432
420 after->start = map->end + 1; 433 after->start = map->end + 1;
421 map_groups__insert(self, after); 434 map_groups__insert(self, after);
422 if (verbose >= 2) 435 if (verbose >= 2)
423 map__fprintf(after, fp); 436 map__fprintf(after, fp);
424 } 437 }
438move_map:
439 /*
440 * If we have references, just move them to a separate list.
441 */
442 if (pos->referenced)
443 list_add_tail(&pos->node, &self->removed_maps[map->type]);
444 else
445 map__delete(pos);
446
447 if (err)
448 return err;
425 } 449 }
426 450
427 return 0; 451 return 0;
@@ -493,6 +517,11 @@ void maps__insert(struct rb_root *maps, struct map *map)
493 rb_insert_color(&map->rb_node, maps); 517 rb_insert_color(&map->rb_node, maps);
494} 518}
495 519
520void maps__remove(struct rb_root *self, struct map *map)
521{
522 rb_erase(&map->rb_node, self);
523}
524
496struct map *maps__find(struct rb_root *maps, u64 ip) 525struct map *maps__find(struct rb_root *maps, u64 ip)
497{ 526{
498 struct rb_node **p = &maps->rb_node; 527 struct rb_node **p = &maps->rb_node;
@@ -526,6 +555,31 @@ int machine__init(struct machine *self, const char *root_dir, pid_t pid)
526 return self->root_dir == NULL ? -ENOMEM : 0; 555 return self->root_dir == NULL ? -ENOMEM : 0;
527} 556}
528 557
558static void dsos__delete(struct list_head *self)
559{
560 struct dso *pos, *n;
561
562 list_for_each_entry_safe(pos, n, self, node) {
563 list_del(&pos->node);
564 dso__delete(pos);
565 }
566}
567
568void machine__exit(struct machine *self)
569{
570 map_groups__exit(&self->kmaps);
571 dsos__delete(&self->user_dsos);
572 dsos__delete(&self->kernel_dsos);
573 free(self->root_dir);
574 self->root_dir = NULL;
575}
576
577void machine__delete(struct machine *self)
578{
579 machine__exit(self);
580 free(self);
581}
582
529struct machine *machines__add(struct rb_root *self, pid_t pid, 583struct machine *machines__add(struct rb_root *self, pid_t pid,
530 const char *root_dir) 584 const char *root_dir)
531{ 585{
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index f39134512829..78575796d5f3 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -29,7 +29,8 @@ struct map {
29 }; 29 };
30 u64 start; 30 u64 start;
31 u64 end; 31 u64 end;
32 enum map_type type; 32 u8 /* enum map_type */ type;
33 bool referenced;
33 u32 priv; 34 u32 priv;
34 u64 pgoff; 35 u64 pgoff;
35 36
@@ -106,7 +107,7 @@ void map__init(struct map *self, enum map_type type,
106 u64 start, u64 end, u64 pgoff, struct dso *dso); 107 u64 start, u64 end, u64 pgoff, struct dso *dso);
107struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, 108struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
108 u64 pgoff, u32 pid, char *filename, 109 u64 pgoff, u32 pid, char *filename,
109 enum map_type type, char *cwd, int cwdlen); 110 enum map_type type);
110void map__delete(struct map *self); 111void map__delete(struct map *self);
111struct map *map__clone(struct map *self); 112struct map *map__clone(struct map *self);
112int map__overlap(struct map *l, struct map *r); 113int map__overlap(struct map *l, struct map *r);
@@ -125,8 +126,10 @@ void map__reloc_vmlinux(struct map *self);
125size_t __map_groups__fprintf_maps(struct map_groups *self, 126size_t __map_groups__fprintf_maps(struct map_groups *self,
126 enum map_type type, int verbose, FILE *fp); 127 enum map_type type, int verbose, FILE *fp);
127void 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);
128struct map *maps__find(struct rb_root *maps, u64 addr); 130struct map *maps__find(struct rb_root *maps, u64 addr);
129void map_groups__init(struct map_groups *self); 131void map_groups__init(struct map_groups *self);
132void map_groups__exit(struct map_groups *self);
130int map_groups__clone(struct map_groups *self, 133int map_groups__clone(struct map_groups *self,
131 struct map_groups *parent, enum map_type type); 134 struct map_groups *parent, enum map_type type);
132size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp); 135size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp);
@@ -142,6 +145,8 @@ struct machine *machines__find(struct rb_root *self, pid_t pid);
142struct machine *machines__findnew(struct rb_root *self, pid_t pid); 145struct machine *machines__findnew(struct rb_root *self, pid_t pid);
143char *machine__mmap_name(struct machine *self, char *bf, size_t size); 146char *machine__mmap_name(struct machine *self, char *bf, size_t size);
144int machine__init(struct machine *self, const char *root_dir, pid_t pid); 147int machine__init(struct machine *self, const char *root_dir, pid_t pid);
148void machine__exit(struct machine *self);
149void machine__delete(struct machine *self);
145 150
146/* 151/*
147 * Default guest kernel is defined by parameter --guestkallsyms 152 * Default guest kernel is defined by parameter --guestkallsyms
@@ -163,6 +168,11 @@ static inline void map_groups__insert(struct map_groups *self, struct map *map)
163 map->groups = self; 168 map->groups = self;
164} 169}
165 170
171static inline void map_groups__remove(struct map_groups *self, struct map *map)
172{
173 maps__remove(&self->maps[map->type], map);
174}
175
166static inline struct map *map_groups__find(struct map_groups *self, 176static inline struct map *map_groups__find(struct map_groups *self,
167 enum map_type type, u64 addr) 177 enum map_type type, u64 addr)
168{ 178{
diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c
deleted file mode 100644
index cf182ca132fe..000000000000
--- a/tools/perf/util/newt.c
+++ /dev/null
@@ -1,1167 +0,0 @@
1#define _GNU_SOURCE
2#include <stdio.h>
3#undef _GNU_SOURCE
4/*
5 * slang versions <= 2.0.6 have a "#if HAVE_LONG_LONG" that breaks
6 * the build if it isn't defined. Use the equivalent one that glibc
7 * has on features.h.
8 */
9#include <features.h>
10#ifndef HAVE_LONG_LONG
11#define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG
12#endif
13#include <slang.h>
14#include <stdlib.h>
15#include <newt.h>
16#include <sys/ttydefaults.h>
17
18#include "cache.h"
19#include "hist.h"
20#include "pstack.h"
21#include "session.h"
22#include "sort.h"
23#include "symbol.h"
24
25#if SLANG_VERSION < 20104
26#define slsmg_printf(msg, args...) SLsmg_printf((char *)msg, ##args)
27#define slsmg_write_nstring(msg, len) SLsmg_write_nstring((char *)msg, len)
28#define sltt_set_color(obj, name, fg, bg) SLtt_set_color(obj,(char *)name,\
29 (char *)fg, (char *)bg)
30#else
31#define slsmg_printf SLsmg_printf
32#define slsmg_write_nstring SLsmg_write_nstring
33#define sltt_set_color SLtt_set_color
34#endif
35
36struct ui_progress {
37 newtComponent form, scale;
38};
39
40struct ui_progress *ui_progress__new(const char *title, u64 total)
41{
42 struct ui_progress *self = malloc(sizeof(*self));
43
44 if (self != NULL) {
45 int cols;
46 newtGetScreenSize(&cols, NULL);
47 cols -= 4;
48 newtCenteredWindow(cols, 1, title);
49 self->form = newtForm(NULL, NULL, 0);
50 if (self->form == NULL)
51 goto out_free_self;
52 self->scale = newtScale(0, 0, cols, total);
53 if (self->scale == NULL)
54 goto out_free_form;
55 newtFormAddComponent(self->form, self->scale);
56 newtRefresh();
57 }
58
59 return self;
60
61out_free_form:
62 newtFormDestroy(self->form);
63out_free_self:
64 free(self);
65 return NULL;
66}
67
68void ui_progress__update(struct ui_progress *self, u64 curr)
69{
70 newtScaleSet(self->scale, curr);
71 newtRefresh();
72}
73
74void ui_progress__delete(struct ui_progress *self)
75{
76 newtFormDestroy(self->form);
77 newtPopWindow();
78 free(self);
79}
80
81static void ui_helpline__pop(void)
82{
83 newtPopHelpLine();
84}
85
86static void ui_helpline__push(const char *msg)
87{
88 newtPushHelpLine(msg);
89}
90
91static void ui_helpline__vpush(const char *fmt, va_list ap)
92{
93 char *s;
94
95 if (vasprintf(&s, fmt, ap) < 0)
96 vfprintf(stderr, fmt, ap);
97 else {
98 ui_helpline__push(s);
99 free(s);
100 }
101}
102
103static void ui_helpline__fpush(const char *fmt, ...)
104{
105 va_list ap;
106
107 va_start(ap, fmt);
108 ui_helpline__vpush(fmt, ap);
109 va_end(ap);
110}
111
112static void ui_helpline__puts(const char *msg)
113{
114 ui_helpline__pop();
115 ui_helpline__push(msg);
116}
117
118static char browser__last_msg[1024];
119
120int browser__show_help(const char *format, va_list ap)
121{
122 int ret;
123 static int backlog;
124
125 ret = vsnprintf(browser__last_msg + backlog,
126 sizeof(browser__last_msg) - backlog, format, ap);
127 backlog += ret;
128
129 if (browser__last_msg[backlog - 1] == '\n') {
130 ui_helpline__puts(browser__last_msg);
131 newtRefresh();
132 backlog = 0;
133 }
134
135 return ret;
136}
137
138static void newt_form__set_exit_keys(newtComponent self)
139{
140 newtFormAddHotKey(self, NEWT_KEY_LEFT);
141 newtFormAddHotKey(self, NEWT_KEY_ESCAPE);
142 newtFormAddHotKey(self, 'Q');
143 newtFormAddHotKey(self, 'q');
144 newtFormAddHotKey(self, CTRL('c'));
145}
146
147static newtComponent newt_form__new(void)
148{
149 newtComponent self = newtForm(NULL, NULL, 0);
150 if (self)
151 newt_form__set_exit_keys(self);
152 return self;
153}
154
155static int popup_menu(int argc, char * const argv[])
156{
157 struct newtExitStruct es;
158 int i, rc = -1, max_len = 5;
159 newtComponent listbox, form = newt_form__new();
160
161 if (form == NULL)
162 return -1;
163
164 listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT);
165 if (listbox == NULL)
166 goto out_destroy_form;
167
168 newtFormAddComponent(form, listbox);
169
170 for (i = 0; i < argc; ++i) {
171 int len = strlen(argv[i]);
172 if (len > max_len)
173 max_len = len;
174 if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i))
175 goto out_destroy_form;
176 }
177
178 newtCenteredWindow(max_len, argc, NULL);
179 newtFormRun(form, &es);
180 rc = newtListboxGetCurrent(listbox) - NULL;
181 if (es.reason == NEWT_EXIT_HOTKEY)
182 rc = -1;
183 newtPopWindow();
184out_destroy_form:
185 newtFormDestroy(form);
186 return rc;
187}
188
189static int ui__help_window(const char *text)
190{
191 struct newtExitStruct es;
192 newtComponent tb, form = newt_form__new();
193 int rc = -1;
194 int max_len = 0, nr_lines = 0;
195 const char *t;
196
197 if (form == NULL)
198 return -1;
199
200 t = text;
201 while (1) {
202 const char *sep = strchr(t, '\n');
203 int len;
204
205 if (sep == NULL)
206 sep = strchr(t, '\0');
207 len = sep - t;
208 if (max_len < len)
209 max_len = len;
210 ++nr_lines;
211 if (*sep == '\0')
212 break;
213 t = sep + 1;
214 }
215
216 tb = newtTextbox(0, 0, max_len, nr_lines, 0);
217 if (tb == NULL)
218 goto out_destroy_form;
219
220 newtTextboxSetText(tb, text);
221 newtFormAddComponent(form, tb);
222 newtCenteredWindow(max_len, nr_lines, NULL);
223 newtFormRun(form, &es);
224 newtPopWindow();
225 rc = 0;
226out_destroy_form:
227 newtFormDestroy(form);
228 return rc;
229}
230
231static bool dialog_yesno(const char *msg)
232{
233 /* newtWinChoice should really be accepting const char pointers... */
234 char yes[] = "Yes", no[] = "No";
235 return newtWinChoice(NULL, yes, no, (char *)msg) == 1;
236}
237
238static void ui__error_window(const char *fmt, ...)
239{
240 va_list ap;
241
242 va_start(ap, fmt);
243 newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap);
244 va_end(ap);
245}
246
247#define HE_COLORSET_TOP 50
248#define HE_COLORSET_MEDIUM 51
249#define HE_COLORSET_NORMAL 52
250#define HE_COLORSET_SELECTED 53
251#define HE_COLORSET_CODE 54
252
253static int ui_browser__percent_color(double percent, bool current)
254{
255 if (current)
256 return HE_COLORSET_SELECTED;
257 if (percent >= MIN_RED)
258 return HE_COLORSET_TOP;
259 if (percent >= MIN_GREEN)
260 return HE_COLORSET_MEDIUM;
261 return HE_COLORSET_NORMAL;
262}
263
264struct ui_browser {
265 newtComponent form, sb;
266 u64 index, first_visible_entry_idx;
267 void *first_visible_entry, *entries;
268 u16 top, left, width, height;
269 void *priv;
270 u32 nr_entries;
271};
272
273static void ui_browser__refresh_dimensions(struct ui_browser *self)
274{
275 int cols, rows;
276 newtGetScreenSize(&cols, &rows);
277
278 if (self->width > cols - 4)
279 self->width = cols - 4;
280 self->height = rows - 5;
281 if (self->height > self->nr_entries)
282 self->height = self->nr_entries;
283 self->top = (rows - self->height) / 2;
284 self->left = (cols - self->width) / 2;
285}
286
287static void ui_browser__reset_index(struct ui_browser *self)
288{
289 self->index = self->first_visible_entry_idx = 0;
290 self->first_visible_entry = NULL;
291}
292
293static int objdump_line__show(struct objdump_line *self, struct list_head *head,
294 int width, struct hist_entry *he, int len,
295 bool current_entry)
296{
297 if (self->offset != -1) {
298 struct symbol *sym = he->ms.sym;
299 unsigned int hits = 0;
300 double percent = 0.0;
301 int color;
302 struct sym_priv *priv = symbol__priv(sym);
303 struct sym_ext *sym_ext = priv->ext;
304 struct sym_hist *h = priv->hist;
305 s64 offset = self->offset;
306 struct objdump_line *next = objdump__get_next_ip_line(head, self);
307
308 while (offset < (s64)len &&
309 (next == NULL || offset < next->offset)) {
310 if (sym_ext) {
311 percent += sym_ext[offset].percent;
312 } else
313 hits += h->ip[offset];
314
315 ++offset;
316 }
317
318 if (sym_ext == NULL && h->sum)
319 percent = 100.0 * hits / h->sum;
320
321 color = ui_browser__percent_color(percent, current_entry);
322 SLsmg_set_color(color);
323 slsmg_printf(" %7.2f ", percent);
324 if (!current_entry)
325 SLsmg_set_color(HE_COLORSET_CODE);
326 } else {
327 int color = ui_browser__percent_color(0, current_entry);
328 SLsmg_set_color(color);
329 slsmg_write_nstring(" ", 9);
330 }
331
332 SLsmg_write_char(':');
333 slsmg_write_nstring(" ", 8);
334 if (!*self->line)
335 slsmg_write_nstring(" ", width - 18);
336 else
337 slsmg_write_nstring(self->line, width - 18);
338
339 return 0;
340}
341
342static int ui_browser__refresh_entries(struct ui_browser *self)
343{
344 struct objdump_line *pos;
345 struct list_head *head = self->entries;
346 struct hist_entry *he = self->priv;
347 int row = 0;
348 int len = he->ms.sym->end - he->ms.sym->start;
349
350 if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries)
351 self->first_visible_entry = head->next;
352
353 pos = list_entry(self->first_visible_entry, struct objdump_line, node);
354
355 list_for_each_entry_from(pos, head, node) {
356 bool current_entry = (self->first_visible_entry_idx + row) == self->index;
357 SLsmg_gotorc(self->top + row, self->left);
358 objdump_line__show(pos, head, self->width,
359 he, len, current_entry);
360 if (++row == self->height)
361 break;
362 }
363
364 SLsmg_set_color(HE_COLORSET_NORMAL);
365 SLsmg_fill_region(self->top + row, self->left,
366 self->height - row, self->width, ' ');
367
368 return 0;
369}
370
371static int ui_browser__run(struct ui_browser *self, const char *title,
372 struct newtExitStruct *es)
373{
374 if (self->form) {
375 newtFormDestroy(self->form);
376 newtPopWindow();
377 }
378
379 ui_browser__refresh_dimensions(self);
380 newtCenteredWindow(self->width + 2, self->height, title);
381 self->form = newt_form__new();
382 if (self->form == NULL)
383 return -1;
384
385 self->sb = newtVerticalScrollbar(self->width + 1, 0, self->height,
386 HE_COLORSET_NORMAL,
387 HE_COLORSET_SELECTED);
388 if (self->sb == NULL)
389 return -1;
390
391 newtFormAddHotKey(self->form, NEWT_KEY_UP);
392 newtFormAddHotKey(self->form, NEWT_KEY_DOWN);
393 newtFormAddHotKey(self->form, NEWT_KEY_PGUP);
394 newtFormAddHotKey(self->form, NEWT_KEY_PGDN);
395 newtFormAddHotKey(self->form, ' ');
396 newtFormAddHotKey(self->form, NEWT_KEY_HOME);
397 newtFormAddHotKey(self->form, NEWT_KEY_END);
398 newtFormAddHotKey(self->form, NEWT_KEY_TAB);
399 newtFormAddHotKey(self->form, NEWT_KEY_RIGHT);
400
401 if (ui_browser__refresh_entries(self) < 0)
402 return -1;
403 newtFormAddComponent(self->form, self->sb);
404
405 while (1) {
406 unsigned int offset;
407
408 newtFormRun(self->form, es);
409
410 if (es->reason != NEWT_EXIT_HOTKEY)
411 break;
412 if (is_exit_key(es->u.key))
413 return es->u.key;
414 switch (es->u.key) {
415 case NEWT_KEY_DOWN:
416 if (self->index == self->nr_entries - 1)
417 break;
418 ++self->index;
419 if (self->index == self->first_visible_entry_idx + self->height) {
420 struct list_head *pos = self->first_visible_entry;
421 ++self->first_visible_entry_idx;
422 self->first_visible_entry = pos->next;
423 }
424 break;
425 case NEWT_KEY_UP:
426 if (self->index == 0)
427 break;
428 --self->index;
429 if (self->index < self->first_visible_entry_idx) {
430 struct list_head *pos = self->first_visible_entry;
431 --self->first_visible_entry_idx;
432 self->first_visible_entry = pos->prev;
433 }
434 break;
435 case NEWT_KEY_PGDN:
436 case ' ':
437 if (self->first_visible_entry_idx + self->height > self->nr_entries - 1)
438 break;
439
440 offset = self->height;
441 if (self->index + offset > self->nr_entries - 1)
442 offset = self->nr_entries - 1 - self->index;
443 self->index += offset;
444 self->first_visible_entry_idx += offset;
445
446 while (offset--) {
447 struct list_head *pos = self->first_visible_entry;
448 self->first_visible_entry = pos->next;
449 }
450
451 break;
452 case NEWT_KEY_PGUP:
453 if (self->first_visible_entry_idx == 0)
454 break;
455
456 if (self->first_visible_entry_idx < self->height)
457 offset = self->first_visible_entry_idx;
458 else
459 offset = self->height;
460
461 self->index -= offset;
462 self->first_visible_entry_idx -= offset;
463
464 while (offset--) {
465 struct list_head *pos = self->first_visible_entry;
466 self->first_visible_entry = pos->prev;
467 }
468 break;
469 case NEWT_KEY_HOME:
470 ui_browser__reset_index(self);
471 break;
472 case NEWT_KEY_END: {
473 struct list_head *head = self->entries;
474 offset = self->height - 1;
475
476 if (offset > self->nr_entries)
477 offset = self->nr_entries;
478
479 self->index = self->first_visible_entry_idx = self->nr_entries - 1 - offset;
480 self->first_visible_entry = head->prev;
481 while (offset-- != 0) {
482 struct list_head *pos = self->first_visible_entry;
483 self->first_visible_entry = pos->prev;
484 }
485 }
486 break;
487 case NEWT_KEY_RIGHT:
488 case NEWT_KEY_LEFT:
489 case NEWT_KEY_TAB:
490 return es->u.key;
491 default:
492 continue;
493 }
494 if (ui_browser__refresh_entries(self) < 0)
495 return -1;
496 }
497 return 0;
498}
499
500/*
501 * When debugging newt problems it was useful to be able to "unroll"
502 * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate
503 * a source file with the sequence of calls to these methods, to then
504 * tweak the arrays to get the intended results, so I'm keeping this code
505 * here, may be useful again in the future.
506 */
507#undef NEWT_DEBUG
508
509static void newt_checkbox_tree__add(newtComponent tree, const char *str,
510 void *priv, int *indexes)
511{
512#ifdef NEWT_DEBUG
513 /* Print the newtCheckboxTreeAddArray to tinker with its index arrays */
514 int i = 0, len = 40 - strlen(str);
515
516 fprintf(stderr,
517 "\tnewtCheckboxTreeAddItem(tree, %*.*s\"%s\", (void *)%p, 0, ",
518 len, len, " ", str, priv);
519 while (indexes[i] != NEWT_ARG_LAST) {
520 if (indexes[i] != NEWT_ARG_APPEND)
521 fprintf(stderr, " %d,", indexes[i]);
522 else
523 fprintf(stderr, " %s,", "NEWT_ARG_APPEND");
524 ++i;
525 }
526 fprintf(stderr, " %s", " NEWT_ARG_LAST);\n");
527 fflush(stderr);
528#endif
529 newtCheckboxTreeAddArray(tree, str, priv, 0, indexes);
530}
531
532static char *callchain_list__sym_name(struct callchain_list *self,
533 char *bf, size_t bfsize)
534{
535 if (self->ms.sym)
536 return self->ms.sym->name;
537
538 snprintf(bf, bfsize, "%#Lx", self->ip);
539 return bf;
540}
541
542static void __callchain__append_graph_browser(struct callchain_node *self,
543 newtComponent tree, u64 total,
544 int *indexes, int depth)
545{
546 struct rb_node *node;
547 u64 new_total, remaining;
548 int idx = 0;
549
550 if (callchain_param.mode == CHAIN_GRAPH_REL)
551 new_total = self->children_hit;
552 else
553 new_total = total;
554
555 remaining = new_total;
556 node = rb_first(&self->rb_root);
557 while (node) {
558 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
559 struct rb_node *next = rb_next(node);
560 u64 cumul = cumul_hits(child);
561 struct callchain_list *chain;
562 int first = true, printed = 0;
563 int chain_idx = -1;
564 remaining -= cumul;
565
566 indexes[depth] = NEWT_ARG_APPEND;
567 indexes[depth + 1] = NEWT_ARG_LAST;
568
569 list_for_each_entry(chain, &child->val, list) {
570 char ipstr[BITS_PER_LONG / 4 + 1],
571 *alloc_str = NULL;
572 const char *str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
573
574 if (first) {
575 double percent = cumul * 100.0 / new_total;
576
577 first = false;
578 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
579 str = "Not enough memory!";
580 else
581 str = alloc_str;
582 } else {
583 indexes[depth] = idx;
584 indexes[depth + 1] = NEWT_ARG_APPEND;
585 indexes[depth + 2] = NEWT_ARG_LAST;
586 ++chain_idx;
587 }
588 newt_checkbox_tree__add(tree, str, &chain->ms, indexes);
589 free(alloc_str);
590 ++printed;
591 }
592
593 indexes[depth] = idx;
594 if (chain_idx != -1)
595 indexes[depth + 1] = chain_idx;
596 if (printed != 0)
597 ++idx;
598 __callchain__append_graph_browser(child, tree, new_total, indexes,
599 depth + (chain_idx != -1 ? 2 : 1));
600 node = next;
601 }
602}
603
604static void callchain__append_graph_browser(struct callchain_node *self,
605 newtComponent tree, u64 total,
606 int *indexes, int parent_idx)
607{
608 struct callchain_list *chain;
609 int i = 0;
610
611 indexes[1] = NEWT_ARG_APPEND;
612 indexes[2] = NEWT_ARG_LAST;
613
614 list_for_each_entry(chain, &self->val, list) {
615 char ipstr[BITS_PER_LONG / 4 + 1], *str;
616
617 if (chain->ip >= PERF_CONTEXT_MAX)
618 continue;
619
620 if (!i++ && sort__first_dimension == SORT_SYM)
621 continue;
622
623 str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
624 newt_checkbox_tree__add(tree, str, &chain->ms, indexes);
625 }
626
627 indexes[1] = parent_idx;
628 indexes[2] = NEWT_ARG_APPEND;
629 indexes[3] = NEWT_ARG_LAST;
630 __callchain__append_graph_browser(self, tree, total, indexes, 2);
631}
632
633static void hist_entry__append_callchain_browser(struct hist_entry *self,
634 newtComponent tree, u64 total, int parent_idx)
635{
636 struct rb_node *rb_node;
637 int indexes[1024] = { [0] = parent_idx, };
638 int idx = 0;
639 struct callchain_node *chain;
640
641 rb_node = rb_first(&self->sorted_chain);
642 while (rb_node) {
643 chain = rb_entry(rb_node, struct callchain_node, rb_node);
644 switch (callchain_param.mode) {
645 case CHAIN_FLAT:
646 break;
647 case CHAIN_GRAPH_ABS: /* falldown */
648 case CHAIN_GRAPH_REL:
649 callchain__append_graph_browser(chain, tree, total, indexes, idx++);
650 break;
651 case CHAIN_NONE:
652 default:
653 break;
654 }
655 rb_node = rb_next(rb_node);
656 }
657}
658
659static size_t hist_entry__append_browser(struct hist_entry *self,
660 newtComponent tree, u64 total)
661{
662 char s[256];
663 size_t ret;
664
665 if (symbol_conf.exclude_other && !self->parent)
666 return 0;
667
668 ret = hist_entry__snprintf(self, s, sizeof(s), NULL,
669 false, 0, false, total);
670 if (symbol_conf.use_callchain) {
671 int indexes[2];
672
673 indexes[0] = NEWT_ARG_APPEND;
674 indexes[1] = NEWT_ARG_LAST;
675 newt_checkbox_tree__add(tree, s, &self->ms, indexes);
676 } else
677 newtListboxAppendEntry(tree, s, &self->ms);
678
679 return ret;
680}
681
682int hist_entry__tui_annotate(struct hist_entry *self)
683{
684 struct ui_browser browser;
685 struct newtExitStruct es;
686 struct objdump_line *pos, *n;
687 LIST_HEAD(head);
688 int ret;
689
690 if (self->ms.sym == NULL)
691 return -1;
692
693 if (self->ms.map->dso->annotate_warned)
694 return -1;
695
696 if (hist_entry__annotate(self, &head) < 0) {
697 ui__error_window(browser__last_msg);
698 return -1;
699 }
700
701 ui_helpline__push("Press <- or ESC to exit");
702
703 memset(&browser, 0, sizeof(browser));
704 browser.entries = &head;
705 browser.priv = self;
706 list_for_each_entry(pos, &head, node) {
707 size_t line_len = strlen(pos->line);
708 if (browser.width < line_len)
709 browser.width = line_len;
710 ++browser.nr_entries;
711 }
712
713 browser.width += 18; /* Percentage */
714 ret = ui_browser__run(&browser, self->ms.sym->name, &es);
715 newtFormDestroy(browser.form);
716 newtPopWindow();
717 list_for_each_entry_safe(pos, n, &head, node) {
718 list_del(&pos->node);
719 objdump_line__free(pos);
720 }
721 ui_helpline__pop();
722 return ret;
723}
724
725static const void *newt__symbol_tree_get_current(newtComponent self)
726{
727 if (symbol_conf.use_callchain)
728 return newtCheckboxTreeGetCurrent(self);
729 return newtListboxGetCurrent(self);
730}
731
732static void hist_browser__selection(newtComponent self, void *data)
733{
734 const struct map_symbol **symbol_ptr = data;
735 *symbol_ptr = newt__symbol_tree_get_current(self);
736}
737
738struct hist_browser {
739 newtComponent form, tree;
740 const struct map_symbol *selection;
741};
742
743static struct hist_browser *hist_browser__new(void)
744{
745 struct hist_browser *self = malloc(sizeof(*self));
746
747 if (self != NULL)
748 self->form = NULL;
749
750 return self;
751}
752
753static void hist_browser__delete(struct hist_browser *self)
754{
755 newtFormDestroy(self->form);
756 newtPopWindow();
757 free(self);
758}
759
760static int hist_browser__populate(struct hist_browser *self, struct hists *hists,
761 const char *title)
762{
763 int max_len = 0, idx, cols, rows;
764 struct ui_progress *progress;
765 struct rb_node *nd;
766 u64 curr_hist = 0;
767 char seq[] = ".", unit;
768 char str[256];
769 unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE];
770
771 if (self->form) {
772 newtFormDestroy(self->form);
773 newtPopWindow();
774 }
775
776 nr_events = convert_unit(nr_events, &unit);
777 snprintf(str, sizeof(str), "Events: %lu%c ",
778 nr_events, unit);
779 newtDrawRootText(0, 0, str);
780
781 newtGetScreenSize(NULL, &rows);
782
783 if (symbol_conf.use_callchain)
784 self->tree = newtCheckboxTreeMulti(0, 0, rows - 5, seq,
785 NEWT_FLAG_SCROLL);
786 else
787 self->tree = newtListbox(0, 0, rows - 5,
788 (NEWT_FLAG_SCROLL |
789 NEWT_FLAG_RETURNEXIT));
790
791 newtComponentAddCallback(self->tree, hist_browser__selection,
792 &self->selection);
793
794 progress = ui_progress__new("Adding entries to the browser...",
795 hists->nr_entries);
796 if (progress == NULL)
797 return -1;
798
799 idx = 0;
800 for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
801 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
802 int len;
803
804 if (h->filtered)
805 continue;
806
807 len = hist_entry__append_browser(h, self->tree, hists->stats.total_period);
808 if (len > max_len)
809 max_len = len;
810 if (symbol_conf.use_callchain)
811 hist_entry__append_callchain_browser(h, self->tree,
812 hists->stats.total_period, idx++);
813 ++curr_hist;
814 if (curr_hist % 5)
815 ui_progress__update(progress, curr_hist);
816 }
817
818 ui_progress__delete(progress);
819
820 newtGetScreenSize(&cols, &rows);
821
822 if (max_len > cols)
823 max_len = cols - 3;
824
825 if (!symbol_conf.use_callchain)
826 newtListboxSetWidth(self->tree, max_len);
827
828 newtCenteredWindow(max_len + (symbol_conf.use_callchain ? 5 : 0),
829 rows - 5, title);
830 self->form = newt_form__new();
831 if (self->form == NULL)
832 return -1;
833
834 newtFormAddHotKey(self->form, 'A');
835 newtFormAddHotKey(self->form, 'a');
836 newtFormAddHotKey(self->form, 'D');
837 newtFormAddHotKey(self->form, 'd');
838 newtFormAddHotKey(self->form, 'T');
839 newtFormAddHotKey(self->form, 't');
840 newtFormAddHotKey(self->form, '?');
841 newtFormAddHotKey(self->form, 'H');
842 newtFormAddHotKey(self->form, 'h');
843 newtFormAddHotKey(self->form, NEWT_KEY_F1);
844 newtFormAddHotKey(self->form, NEWT_KEY_RIGHT);
845 newtFormAddHotKey(self->form, NEWT_KEY_TAB);
846 newtFormAddHotKey(self->form, NEWT_KEY_UNTAB);
847 newtFormAddComponents(self->form, self->tree, NULL);
848 self->selection = newt__symbol_tree_get_current(self->tree);
849
850 return 0;
851}
852
853static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
854{
855 int *indexes;
856
857 if (!symbol_conf.use_callchain)
858 goto out;
859
860 indexes = newtCheckboxTreeFindItem(self->tree, (void *)self->selection);
861 if (indexes) {
862 bool is_hist_entry = indexes[1] == NEWT_ARG_LAST;
863 free(indexes);
864 if (is_hist_entry)
865 goto out;
866 }
867 return NULL;
868out:
869 return container_of(self->selection, struct hist_entry, ms);
870}
871
872static struct thread *hist_browser__selected_thread(struct hist_browser *self)
873{
874 struct hist_entry *he = hist_browser__selected_entry(self);
875 return he ? he->thread : NULL;
876}
877
878static int hist_browser__title(char *bf, size_t size, const char *ev_name,
879 const struct dso *dso, const struct thread *thread)
880{
881 int printed = 0;
882
883 if (thread)
884 printed += snprintf(bf + printed, size - printed,
885 "Thread: %s(%d)",
886 (thread->comm_set ? thread->comm : ""),
887 thread->pid);
888 if (dso)
889 printed += snprintf(bf + printed, size - printed,
890 "%sDSO: %s", thread ? " " : "",
891 dso->short_name);
892 return printed ?: snprintf(bf, size, "Event: %s", ev_name);
893}
894
895int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
896{
897 struct hist_browser *browser = hist_browser__new();
898 struct pstack *fstack;
899 const struct thread *thread_filter = NULL;
900 const struct dso *dso_filter = NULL;
901 struct newtExitStruct es;
902 char msg[160];
903 int key = -1;
904
905 if (browser == NULL)
906 return -1;
907
908 fstack = pstack__new(2);
909 if (fstack == NULL)
910 goto out;
911
912 ui_helpline__push(helpline);
913
914 hist_browser__title(msg, sizeof(msg), ev_name,
915 dso_filter, thread_filter);
916 if (hist_browser__populate(browser, self, msg) < 0)
917 goto out_free_stack;
918
919 while (1) {
920 const struct thread *thread;
921 const struct dso *dso;
922 char *options[16];
923 int nr_options = 0, choice = 0, i,
924 annotate = -2, zoom_dso = -2, zoom_thread = -2;
925
926 newtFormRun(browser->form, &es);
927
928 thread = hist_browser__selected_thread(browser);
929 dso = browser->selection->map ? browser->selection->map->dso : NULL;
930
931 if (es.reason == NEWT_EXIT_HOTKEY) {
932 key = es.u.key;
933
934 switch (key) {
935 case NEWT_KEY_F1:
936 goto do_help;
937 case NEWT_KEY_TAB:
938 case NEWT_KEY_UNTAB:
939 /*
940 * Exit the browser, let hists__browser_tree
941 * go to the next or previous
942 */
943 goto out_free_stack;
944 default:;
945 }
946
947 key = toupper(key);
948 switch (key) {
949 case 'A':
950 if (browser->selection->map == NULL &&
951 browser->selection->map->dso->annotate_warned)
952 continue;
953 goto do_annotate;
954 case 'D':
955 goto zoom_dso;
956 case 'T':
957 goto zoom_thread;
958 case 'H':
959 case '?':
960do_help:
961 ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
962 "<- Zoom out\n"
963 "a Annotate current symbol\n"
964 "h/?/F1 Show this window\n"
965 "d Zoom into current DSO\n"
966 "t Zoom into current Thread\n"
967 "q/CTRL+C Exit browser");
968 continue;
969 default:;
970 }
971 if (is_exit_key(key)) {
972 if (key == NEWT_KEY_ESCAPE) {
973 if (dialog_yesno("Do you really want to exit?"))
974 break;
975 else
976 continue;
977 } else
978 break;
979 }
980
981 if (es.u.key == NEWT_KEY_LEFT) {
982 const void *top;
983
984 if (pstack__empty(fstack))
985 continue;
986 top = pstack__pop(fstack);
987 if (top == &dso_filter)
988 goto zoom_out_dso;
989 if (top == &thread_filter)
990 goto zoom_out_thread;
991 continue;
992 }
993 }
994
995 if (browser->selection->sym != NULL &&
996 !browser->selection->map->dso->annotate_warned &&
997 asprintf(&options[nr_options], "Annotate %s",
998 browser->selection->sym->name) > 0)
999 annotate = nr_options++;
1000
1001 if (thread != NULL &&
1002 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
1003 (thread_filter ? "out of" : "into"),
1004 (thread->comm_set ? thread->comm : ""),
1005 thread->pid) > 0)
1006 zoom_thread = nr_options++;
1007
1008 if (dso != NULL &&
1009 asprintf(&options[nr_options], "Zoom %s %s DSO",
1010 (dso_filter ? "out of" : "into"),
1011 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
1012 zoom_dso = nr_options++;
1013
1014 options[nr_options++] = (char *)"Exit";
1015
1016 choice = popup_menu(nr_options, options);
1017
1018 for (i = 0; i < nr_options - 1; ++i)
1019 free(options[i]);
1020
1021 if (choice == nr_options - 1)
1022 break;
1023
1024 if (choice == -1)
1025 continue;
1026
1027 if (choice == annotate) {
1028 struct hist_entry *he;
1029do_annotate:
1030 if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) {
1031 browser->selection->map->dso->annotate_warned = 1;
1032 ui_helpline__puts("No vmlinux file found, can't "
1033 "annotate with just a "
1034 "kallsyms file");
1035 continue;
1036 }
1037
1038 he = hist_browser__selected_entry(browser);
1039 if (he == NULL)
1040 continue;
1041
1042 hist_entry__tui_annotate(he);
1043 } else if (choice == zoom_dso) {
1044zoom_dso:
1045 if (dso_filter) {
1046 pstack__remove(fstack, &dso_filter);
1047zoom_out_dso:
1048 ui_helpline__pop();
1049 dso_filter = NULL;
1050 } else {
1051 if (dso == NULL)
1052 continue;
1053 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
1054 dso->kernel ? "the Kernel" : dso->short_name);
1055 dso_filter = dso;
1056 pstack__push(fstack, &dso_filter);
1057 }
1058 hists__filter_by_dso(self, dso_filter);
1059 hist_browser__title(msg, sizeof(msg), ev_name,
1060 dso_filter, thread_filter);
1061 if (hist_browser__populate(browser, self, msg) < 0)
1062 goto out;
1063 } else if (choice == zoom_thread) {
1064zoom_thread:
1065 if (thread_filter) {
1066 pstack__remove(fstack, &thread_filter);
1067zoom_out_thread:
1068 ui_helpline__pop();
1069 thread_filter = NULL;
1070 } else {
1071 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
1072 thread->comm_set ? thread->comm : "",
1073 thread->pid);
1074 thread_filter = thread;
1075 pstack__push(fstack, &thread_filter);
1076 }
1077 hists__filter_by_thread(self, thread_filter);
1078 hist_browser__title(msg, sizeof(msg), ev_name,
1079 dso_filter, thread_filter);
1080 if (hist_browser__populate(browser, self, msg) < 0)
1081 goto out;
1082 }
1083 }
1084out_free_stack:
1085 pstack__delete(fstack);
1086out:
1087 hist_browser__delete(browser);
1088 return key;
1089}
1090
1091int hists__tui_browse_tree(struct rb_root *self, const char *help)
1092{
1093 struct rb_node *first = rb_first(self), *nd = first, *next;
1094 int key = 0;
1095
1096 while (nd) {
1097 struct hists *hists = rb_entry(nd, struct hists, rb_node);
1098 const char *ev_name = __event_name(hists->type, hists->config);
1099
1100 key = hists__browse(hists, help, ev_name);
1101
1102 if (is_exit_key(key))
1103 break;
1104
1105 switch (key) {
1106 case NEWT_KEY_TAB:
1107 next = rb_next(nd);
1108 if (next)
1109 nd = next;
1110 break;
1111 case NEWT_KEY_UNTAB:
1112 if (nd == first)
1113 continue;
1114 nd = rb_prev(nd);
1115 default:
1116 break;
1117 }
1118 }
1119
1120 return key;
1121}
1122
1123static struct newtPercentTreeColors {
1124 const char *topColorFg, *topColorBg;
1125 const char *mediumColorFg, *mediumColorBg;
1126 const char *normalColorFg, *normalColorBg;
1127 const char *selColorFg, *selColorBg;
1128 const char *codeColorFg, *codeColorBg;
1129} defaultPercentTreeColors = {
1130 "red", "lightgray",
1131 "green", "lightgray",
1132 "black", "lightgray",
1133 "lightgray", "magenta",
1134 "blue", "lightgray",
1135};
1136
1137void setup_browser(void)
1138{
1139 struct newtPercentTreeColors *c = &defaultPercentTreeColors;
1140
1141 if (!isatty(1) || !use_browser || dump_trace) {
1142 use_browser = 0;
1143 setup_pager();
1144 return;
1145 }
1146
1147 use_browser = 1;
1148 newtInit();
1149 newtCls();
1150 ui_helpline__puts(" ");
1151 sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg);
1152 sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg);
1153 sltt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg);
1154 sltt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg);
1155 sltt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg);
1156}
1157
1158void exit_browser(bool wait_for_ok)
1159{
1160 if (use_browser > 0) {
1161 if (wait_for_ok) {
1162 char title[] = "Fatal Error", ok[] = "Ok";
1163 newtWinMessage(title, ok, browser__last_msg);
1164 }
1165 newtFinished();
1166 }
1167}
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 9bf0f402ca73..4af5bd59cfd1 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -602,8 +602,15 @@ parse_breakpoint_event(const char **strp, struct perf_event_attr *attr)
602 return EVT_FAILED; 602 return EVT_FAILED;
603 } 603 }
604 604
605 /* We should find a nice way to override the access type */ 605 /*
606 attr->bp_len = HW_BREAKPOINT_LEN_4; 606 * We should find a nice way to override the access length
607 * Provide some defaults for now
608 */
609 if (attr->bp_type == HW_BREAKPOINT_X)
610 attr->bp_len = sizeof(long);
611 else
612 attr->bp_len = HW_BREAKPOINT_LEN_4;
613
607 attr->type = PERF_TYPE_BREAKPOINT; 614 attr->type = PERF_TYPE_BREAKPOINT;
608 615
609 return EVT_HANDLED; 616 return EVT_HANDLED;
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 914c67095d96..e72f05c3bef0 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * probe-event.c : perf-probe definition to kprobe_events format converter 2 * probe-event.c : perf-probe definition to probe_events format converter
3 * 3 *
4 * Written by Masami Hiramatsu <mhiramat@redhat.com> 4 * Written by Masami Hiramatsu <mhiramat@redhat.com>
5 * 5 *
@@ -120,8 +120,11 @@ static int open_vmlinux(void)
120 return open(machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name, O_RDONLY); 120 return open(machine.vmlinux_maps[MAP__FUNCTION]->dso->long_name, O_RDONLY);
121} 121}
122 122
123/* Convert trace point to probe point with debuginfo */ 123/*
124static int convert_to_perf_probe_point(struct kprobe_trace_point *tp, 124 * Convert trace point to probe point with debuginfo
125 * Currently only handles kprobes.
126 */
127static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
125 struct perf_probe_point *pp) 128 struct perf_probe_point *pp)
126{ 129{
127 struct symbol *sym; 130 struct symbol *sym;
@@ -151,8 +154,8 @@ static int convert_to_perf_probe_point(struct kprobe_trace_point *tp,
151} 154}
152 155
153/* Try to find perf_probe_event with debuginfo */ 156/* Try to find perf_probe_event with debuginfo */
154static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, 157static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
155 struct kprobe_trace_event **tevs, 158 struct probe_trace_event **tevs,
156 int max_tevs) 159 int max_tevs)
157{ 160{
158 bool need_dwarf = perf_probe_event_need_dwarf(pev); 161 bool need_dwarf = perf_probe_event_need_dwarf(pev);
@@ -169,11 +172,11 @@ static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev,
169 } 172 }
170 173
171 /* Searching trace events corresponding to probe event */ 174 /* Searching trace events corresponding to probe event */
172 ntevs = find_kprobe_trace_events(fd, pev, tevs, max_tevs); 175 ntevs = find_probe_trace_events(fd, pev, tevs, max_tevs);
173 close(fd); 176 close(fd);
174 177
175 if (ntevs > 0) { /* Succeeded to find trace events */ 178 if (ntevs > 0) { /* Succeeded to find trace events */
176 pr_debug("find %d kprobe_trace_events.\n", ntevs); 179 pr_debug("find %d probe_trace_events.\n", ntevs);
177 return ntevs; 180 return ntevs;
178 } 181 }
179 182
@@ -195,6 +198,65 @@ static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev,
195 return ntevs; 198 return ntevs;
196} 199}
197 200
201/*
202 * Find a src file from a DWARF tag path. Prepend optional source path prefix
203 * and chop off leading directories that do not exist. Result is passed back as
204 * a newly allocated path on success.
205 * Return 0 if file was found and readable, -errno otherwise.
206 */
207static int get_real_path(const char *raw_path, const char *comp_dir,
208 char **new_path)
209{
210 const char *prefix = symbol_conf.source_prefix;
211
212 if (!prefix) {
213 if (raw_path[0] != '/' && comp_dir)
214 /* If not an absolute path, try to use comp_dir */
215 prefix = comp_dir;
216 else {
217 if (access(raw_path, R_OK) == 0) {
218 *new_path = strdup(raw_path);
219 return 0;
220 } else
221 return -errno;
222 }
223 }
224
225 *new_path = malloc((strlen(prefix) + strlen(raw_path) + 2));
226 if (!*new_path)
227 return -ENOMEM;
228
229 for (;;) {
230 sprintf(*new_path, "%s/%s", prefix, raw_path);
231
232 if (access(*new_path, R_OK) == 0)
233 return 0;
234
235 if (!symbol_conf.source_prefix)
236 /* In case of searching comp_dir, don't retry */
237 return -errno;
238
239 switch (errno) {
240 case ENAMETOOLONG:
241 case ENOENT:
242 case EROFS:
243 case EFAULT:
244 raw_path = strchr(++raw_path, '/');
245 if (!raw_path) {
246 free(*new_path);
247 *new_path = NULL;
248 return -ENOENT;
249 }
250 continue;
251
252 default:
253 free(*new_path);
254 *new_path = NULL;
255 return -errno;
256 }
257 }
258}
259
198#define LINEBUF_SIZE 256 260#define LINEBUF_SIZE 256
199#define NR_ADDITIONAL_LINES 2 261#define NR_ADDITIONAL_LINES 2
200 262
@@ -244,6 +306,7 @@ int show_line_range(struct line_range *lr)
244 struct line_node *ln; 306 struct line_node *ln;
245 FILE *fp; 307 FILE *fp;
246 int fd, ret; 308 int fd, ret;
309 char *tmp;
247 310
248 /* Search a line range */ 311 /* Search a line range */
249 ret = init_vmlinux(); 312 ret = init_vmlinux();
@@ -266,6 +329,15 @@ int show_line_range(struct line_range *lr)
266 return ret; 329 return ret;
267 } 330 }
268 331
332 /* Convert source file path */
333 tmp = lr->path;
334 ret = get_real_path(tmp, lr->comp_dir, &lr->path);
335 free(tmp); /* Free old path */
336 if (ret < 0) {
337 pr_warning("Failed to find source file. (%d)\n", ret);
338 return ret;
339 }
340
269 setup_pager(); 341 setup_pager();
270 342
271 if (lr->function) 343 if (lr->function)
@@ -308,8 +380,8 @@ end:
308 380
309#else /* !DWARF_SUPPORT */ 381#else /* !DWARF_SUPPORT */
310 382
311static int convert_to_perf_probe_point(struct kprobe_trace_point *tp, 383static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
312 struct perf_probe_point *pp) 384 struct perf_probe_point *pp)
313{ 385{
314 pp->function = strdup(tp->symbol); 386 pp->function = strdup(tp->symbol);
315 if (pp->function == NULL) 387 if (pp->function == NULL)
@@ -320,8 +392,8 @@ static int convert_to_perf_probe_point(struct kprobe_trace_point *tp,
320 return 0; 392 return 0;
321} 393}
322 394
323static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, 395static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
324 struct kprobe_trace_event **tevs __unused, 396 struct probe_trace_event **tevs __unused,
325 int max_tevs __unused) 397 int max_tevs __unused)
326{ 398{
327 if (perf_probe_event_need_dwarf(pev)) { 399 if (perf_probe_event_need_dwarf(pev)) {
@@ -557,7 +629,7 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
557/* Parse perf-probe event argument */ 629/* Parse perf-probe event argument */
558static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) 630static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
559{ 631{
560 char *tmp; 632 char *tmp, *goodname;
561 struct perf_probe_arg_field **fieldp; 633 struct perf_probe_arg_field **fieldp;
562 634
563 pr_debug("parsing arg: %s into ", str); 635 pr_debug("parsing arg: %s into ", str);
@@ -580,7 +652,7 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
580 pr_debug("type:%s ", arg->type); 652 pr_debug("type:%s ", arg->type);
581 } 653 }
582 654
583 tmp = strpbrk(str, "-."); 655 tmp = strpbrk(str, "-.[");
584 if (!is_c_varname(str) || !tmp) { 656 if (!is_c_varname(str) || !tmp) {
585 /* A variable, register, symbol or special value */ 657 /* A variable, register, symbol or special value */
586 arg->var = strdup(str); 658 arg->var = strdup(str);
@@ -590,10 +662,11 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
590 return 0; 662 return 0;
591 } 663 }
592 664
593 /* Structure fields */ 665 /* Structure fields or array element */
594 arg->var = strndup(str, tmp - str); 666 arg->var = strndup(str, tmp - str);
595 if (arg->var == NULL) 667 if (arg->var == NULL)
596 return -ENOMEM; 668 return -ENOMEM;
669 goodname = arg->var;
597 pr_debug("%s, ", arg->var); 670 pr_debug("%s, ", arg->var);
598 fieldp = &arg->field; 671 fieldp = &arg->field;
599 672
@@ -601,22 +674,38 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
601 *fieldp = zalloc(sizeof(struct perf_probe_arg_field)); 674 *fieldp = zalloc(sizeof(struct perf_probe_arg_field));
602 if (*fieldp == NULL) 675 if (*fieldp == NULL)
603 return -ENOMEM; 676 return -ENOMEM;
604 if (*tmp == '.') { 677 if (*tmp == '[') { /* Array */
605 str = tmp + 1; 678 str = tmp;
606 (*fieldp)->ref = false; 679 (*fieldp)->index = strtol(str + 1, &tmp, 0);
607 } else if (tmp[1] == '>') {
608 str = tmp + 2;
609 (*fieldp)->ref = true; 680 (*fieldp)->ref = true;
610 } else { 681 if (*tmp != ']' || tmp == str + 1) {
611 semantic_error("Argument parse error: %s\n", str); 682 semantic_error("Array index must be a"
612 return -EINVAL; 683 " number.\n");
684 return -EINVAL;
685 }
686 tmp++;
687 if (*tmp == '\0')
688 tmp = NULL;
689 } else { /* Structure */
690 if (*tmp == '.') {
691 str = tmp + 1;
692 (*fieldp)->ref = false;
693 } else if (tmp[1] == '>') {
694 str = tmp + 2;
695 (*fieldp)->ref = true;
696 } else {
697 semantic_error("Argument parse error: %s\n",
698 str);
699 return -EINVAL;
700 }
701 tmp = strpbrk(str, "-.[");
613 } 702 }
614
615 tmp = strpbrk(str, "-.");
616 if (tmp) { 703 if (tmp) {
617 (*fieldp)->name = strndup(str, tmp - str); 704 (*fieldp)->name = strndup(str, tmp - str);
618 if ((*fieldp)->name == NULL) 705 if ((*fieldp)->name == NULL)
619 return -ENOMEM; 706 return -ENOMEM;
707 if (*str != '[')
708 goodname = (*fieldp)->name;
620 pr_debug("%s(%d), ", (*fieldp)->name, (*fieldp)->ref); 709 pr_debug("%s(%d), ", (*fieldp)->name, (*fieldp)->ref);
621 fieldp = &(*fieldp)->next; 710 fieldp = &(*fieldp)->next;
622 } 711 }
@@ -624,11 +713,13 @@ static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg)
624 (*fieldp)->name = strdup(str); 713 (*fieldp)->name = strdup(str);
625 if ((*fieldp)->name == NULL) 714 if ((*fieldp)->name == NULL)
626 return -ENOMEM; 715 return -ENOMEM;
716 if (*str != '[')
717 goodname = (*fieldp)->name;
627 pr_debug("%s(%d)\n", (*fieldp)->name, (*fieldp)->ref); 718 pr_debug("%s(%d)\n", (*fieldp)->name, (*fieldp)->ref);
628 719
629 /* If no name is specified, set the last field name */ 720 /* If no name is specified, set the last field name (not array index)*/
630 if (!arg->name) { 721 if (!arg->name) {
631 arg->name = strdup((*fieldp)->name); 722 arg->name = strdup(goodname);
632 if (arg->name == NULL) 723 if (arg->name == NULL)
633 return -ENOMEM; 724 return -ENOMEM;
634 } 725 }
@@ -693,16 +784,17 @@ bool perf_probe_event_need_dwarf(struct perf_probe_event *pev)
693 return false; 784 return false;
694} 785}
695 786
696/* Parse kprobe_events event into struct probe_point */ 787/* Parse probe_events event into struct probe_point */
697int parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev) 788static int parse_probe_trace_command(const char *cmd,
789 struct probe_trace_event *tev)
698{ 790{
699 struct kprobe_trace_point *tp = &tev->point; 791 struct probe_trace_point *tp = &tev->point;
700 char pr; 792 char pr;
701 char *p; 793 char *p;
702 int ret, i, argc; 794 int ret, i, argc;
703 char **argv; 795 char **argv;
704 796
705 pr_debug("Parsing kprobe_events: %s\n", cmd); 797 pr_debug("Parsing probe_events: %s\n", cmd);
706 argv = argv_split(cmd, &argc); 798 argv = argv_split(cmd, &argc);
707 if (!argv) { 799 if (!argv) {
708 pr_debug("Failed to split arguments.\n"); 800 pr_debug("Failed to split arguments.\n");
@@ -734,7 +826,7 @@ int parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev)
734 tp->offset = 0; 826 tp->offset = 0;
735 827
736 tev->nargs = argc - 2; 828 tev->nargs = argc - 2;
737 tev->args = zalloc(sizeof(struct kprobe_trace_arg) * tev->nargs); 829 tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
738 if (tev->args == NULL) { 830 if (tev->args == NULL) {
739 ret = -ENOMEM; 831 ret = -ENOMEM;
740 goto out; 832 goto out;
@@ -776,8 +868,11 @@ int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len)
776 len -= ret; 868 len -= ret;
777 869
778 while (field) { 870 while (field) {
779 ret = e_snprintf(tmp, len, "%s%s", field->ref ? "->" : ".", 871 if (field->name[0] == '[')
780 field->name); 872 ret = e_snprintf(tmp, len, "%s", field->name);
873 else
874 ret = e_snprintf(tmp, len, "%s%s",
875 field->ref ? "->" : ".", field->name);
781 if (ret <= 0) 876 if (ret <= 0)
782 goto error; 877 goto error;
783 tmp += ret; 878 tmp += ret;
@@ -877,13 +972,13 @@ char *synthesize_perf_probe_command(struct perf_probe_event *pev)
877} 972}
878#endif 973#endif
879 974
880static int __synthesize_kprobe_trace_arg_ref(struct kprobe_trace_arg_ref *ref, 975static int __synthesize_probe_trace_arg_ref(struct probe_trace_arg_ref *ref,
881 char **buf, size_t *buflen, 976 char **buf, size_t *buflen,
882 int depth) 977 int depth)
883{ 978{
884 int ret; 979 int ret;
885 if (ref->next) { 980 if (ref->next) {
886 depth = __synthesize_kprobe_trace_arg_ref(ref->next, buf, 981 depth = __synthesize_probe_trace_arg_ref(ref->next, buf,
887 buflen, depth + 1); 982 buflen, depth + 1);
888 if (depth < 0) 983 if (depth < 0)
889 goto out; 984 goto out;
@@ -901,9 +996,10 @@ out:
901 996
902} 997}
903 998
904static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg, 999static int synthesize_probe_trace_arg(struct probe_trace_arg *arg,
905 char *buf, size_t buflen) 1000 char *buf, size_t buflen)
906{ 1001{
1002 struct probe_trace_arg_ref *ref = arg->ref;
907 int ret, depth = 0; 1003 int ret, depth = 0;
908 char *tmp = buf; 1004 char *tmp = buf;
909 1005
@@ -917,16 +1013,24 @@ static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg,
917 buf += ret; 1013 buf += ret;
918 buflen -= ret; 1014 buflen -= ret;
919 1015
1016 /* Special case: @XXX */
1017 if (arg->value[0] == '@' && arg->ref)
1018 ref = ref->next;
1019
920 /* Dereferencing arguments */ 1020 /* Dereferencing arguments */
921 if (arg->ref) { 1021 if (ref) {
922 depth = __synthesize_kprobe_trace_arg_ref(arg->ref, &buf, 1022 depth = __synthesize_probe_trace_arg_ref(ref, &buf,
923 &buflen, 1); 1023 &buflen, 1);
924 if (depth < 0) 1024 if (depth < 0)
925 return depth; 1025 return depth;
926 } 1026 }
927 1027
928 /* Print argument value */ 1028 /* Print argument value */
929 ret = e_snprintf(buf, buflen, "%s", arg->value); 1029 if (arg->value[0] == '@' && arg->ref)
1030 ret = e_snprintf(buf, buflen, "%s%+ld", arg->value,
1031 arg->ref->offset);
1032 else
1033 ret = e_snprintf(buf, buflen, "%s", arg->value);
930 if (ret < 0) 1034 if (ret < 0)
931 return ret; 1035 return ret;
932 buf += ret; 1036 buf += ret;
@@ -951,9 +1055,9 @@ static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg,
951 return buf - tmp; 1055 return buf - tmp;
952} 1056}
953 1057
954char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev) 1058char *synthesize_probe_trace_command(struct probe_trace_event *tev)
955{ 1059{
956 struct kprobe_trace_point *tp = &tev->point; 1060 struct probe_trace_point *tp = &tev->point;
957 char *buf; 1061 char *buf;
958 int i, len, ret; 1062 int i, len, ret;
959 1063
@@ -969,7 +1073,7 @@ char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev)
969 goto error; 1073 goto error;
970 1074
971 for (i = 0; i < tev->nargs; i++) { 1075 for (i = 0; i < tev->nargs; i++) {
972 ret = synthesize_kprobe_trace_arg(&tev->args[i], buf + len, 1076 ret = synthesize_probe_trace_arg(&tev->args[i], buf + len,
973 MAX_CMDLEN - len); 1077 MAX_CMDLEN - len);
974 if (ret <= 0) 1078 if (ret <= 0)
975 goto error; 1079 goto error;
@@ -982,7 +1086,7 @@ error:
982 return NULL; 1086 return NULL;
983} 1087}
984 1088
985int convert_to_perf_probe_event(struct kprobe_trace_event *tev, 1089static int convert_to_perf_probe_event(struct probe_trace_event *tev,
986 struct perf_probe_event *pev) 1090 struct perf_probe_event *pev)
987{ 1091{
988 char buf[64] = ""; 1092 char buf[64] = "";
@@ -995,7 +1099,7 @@ int convert_to_perf_probe_event(struct kprobe_trace_event *tev,
995 return -ENOMEM; 1099 return -ENOMEM;
996 1100
997 /* Convert trace_point to probe_point */ 1101 /* Convert trace_point to probe_point */
998 ret = convert_to_perf_probe_point(&tev->point, &pev->point); 1102 ret = kprobe_convert_to_perf_probe(&tev->point, &pev->point);
999 if (ret < 0) 1103 if (ret < 0)
1000 return ret; 1104 return ret;
1001 1105
@@ -1008,7 +1112,7 @@ int convert_to_perf_probe_event(struct kprobe_trace_event *tev,
1008 if (tev->args[i].name) 1112 if (tev->args[i].name)
1009 pev->args[i].name = strdup(tev->args[i].name); 1113 pev->args[i].name = strdup(tev->args[i].name);
1010 else { 1114 else {
1011 ret = synthesize_kprobe_trace_arg(&tev->args[i], 1115 ret = synthesize_probe_trace_arg(&tev->args[i],
1012 buf, 64); 1116 buf, 64);
1013 pev->args[i].name = strdup(buf); 1117 pev->args[i].name = strdup(buf);
1014 } 1118 }
@@ -1059,9 +1163,9 @@ void clear_perf_probe_event(struct perf_probe_event *pev)
1059 memset(pev, 0, sizeof(*pev)); 1163 memset(pev, 0, sizeof(*pev));
1060} 1164}
1061 1165
1062void clear_kprobe_trace_event(struct kprobe_trace_event *tev) 1166static void clear_probe_trace_event(struct probe_trace_event *tev)
1063{ 1167{
1064 struct kprobe_trace_arg_ref *ref, *next; 1168 struct probe_trace_arg_ref *ref, *next;
1065 int i; 1169 int i;
1066 1170
1067 if (tev->event) 1171 if (tev->event)
@@ -1122,7 +1226,7 @@ static int open_kprobe_events(bool readwrite)
1122} 1226}
1123 1227
1124/* Get raw string list of current kprobe_events */ 1228/* Get raw string list of current kprobe_events */
1125static struct strlist *get_kprobe_trace_command_rawlist(int fd) 1229static struct strlist *get_probe_trace_command_rawlist(int fd)
1126{ 1230{
1127 int ret, idx; 1231 int ret, idx;
1128 FILE *fp; 1232 FILE *fp;
@@ -1190,7 +1294,7 @@ static int show_perf_probe_event(struct perf_probe_event *pev)
1190int show_perf_probe_events(void) 1294int show_perf_probe_events(void)
1191{ 1295{
1192 int fd, ret; 1296 int fd, ret;
1193 struct kprobe_trace_event tev; 1297 struct probe_trace_event tev;
1194 struct perf_probe_event pev; 1298 struct perf_probe_event pev;
1195 struct strlist *rawlist; 1299 struct strlist *rawlist;
1196 struct str_node *ent; 1300 struct str_node *ent;
@@ -1207,20 +1311,20 @@ int show_perf_probe_events(void)
1207 if (fd < 0) 1311 if (fd < 0)
1208 return fd; 1312 return fd;
1209 1313
1210 rawlist = get_kprobe_trace_command_rawlist(fd); 1314 rawlist = get_probe_trace_command_rawlist(fd);
1211 close(fd); 1315 close(fd);
1212 if (!rawlist) 1316 if (!rawlist)
1213 return -ENOENT; 1317 return -ENOENT;
1214 1318
1215 strlist__for_each(ent, rawlist) { 1319 strlist__for_each(ent, rawlist) {
1216 ret = parse_kprobe_trace_command(ent->s, &tev); 1320 ret = parse_probe_trace_command(ent->s, &tev);
1217 if (ret >= 0) { 1321 if (ret >= 0) {
1218 ret = convert_to_perf_probe_event(&tev, &pev); 1322 ret = convert_to_perf_probe_event(&tev, &pev);
1219 if (ret >= 0) 1323 if (ret >= 0)
1220 ret = show_perf_probe_event(&pev); 1324 ret = show_perf_probe_event(&pev);
1221 } 1325 }
1222 clear_perf_probe_event(&pev); 1326 clear_perf_probe_event(&pev);
1223 clear_kprobe_trace_event(&tev); 1327 clear_probe_trace_event(&tev);
1224 if (ret < 0) 1328 if (ret < 0)
1225 break; 1329 break;
1226 } 1330 }
@@ -1230,20 +1334,19 @@ int show_perf_probe_events(void)
1230} 1334}
1231 1335
1232/* Get current perf-probe event names */ 1336/* Get current perf-probe event names */
1233static struct strlist *get_kprobe_trace_event_names(int fd, bool include_group) 1337static struct strlist *get_probe_trace_event_names(int fd, bool include_group)
1234{ 1338{
1235 char buf[128]; 1339 char buf[128];
1236 struct strlist *sl, *rawlist; 1340 struct strlist *sl, *rawlist;
1237 struct str_node *ent; 1341 struct str_node *ent;
1238 struct kprobe_trace_event tev; 1342 struct probe_trace_event tev;
1239 int ret = 0; 1343 int ret = 0;
1240 1344
1241 memset(&tev, 0, sizeof(tev)); 1345 memset(&tev, 0, sizeof(tev));
1242 1346 rawlist = get_probe_trace_command_rawlist(fd);
1243 rawlist = get_kprobe_trace_command_rawlist(fd);
1244 sl = strlist__new(true, NULL); 1347 sl = strlist__new(true, NULL);
1245 strlist__for_each(ent, rawlist) { 1348 strlist__for_each(ent, rawlist) {
1246 ret = parse_kprobe_trace_command(ent->s, &tev); 1349 ret = parse_probe_trace_command(ent->s, &tev);
1247 if (ret < 0) 1350 if (ret < 0)
1248 break; 1351 break;
1249 if (include_group) { 1352 if (include_group) {
@@ -1253,7 +1356,7 @@ static struct strlist *get_kprobe_trace_event_names(int fd, bool include_group)
1253 ret = strlist__add(sl, buf); 1356 ret = strlist__add(sl, buf);
1254 } else 1357 } else
1255 ret = strlist__add(sl, tev.event); 1358 ret = strlist__add(sl, tev.event);
1256 clear_kprobe_trace_event(&tev); 1359 clear_probe_trace_event(&tev);
1257 if (ret < 0) 1360 if (ret < 0)
1258 break; 1361 break;
1259 } 1362 }
@@ -1266,13 +1369,13 @@ static struct strlist *get_kprobe_trace_event_names(int fd, bool include_group)
1266 return sl; 1369 return sl;
1267} 1370}
1268 1371
1269static int write_kprobe_trace_event(int fd, struct kprobe_trace_event *tev) 1372static int write_probe_trace_event(int fd, struct probe_trace_event *tev)
1270{ 1373{
1271 int ret = 0; 1374 int ret = 0;
1272 char *buf = synthesize_kprobe_trace_command(tev); 1375 char *buf = synthesize_probe_trace_command(tev);
1273 1376
1274 if (!buf) { 1377 if (!buf) {
1275 pr_debug("Failed to synthesize kprobe trace event.\n"); 1378 pr_debug("Failed to synthesize probe trace event.\n");
1276 return -EINVAL; 1379 return -EINVAL;
1277 } 1380 }
1278 1381
@@ -1325,12 +1428,12 @@ static int get_new_event_name(char *buf, size_t len, const char *base,
1325 return ret; 1428 return ret;
1326} 1429}
1327 1430
1328static int __add_kprobe_trace_events(struct perf_probe_event *pev, 1431static int __add_probe_trace_events(struct perf_probe_event *pev,
1329 struct kprobe_trace_event *tevs, 1432 struct probe_trace_event *tevs,
1330 int ntevs, bool allow_suffix) 1433 int ntevs, bool allow_suffix)
1331{ 1434{
1332 int i, fd, ret; 1435 int i, fd, ret;
1333 struct kprobe_trace_event *tev = NULL; 1436 struct probe_trace_event *tev = NULL;
1334 char buf[64]; 1437 char buf[64];
1335 const char *event, *group; 1438 const char *event, *group;
1336 struct strlist *namelist; 1439 struct strlist *namelist;
@@ -1339,7 +1442,7 @@ static int __add_kprobe_trace_events(struct perf_probe_event *pev,
1339 if (fd < 0) 1442 if (fd < 0)
1340 return fd; 1443 return fd;
1341 /* Get current event names */ 1444 /* Get current event names */
1342 namelist = get_kprobe_trace_event_names(fd, false); 1445 namelist = get_probe_trace_event_names(fd, false);
1343 if (!namelist) { 1446 if (!namelist) {
1344 pr_debug("Failed to get current event list.\n"); 1447 pr_debug("Failed to get current event list.\n");
1345 return -EIO; 1448 return -EIO;
@@ -1374,7 +1477,7 @@ static int __add_kprobe_trace_events(struct perf_probe_event *pev,
1374 ret = -ENOMEM; 1477 ret = -ENOMEM;
1375 break; 1478 break;
1376 } 1479 }
1377 ret = write_kprobe_trace_event(fd, tev); 1480 ret = write_probe_trace_event(fd, tev);
1378 if (ret < 0) 1481 if (ret < 0)
1379 break; 1482 break;
1380 /* Add added event name to namelist */ 1483 /* Add added event name to namelist */
@@ -1411,21 +1514,21 @@ static int __add_kprobe_trace_events(struct perf_probe_event *pev,
1411 return ret; 1514 return ret;
1412} 1515}
1413 1516
1414static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, 1517static int convert_to_probe_trace_events(struct perf_probe_event *pev,
1415 struct kprobe_trace_event **tevs, 1518 struct probe_trace_event **tevs,
1416 int max_tevs) 1519 int max_tevs)
1417{ 1520{
1418 struct symbol *sym; 1521 struct symbol *sym;
1419 int ret = 0, i; 1522 int ret = 0, i;
1420 struct kprobe_trace_event *tev; 1523 struct probe_trace_event *tev;
1421 1524
1422 /* Convert perf_probe_event with debuginfo */ 1525 /* Convert perf_probe_event with debuginfo */
1423 ret = try_to_find_kprobe_trace_events(pev, tevs, max_tevs); 1526 ret = try_to_find_probe_trace_events(pev, tevs, max_tevs);
1424 if (ret != 0) 1527 if (ret != 0)
1425 return ret; 1528 return ret;
1426 1529
1427 /* Allocate trace event buffer */ 1530 /* Allocate trace event buffer */
1428 tev = *tevs = zalloc(sizeof(struct kprobe_trace_event)); 1531 tev = *tevs = zalloc(sizeof(struct probe_trace_event));
1429 if (tev == NULL) 1532 if (tev == NULL)
1430 return -ENOMEM; 1533 return -ENOMEM;
1431 1534
@@ -1438,7 +1541,7 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev,
1438 tev->point.offset = pev->point.offset; 1541 tev->point.offset = pev->point.offset;
1439 tev->nargs = pev->nargs; 1542 tev->nargs = pev->nargs;
1440 if (tev->nargs) { 1543 if (tev->nargs) {
1441 tev->args = zalloc(sizeof(struct kprobe_trace_arg) 1544 tev->args = zalloc(sizeof(struct probe_trace_arg)
1442 * tev->nargs); 1545 * tev->nargs);
1443 if (tev->args == NULL) { 1546 if (tev->args == NULL) {
1444 ret = -ENOMEM; 1547 ret = -ENOMEM;
@@ -1479,7 +1582,7 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev,
1479 1582
1480 return 1; 1583 return 1;
1481error: 1584error:
1482 clear_kprobe_trace_event(tev); 1585 clear_probe_trace_event(tev);
1483 free(tev); 1586 free(tev);
1484 *tevs = NULL; 1587 *tevs = NULL;
1485 return ret; 1588 return ret;
@@ -1487,7 +1590,7 @@ error:
1487 1590
1488struct __event_package { 1591struct __event_package {
1489 struct perf_probe_event *pev; 1592 struct perf_probe_event *pev;
1490 struct kprobe_trace_event *tevs; 1593 struct probe_trace_event *tevs;
1491 int ntevs; 1594 int ntevs;
1492}; 1595};
1493 1596
@@ -1503,14 +1606,16 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
1503 1606
1504 /* Init vmlinux path */ 1607 /* Init vmlinux path */
1505 ret = init_vmlinux(); 1608 ret = init_vmlinux();
1506 if (ret < 0) 1609 if (ret < 0) {
1610 free(pkgs);
1507 return ret; 1611 return ret;
1612 }
1508 1613
1509 /* Loop 1: convert all events */ 1614 /* Loop 1: convert all events */
1510 for (i = 0; i < npevs; i++) { 1615 for (i = 0; i < npevs; i++) {
1511 pkgs[i].pev = &pevs[i]; 1616 pkgs[i].pev = &pevs[i];
1512 /* Convert with or without debuginfo */ 1617 /* Convert with or without debuginfo */
1513 ret = convert_to_kprobe_trace_events(pkgs[i].pev, 1618 ret = convert_to_probe_trace_events(pkgs[i].pev,
1514 &pkgs[i].tevs, max_tevs); 1619 &pkgs[i].tevs, max_tevs);
1515 if (ret < 0) 1620 if (ret < 0)
1516 goto end; 1621 goto end;
@@ -1519,24 +1624,27 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs,
1519 1624
1520 /* Loop 2: add all events */ 1625 /* Loop 2: add all events */
1521 for (i = 0; i < npevs && ret >= 0; i++) 1626 for (i = 0; i < npevs && ret >= 0; i++)
1522 ret = __add_kprobe_trace_events(pkgs[i].pev, pkgs[i].tevs, 1627 ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs,
1523 pkgs[i].ntevs, force_add); 1628 pkgs[i].ntevs, force_add);
1524end: 1629end:
1525 /* Loop 3: cleanup trace events */ 1630 /* Loop 3: cleanup and free trace events */
1526 for (i = 0; i < npevs; i++) 1631 for (i = 0; i < npevs; i++) {
1527 for (j = 0; j < pkgs[i].ntevs; j++) 1632 for (j = 0; j < pkgs[i].ntevs; j++)
1528 clear_kprobe_trace_event(&pkgs[i].tevs[j]); 1633 clear_probe_trace_event(&pkgs[i].tevs[j]);
1634 free(pkgs[i].tevs);
1635 }
1636 free(pkgs);
1529 1637
1530 return ret; 1638 return ret;
1531} 1639}
1532 1640
1533static int __del_trace_kprobe_event(int fd, struct str_node *ent) 1641static int __del_trace_probe_event(int fd, struct str_node *ent)
1534{ 1642{
1535 char *p; 1643 char *p;
1536 char buf[128]; 1644 char buf[128];
1537 int ret; 1645 int ret;
1538 1646
1539 /* Convert from perf-probe event to trace-kprobe event */ 1647 /* Convert from perf-probe event to trace-probe event */
1540 ret = e_snprintf(buf, 128, "-:%s", ent->s); 1648 ret = e_snprintf(buf, 128, "-:%s", ent->s);
1541 if (ret < 0) 1649 if (ret < 0)
1542 goto error; 1650 goto error;
@@ -1562,7 +1670,7 @@ error:
1562 return ret; 1670 return ret;
1563} 1671}
1564 1672
1565static int del_trace_kprobe_event(int fd, const char *group, 1673static int del_trace_probe_event(int fd, const char *group,
1566 const char *event, struct strlist *namelist) 1674 const char *event, struct strlist *namelist)
1567{ 1675{
1568 char buf[128]; 1676 char buf[128];
@@ -1579,7 +1687,7 @@ static int del_trace_kprobe_event(int fd, const char *group,
1579 strlist__for_each_safe(ent, n, namelist) 1687 strlist__for_each_safe(ent, n, namelist)
1580 if (strglobmatch(ent->s, buf)) { 1688 if (strglobmatch(ent->s, buf)) {
1581 found++; 1689 found++;
1582 ret = __del_trace_kprobe_event(fd, ent); 1690 ret = __del_trace_probe_event(fd, ent);
1583 if (ret < 0) 1691 if (ret < 0)
1584 break; 1692 break;
1585 strlist__remove(namelist, ent); 1693 strlist__remove(namelist, ent);
@@ -1588,7 +1696,7 @@ static int del_trace_kprobe_event(int fd, const char *group,
1588 ent = strlist__find(namelist, buf); 1696 ent = strlist__find(namelist, buf);
1589 if (ent) { 1697 if (ent) {
1590 found++; 1698 found++;
1591 ret = __del_trace_kprobe_event(fd, ent); 1699 ret = __del_trace_probe_event(fd, ent);
1592 if (ret >= 0) 1700 if (ret >= 0)
1593 strlist__remove(namelist, ent); 1701 strlist__remove(namelist, ent);
1594 } 1702 }
@@ -1612,7 +1720,7 @@ int del_perf_probe_events(struct strlist *dellist)
1612 return fd; 1720 return fd;
1613 1721
1614 /* Get current event names */ 1722 /* Get current event names */
1615 namelist = get_kprobe_trace_event_names(fd, true); 1723 namelist = get_probe_trace_event_names(fd, true);
1616 if (namelist == NULL) 1724 if (namelist == NULL)
1617 return -EINVAL; 1725 return -EINVAL;
1618 1726
@@ -1633,7 +1741,7 @@ int del_perf_probe_events(struct strlist *dellist)
1633 event = str; 1741 event = str;
1634 } 1742 }
1635 pr_debug("Group: %s, Event: %s\n", group, event); 1743 pr_debug("Group: %s, Event: %s\n", group, event);
1636 ret = del_trace_kprobe_event(fd, group, event, namelist); 1744 ret = del_trace_probe_event(fd, group, event, namelist);
1637 free(str); 1745 free(str);
1638 if (ret < 0) 1746 if (ret < 0)
1639 break; 1747 break;
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index e9db1a214ca4..5af39243a25b 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -7,33 +7,33 @@
7extern bool probe_event_dry_run; 7extern bool probe_event_dry_run;
8 8
9/* kprobe-tracer tracing point */ 9/* kprobe-tracer tracing point */
10struct kprobe_trace_point { 10struct probe_trace_point {
11 char *symbol; /* Base symbol */ 11 char *symbol; /* Base symbol */
12 unsigned long offset; /* Offset from symbol */ 12 unsigned long offset; /* Offset from symbol */
13 bool retprobe; /* Return probe flag */ 13 bool retprobe; /* Return probe flag */
14}; 14};
15 15
16/* kprobe-tracer tracing argument referencing offset */ 16/* probe-tracer tracing argument referencing offset */
17struct kprobe_trace_arg_ref { 17struct probe_trace_arg_ref {
18 struct kprobe_trace_arg_ref *next; /* Next reference */ 18 struct probe_trace_arg_ref *next; /* Next reference */
19 long offset; /* Offset value */ 19 long offset; /* Offset value */
20}; 20};
21 21
22/* kprobe-tracer tracing argument */ 22/* kprobe-tracer tracing argument */
23struct kprobe_trace_arg { 23struct probe_trace_arg {
24 char *name; /* Argument name */ 24 char *name; /* Argument name */
25 char *value; /* Base value */ 25 char *value; /* Base value */
26 char *type; /* Type name */ 26 char *type; /* Type name */
27 struct kprobe_trace_arg_ref *ref; /* Referencing offset */ 27 struct probe_trace_arg_ref *ref; /* Referencing offset */
28}; 28};
29 29
30/* kprobe-tracer tracing event (point + arg) */ 30/* kprobe-tracer tracing event (point + arg) */
31struct kprobe_trace_event { 31struct probe_trace_event {
32 char *event; /* Event name */ 32 char *event; /* Event name */
33 char *group; /* Group name */ 33 char *group; /* Group name */
34 struct kprobe_trace_point point; /* Trace point */ 34 struct probe_trace_point point; /* Trace point */
35 int nargs; /* Number of args */ 35 int nargs; /* Number of args */
36 struct kprobe_trace_arg *args; /* Arguments */ 36 struct probe_trace_arg *args; /* Arguments */
37}; 37};
38 38
39/* Perf probe probing point */ 39/* Perf probe probing point */
@@ -50,6 +50,7 @@ struct perf_probe_point {
50struct perf_probe_arg_field { 50struct perf_probe_arg_field {
51 struct perf_probe_arg_field *next; /* Next field */ 51 struct perf_probe_arg_field *next; /* Next field */
52 char *name; /* Name of the field */ 52 char *name; /* Name of the field */
53 long index; /* Array index number */
53 bool ref; /* Referencing flag */ 54 bool ref; /* Referencing flag */
54}; 55};
55 56
@@ -85,31 +86,25 @@ struct line_range {
85 int end; /* End line number */ 86 int end; /* End line number */
86 int offset; /* Start line offset */ 87 int offset; /* Start line offset */
87 char *path; /* Real path name */ 88 char *path; /* Real path name */
89 char *comp_dir; /* Compile directory */
88 struct list_head line_list; /* Visible lines */ 90 struct list_head line_list; /* Visible lines */
89}; 91};
90 92
91/* Command string to events */ 93/* Command string to events */
92extern int parse_perf_probe_command(const char *cmd, 94extern int parse_perf_probe_command(const char *cmd,
93 struct perf_probe_event *pev); 95 struct perf_probe_event *pev);
94extern int parse_kprobe_trace_command(const char *cmd,
95 struct kprobe_trace_event *tev);
96 96
97/* Events to command string */ 97/* Events to command string */
98extern char *synthesize_perf_probe_command(struct perf_probe_event *pev); 98extern char *synthesize_perf_probe_command(struct perf_probe_event *pev);
99extern char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev); 99extern char *synthesize_probe_trace_command(struct probe_trace_event *tev);
100extern int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, 100extern int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf,
101 size_t len); 101 size_t len);
102 102
103/* Check the perf_probe_event needs debuginfo */ 103/* Check the perf_probe_event needs debuginfo */
104extern bool perf_probe_event_need_dwarf(struct perf_probe_event *pev); 104extern bool perf_probe_event_need_dwarf(struct perf_probe_event *pev);
105 105
106/* Convert from kprobe_trace_event to perf_probe_event */
107extern int convert_to_perf_probe_event(struct kprobe_trace_event *tev,
108 struct perf_probe_event *pev);
109
110/* Release event contents */ 106/* Release event contents */
111extern void clear_perf_probe_event(struct perf_probe_event *pev); 107extern void clear_perf_probe_event(struct perf_probe_event *pev);
112extern void clear_kprobe_trace_event(struct kprobe_trace_event *tev);
113 108
114/* Command string to line-range */ 109/* Command string to line-range */
115extern int parse_line_range_desc(const char *cmd, struct line_range *lr); 110extern int parse_line_range_desc(const char *cmd, struct line_range *lr);
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index d964cb199c67..525136684d4e 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -33,10 +33,10 @@
33#include <ctype.h> 33#include <ctype.h>
34#include <dwarf-regs.h> 34#include <dwarf-regs.h>
35 35
36#include "string.h"
37#include "event.h" 36#include "event.h"
38#include "debug.h" 37#include "debug.h"
39#include "util.h" 38#include "util.h"
39#include "symbol.h"
40#include "probe-finder.h" 40#include "probe-finder.h"
41 41
42/* Kprobe tracer basic type is up to u64 */ 42/* Kprobe tracer basic type is up to u64 */
@@ -143,12 +143,21 @@ static const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname)
143 return src; 143 return src;
144} 144}
145 145
146/* Get DW_AT_comp_dir (should be NULL with older gcc) */
147static const char *cu_get_comp_dir(Dwarf_Die *cu_die)
148{
149 Dwarf_Attribute attr;
150 if (dwarf_attr(cu_die, DW_AT_comp_dir, &attr) == NULL)
151 return NULL;
152 return dwarf_formstring(&attr);
153}
154
146/* Compare diename and tname */ 155/* Compare diename and tname */
147static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) 156static bool die_compare_name(Dwarf_Die *dw_die, const char *tname)
148{ 157{
149 const char *name; 158 const char *name;
150 name = dwarf_diename(dw_die); 159 name = dwarf_diename(dw_die);
151 return name ? strcmp(tname, name) : -1; 160 return name ? (strcmp(tname, name) == 0) : false;
152} 161}
153 162
154/* Get type die, but skip qualifiers and typedef */ 163/* Get type die, but skip qualifiers and typedef */
@@ -319,7 +328,7 @@ static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data)
319 tag = dwarf_tag(die_mem); 328 tag = dwarf_tag(die_mem);
320 if ((tag == DW_TAG_formal_parameter || 329 if ((tag == DW_TAG_formal_parameter ||
321 tag == DW_TAG_variable) && 330 tag == DW_TAG_variable) &&
322 (die_compare_name(die_mem, name) == 0)) 331 die_compare_name(die_mem, name))
323 return DIE_FIND_CB_FOUND; 332 return DIE_FIND_CB_FOUND;
324 333
325 return DIE_FIND_CB_CONTINUE; 334 return DIE_FIND_CB_CONTINUE;
@@ -338,7 +347,7 @@ static int __die_find_member_cb(Dwarf_Die *die_mem, void *data)
338 const char *name = data; 347 const char *name = data;
339 348
340 if ((dwarf_tag(die_mem) == DW_TAG_member) && 349 if ((dwarf_tag(die_mem) == DW_TAG_member) &&
341 (die_compare_name(die_mem, name) == 0)) 350 die_compare_name(die_mem, name))
342 return DIE_FIND_CB_FOUND; 351 return DIE_FIND_CB_FOUND;
343 352
344 return DIE_FIND_CB_SIBLING; 353 return DIE_FIND_CB_SIBLING;
@@ -356,14 +365,50 @@ static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name,
356 * Probe finder related functions 365 * Probe finder related functions
357 */ 366 */
358 367
368static struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs)
369{
370 struct probe_trace_arg_ref *ref;
371 ref = zalloc(sizeof(struct probe_trace_arg_ref));
372 if (ref != NULL)
373 ref->offset = offs;
374 return ref;
375}
376
359/* Show a location */ 377/* Show a location */
360static int convert_location(Dwarf_Op *op, struct probe_finder *pf) 378static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf)
361{ 379{
380 Dwarf_Attribute attr;
381 Dwarf_Op *op;
382 size_t nops;
362 unsigned int regn; 383 unsigned int regn;
363 Dwarf_Word offs = 0; 384 Dwarf_Word offs = 0;
364 bool ref = false; 385 bool ref = false;
365 const char *regs; 386 const char *regs;
366 struct kprobe_trace_arg *tvar = pf->tvar; 387 struct probe_trace_arg *tvar = pf->tvar;
388 int ret;
389
390 /* TODO: handle more than 1 exprs */
391 if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL ||
392 dwarf_getlocation_addr(&attr, pf->addr, &op, &nops, 1) <= 0 ||
393 nops == 0) {
394 /* TODO: Support const_value */
395 pr_err("Failed to find the location of %s at this address.\n"
396 " Perhaps, it has been optimized out.\n", pf->pvar->var);
397 return -ENOENT;
398 }
399
400 if (op->atom == DW_OP_addr) {
401 /* Static variables on memory (not stack), make @varname */
402 ret = strlen(dwarf_diename(vr_die));
403 tvar->value = zalloc(ret + 2);
404 if (tvar->value == NULL)
405 return -ENOMEM;
406 snprintf(tvar->value, ret + 2, "@%s", dwarf_diename(vr_die));
407 tvar->ref = alloc_trace_arg_ref((long)offs);
408 if (tvar->ref == NULL)
409 return -ENOMEM;
410 return 0;
411 }
367 412
368 /* If this is based on frame buffer, set the offset */ 413 /* If this is based on frame buffer, set the offset */
369 if (op->atom == DW_OP_fbreg) { 414 if (op->atom == DW_OP_fbreg) {
@@ -405,27 +450,72 @@ static int convert_location(Dwarf_Op *op, struct probe_finder *pf)
405 return -ENOMEM; 450 return -ENOMEM;
406 451
407 if (ref) { 452 if (ref) {
408 tvar->ref = zalloc(sizeof(struct kprobe_trace_arg_ref)); 453 tvar->ref = alloc_trace_arg_ref((long)offs);
409 if (tvar->ref == NULL) 454 if (tvar->ref == NULL)
410 return -ENOMEM; 455 return -ENOMEM;
411 tvar->ref->offset = (long)offs;
412 } 456 }
413 return 0; 457 return 0;
414} 458}
415 459
416static int convert_variable_type(Dwarf_Die *vr_die, 460static int convert_variable_type(Dwarf_Die *vr_die,
417 struct kprobe_trace_arg *targ) 461 struct probe_trace_arg *tvar,
462 const char *cast)
418{ 463{
464 struct probe_trace_arg_ref **ref_ptr = &tvar->ref;
419 Dwarf_Die type; 465 Dwarf_Die type;
420 char buf[16]; 466 char buf[16];
421 int ret; 467 int ret;
422 468
469 /* TODO: check all types */
470 if (cast && strcmp(cast, "string") != 0) {
471 /* Non string type is OK */
472 tvar->type = strdup(cast);
473 return (tvar->type == NULL) ? -ENOMEM : 0;
474 }
475
423 if (die_get_real_type(vr_die, &type) == NULL) { 476 if (die_get_real_type(vr_die, &type) == NULL) {
424 pr_warning("Failed to get a type information of %s.\n", 477 pr_warning("Failed to get a type information of %s.\n",
425 dwarf_diename(vr_die)); 478 dwarf_diename(vr_die));
426 return -ENOENT; 479 return -ENOENT;
427 } 480 }
428 481
482 pr_debug("%s type is %s.\n",
483 dwarf_diename(vr_die), dwarf_diename(&type));
484
485 if (cast && strcmp(cast, "string") == 0) { /* String type */
486 ret = dwarf_tag(&type);
487 if (ret != DW_TAG_pointer_type &&
488 ret != DW_TAG_array_type) {
489 pr_warning("Failed to cast into string: "
490 "%s(%s) is not a pointer nor array.",
491 dwarf_diename(vr_die), dwarf_diename(&type));
492 return -EINVAL;
493 }
494 if (ret == DW_TAG_pointer_type) {
495 if (die_get_real_type(&type, &type) == NULL) {
496 pr_warning("Failed to get a type information.");
497 return -ENOENT;
498 }
499 while (*ref_ptr)
500 ref_ptr = &(*ref_ptr)->next;
501 /* Add new reference with offset +0 */
502 *ref_ptr = zalloc(sizeof(struct probe_trace_arg_ref));
503 if (*ref_ptr == NULL) {
504 pr_warning("Out of memory error\n");
505 return -ENOMEM;
506 }
507 }
508 if (!die_compare_name(&type, "char") &&
509 !die_compare_name(&type, "unsigned char")) {
510 pr_warning("Failed to cast into string: "
511 "%s is not (unsigned) char *.",
512 dwarf_diename(vr_die));
513 return -EINVAL;
514 }
515 tvar->type = strdup(cast);
516 return (tvar->type == NULL) ? -ENOMEM : 0;
517 }
518
429 ret = die_get_byte_size(&type) * 8; 519 ret = die_get_byte_size(&type) * 8;
430 if (ret) { 520 if (ret) {
431 /* Check the bitwidth */ 521 /* Check the bitwidth */
@@ -445,8 +535,8 @@ static int convert_variable_type(Dwarf_Die *vr_die,
445 strerror(-ret)); 535 strerror(-ret));
446 return ret; 536 return ret;
447 } 537 }
448 targ->type = strdup(buf); 538 tvar->type = strdup(buf);
449 if (targ->type == NULL) 539 if (tvar->type == NULL)
450 return -ENOMEM; 540 return -ENOMEM;
451 } 541 }
452 return 0; 542 return 0;
@@ -454,22 +544,50 @@ static int convert_variable_type(Dwarf_Die *vr_die,
454 544
455static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, 545static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
456 struct perf_probe_arg_field *field, 546 struct perf_probe_arg_field *field,
457 struct kprobe_trace_arg_ref **ref_ptr, 547 struct probe_trace_arg_ref **ref_ptr,
458 Dwarf_Die *die_mem) 548 Dwarf_Die *die_mem)
459{ 549{
460 struct kprobe_trace_arg_ref *ref = *ref_ptr; 550 struct probe_trace_arg_ref *ref = *ref_ptr;
461 Dwarf_Die type; 551 Dwarf_Die type;
462 Dwarf_Word offs; 552 Dwarf_Word offs;
463 int ret; 553 int ret, tag;
464 554
465 pr_debug("converting %s in %s\n", field->name, varname); 555 pr_debug("converting %s in %s\n", field->name, varname);
466 if (die_get_real_type(vr_die, &type) == NULL) { 556 if (die_get_real_type(vr_die, &type) == NULL) {
467 pr_warning("Failed to get the type of %s.\n", varname); 557 pr_warning("Failed to get the type of %s.\n", varname);
468 return -ENOENT; 558 return -ENOENT;
469 } 559 }
470 560 pr_debug2("Var real type: (%x)\n", (unsigned)dwarf_dieoffset(&type));
471 /* Check the pointer and dereference */ 561 tag = dwarf_tag(&type);
472 if (dwarf_tag(&type) == DW_TAG_pointer_type) { 562
563 if (field->name[0] == '[' &&
564 (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)) {
565 if (field->next)
566 /* Save original type for next field */
567 memcpy(die_mem, &type, sizeof(*die_mem));
568 /* Get the type of this array */
569 if (die_get_real_type(&type, &type) == NULL) {
570 pr_warning("Failed to get the type of %s.\n", varname);
571 return -ENOENT;
572 }
573 pr_debug2("Array real type: (%x)\n",
574 (unsigned)dwarf_dieoffset(&type));
575 if (tag == DW_TAG_pointer_type) {
576 ref = zalloc(sizeof(struct probe_trace_arg_ref));
577 if (ref == NULL)
578 return -ENOMEM;
579 if (*ref_ptr)
580 (*ref_ptr)->next = ref;
581 else
582 *ref_ptr = ref;
583 }
584 ref->offset += die_get_byte_size(&type) * field->index;
585 if (!field->next)
586 /* Save vr_die for converting types */
587 memcpy(die_mem, vr_die, sizeof(*die_mem));
588 goto next;
589 } else if (tag == DW_TAG_pointer_type) {
590 /* Check the pointer and dereference */
473 if (!field->ref) { 591 if (!field->ref) {
474 pr_err("Semantic error: %s must be referred by '->'\n", 592 pr_err("Semantic error: %s must be referred by '->'\n",
475 field->name); 593 field->name);
@@ -486,7 +604,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
486 return -EINVAL; 604 return -EINVAL;
487 } 605 }
488 606
489 ref = zalloc(sizeof(struct kprobe_trace_arg_ref)); 607 ref = zalloc(sizeof(struct probe_trace_arg_ref));
490 if (ref == NULL) 608 if (ref == NULL)
491 return -ENOMEM; 609 return -ENOMEM;
492 if (*ref_ptr) 610 if (*ref_ptr)
@@ -495,10 +613,15 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
495 *ref_ptr = ref; 613 *ref_ptr = ref;
496 } else { 614 } else {
497 /* Verify it is a data structure */ 615 /* Verify it is a data structure */
498 if (dwarf_tag(&type) != DW_TAG_structure_type) { 616 if (tag != DW_TAG_structure_type) {
499 pr_warning("%s is not a data structure.\n", varname); 617 pr_warning("%s is not a data structure.\n", varname);
500 return -EINVAL; 618 return -EINVAL;
501 } 619 }
620 if (field->name[0] == '[') {
621 pr_err("Semantic error: %s is not a pointor nor array.",
622 varname);
623 return -EINVAL;
624 }
502 if (field->ref) { 625 if (field->ref) {
503 pr_err("Semantic error: %s must be referred by '.'\n", 626 pr_err("Semantic error: %s must be referred by '.'\n",
504 field->name); 627 field->name);
@@ -525,6 +648,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
525 } 648 }
526 ref->offset += (long)offs; 649 ref->offset += (long)offs;
527 650
651next:
528 /* Converting next field */ 652 /* Converting next field */
529 if (field->next) 653 if (field->next)
530 return convert_variable_fields(die_mem, field->name, 654 return convert_variable_fields(die_mem, field->name,
@@ -536,51 +660,32 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
536/* Show a variables in kprobe event format */ 660/* Show a variables in kprobe event format */
537static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) 661static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
538{ 662{
539 Dwarf_Attribute attr;
540 Dwarf_Die die_mem; 663 Dwarf_Die die_mem;
541 Dwarf_Op *expr;
542 size_t nexpr;
543 int ret; 664 int ret;
544 665
545 if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL) 666 pr_debug("Converting variable %s into trace event.\n",
546 goto error; 667 dwarf_diename(vr_die));
547 /* TODO: handle more than 1 exprs */
548 ret = dwarf_getlocation_addr(&attr, pf->addr, &expr, &nexpr, 1);
549 if (ret <= 0 || nexpr == 0)
550 goto error;
551 668
552 ret = convert_location(expr, pf); 669 ret = convert_variable_location(vr_die, pf);
553 if (ret == 0 && pf->pvar->field) { 670 if (ret == 0 && pf->pvar->field) {
554 ret = convert_variable_fields(vr_die, pf->pvar->var, 671 ret = convert_variable_fields(vr_die, pf->pvar->var,
555 pf->pvar->field, &pf->tvar->ref, 672 pf->pvar->field, &pf->tvar->ref,
556 &die_mem); 673 &die_mem);
557 vr_die = &die_mem; 674 vr_die = &die_mem;
558 } 675 }
559 if (ret == 0) { 676 if (ret == 0)
560 if (pf->pvar->type) { 677 ret = convert_variable_type(vr_die, pf->tvar, pf->pvar->type);
561 pf->tvar->type = strdup(pf->pvar->type);
562 if (pf->tvar->type == NULL)
563 ret = -ENOMEM;
564 } else
565 ret = convert_variable_type(vr_die, pf->tvar);
566 }
567 /* *expr will be cached in libdw. Don't free it. */ 678 /* *expr will be cached in libdw. Don't free it. */
568 return ret; 679 return ret;
569error:
570 /* TODO: Support const_value */
571 pr_err("Failed to find the location of %s at this address.\n"
572 " Perhaps, it has been optimized out.\n", pf->pvar->var);
573 return -ENOENT;
574} 680}
575 681
576/* Find a variable in a subprogram die */ 682/* Find a variable in a subprogram die */
577static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) 683static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
578{ 684{
579 Dwarf_Die vr_die; 685 Dwarf_Die vr_die, *scopes;
580 char buf[32], *ptr; 686 char buf[32], *ptr;
581 int ret; 687 int ret, nscopes;
582 688
583 /* TODO: Support arrays */
584 if (pf->pvar->name) 689 if (pf->pvar->name)
585 pf->tvar->name = strdup(pf->pvar->name); 690 pf->tvar->name = strdup(pf->pvar->name);
586 else { 691 else {
@@ -600,25 +705,43 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
600 pf->tvar->value = strdup(pf->pvar->var); 705 pf->tvar->value = strdup(pf->pvar->var);
601 if (pf->tvar->value == NULL) 706 if (pf->tvar->value == NULL)
602 return -ENOMEM; 707 return -ENOMEM;
603 else 708 if (pf->pvar->type) {
604 return 0; 709 pf->tvar->type = strdup(pf->pvar->type);
710 if (pf->tvar->type == NULL)
711 return -ENOMEM;
712 }
713 return 0;
605 } 714 }
606 715
607 pr_debug("Searching '%s' variable in context.\n", 716 pr_debug("Searching '%s' variable in context.\n",
608 pf->pvar->var); 717 pf->pvar->var);
609 /* Search child die for local variables and parameters. */ 718 /* Search child die for local variables and parameters. */
610 if (!die_find_variable(sp_die, pf->pvar->var, &vr_die)) { 719 if (die_find_variable(sp_die, pf->pvar->var, &vr_die))
720 ret = convert_variable(&vr_die, pf);
721 else {
722 /* Search upper class */
723 nscopes = dwarf_getscopes_die(sp_die, &scopes);
724 if (nscopes > 0) {
725 ret = dwarf_getscopevar(scopes, nscopes, pf->pvar->var,
726 0, NULL, 0, 0, &vr_die);
727 if (ret >= 0)
728 ret = convert_variable(&vr_die, pf);
729 else
730 ret = -ENOENT;
731 free(scopes);
732 } else
733 ret = -ENOENT;
734 }
735 if (ret < 0)
611 pr_warning("Failed to find '%s' in this function.\n", 736 pr_warning("Failed to find '%s' in this function.\n",
612 pf->pvar->var); 737 pf->pvar->var);
613 return -ENOENT; 738 return ret;
614 }
615 return convert_variable(&vr_die, pf);
616} 739}
617 740
618/* Show a probe point to output buffer */ 741/* Show a probe point to output buffer */
619static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) 742static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
620{ 743{
621 struct kprobe_trace_event *tev; 744 struct probe_trace_event *tev;
622 Dwarf_Addr eaddr; 745 Dwarf_Addr eaddr;
623 Dwarf_Die die_mem; 746 Dwarf_Die die_mem;
624 const char *name; 747 const char *name;
@@ -683,7 +806,7 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf)
683 806
684 /* Find each argument */ 807 /* Find each argument */
685 tev->nargs = pf->pev->nargs; 808 tev->nargs = pf->pev->nargs;
686 tev->args = zalloc(sizeof(struct kprobe_trace_arg) * tev->nargs); 809 tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
687 if (tev->args == NULL) 810 if (tev->args == NULL)
688 return -ENOMEM; 811 return -ENOMEM;
689 for (i = 0; i < pf->pev->nargs; i++) { 812 for (i = 0; i < pf->pev->nargs; i++) {
@@ -897,7 +1020,7 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
897 1020
898 /* Check tag and diename */ 1021 /* Check tag and diename */
899 if (dwarf_tag(sp_die) != DW_TAG_subprogram || 1022 if (dwarf_tag(sp_die) != DW_TAG_subprogram ||
900 die_compare_name(sp_die, pp->function) != 0) 1023 !die_compare_name(sp_die, pp->function))
901 return DWARF_CB_OK; 1024 return DWARF_CB_OK;
902 1025
903 pf->fname = dwarf_decl_file(sp_die); 1026 pf->fname = dwarf_decl_file(sp_die);
@@ -940,9 +1063,9 @@ static int find_probe_point_by_func(struct probe_finder *pf)
940 return _param.retval; 1063 return _param.retval;
941} 1064}
942 1065
943/* Find kprobe_trace_events specified by perf_probe_event from debuginfo */ 1066/* Find probe_trace_events specified by perf_probe_event from debuginfo */
944int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, 1067int find_probe_trace_events(int fd, struct perf_probe_event *pev,
945 struct kprobe_trace_event **tevs, int max_tevs) 1068 struct probe_trace_event **tevs, int max_tevs)
946{ 1069{
947 struct probe_finder pf = {.pev = pev, .max_tevs = max_tevs}; 1070 struct probe_finder pf = {.pev = pev, .max_tevs = max_tevs};
948 struct perf_probe_point *pp = &pev->point; 1071 struct perf_probe_point *pp = &pev->point;
@@ -952,7 +1075,7 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev,
952 Dwarf *dbg; 1075 Dwarf *dbg;
953 int ret = 0; 1076 int ret = 0;
954 1077
955 pf.tevs = zalloc(sizeof(struct kprobe_trace_event) * max_tevs); 1078 pf.tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs);
956 if (pf.tevs == NULL) 1079 if (pf.tevs == NULL)
957 return -ENOMEM; 1080 return -ENOMEM;
958 *tevs = pf.tevs; 1081 *tevs = pf.tevs;
@@ -1096,7 +1219,7 @@ end:
1096static int line_range_add_line(const char *src, unsigned int lineno, 1219static int line_range_add_line(const char *src, unsigned int lineno,
1097 struct line_range *lr) 1220 struct line_range *lr)
1098{ 1221{
1099 /* Copy real path */ 1222 /* Copy source path */
1100 if (!lr->path) { 1223 if (!lr->path) {
1101 lr->path = strdup(src); 1224 lr->path = strdup(src);
1102 if (lr->path == NULL) 1225 if (lr->path == NULL)
@@ -1220,7 +1343,7 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data)
1220 struct line_range *lr = lf->lr; 1343 struct line_range *lr = lf->lr;
1221 1344
1222 if (dwarf_tag(sp_die) == DW_TAG_subprogram && 1345 if (dwarf_tag(sp_die) == DW_TAG_subprogram &&
1223 die_compare_name(sp_die, lr->function) == 0) { 1346 die_compare_name(sp_die, lr->function)) {
1224 lf->fname = dwarf_decl_file(sp_die); 1347 lf->fname = dwarf_decl_file(sp_die);
1225 dwarf_decl_line(sp_die, &lr->offset); 1348 dwarf_decl_line(sp_die, &lr->offset);
1226 pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset); 1349 pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset);
@@ -1263,6 +1386,7 @@ int find_line_range(int fd, struct line_range *lr)
1263 size_t cuhl; 1386 size_t cuhl;
1264 Dwarf_Die *diep; 1387 Dwarf_Die *diep;
1265 Dwarf *dbg; 1388 Dwarf *dbg;
1389 const char *comp_dir;
1266 1390
1267 dbg = dwarf_begin(fd, DWARF_C_READ); 1391 dbg = dwarf_begin(fd, DWARF_C_READ);
1268 if (!dbg) { 1392 if (!dbg) {
@@ -1298,7 +1422,18 @@ int find_line_range(int fd, struct line_range *lr)
1298 } 1422 }
1299 off = noff; 1423 off = noff;
1300 } 1424 }
1301 pr_debug("path: %lx\n", (unsigned long)lr->path); 1425
1426 /* Store comp_dir */
1427 if (lf.found) {
1428 comp_dir = cu_get_comp_dir(&lf.cu_die);
1429 if (comp_dir) {
1430 lr->comp_dir = strdup(comp_dir);
1431 if (!lr->comp_dir)
1432 ret = -ENOMEM;
1433 }
1434 }
1435
1436 pr_debug("path: %s\n", lr->path);
1302 dwarf_end(dbg); 1437 dwarf_end(dbg);
1303 1438
1304 return (ret < 0) ? ret : lf.found; 1439 return (ret < 0) ? ret : lf.found;
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h
index e1f61dcd18ff..4507d519f183 100644
--- a/tools/perf/util/probe-finder.h
+++ b/tools/perf/util/probe-finder.h
@@ -16,9 +16,9 @@ static inline int is_c_varname(const char *name)
16} 16}
17 17
18#ifdef DWARF_SUPPORT 18#ifdef DWARF_SUPPORT
19/* Find kprobe_trace_events specified by perf_probe_event from debuginfo */ 19/* Find probe_trace_events specified by perf_probe_event from debuginfo */
20extern int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, 20extern int find_probe_trace_events(int fd, struct perf_probe_event *pev,
21 struct kprobe_trace_event **tevs, 21 struct probe_trace_event **tevs,
22 int max_tevs); 22 int max_tevs);
23 23
24/* Find a perf_probe_point from debuginfo */ 24/* Find a perf_probe_point from debuginfo */
@@ -33,7 +33,7 @@ extern int find_line_range(int fd, struct line_range *lr);
33 33
34struct probe_finder { 34struct probe_finder {
35 struct perf_probe_event *pev; /* Target probe event */ 35 struct perf_probe_event *pev; /* Target probe event */
36 struct kprobe_trace_event *tevs; /* Result trace events */ 36 struct probe_trace_event *tevs; /* Result trace events */
37 int ntevs; /* Number of trace events */ 37 int ntevs; /* Number of trace events */
38 int max_tevs; /* Max number of trace events */ 38 int max_tevs; /* Max number of trace events */
39 39
@@ -50,7 +50,7 @@ struct probe_finder {
50#endif 50#endif
51 Dwarf_Op *fb_ops; /* Frame base attribute */ 51 Dwarf_Op *fb_ops; /* Frame base attribute */
52 struct perf_probe_arg *pvar; /* Current target variable */ 52 struct perf_probe_arg *pvar; /* Current target variable */
53 struct kprobe_trace_arg *tvar; /* Current result variable */ 53 struct probe_trace_arg *tvar; /* Current result variable */
54}; 54};
55 55
56struct line_finder { 56struct line_finder {
diff --git a/tools/perf/util/pstack.h b/tools/perf/util/pstack.h
index 5ad07023504b..4cedea59f518 100644
--- a/tools/perf/util/pstack.h
+++ b/tools/perf/util/pstack.h
@@ -1,6 +1,8 @@
1#ifndef _PERF_PSTACK_ 1#ifndef _PERF_PSTACK_
2#define _PERF_PSTACK_ 2#define _PERF_PSTACK_
3 3
4#include <stdbool.h>
5
4struct pstack; 6struct pstack;
5struct pstack *pstack__new(unsigned short max_nr_entries); 7struct pstack *pstack__new(unsigned short max_nr_entries);
6void pstack__delete(struct pstack *self); 8void pstack__delete(struct pstack *self);
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c
index 81f39cab3aaa..33a632523743 100644
--- a/tools/perf/util/scripting-engines/trace-event-python.c
+++ b/tools/perf/util/scripting-engines/trace-event-python.c
@@ -208,7 +208,7 @@ static void python_process_event(int cpu, void *data,
208 int size __unused, 208 int size __unused,
209 unsigned long long nsecs, char *comm) 209 unsigned long long nsecs, char *comm)
210{ 210{
211 PyObject *handler, *retval, *context, *t, *obj; 211 PyObject *handler, *retval, *context, *t, *obj, *dict = NULL;
212 static char handler_name[256]; 212 static char handler_name[256];
213 struct format_field *field; 213 struct format_field *field;
214 unsigned long long val; 214 unsigned long long val;
@@ -232,6 +232,14 @@ static void python_process_event(int cpu, void *data,
232 232
233 sprintf(handler_name, "%s__%s", event->system, event->name); 233 sprintf(handler_name, "%s__%s", event->system, event->name);
234 234
235 handler = PyDict_GetItemString(main_dict, handler_name);
236 if (handler && !PyCallable_Check(handler))
237 handler = NULL;
238 if (!handler) {
239 dict = PyDict_New();
240 if (!dict)
241 Py_FatalError("couldn't create Python dict");
242 }
235 s = nsecs / NSECS_PER_SEC; 243 s = nsecs / NSECS_PER_SEC;
236 ns = nsecs - s * NSECS_PER_SEC; 244 ns = nsecs - s * NSECS_PER_SEC;
237 245
@@ -242,12 +250,20 @@ static void python_process_event(int cpu, void *data,
242 PyTuple_SetItem(t, n++, PyString_FromString(handler_name)); 250 PyTuple_SetItem(t, n++, PyString_FromString(handler_name));
243 PyTuple_SetItem(t, n++, 251 PyTuple_SetItem(t, n++,
244 PyCObject_FromVoidPtr(scripting_context, NULL)); 252 PyCObject_FromVoidPtr(scripting_context, NULL));
245 PyTuple_SetItem(t, n++, PyInt_FromLong(cpu));
246 PyTuple_SetItem(t, n++, PyInt_FromLong(s));
247 PyTuple_SetItem(t, n++, PyInt_FromLong(ns));
248 PyTuple_SetItem(t, n++, PyInt_FromLong(pid));
249 PyTuple_SetItem(t, n++, PyString_FromString(comm));
250 253
254 if (handler) {
255 PyTuple_SetItem(t, n++, PyInt_FromLong(cpu));
256 PyTuple_SetItem(t, n++, PyInt_FromLong(s));
257 PyTuple_SetItem(t, n++, PyInt_FromLong(ns));
258 PyTuple_SetItem(t, n++, PyInt_FromLong(pid));
259 PyTuple_SetItem(t, n++, PyString_FromString(comm));
260 } else {
261 PyDict_SetItemString(dict, "common_cpu", PyInt_FromLong(cpu));
262 PyDict_SetItemString(dict, "common_s", PyInt_FromLong(s));
263 PyDict_SetItemString(dict, "common_ns", PyInt_FromLong(ns));
264 PyDict_SetItemString(dict, "common_pid", PyInt_FromLong(pid));
265 PyDict_SetItemString(dict, "common_comm", PyString_FromString(comm));
266 }
251 for (field = event->format.fields; field; field = field->next) { 267 for (field = event->format.fields; field; field = field->next) {
252 if (field->flags & FIELD_IS_STRING) { 268 if (field->flags & FIELD_IS_STRING) {
253 int offset; 269 int offset;
@@ -272,27 +288,31 @@ static void python_process_event(int cpu, void *data,
272 obj = PyLong_FromUnsignedLongLong(val); 288 obj = PyLong_FromUnsignedLongLong(val);
273 } 289 }
274 } 290 }
275 PyTuple_SetItem(t, n++, obj); 291 if (handler)
292 PyTuple_SetItem(t, n++, obj);
293 else
294 PyDict_SetItemString(dict, field->name, obj);
295
276 } 296 }
297 if (!handler)
298 PyTuple_SetItem(t, n++, dict);
277 299
278 if (_PyTuple_Resize(&t, n) == -1) 300 if (_PyTuple_Resize(&t, n) == -1)
279 Py_FatalError("error resizing Python tuple"); 301 Py_FatalError("error resizing Python tuple");
280 302
281 handler = PyDict_GetItemString(main_dict, handler_name); 303 if (handler) {
282 if (handler && PyCallable_Check(handler)) {
283 retval = PyObject_CallObject(handler, t); 304 retval = PyObject_CallObject(handler, t);
284 if (retval == NULL) 305 if (retval == NULL)
285 handler_call_die(handler_name); 306 handler_call_die(handler_name);
286 } else { 307 } else {
287 handler = PyDict_GetItemString(main_dict, "trace_unhandled"); 308 handler = PyDict_GetItemString(main_dict, "trace_unhandled");
288 if (handler && PyCallable_Check(handler)) { 309 if (handler && PyCallable_Check(handler)) {
289 if (_PyTuple_Resize(&t, N_COMMON_FIELDS) == -1)
290 Py_FatalError("error resizing Python tuple");
291 310
292 retval = PyObject_CallObject(handler, t); 311 retval = PyObject_CallObject(handler, t);
293 if (retval == NULL) 312 if (retval == NULL)
294 handler_call_die("trace_unhandled"); 313 handler_call_die("trace_unhandled");
295 } 314 }
315 Py_DECREF(dict);
296 } 316 }
297 317
298 Py_DECREF(t); 318 Py_DECREF(t);
@@ -548,12 +568,10 @@ static int python_generate_script(const char *outfile)
548 } 568 }
549 569
550 fprintf(ofp, "def trace_unhandled(event_name, context, " 570 fprintf(ofp, "def trace_unhandled(event_name, context, "
551 "common_cpu, common_secs, common_nsecs,\n\t\t" 571 "event_fields_dict):\n");
552 "common_pid, common_comm):\n");
553 572
554 fprintf(ofp, "\t\tprint_header(event_name, common_cpu, " 573 fprintf(ofp, "\t\tprint ' '.join(['%%s=%%s'%%(k,str(v))"
555 "common_secs, common_nsecs,\n\t\tcommon_pid, " 574 "for k,v in sorted(event_fields_dict.items())])\n\n");
556 "common_comm)\n\n");
557 575
558 fprintf(ofp, "def print_header(" 576 fprintf(ofp, "def print_header("
559 "event_name, cpu, secs, nsecs, pid, comm):\n" 577 "event_name, cpu, secs, nsecs, pid, comm):\n"
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 8f83a1835766..fa9d652c2dc3 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -27,8 +27,10 @@ static int perf_session__open(struct perf_session *self, bool force)
27 27
28 self->fd = open(self->filename, O_RDONLY); 28 self->fd = open(self->filename, O_RDONLY);
29 if (self->fd < 0) { 29 if (self->fd < 0) {
30 pr_err("failed to open file: %s", self->filename); 30 int err = errno;
31 if (!strcmp(self->filename, "perf.data")) 31
32 pr_err("failed to open %s: %s", self->filename, strerror(err));
33 if (err == ENOENT && !strcmp(self->filename, "perf.data"))
32 pr_err(" (try 'perf record' first)"); 34 pr_err(" (try 'perf record' first)");
33 pr_err("\n"); 35 pr_err("\n");
34 return -errno; 36 return -errno;
@@ -77,6 +79,12 @@ int perf_session__create_kernel_maps(struct perf_session *self)
77 return ret; 79 return ret;
78} 80}
79 81
82static void perf_session__destroy_kernel_maps(struct perf_session *self)
83{
84 machine__destroy_kernel_maps(&self->host_machine);
85 machines__destroy_guest_kernel_maps(&self->machines);
86}
87
80struct perf_session *perf_session__new(const char *filename, int mode, bool force, bool repipe) 88struct perf_session *perf_session__new(const char *filename, int mode, bool force, bool repipe)
81{ 89{
82 size_t len = filename ? strlen(filename) + 1 : 0; 90 size_t len = filename ? strlen(filename) + 1 : 0;
@@ -90,11 +98,10 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc
90 98
91 memcpy(self->filename, filename, len); 99 memcpy(self->filename, filename, len);
92 self->threads = RB_ROOT; 100 self->threads = RB_ROOT;
101 INIT_LIST_HEAD(&self->dead_threads);
93 self->hists_tree = RB_ROOT; 102 self->hists_tree = RB_ROOT;
94 self->last_match = NULL; 103 self->last_match = NULL;
95 self->mmap_window = 32; 104 self->mmap_window = 32;
96 self->cwd = NULL;
97 self->cwdlen = 0;
98 self->machines = RB_ROOT; 105 self->machines = RB_ROOT;
99 self->repipe = repipe; 106 self->repipe = repipe;
100 INIT_LIST_HEAD(&self->ordered_samples.samples_head); 107 INIT_LIST_HEAD(&self->ordered_samples.samples_head);
@@ -123,14 +130,51 @@ out_delete:
123 return NULL; 130 return NULL;
124} 131}
125 132
133static void perf_session__delete_dead_threads(struct perf_session *self)
134{
135 struct thread *n, *t;
136
137 list_for_each_entry_safe(t, n, &self->dead_threads, node) {
138 list_del(&t->node);
139 thread__delete(t);
140 }
141}
142
143static void perf_session__delete_threads(struct perf_session *self)
144{
145 struct rb_node *nd = rb_first(&self->threads);
146
147 while (nd) {
148 struct thread *t = rb_entry(nd, struct thread, rb_node);
149
150 rb_erase(&t->rb_node, &self->threads);
151 nd = rb_next(nd);
152 thread__delete(t);
153 }
154}
155
126void perf_session__delete(struct perf_session *self) 156void perf_session__delete(struct perf_session *self)
127{ 157{
128 perf_header__exit(&self->header); 158 perf_header__exit(&self->header);
159 perf_session__destroy_kernel_maps(self);
160 perf_session__delete_dead_threads(self);
161 perf_session__delete_threads(self);
162 machine__exit(&self->host_machine);
129 close(self->fd); 163 close(self->fd);
130 free(self->cwd);
131 free(self); 164 free(self);
132} 165}
133 166
167void perf_session__remove_thread(struct perf_session *self, struct thread *th)
168{
169 self->last_match = NULL;
170 rb_erase(&th->rb_node, &self->threads);
171 /*
172 * We may have references to this thread, for instance in some hist_entry
173 * instances, so just move them to a separate list.
174 */
175 list_add_tail(&th->node, &self->dead_threads);
176}
177
134static bool symbol__match_parent_regex(struct symbol *sym) 178static bool symbol__match_parent_regex(struct symbol *sym)
135{ 179{
136 if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0)) 180 if (sym->name && !regexec(&parent_regex, sym->name, 0, NULL, 0))
@@ -819,23 +863,6 @@ int perf_session__process_events(struct perf_session *self,
819 if (perf_session__register_idle_thread(self) == NULL) 863 if (perf_session__register_idle_thread(self) == NULL)
820 return -ENOMEM; 864 return -ENOMEM;
821 865
822 if (!symbol_conf.full_paths) {
823 char bf[PATH_MAX];
824
825 if (getcwd(bf, sizeof(bf)) == NULL) {
826 err = -errno;
827out_getcwd_err:
828 pr_err("failed to get the current directory\n");
829 goto out_err;
830 }
831 self->cwd = strdup(bf);
832 if (self->cwd == NULL) {
833 err = -ENOMEM;
834 goto out_getcwd_err;
835 }
836 self->cwdlen = strlen(self->cwd);
837 }
838
839 if (!self->fd_pipe) 866 if (!self->fd_pipe)
840 err = __perf_session__process_events(self, 867 err = __perf_session__process_events(self,
841 self->header.data_offset, 868 self->header.data_offset,
@@ -843,7 +870,7 @@ out_getcwd_err:
843 self->size, ops); 870 self->size, ops);
844 else 871 else
845 err = __perf_session__process_pipe_events(self, ops); 872 err = __perf_session__process_pipe_events(self, ops);
846out_err: 873
847 return err; 874 return err;
848} 875}
849 876
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index 55c6881b218d..9fa0fc2a863f 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -26,6 +26,7 @@ struct perf_session {
26 unsigned long size; 26 unsigned long size;
27 unsigned long mmap_window; 27 unsigned long mmap_window;
28 struct rb_root threads; 28 struct rb_root threads;
29 struct list_head dead_threads;
29 struct thread *last_match; 30 struct thread *last_match;
30 struct machine host_machine; 31 struct machine host_machine;
31 struct rb_root machines; 32 struct rb_root machines;
@@ -99,6 +100,7 @@ int perf_session__create_kernel_maps(struct perf_session *self);
99 100
100int do_read(int fd, void *buf, size_t size); 101int do_read(int fd, void *buf, size_t size);
101void perf_session__update_sample_type(struct perf_session *self); 102void perf_session__update_sample_type(struct perf_session *self);
103void perf_session__remove_thread(struct perf_session *self, struct thread *th);
102 104
103static inline 105static inline
104struct machine *perf_session__find_host_machine(struct perf_session *self) 106struct machine *perf_session__find_host_machine(struct perf_session *self)
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index 2316cb5a4116..b62a553cc67d 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -1,4 +1,5 @@
1#include "sort.h" 1#include "sort.h"
2#include "hist.h"
2 3
3regex_t parent_regex; 4regex_t parent_regex;
4const char default_parent_pattern[] = "^sys_|^do_page_fault"; 5const char default_parent_pattern[] = "^sys_|^do_page_fault";
@@ -10,10 +11,6 @@ int sort__has_parent = 0;
10 11
11enum sort_type sort__first_dimension; 12enum sort_type sort__first_dimension;
12 13
13unsigned int dsos__col_width;
14unsigned int comms__col_width;
15unsigned int threads__col_width;
16static unsigned int parent_symbol__col_width;
17char * field_sep; 14char * field_sep;
18 15
19LIST_HEAD(hist_entry__sort_list); 16LIST_HEAD(hist_entry__sort_list);
@@ -28,12 +25,14 @@ static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
28 size_t size, unsigned int width); 25 size_t size, unsigned int width);
29static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, 26static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf,
30 size_t size, unsigned int width); 27 size_t size, unsigned int width);
28static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf,
29 size_t size, unsigned int width);
31 30
32struct sort_entry sort_thread = { 31struct sort_entry sort_thread = {
33 .se_header = "Command: Pid", 32 .se_header = "Command: Pid",
34 .se_cmp = sort__thread_cmp, 33 .se_cmp = sort__thread_cmp,
35 .se_snprintf = hist_entry__thread_snprintf, 34 .se_snprintf = hist_entry__thread_snprintf,
36 .se_width = &threads__col_width, 35 .se_width_idx = HISTC_THREAD,
37}; 36};
38 37
39struct sort_entry sort_comm = { 38struct sort_entry sort_comm = {
@@ -41,27 +40,35 @@ struct sort_entry sort_comm = {
41 .se_cmp = sort__comm_cmp, 40 .se_cmp = sort__comm_cmp,
42 .se_collapse = sort__comm_collapse, 41 .se_collapse = sort__comm_collapse,
43 .se_snprintf = hist_entry__comm_snprintf, 42 .se_snprintf = hist_entry__comm_snprintf,
44 .se_width = &comms__col_width, 43 .se_width_idx = HISTC_COMM,
45}; 44};
46 45
47struct sort_entry sort_dso = { 46struct sort_entry sort_dso = {
48 .se_header = "Shared Object", 47 .se_header = "Shared Object",
49 .se_cmp = sort__dso_cmp, 48 .se_cmp = sort__dso_cmp,
50 .se_snprintf = hist_entry__dso_snprintf, 49 .se_snprintf = hist_entry__dso_snprintf,
51 .se_width = &dsos__col_width, 50 .se_width_idx = HISTC_DSO,
52}; 51};
53 52
54struct sort_entry sort_sym = { 53struct sort_entry sort_sym = {
55 .se_header = "Symbol", 54 .se_header = "Symbol",
56 .se_cmp = sort__sym_cmp, 55 .se_cmp = sort__sym_cmp,
57 .se_snprintf = hist_entry__sym_snprintf, 56 .se_snprintf = hist_entry__sym_snprintf,
57 .se_width_idx = HISTC_SYMBOL,
58}; 58};
59 59
60struct sort_entry sort_parent = { 60struct sort_entry sort_parent = {
61 .se_header = "Parent symbol", 61 .se_header = "Parent symbol",
62 .se_cmp = sort__parent_cmp, 62 .se_cmp = sort__parent_cmp,
63 .se_snprintf = hist_entry__parent_snprintf, 63 .se_snprintf = hist_entry__parent_snprintf,
64 .se_width = &parent_symbol__col_width, 64 .se_width_idx = HISTC_PARENT,
65};
66
67struct sort_entry sort_cpu = {
68 .se_header = "CPU",
69 .se_cmp = sort__cpu_cmp,
70 .se_snprintf = hist_entry__cpu_snprintf,
71 .se_width_idx = HISTC_CPU,
65}; 72};
66 73
67struct sort_dimension { 74struct sort_dimension {
@@ -76,6 +83,7 @@ static struct sort_dimension sort_dimensions[] = {
76 { .name = "dso", .entry = &sort_dso, }, 83 { .name = "dso", .entry = &sort_dso, },
77 { .name = "symbol", .entry = &sort_sym, }, 84 { .name = "symbol", .entry = &sort_sym, },
78 { .name = "parent", .entry = &sort_parent, }, 85 { .name = "parent", .entry = &sort_parent, },
86 { .name = "cpu", .entry = &sort_cpu, },
79}; 87};
80 88
81int64_t cmp_null(void *l, void *r) 89int64_t cmp_null(void *l, void *r)
@@ -188,7 +196,8 @@ static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
188 196
189 if (verbose) { 197 if (verbose) {
190 char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!'; 198 char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!';
191 ret += repsep_snprintf(bf, size, "%#018llx %c ", self->ip, o); 199 ret += repsep_snprintf(bf, size, "%*Lx %c ",
200 BITS_PER_LONG / 4, self->ip, o);
192 } 201 }
193 202
194 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level); 203 ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", self->level);
@@ -196,7 +205,8 @@ static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
196 ret += repsep_snprintf(bf + ret, size - ret, "%s", 205 ret += repsep_snprintf(bf + ret, size - ret, "%s",
197 self->ms.sym->name); 206 self->ms.sym->name);
198 else 207 else
199 ret += repsep_snprintf(bf + ret, size - ret, "%#016llx", self->ip); 208 ret += repsep_snprintf(bf + ret, size - ret, "%*Lx",
209 BITS_PER_LONG / 4, self->ip);
200 210
201 return ret; 211 return ret;
202} 212}
@@ -242,6 +252,20 @@ static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf,
242 self->parent ? self->parent->name : "[other]"); 252 self->parent ? self->parent->name : "[other]");
243} 253}
244 254
255/* --sort cpu */
256
257int64_t
258sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right)
259{
260 return right->cpu - left->cpu;
261}
262
263static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf,
264 size_t size, unsigned int width)
265{
266 return repsep_snprintf(bf, size, "%-*d", width, self->cpu);
267}
268
245int sort_dimension__add(const char *tok) 269int sort_dimension__add(const char *tok)
246{ 270{
247 unsigned int i; 271 unsigned int i;
@@ -281,6 +305,8 @@ int sort_dimension__add(const char *tok)
281 sort__first_dimension = SORT_SYM; 305 sort__first_dimension = SORT_SYM;
282 else if (!strcmp(sd->name, "parent")) 306 else if (!strcmp(sd->name, "parent"))
283 sort__first_dimension = SORT_PARENT; 307 sort__first_dimension = SORT_PARENT;
308 else if (!strcmp(sd->name, "cpu"))
309 sort__first_dimension = SORT_CPU;
284 } 310 }
285 311
286 list_add_tail(&sd->entry->list, &hist_entry__sort_list); 312 list_add_tail(&sd->entry->list, &hist_entry__sort_list);
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index 0d61c4082f43..46e531d09e8b 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -36,11 +36,14 @@ extern struct sort_entry sort_comm;
36extern struct sort_entry sort_dso; 36extern struct sort_entry sort_dso;
37extern struct sort_entry sort_sym; 37extern struct sort_entry sort_sym;
38extern struct sort_entry sort_parent; 38extern struct sort_entry sort_parent;
39extern unsigned int dsos__col_width;
40extern unsigned int comms__col_width;
41extern unsigned int threads__col_width;
42extern enum sort_type sort__first_dimension; 39extern enum sort_type sort__first_dimension;
43 40
41/**
42 * struct hist_entry - histogram entry
43 *
44 * @row_offset - offset from the first callchain expanded to appear on screen
45 * @nr_rows - rows expanded in callchain, recalculated on folding/unfolding
46 */
44struct hist_entry { 47struct hist_entry {
45 struct rb_node rb_node; 48 struct rb_node rb_node;
46 u64 period; 49 u64 period;
@@ -51,7 +54,14 @@ struct hist_entry {
51 struct map_symbol ms; 54 struct map_symbol ms;
52 struct thread *thread; 55 struct thread *thread;
53 u64 ip; 56 u64 ip;
57 s32 cpu;
54 u32 nr_events; 58 u32 nr_events;
59
60 /* XXX These two should move to some tree widget lib */
61 u16 row_offset;
62 u16 nr_rows;
63
64 bool init_have_children;
55 char level; 65 char level;
56 u8 filtered; 66 u8 filtered;
57 struct symbol *parent; 67 struct symbol *parent;
@@ -68,7 +78,8 @@ enum sort_type {
68 SORT_COMM, 78 SORT_COMM,
69 SORT_DSO, 79 SORT_DSO,
70 SORT_SYM, 80 SORT_SYM,
71 SORT_PARENT 81 SORT_PARENT,
82 SORT_CPU,
72}; 83};
73 84
74/* 85/*
@@ -84,7 +95,7 @@ struct sort_entry {
84 int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *); 95 int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *);
85 int (*se_snprintf)(struct hist_entry *self, char *bf, size_t size, 96 int (*se_snprintf)(struct hist_entry *self, char *bf, size_t size,
86 unsigned int width); 97 unsigned int width);
87 unsigned int *se_width; 98 u8 se_width_idx;
88 bool elide; 99 bool elide;
89}; 100};
90 101
@@ -104,6 +115,7 @@ extern int64_t sort__comm_collapse(struct hist_entry *, struct hist_entry *);
104extern int64_t sort__dso_cmp(struct hist_entry *, struct hist_entry *); 115extern int64_t sort__dso_cmp(struct hist_entry *, struct hist_entry *);
105extern int64_t sort__sym_cmp(struct hist_entry *, struct hist_entry *); 116extern int64_t sort__sym_cmp(struct hist_entry *, struct hist_entry *);
106extern int64_t sort__parent_cmp(struct hist_entry *, struct hist_entry *); 117extern int64_t sort__parent_cmp(struct hist_entry *, struct hist_entry *);
118int64_t sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right);
107extern size_t sort__parent_print(FILE *, struct hist_entry *, unsigned int); 119extern size_t sort__parent_print(FILE *, struct hist_entry *, unsigned int);
108extern int sort_dimension__add(const char *); 120extern int sort_dimension__add(const char *);
109void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, 121void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list,
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 7fd6b151feb5..1a367734e016 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -12,6 +12,7 @@
12#include <fcntl.h> 12#include <fcntl.h>
13#include <unistd.h> 13#include <unistd.h>
14#include "build-id.h" 14#include "build-id.h"
15#include "debug.h"
15#include "symbol.h" 16#include "symbol.h"
16#include "strlist.h" 17#include "strlist.h"
17 18
@@ -25,6 +26,8 @@
25#define NT_GNU_BUILD_ID 3 26#define NT_GNU_BUILD_ID 3
26#endif 27#endif
27 28
29static bool dso__build_id_equal(const struct dso *self, u8 *build_id);
30static int elf_read_build_id(Elf *elf, void *bf, size_t size);
28static void dsos__add(struct list_head *head, struct dso *dso); 31static void dsos__add(struct list_head *head, struct dso *dso);
29static struct map *map__new2(u64 start, struct dso *dso, enum map_type type); 32static struct map *map__new2(u64 start, struct dso *dso, enum map_type type);
30static int dso__load_kernel_sym(struct dso *self, struct map *map, 33static int dso__load_kernel_sym(struct dso *self, struct map *map,
@@ -40,6 +43,14 @@ struct symbol_conf symbol_conf = {
40 .try_vmlinux_path = true, 43 .try_vmlinux_path = true,
41}; 44};
42 45
46int dso__name_len(const struct dso *self)
47{
48 if (verbose)
49 return self->long_name_len;
50
51 return self->short_name_len;
52}
53
43bool dso__loaded(const struct dso *self, enum map_type type) 54bool dso__loaded(const struct dso *self, enum map_type type)
44{ 55{
45 return self->loaded & (1 << type); 56 return self->loaded & (1 << type);
@@ -120,7 +131,8 @@ static void map_groups__fixup_end(struct map_groups *self)
120 __map_groups__fixup_end(self, i); 131 __map_groups__fixup_end(self, i);
121} 132}
122 133
123static struct symbol *symbol__new(u64 start, u64 len, const char *name) 134static struct symbol *symbol__new(u64 start, u64 len, u8 binding,
135 const char *name)
124{ 136{
125 size_t namelen = strlen(name) + 1; 137 size_t namelen = strlen(name) + 1;
126 struct symbol *self = calloc(1, (symbol_conf.priv_size + 138 struct symbol *self = calloc(1, (symbol_conf.priv_size +
@@ -133,6 +145,7 @@ static struct symbol *symbol__new(u64 start, u64 len, const char *name)
133 145
134 self->start = start; 146 self->start = start;
135 self->end = len ? start + len - 1 : start; 147 self->end = len ? start + len - 1 : start;
148 self->binding = binding;
136 self->namelen = namelen - 1; 149 self->namelen = namelen - 1;
137 150
138 pr_debug4("%s: %s %#Lx-%#Lx\n", __func__, name, start, self->end); 151 pr_debug4("%s: %s %#Lx-%#Lx\n", __func__, name, start, self->end);
@@ -149,8 +162,11 @@ void symbol__delete(struct symbol *self)
149 162
150static size_t symbol__fprintf(struct symbol *self, FILE *fp) 163static size_t symbol__fprintf(struct symbol *self, FILE *fp)
151{ 164{
152 return fprintf(fp, " %llx-%llx %s\n", 165 return fprintf(fp, " %llx-%llx %c %s\n",
153 self->start, self->end, self->name); 166 self->start, self->end,
167 self->binding == STB_GLOBAL ? 'g' :
168 self->binding == STB_LOCAL ? 'l' : 'w',
169 self->name);
154} 170}
155 171
156void dso__set_long_name(struct dso *self, char *name) 172void dso__set_long_name(struct dso *self, char *name)
@@ -215,7 +231,9 @@ void dso__delete(struct dso *self)
215 int i; 231 int i;
216 for (i = 0; i < MAP__NR_TYPES; ++i) 232 for (i = 0; i < MAP__NR_TYPES; ++i)
217 symbols__delete(&self->symbols[i]); 233 symbols__delete(&self->symbols[i]);
218 if (self->long_name != self->name) 234 if (self->sname_alloc)
235 free((char *)self->short_name);
236 if (self->lname_alloc)
219 free(self->long_name); 237 free(self->long_name);
220 free(self); 238 free(self);
221} 239}
@@ -440,6 +458,14 @@ struct process_kallsyms_args {
440 struct dso *dso; 458 struct dso *dso;
441}; 459};
442 460
461static u8 kallsyms2elf_type(char type)
462{
463 if (type == 'W')
464 return STB_WEAK;
465
466 return isupper(type) ? STB_GLOBAL : STB_LOCAL;
467}
468
443static int map__process_kallsym_symbol(void *arg, const char *name, 469static int map__process_kallsym_symbol(void *arg, const char *name,
444 char type, u64 start) 470 char type, u64 start)
445{ 471{
@@ -453,7 +479,7 @@ static int map__process_kallsym_symbol(void *arg, const char *name,
453 /* 479 /*
454 * Will fix up the end later, when we have all symbols sorted. 480 * Will fix up the end later, when we have all symbols sorted.
455 */ 481 */
456 sym = symbol__new(start, 0, name); 482 sym = symbol__new(start, 0, kallsyms2elf_type(type), name);
457 483
458 if (sym == NULL) 484 if (sym == NULL)
459 return -ENOMEM; 485 return -ENOMEM;
@@ -648,7 +674,7 @@ static int dso__load_perf_map(struct dso *self, struct map *map,
648 if (len + 2 >= line_len) 674 if (len + 2 >= line_len)
649 continue; 675 continue;
650 676
651 sym = symbol__new(start, size, line + len); 677 sym = symbol__new(start, size, STB_GLOBAL, line + len);
652 678
653 if (sym == NULL) 679 if (sym == NULL)
654 goto out_delete_line; 680 goto out_delete_line;
@@ -860,7 +886,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, struct map *map,
860 "%s@plt", elf_sym__name(&sym, symstrs)); 886 "%s@plt", elf_sym__name(&sym, symstrs));
861 887
862 f = symbol__new(plt_offset, shdr_plt.sh_entsize, 888 f = symbol__new(plt_offset, shdr_plt.sh_entsize,
863 sympltname); 889 STB_GLOBAL, sympltname);
864 if (!f) 890 if (!f)
865 goto out_elf_end; 891 goto out_elf_end;
866 892
@@ -882,7 +908,7 @@ static int dso__synthesize_plt_symbols(struct dso *self, struct map *map,
882 "%s@plt", elf_sym__name(&sym, symstrs)); 908 "%s@plt", elf_sym__name(&sym, symstrs));
883 909
884 f = symbol__new(plt_offset, shdr_plt.sh_entsize, 910 f = symbol__new(plt_offset, shdr_plt.sh_entsize,
885 sympltname); 911 STB_GLOBAL, sympltname);
886 if (!f) 912 if (!f)
887 goto out_elf_end; 913 goto out_elf_end;
888 914
@@ -933,8 +959,28 @@ static bool elf_sec__is_a(GElf_Shdr *self, Elf_Data *secstrs, enum map_type type
933 } 959 }
934} 960}
935 961
962static size_t elf_addr_to_index(Elf *elf, GElf_Addr addr)
963{
964 Elf_Scn *sec = NULL;
965 GElf_Shdr shdr;
966 size_t cnt = 1;
967
968 while ((sec = elf_nextscn(elf, sec)) != NULL) {
969 gelf_getshdr(sec, &shdr);
970
971 if ((addr >= shdr.sh_addr) &&
972 (addr < (shdr.sh_addr + shdr.sh_size)))
973 return cnt;
974
975 ++cnt;
976 }
977
978 return -1;
979}
980
936static int dso__load_sym(struct dso *self, struct map *map, const char *name, 981static int dso__load_sym(struct dso *self, struct map *map, const char *name,
937 int fd, symbol_filter_t filter, int kmodule) 982 int fd, symbol_filter_t filter, int kmodule,
983 int want_symtab)
938{ 984{
939 struct kmap *kmap = self->kernel ? map__kmap(map) : NULL; 985 struct kmap *kmap = self->kernel ? map__kmap(map) : NULL;
940 struct map *curr_map = map; 986 struct map *curr_map = map;
@@ -944,31 +990,51 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name,
944 int err = -1; 990 int err = -1;
945 uint32_t idx; 991 uint32_t idx;
946 GElf_Ehdr ehdr; 992 GElf_Ehdr ehdr;
947 GElf_Shdr shdr; 993 GElf_Shdr shdr, opdshdr;
948 Elf_Data *syms; 994 Elf_Data *syms, *opddata = NULL;
949 GElf_Sym sym; 995 GElf_Sym sym;
950 Elf_Scn *sec, *sec_strndx; 996 Elf_Scn *sec, *sec_strndx, *opdsec;
951 Elf *elf; 997 Elf *elf;
952 int nr = 0; 998 int nr = 0;
999 size_t opdidx = 0;
953 1000
954 elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); 1001 elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
955 if (elf == NULL) { 1002 if (elf == NULL) {
956 pr_err("%s: cannot read %s ELF file.\n", __func__, name); 1003 pr_debug("%s: cannot read %s ELF file.\n", __func__, name);
957 goto out_close; 1004 goto out_close;
958 } 1005 }
959 1006
960 if (gelf_getehdr(elf, &ehdr) == NULL) { 1007 if (gelf_getehdr(elf, &ehdr) == NULL) {
961 pr_err("%s: cannot get elf header.\n", __func__); 1008 pr_debug("%s: cannot get elf header.\n", __func__);
962 goto out_elf_end; 1009 goto out_elf_end;
963 } 1010 }
964 1011
1012 /* Always reject images with a mismatched build-id: */
1013 if (self->has_build_id) {
1014 u8 build_id[BUILD_ID_SIZE];
1015
1016 if (elf_read_build_id(elf, build_id,
1017 BUILD_ID_SIZE) != BUILD_ID_SIZE)
1018 goto out_elf_end;
1019
1020 if (!dso__build_id_equal(self, build_id))
1021 goto out_elf_end;
1022 }
1023
965 sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL); 1024 sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL);
966 if (sec == NULL) { 1025 if (sec == NULL) {
1026 if (want_symtab)
1027 goto out_elf_end;
1028
967 sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL); 1029 sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL);
968 if (sec == NULL) 1030 if (sec == NULL)
969 goto out_elf_end; 1031 goto out_elf_end;
970 } 1032 }
971 1033
1034 opdsec = elf_section_by_name(elf, &ehdr, &opdshdr, ".opd", &opdidx);
1035 if (opdsec)
1036 opddata = elf_rawdata(opdsec, NULL);
1037
972 syms = elf_getdata(sec, NULL); 1038 syms = elf_getdata(sec, NULL);
973 if (syms == NULL) 1039 if (syms == NULL)
974 goto out_elf_end; 1040 goto out_elf_end;
@@ -1013,6 +1079,23 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name,
1013 if (!is_label && !elf_sym__is_a(&sym, map->type)) 1079 if (!is_label && !elf_sym__is_a(&sym, map->type))
1014 continue; 1080 continue;
1015 1081
1082 /* Reject ARM ELF "mapping symbols": these aren't unique and
1083 * don't identify functions, so will confuse the profile
1084 * output: */
1085 if (ehdr.e_machine == EM_ARM) {
1086 if (!strcmp(elf_name, "$a") ||
1087 !strcmp(elf_name, "$d") ||
1088 !strcmp(elf_name, "$t"))
1089 continue;
1090 }
1091
1092 if (opdsec && sym.st_shndx == opdidx) {
1093 u32 offset = sym.st_value - opdshdr.sh_addr;
1094 u64 *opd = opddata->d_buf + offset;
1095 sym.st_value = *opd;
1096 sym.st_shndx = elf_addr_to_index(elf, sym.st_value);
1097 }
1098
1016 sec = elf_getscn(elf, sym.st_shndx); 1099 sec = elf_getscn(elf, sym.st_shndx);
1017 if (!sec) 1100 if (!sec)
1018 goto out_elf_end; 1101 goto out_elf_end;
@@ -1086,7 +1169,8 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name,
1086 if (demangled != NULL) 1169 if (demangled != NULL)
1087 elf_name = demangled; 1170 elf_name = demangled;
1088new_symbol: 1171new_symbol:
1089 f = symbol__new(sym.st_value, sym.st_size, elf_name); 1172 f = symbol__new(sym.st_value, sym.st_size,
1173 GELF_ST_BIND(sym.st_info), elf_name);
1090 free(demangled); 1174 free(demangled);
1091 if (!f) 1175 if (!f)
1092 goto out_elf_end; 1176 goto out_elf_end;
@@ -1151,37 +1235,26 @@ bool __dsos__read_build_ids(struct list_head *head, bool with_hits)
1151 */ 1235 */
1152#define NOTE_ALIGN(n) (((n) + 3) & -4U) 1236#define NOTE_ALIGN(n) (((n) + 3) & -4U)
1153 1237
1154int filename__read_build_id(const char *filename, void *bf, size_t size) 1238static int elf_read_build_id(Elf *elf, void *bf, size_t size)
1155{ 1239{
1156 int fd, err = -1; 1240 int err = -1;
1157 GElf_Ehdr ehdr; 1241 GElf_Ehdr ehdr;
1158 GElf_Shdr shdr; 1242 GElf_Shdr shdr;
1159 Elf_Data *data; 1243 Elf_Data *data;
1160 Elf_Scn *sec; 1244 Elf_Scn *sec;
1161 Elf_Kind ek; 1245 Elf_Kind ek;
1162 void *ptr; 1246 void *ptr;
1163 Elf *elf;
1164 1247
1165 if (size < BUILD_ID_SIZE) 1248 if (size < BUILD_ID_SIZE)
1166 goto out; 1249 goto out;
1167 1250
1168 fd = open(filename, O_RDONLY);
1169 if (fd < 0)
1170 goto out;
1171
1172 elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
1173 if (elf == NULL) {
1174 pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename);
1175 goto out_close;
1176 }
1177
1178 ek = elf_kind(elf); 1251 ek = elf_kind(elf);
1179 if (ek != ELF_K_ELF) 1252 if (ek != ELF_K_ELF)
1180 goto out_elf_end; 1253 goto out;
1181 1254
1182 if (gelf_getehdr(elf, &ehdr) == NULL) { 1255 if (gelf_getehdr(elf, &ehdr) == NULL) {
1183 pr_err("%s: cannot get elf header.\n", __func__); 1256 pr_err("%s: cannot get elf header.\n", __func__);
1184 goto out_elf_end; 1257 goto out;
1185 } 1258 }
1186 1259
1187 sec = elf_section_by_name(elf, &ehdr, &shdr, 1260 sec = elf_section_by_name(elf, &ehdr, &shdr,
@@ -1190,12 +1263,12 @@ int filename__read_build_id(const char *filename, void *bf, size_t size)
1190 sec = elf_section_by_name(elf, &ehdr, &shdr, 1263 sec = elf_section_by_name(elf, &ehdr, &shdr,
1191 ".notes", NULL); 1264 ".notes", NULL);
1192 if (sec == NULL) 1265 if (sec == NULL)
1193 goto out_elf_end; 1266 goto out;
1194 } 1267 }
1195 1268
1196 data = elf_getdata(sec, NULL); 1269 data = elf_getdata(sec, NULL);
1197 if (data == NULL) 1270 if (data == NULL)
1198 goto out_elf_end; 1271 goto out;
1199 1272
1200 ptr = data->d_buf; 1273 ptr = data->d_buf;
1201 while (ptr < (data->d_buf + data->d_size)) { 1274 while (ptr < (data->d_buf + data->d_size)) {
@@ -1217,7 +1290,31 @@ int filename__read_build_id(const char *filename, void *bf, size_t size)
1217 } 1290 }
1218 ptr += descsz; 1291 ptr += descsz;
1219 } 1292 }
1220out_elf_end: 1293
1294out:
1295 return err;
1296}
1297
1298int filename__read_build_id(const char *filename, void *bf, size_t size)
1299{
1300 int fd, err = -1;
1301 Elf *elf;
1302
1303 if (size < BUILD_ID_SIZE)
1304 goto out;
1305
1306 fd = open(filename, O_RDONLY);
1307 if (fd < 0)
1308 goto out;
1309
1310 elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
1311 if (elf == NULL) {
1312 pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename);
1313 goto out_close;
1314 }
1315
1316 err = elf_read_build_id(elf, bf, size);
1317
1221 elf_end(elf); 1318 elf_end(elf);
1222out_close: 1319out_close:
1223 close(fd); 1320 close(fd);
@@ -1293,11 +1390,11 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter)
1293{ 1390{
1294 int size = PATH_MAX; 1391 int size = PATH_MAX;
1295 char *name; 1392 char *name;
1296 u8 build_id[BUILD_ID_SIZE];
1297 int ret = -1; 1393 int ret = -1;
1298 int fd; 1394 int fd;
1299 struct machine *machine; 1395 struct machine *machine;
1300 const char *root_dir; 1396 const char *root_dir;
1397 int want_symtab;
1301 1398
1302 dso__set_loaded(self, map->type); 1399 dso__set_loaded(self, map->type);
1303 1400
@@ -1324,13 +1421,18 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter)
1324 return ret; 1421 return ret;
1325 } 1422 }
1326 1423
1327 self->origin = DSO__ORIG_BUILD_ID_CACHE; 1424 /* Iterate over candidate debug images.
1328 if (dso__build_id_filename(self, name, size) != NULL) 1425 * On the first pass, only load images if they have a full symtab.
1329 goto open_file; 1426 * Failing that, do a second pass where we accept .dynsym also
1330more: 1427 */
1331 do { 1428 for (self->origin = DSO__ORIG_BUILD_ID_CACHE, want_symtab = 1;
1332 self->origin++; 1429 self->origin != DSO__ORIG_NOT_FOUND;
1430 self->origin++) {
1333 switch (self->origin) { 1431 switch (self->origin) {
1432 case DSO__ORIG_BUILD_ID_CACHE:
1433 if (dso__build_id_filename(self, name, size) == NULL)
1434 continue;
1435 break;
1334 case DSO__ORIG_FEDORA: 1436 case DSO__ORIG_FEDORA:
1335 snprintf(name, size, "/usr/lib/debug%s.debug", 1437 snprintf(name, size, "/usr/lib/debug%s.debug",
1336 self->long_name); 1438 self->long_name);
@@ -1339,21 +1441,20 @@ more:
1339 snprintf(name, size, "/usr/lib/debug%s", 1441 snprintf(name, size, "/usr/lib/debug%s",
1340 self->long_name); 1442 self->long_name);
1341 break; 1443 break;
1342 case DSO__ORIG_BUILDID: 1444 case DSO__ORIG_BUILDID: {
1343 if (filename__read_build_id(self->long_name, build_id, 1445 char build_id_hex[BUILD_ID_SIZE * 2 + 1];
1344 sizeof(build_id))) { 1446
1345 char build_id_hex[BUILD_ID_SIZE * 2 + 1]; 1447 if (!self->has_build_id)
1346 build_id__sprintf(build_id, sizeof(build_id), 1448 continue;
1347 build_id_hex); 1449
1348 snprintf(name, size, 1450 build_id__sprintf(self->build_id,
1349 "/usr/lib/debug/.build-id/%.2s/%s.debug", 1451 sizeof(self->build_id),
1350 build_id_hex, build_id_hex + 2); 1452 build_id_hex);
1351 if (self->has_build_id) 1453 snprintf(name, size,
1352 goto compare_build_id; 1454 "/usr/lib/debug/.build-id/%.2s/%s.debug",
1353 break; 1455 build_id_hex, build_id_hex + 2);
1354 } 1456 }
1355 self->origin++; 1457 break;
1356 /* Fall thru */
1357 case DSO__ORIG_DSO: 1458 case DSO__ORIG_DSO:
1358 snprintf(name, size, "%s", self->long_name); 1459 snprintf(name, size, "%s", self->long_name);
1359 break; 1460 break;
@@ -1366,36 +1467,41 @@ more:
1366 break; 1467 break;
1367 1468
1368 default: 1469 default:
1369 goto out; 1470 /*
1471 * If we wanted a full symtab but no image had one,
1472 * relax our requirements and repeat the search.
1473 */
1474 if (want_symtab) {
1475 want_symtab = 0;
1476 self->origin = DSO__ORIG_BUILD_ID_CACHE;
1477 } else
1478 continue;
1370 } 1479 }
1371 1480
1372 if (self->has_build_id) { 1481 /* Name is now the name of the next image to try */
1373 if (filename__read_build_id(name, build_id,
1374 sizeof(build_id)) < 0)
1375 goto more;
1376compare_build_id:
1377 if (!dso__build_id_equal(self, build_id))
1378 goto more;
1379 }
1380open_file:
1381 fd = open(name, O_RDONLY); 1482 fd = open(name, O_RDONLY);
1382 } while (fd < 0); 1483 if (fd < 0)
1484 continue;
1383 1485
1384 ret = dso__load_sym(self, map, name, fd, filter, 0); 1486 ret = dso__load_sym(self, map, name, fd, filter, 0,
1385 close(fd); 1487 want_symtab);
1488 close(fd);
1386 1489
1387 /* 1490 /*
1388 * Some people seem to have debuginfo files _WITHOUT_ debug info!?!? 1491 * Some people seem to have debuginfo files _WITHOUT_ debug
1389 */ 1492 * info!?!?
1390 if (!ret) 1493 */
1391 goto more; 1494 if (!ret)
1495 continue;
1392 1496
1393 if (ret > 0) { 1497 if (ret > 0) {
1394 int nr_plt = dso__synthesize_plt_symbols(self, map, filter); 1498 int nr_plt = dso__synthesize_plt_symbols(self, map, filter);
1395 if (nr_plt > 0) 1499 if (nr_plt > 0)
1396 ret += nr_plt; 1500 ret += nr_plt;
1501 break;
1502 }
1397 } 1503 }
1398out: 1504
1399 free(name); 1505 free(name);
1400 if (ret < 0 && strstr(self->name, " (deleted)") != NULL) 1506 if (ret < 0 && strstr(self->name, " (deleted)") != NULL)
1401 return 0; 1507 return 0;
@@ -1443,6 +1549,7 @@ static int map_groups__set_modules_path_dir(struct map_groups *self,
1443{ 1549{
1444 struct dirent *dent; 1550 struct dirent *dent;
1445 DIR *dir = opendir(dir_name); 1551 DIR *dir = opendir(dir_name);
1552 int ret = 0;
1446 1553
1447 if (!dir) { 1554 if (!dir) {
1448 pr_debug("%s: cannot open %s dir\n", __func__, dir_name); 1555 pr_debug("%s: cannot open %s dir\n", __func__, dir_name);
@@ -1465,8 +1572,9 @@ static int map_groups__set_modules_path_dir(struct map_groups *self,
1465 1572
1466 snprintf(path, sizeof(path), "%s/%s", 1573 snprintf(path, sizeof(path), "%s/%s",
1467 dir_name, dent->d_name); 1574 dir_name, dent->d_name);
1468 if (map_groups__set_modules_path_dir(self, path) < 0) 1575 ret = map_groups__set_modules_path_dir(self, path);
1469 goto failure; 1576 if (ret < 0)
1577 goto out;
1470 } else { 1578 } else {
1471 char *dot = strrchr(dent->d_name, '.'), 1579 char *dot = strrchr(dent->d_name, '.'),
1472 dso_name[PATH_MAX]; 1580 dso_name[PATH_MAX];
@@ -1487,17 +1595,19 @@ static int map_groups__set_modules_path_dir(struct map_groups *self,
1487 dir_name, dent->d_name); 1595 dir_name, dent->d_name);
1488 1596
1489 long_name = strdup(path); 1597 long_name = strdup(path);
1490 if (long_name == NULL) 1598 if (long_name == NULL) {
1491 goto failure; 1599 ret = -1;
1600 goto out;
1601 }
1492 dso__set_long_name(map->dso, long_name); 1602 dso__set_long_name(map->dso, long_name);
1603 map->dso->lname_alloc = 1;
1493 dso__kernel_module_get_build_id(map->dso, ""); 1604 dso__kernel_module_get_build_id(map->dso, "");
1494 } 1605 }
1495 } 1606 }
1496 1607
1497 return 0; 1608out:
1498failure:
1499 closedir(dir); 1609 closedir(dir);
1500 return -1; 1610 return ret;
1501} 1611}
1502 1612
1503static char *get_kernel_version(const char *root_dir) 1613static char *get_kernel_version(const char *root_dir)
@@ -1653,36 +1763,12 @@ static int dso__load_vmlinux(struct dso *self, struct map *map,
1653{ 1763{
1654 int err = -1, fd; 1764 int err = -1, fd;
1655 1765
1656 if (self->has_build_id) {
1657 u8 build_id[BUILD_ID_SIZE];
1658
1659 if (filename__read_build_id(vmlinux, build_id,
1660 sizeof(build_id)) < 0) {
1661 pr_debug("No build_id in %s, ignoring it\n", vmlinux);
1662 return -1;
1663 }
1664 if (!dso__build_id_equal(self, build_id)) {
1665 char expected_build_id[BUILD_ID_SIZE * 2 + 1],
1666 vmlinux_build_id[BUILD_ID_SIZE * 2 + 1];
1667
1668 build_id__sprintf(self->build_id,
1669 sizeof(self->build_id),
1670 expected_build_id);
1671 build_id__sprintf(build_id, sizeof(build_id),
1672 vmlinux_build_id);
1673 pr_debug("build_id in %s is %s while expected is %s, "
1674 "ignoring it\n", vmlinux, vmlinux_build_id,
1675 expected_build_id);
1676 return -1;
1677 }
1678 }
1679
1680 fd = open(vmlinux, O_RDONLY); 1766 fd = open(vmlinux, O_RDONLY);
1681 if (fd < 0) 1767 if (fd < 0)
1682 return -1; 1768 return -1;
1683 1769
1684 dso__set_loaded(self, map->type); 1770 dso__set_loaded(self, map->type);
1685 err = dso__load_sym(self, map, vmlinux, fd, filter, 0); 1771 err = dso__load_sym(self, map, vmlinux, fd, filter, 0, 0);
1686 close(fd); 1772 close(fd);
1687 1773
1688 if (err > 0) 1774 if (err > 0)
@@ -1745,7 +1831,12 @@ static int dso__load_kernel_sym(struct dso *self, struct map *map,
1745 if (symbol_conf.vmlinux_name != NULL) { 1831 if (symbol_conf.vmlinux_name != NULL) {
1746 err = dso__load_vmlinux(self, map, 1832 err = dso__load_vmlinux(self, map,
1747 symbol_conf.vmlinux_name, filter); 1833 symbol_conf.vmlinux_name, filter);
1748 goto out_try_fixup; 1834 if (err > 0) {
1835 dso__set_long_name(self,
1836 strdup(symbol_conf.vmlinux_name));
1837 goto out_fixup;
1838 }
1839 return err;
1749 } 1840 }
1750 1841
1751 if (vmlinux_path != NULL) { 1842 if (vmlinux_path != NULL) {
@@ -1806,7 +1897,6 @@ do_kallsyms:
1806 pr_debug("Using %s for symbols\n", kallsyms_filename); 1897 pr_debug("Using %s for symbols\n", kallsyms_filename);
1807 free(kallsyms_allocated_filename); 1898 free(kallsyms_allocated_filename);
1808 1899
1809out_try_fixup:
1810 if (err > 0) { 1900 if (err > 0) {
1811out_fixup: 1901out_fixup:
1812 if (kallsyms_filename != NULL) 1902 if (kallsyms_filename != NULL)
@@ -2041,6 +2131,36 @@ int __machine__create_kernel_maps(struct machine *self, struct dso *kernel)
2041 return 0; 2131 return 0;
2042} 2132}
2043 2133
2134void machine__destroy_kernel_maps(struct machine *self)
2135{
2136 enum map_type type;
2137
2138 for (type = 0; type < MAP__NR_TYPES; ++type) {
2139 struct kmap *kmap;
2140
2141 if (self->vmlinux_maps[type] == NULL)
2142 continue;
2143
2144 kmap = map__kmap(self->vmlinux_maps[type]);
2145 map_groups__remove(&self->kmaps, self->vmlinux_maps[type]);
2146 if (kmap->ref_reloc_sym) {
2147 /*
2148 * ref_reloc_sym is shared among all maps, so free just
2149 * on one of them.
2150 */
2151 if (type == MAP__FUNCTION) {
2152 free((char *)kmap->ref_reloc_sym->name);
2153 kmap->ref_reloc_sym->name = NULL;
2154 free(kmap->ref_reloc_sym);
2155 }
2156 kmap->ref_reloc_sym = NULL;
2157 }
2158
2159 map__delete(self->vmlinux_maps[type]);
2160 self->vmlinux_maps[type] = NULL;
2161 }
2162}
2163
2044int machine__create_kernel_maps(struct machine *self) 2164int machine__create_kernel_maps(struct machine *self)
2045{ 2165{
2046 struct dso *kernel = machine__create_kernel(self); 2166 struct dso *kernel = machine__create_kernel(self);
@@ -2182,6 +2302,15 @@ out_free_comm_list:
2182 return -1; 2302 return -1;
2183} 2303}
2184 2304
2305void symbol__exit(void)
2306{
2307 strlist__delete(symbol_conf.sym_list);
2308 strlist__delete(symbol_conf.dso_list);
2309 strlist__delete(symbol_conf.comm_list);
2310 vmlinux_path__exit();
2311 symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL;
2312}
2313
2185int machines__create_kernel_maps(struct rb_root *self, pid_t pid) 2314int machines__create_kernel_maps(struct rb_root *self, pid_t pid)
2186{ 2315{
2187 struct machine *machine = machines__findnew(self, pid); 2316 struct machine *machine = machines__findnew(self, pid);
@@ -2276,6 +2405,19 @@ failure:
2276 return ret; 2405 return ret;
2277} 2406}
2278 2407
2408void machines__destroy_guest_kernel_maps(struct rb_root *self)
2409{
2410 struct rb_node *next = rb_first(self);
2411
2412 while (next) {
2413 struct machine *pos = rb_entry(next, struct machine, rb_node);
2414
2415 next = rb_next(&pos->rb_node);
2416 rb_erase(&pos->rb_node, self);
2417 machine__delete(pos);
2418 }
2419}
2420
2279int machine__load_kallsyms(struct machine *self, const char *filename, 2421int machine__load_kallsyms(struct machine *self, const char *filename,
2280 enum map_type type, symbol_filter_t filter) 2422 enum map_type type, symbol_filter_t filter)
2281{ 2423{
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 5e02d2c17154..b7a8da4af5a0 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -9,8 +9,6 @@
9#include <linux/rbtree.h> 9#include <linux/rbtree.h>
10#include <stdio.h> 10#include <stdio.h>
11 11
12#define DEBUG_CACHE_DIR ".debug"
13
14#ifdef HAVE_CPLUS_DEMANGLE 12#ifdef HAVE_CPLUS_DEMANGLE
15extern char *cplus_demangle(const char *, int); 13extern char *cplus_demangle(const char *, int);
16 14
@@ -55,6 +53,7 @@ struct symbol {
55 u64 start; 53 u64 start;
56 u64 end; 54 u64 end;
57 u16 namelen; 55 u16 namelen;
56 u8 binding;
58 char name[0]; 57 char name[0];
59}; 58};
60 59
@@ -70,9 +69,9 @@ struct symbol_conf {
70 show_nr_samples, 69 show_nr_samples,
71 use_callchain, 70 use_callchain,
72 exclude_other, 71 exclude_other,
73 full_paths,
74 show_cpu_utilization; 72 show_cpu_utilization;
75 const char *vmlinux_name, 73 const char *vmlinux_name,
74 *source_prefix,
76 *field_sep; 75 *field_sep;
77 const char *default_guest_vmlinux_name, 76 const char *default_guest_vmlinux_name,
78 *default_guest_kallsyms, 77 *default_guest_kallsyms,
@@ -103,6 +102,8 @@ struct ref_reloc_sym {
103struct map_symbol { 102struct map_symbol {
104 struct map *map; 103 struct map *map;
105 struct symbol *sym; 104 struct symbol *sym;
105 bool unfolded;
106 bool has_children;
106}; 107};
107 108
108struct addr_location { 109struct addr_location {
@@ -112,7 +113,8 @@ struct addr_location {
112 u64 addr; 113 u64 addr;
113 char level; 114 char level;
114 bool filtered; 115 bool filtered;
115 unsigned int cpumode; 116 u8 cpumode;
117 s32 cpu;
116}; 118};
117 119
118enum dso_kernel_type { 120enum dso_kernel_type {
@@ -125,12 +127,14 @@ struct dso {
125 struct list_head node; 127 struct list_head node;
126 struct rb_root symbols[MAP__NR_TYPES]; 128 struct rb_root symbols[MAP__NR_TYPES];
127 struct rb_root symbol_names[MAP__NR_TYPES]; 129 struct rb_root symbol_names[MAP__NR_TYPES];
130 enum dso_kernel_type kernel;
128 u8 adjust_symbols:1; 131 u8 adjust_symbols:1;
129 u8 slen_calculated:1; 132 u8 slen_calculated:1;
130 u8 has_build_id:1; 133 u8 has_build_id:1;
131 enum dso_kernel_type kernel;
132 u8 hit:1; 134 u8 hit:1;
133 u8 annotate_warned:1; 135 u8 annotate_warned:1;
136 u8 sname_alloc:1;
137 u8 lname_alloc:1;
134 unsigned char origin; 138 unsigned char origin;
135 u8 sorted_by_name; 139 u8 sorted_by_name;
136 u8 loaded; 140 u8 loaded;
@@ -146,6 +150,8 @@ struct dso *dso__new(const char *name);
146struct dso *dso__new_kernel(const char *name); 150struct dso *dso__new_kernel(const char *name);
147void dso__delete(struct dso *self); 151void dso__delete(struct dso *self);
148 152
153int dso__name_len(const struct dso *self);
154
149bool dso__loaded(const struct dso *self, enum map_type type); 155bool dso__loaded(const struct dso *self, enum map_type type);
150bool dso__sorted_by_name(const struct dso *self, enum map_type type); 156bool dso__sorted_by_name(const struct dso *self, enum map_type type);
151 157
@@ -207,13 +213,16 @@ int kallsyms__parse(const char *filename, void *arg,
207 int (*process_symbol)(void *arg, const char *name, 213 int (*process_symbol)(void *arg, const char *name,
208 char type, u64 start)); 214 char type, u64 start));
209 215
216void machine__destroy_kernel_maps(struct machine *self);
210int __machine__create_kernel_maps(struct machine *self, struct dso *kernel); 217int __machine__create_kernel_maps(struct machine *self, struct dso *kernel);
211int machine__create_kernel_maps(struct machine *self); 218int machine__create_kernel_maps(struct machine *self);
212 219
213int machines__create_kernel_maps(struct rb_root *self, pid_t pid); 220int machines__create_kernel_maps(struct rb_root *self, pid_t pid);
214int machines__create_guest_kernel_maps(struct rb_root *self); 221int machines__create_guest_kernel_maps(struct rb_root *self);
222void machines__destroy_guest_kernel_maps(struct rb_root *self);
215 223
216int symbol__init(void); 224int symbol__init(void);
225void symbol__exit(void);
217bool symbol_type__is_a(char symbol_type, enum map_type map_type); 226bool symbol_type__is_a(char symbol_type, enum map_type map_type);
218 227
219size_t machine__fprintf_vmlinux_path(struct machine *self, FILE *fp); 228size_t machine__fprintf_vmlinux_path(struct machine *self, FILE *fp);
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 1f7ecd47f499..8c72d888e449 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -7,6 +7,15 @@
7#include "util.h" 7#include "util.h"
8#include "debug.h" 8#include "debug.h"
9 9
10/* Skip "." and ".." directories */
11static int filter(const struct dirent *dir)
12{
13 if (dir->d_name[0] == '.')
14 return 0;
15 else
16 return 1;
17}
18
10int find_all_tid(int pid, pid_t ** all_tid) 19int find_all_tid(int pid, pid_t ** all_tid)
11{ 20{
12 char name[256]; 21 char name[256];
@@ -16,7 +25,7 @@ int find_all_tid(int pid, pid_t ** all_tid)
16 int i; 25 int i;
17 26
18 sprintf(name, "/proc/%d/task", pid); 27 sprintf(name, "/proc/%d/task", pid);
19 items = scandir(name, &namelist, NULL, NULL); 28 items = scandir(name, &namelist, filter, NULL);
20 if (items <= 0) 29 if (items <= 0)
21 return -ENOENT; 30 return -ENOENT;
22 *all_tid = malloc(sizeof(pid_t) * items); 31 *all_tid = malloc(sizeof(pid_t) * items);
@@ -53,6 +62,13 @@ static struct thread *thread__new(pid_t pid)
53 return self; 62 return self;
54} 63}
55 64
65void thread__delete(struct thread *self)
66{
67 map_groups__exit(&self->mg);
68 free(self->comm);
69 free(self);
70}
71
56int thread__set_comm(struct thread *self, const char *comm) 72int thread__set_comm(struct thread *self, const char *comm)
57{ 73{
58 int err; 74 int err;
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index 1dfd9ff8bdcd..688500ff826f 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -6,7 +6,10 @@
6#include "symbol.h" 6#include "symbol.h"
7 7
8struct thread { 8struct thread {
9 struct rb_node rb_node; 9 union {
10 struct rb_node rb_node;
11 struct list_head node;
12 };
10 struct map_groups mg; 13 struct map_groups mg;
11 pid_t pid; 14 pid_t pid;
12 char shortname[3]; 15 char shortname[3];
@@ -17,6 +20,8 @@ struct thread {
17 20
18struct perf_session; 21struct perf_session;
19 22
23void thread__delete(struct thread *self);
24
20int find_all_tid(int pid, pid_t ** all_tid); 25int find_all_tid(int pid, pid_t ** all_tid);
21int thread__set_comm(struct thread *self, const char *comm); 26int thread__set_comm(struct thread *self, const char *comm);
22int thread__comm_len(struct thread *self); 27int thread__comm_len(struct thread *self);
diff --git a/tools/perf/util/ui/browser.c b/tools/perf/util/ui/browser.c
new file mode 100644
index 000000000000..66f2d583d8c4
--- /dev/null
+++ b/tools/perf/util/ui/browser.c
@@ -0,0 +1,329 @@
1#define _GNU_SOURCE
2#include <stdio.h>
3#undef _GNU_SOURCE
4/*
5 * slang versions <= 2.0.6 have a "#if HAVE_LONG_LONG" that breaks
6 * the build if it isn't defined. Use the equivalent one that glibc
7 * has on features.h.
8 */
9#include <features.h>
10#ifndef HAVE_LONG_LONG
11#define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG
12#endif
13#include <slang.h>
14#include <linux/list.h>
15#include <linux/rbtree.h>
16#include <stdlib.h>
17#include <sys/ttydefaults.h>
18#include "browser.h"
19#include "helpline.h"
20#include "../color.h"
21#include "../util.h"
22
23#if SLANG_VERSION < 20104
24#define sltt_set_color(obj, name, fg, bg) \
25 SLtt_set_color(obj,(char *)name, (char *)fg, (char *)bg)
26#else
27#define sltt_set_color SLtt_set_color
28#endif
29
30newtComponent newt_form__new(void);
31
32int ui_browser__percent_color(double percent, bool current)
33{
34 if (current)
35 return HE_COLORSET_SELECTED;
36 if (percent >= MIN_RED)
37 return HE_COLORSET_TOP;
38 if (percent >= MIN_GREEN)
39 return HE_COLORSET_MEDIUM;
40 return HE_COLORSET_NORMAL;
41}
42
43void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence)
44{
45 struct list_head *head = self->entries;
46 struct list_head *pos;
47
48 switch (whence) {
49 case SEEK_SET:
50 pos = head->next;
51 break;
52 case SEEK_CUR:
53 pos = self->top;
54 break;
55 case SEEK_END:
56 pos = head->prev;
57 break;
58 default:
59 return;
60 }
61
62 if (offset > 0) {
63 while (offset-- != 0)
64 pos = pos->next;
65 } else {
66 while (offset++ != 0)
67 pos = pos->prev;
68 }
69
70 self->top = pos;
71}
72
73void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence)
74{
75 struct rb_root *root = self->entries;
76 struct rb_node *nd;
77
78 switch (whence) {
79 case SEEK_SET:
80 nd = rb_first(root);
81 break;
82 case SEEK_CUR:
83 nd = self->top;
84 break;
85 case SEEK_END:
86 nd = rb_last(root);
87 break;
88 default:
89 return;
90 }
91
92 if (offset > 0) {
93 while (offset-- != 0)
94 nd = rb_next(nd);
95 } else {
96 while (offset++ != 0)
97 nd = rb_prev(nd);
98 }
99
100 self->top = nd;
101}
102
103unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self)
104{
105 struct rb_node *nd;
106 int row = 0;
107
108 if (self->top == NULL)
109 self->top = rb_first(self->entries);
110
111 nd = self->top;
112
113 while (nd != NULL) {
114 SLsmg_gotorc(self->y + row, self->x);
115 self->write(self, nd, row);
116 if (++row == self->height)
117 break;
118 nd = rb_next(nd);
119 }
120
121 return row;
122}
123
124bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row)
125{
126 return self->top_idx + row == self->index;
127}
128
129void ui_browser__refresh_dimensions(struct ui_browser *self)
130{
131 int cols, rows;
132 newtGetScreenSize(&cols, &rows);
133
134 if (self->width > cols - 4)
135 self->width = cols - 4;
136 self->height = rows - 5;
137 if (self->height > self->nr_entries)
138 self->height = self->nr_entries;
139 self->y = (rows - self->height) / 2;
140 self->x = (cols - self->width) / 2;
141}
142
143void ui_browser__reset_index(struct ui_browser *self)
144{
145 self->index = self->top_idx = 0;
146 self->seek(self, 0, SEEK_SET);
147}
148
149int ui_browser__show(struct ui_browser *self, const char *title,
150 const char *helpline, ...)
151{
152 va_list ap;
153
154 if (self->form != NULL) {
155 newtFormDestroy(self->form);
156 newtPopWindow();
157 }
158 ui_browser__refresh_dimensions(self);
159 newtCenteredWindow(self->width, self->height, title);
160 self->form = newt_form__new();
161 if (self->form == NULL)
162 return -1;
163
164 self->sb = newtVerticalScrollbar(self->width, 0, self->height,
165 HE_COLORSET_NORMAL,
166 HE_COLORSET_SELECTED);
167 if (self->sb == NULL)
168 return -1;
169
170 newtFormAddHotKey(self->form, NEWT_KEY_UP);
171 newtFormAddHotKey(self->form, NEWT_KEY_DOWN);
172 newtFormAddHotKey(self->form, NEWT_KEY_PGUP);
173 newtFormAddHotKey(self->form, NEWT_KEY_PGDN);
174 newtFormAddHotKey(self->form, NEWT_KEY_HOME);
175 newtFormAddHotKey(self->form, NEWT_KEY_END);
176 newtFormAddHotKey(self->form, ' ');
177 newtFormAddComponent(self->form, self->sb);
178
179 va_start(ap, helpline);
180 ui_helpline__vpush(helpline, ap);
181 va_end(ap);
182 return 0;
183}
184
185void ui_browser__hide(struct ui_browser *self)
186{
187 newtFormDestroy(self->form);
188 newtPopWindow();
189 self->form = NULL;
190 ui_helpline__pop();
191}
192
193int ui_browser__refresh(struct ui_browser *self)
194{
195 int row;
196
197 newtScrollbarSet(self->sb, self->index, self->nr_entries - 1);
198 row = self->refresh(self);
199 SLsmg_set_color(HE_COLORSET_NORMAL);
200 SLsmg_fill_region(self->y + row, self->x,
201 self->height - row, self->width, ' ');
202
203 return 0;
204}
205
206int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es)
207{
208 if (ui_browser__refresh(self) < 0)
209 return -1;
210
211 while (1) {
212 off_t offset;
213
214 newtFormRun(self->form, es);
215
216 if (es->reason != NEWT_EXIT_HOTKEY)
217 break;
218 if (is_exit_key(es->u.key))
219 return es->u.key;
220 switch (es->u.key) {
221 case NEWT_KEY_DOWN:
222 if (self->index == self->nr_entries - 1)
223 break;
224 ++self->index;
225 if (self->index == self->top_idx + self->height) {
226 ++self->top_idx;
227 self->seek(self, +1, SEEK_CUR);
228 }
229 break;
230 case NEWT_KEY_UP:
231 if (self->index == 0)
232 break;
233 --self->index;
234 if (self->index < self->top_idx) {
235 --self->top_idx;
236 self->seek(self, -1, SEEK_CUR);
237 }
238 break;
239 case NEWT_KEY_PGDN:
240 case ' ':
241 if (self->top_idx + self->height > self->nr_entries - 1)
242 break;
243
244 offset = self->height;
245 if (self->index + offset > self->nr_entries - 1)
246 offset = self->nr_entries - 1 - self->index;
247 self->index += offset;
248 self->top_idx += offset;
249 self->seek(self, +offset, SEEK_CUR);
250 break;
251 case NEWT_KEY_PGUP:
252 if (self->top_idx == 0)
253 break;
254
255 if (self->top_idx < self->height)
256 offset = self->top_idx;
257 else
258 offset = self->height;
259
260 self->index -= offset;
261 self->top_idx -= offset;
262 self->seek(self, -offset, SEEK_CUR);
263 break;
264 case NEWT_KEY_HOME:
265 ui_browser__reset_index(self);
266 break;
267 case NEWT_KEY_END:
268 offset = self->height - 1;
269 if (offset >= self->nr_entries)
270 offset = self->nr_entries - 1;
271
272 self->index = self->nr_entries - 1;
273 self->top_idx = self->index - offset;
274 self->seek(self, -offset, SEEK_END);
275 break;
276 default:
277 return es->u.key;
278 }
279 if (ui_browser__refresh(self) < 0)
280 return -1;
281 }
282 return 0;
283}
284
285unsigned int ui_browser__list_head_refresh(struct ui_browser *self)
286{
287 struct list_head *pos;
288 struct list_head *head = self->entries;
289 int row = 0;
290
291 if (self->top == NULL || self->top == self->entries)
292 self->top = head->next;
293
294 pos = self->top;
295
296 list_for_each_from(pos, head) {
297 SLsmg_gotorc(self->y + row, self->x);
298 self->write(self, pos, row);
299 if (++row == self->height)
300 break;
301 }
302
303 return row;
304}
305
306static struct newtPercentTreeColors {
307 const char *topColorFg, *topColorBg;
308 const char *mediumColorFg, *mediumColorBg;
309 const char *normalColorFg, *normalColorBg;
310 const char *selColorFg, *selColorBg;
311 const char *codeColorFg, *codeColorBg;
312} defaultPercentTreeColors = {
313 "red", "lightgray",
314 "green", "lightgray",
315 "black", "lightgray",
316 "lightgray", "magenta",
317 "blue", "lightgray",
318};
319
320void ui_browser__init(void)
321{
322 struct newtPercentTreeColors *c = &defaultPercentTreeColors;
323
324 sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg);
325 sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg);
326 sltt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg);
327 sltt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg);
328 sltt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg);
329}
diff --git a/tools/perf/util/ui/browser.h b/tools/perf/util/ui/browser.h
new file mode 100644
index 000000000000..0b9f829214f7
--- /dev/null
+++ b/tools/perf/util/ui/browser.h
@@ -0,0 +1,46 @@
1#ifndef _PERF_UI_BROWSER_H_
2#define _PERF_UI_BROWSER_H_ 1
3
4#include <stdbool.h>
5#include <newt.h>
6#include <sys/types.h>
7#include "../types.h"
8
9#define HE_COLORSET_TOP 50
10#define HE_COLORSET_MEDIUM 51
11#define HE_COLORSET_NORMAL 52
12#define HE_COLORSET_SELECTED 53
13#define HE_COLORSET_CODE 54
14
15struct ui_browser {
16 newtComponent form, sb;
17 u64 index, top_idx;
18 void *top, *entries;
19 u16 y, x, width, height;
20 void *priv;
21 unsigned int (*refresh)(struct ui_browser *self);
22 void (*write)(struct ui_browser *self, void *entry, int row);
23 void (*seek)(struct ui_browser *self, off_t offset, int whence);
24 u32 nr_entries;
25};
26
27
28int ui_browser__percent_color(double percent, bool current);
29bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row);
30void ui_browser__refresh_dimensions(struct ui_browser *self);
31void ui_browser__reset_index(struct ui_browser *self);
32
33int ui_browser__show(struct ui_browser *self, const char *title,
34 const char *helpline, ...);
35void ui_browser__hide(struct ui_browser *self);
36int ui_browser__refresh(struct ui_browser *self);
37int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es);
38
39void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence);
40unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self);
41
42void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence);
43unsigned int ui_browser__list_head_refresh(struct ui_browser *self);
44
45void ui_browser__init(void);
46#endif /* _PERF_UI_BROWSER_H_ */
diff --git a/tools/perf/util/ui/browsers/annotate.c b/tools/perf/util/ui/browsers/annotate.c
new file mode 100644
index 000000000000..55ff792459ac
--- /dev/null
+++ b/tools/perf/util/ui/browsers/annotate.c
@@ -0,0 +1,240 @@
1#include "../browser.h"
2#include "../helpline.h"
3#include "../libslang.h"
4#include "../../hist.h"
5#include "../../sort.h"
6#include "../../symbol.h"
7
8static void ui__error_window(const char *fmt, ...)
9{
10 va_list ap;
11
12 va_start(ap, fmt);
13 newtWinMessagev((char *)"Error", (char *)"Ok", (char *)fmt, ap);
14 va_end(ap);
15}
16
17struct annotate_browser {
18 struct ui_browser b;
19 struct rb_root entries;
20 struct rb_node *curr_hot;
21};
22
23struct objdump_line_rb_node {
24 struct rb_node rb_node;
25 double percent;
26 u32 idx;
27};
28
29static inline
30struct objdump_line_rb_node *objdump_line__rb(struct objdump_line *self)
31{
32 return (struct objdump_line_rb_node *)(self + 1);
33}
34
35static void annotate_browser__write(struct ui_browser *self, void *entry, int row)
36{
37 struct objdump_line *ol = rb_entry(entry, struct objdump_line, node);
38 bool current_entry = ui_browser__is_current_entry(self, row);
39 int width = self->width;
40
41 if (ol->offset != -1) {
42 struct objdump_line_rb_node *olrb = objdump_line__rb(ol);
43 int color = ui_browser__percent_color(olrb->percent, current_entry);
44 SLsmg_set_color(color);
45 slsmg_printf(" %7.2f ", olrb->percent);
46 if (!current_entry)
47 SLsmg_set_color(HE_COLORSET_CODE);
48 } else {
49 int color = ui_browser__percent_color(0, current_entry);
50 SLsmg_set_color(color);
51 slsmg_write_nstring(" ", 9);
52 }
53
54 SLsmg_write_char(':');
55 slsmg_write_nstring(" ", 8);
56 if (!*ol->line)
57 slsmg_write_nstring(" ", width - 18);
58 else
59 slsmg_write_nstring(ol->line, width - 18);
60}
61
62static double objdump_line__calc_percent(struct objdump_line *self,
63 struct list_head *head,
64 struct symbol *sym)
65{
66 double percent = 0.0;
67
68 if (self->offset != -1) {
69 int len = sym->end - sym->start;
70 unsigned int hits = 0;
71 struct sym_priv *priv = symbol__priv(sym);
72 struct sym_ext *sym_ext = priv->ext;
73 struct sym_hist *h = priv->hist;
74 s64 offset = self->offset;
75 struct objdump_line *next = objdump__get_next_ip_line(head, self);
76
77
78 while (offset < (s64)len &&
79 (next == NULL || offset < next->offset)) {
80 if (sym_ext) {
81 percent += sym_ext[offset].percent;
82 } else
83 hits += h->ip[offset];
84
85 ++offset;
86 }
87
88 if (sym_ext == NULL && h->sum)
89 percent = 100.0 * hits / h->sum;
90 }
91
92 return percent;
93}
94
95static void objdump__insert_line(struct rb_root *self,
96 struct objdump_line_rb_node *line)
97{
98 struct rb_node **p = &self->rb_node;
99 struct rb_node *parent = NULL;
100 struct objdump_line_rb_node *l;
101
102 while (*p != NULL) {
103 parent = *p;
104 l = rb_entry(parent, struct objdump_line_rb_node, rb_node);
105 if (line->percent < l->percent)
106 p = &(*p)->rb_left;
107 else
108 p = &(*p)->rb_right;
109 }
110 rb_link_node(&line->rb_node, parent, p);
111 rb_insert_color(&line->rb_node, self);
112}
113
114static void annotate_browser__set_top(struct annotate_browser *self,
115 struct rb_node *nd)
116{
117 struct objdump_line_rb_node *rbpos;
118 struct objdump_line *pos;
119 unsigned back;
120
121 ui_browser__refresh_dimensions(&self->b);
122 back = self->b.height / 2;
123 rbpos = rb_entry(nd, struct objdump_line_rb_node, rb_node);
124 pos = ((struct objdump_line *)rbpos) - 1;
125 self->b.top_idx = self->b.index = rbpos->idx;
126
127 while (self->b.top_idx != 0 && back != 0) {
128 pos = list_entry(pos->node.prev, struct objdump_line, node);
129
130 --self->b.top_idx;
131 --back;
132 }
133
134 self->b.top = pos;
135 self->curr_hot = nd;
136}
137
138static int annotate_browser__run(struct annotate_browser *self,
139 struct newtExitStruct *es)
140{
141 struct rb_node *nd;
142 struct hist_entry *he = self->b.priv;
143
144 if (ui_browser__show(&self->b, he->ms.sym->name,
145 "<- or ESC: exit, TAB/shift+TAB: cycle thru samples") < 0)
146 return -1;
147
148 newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
149
150 nd = self->curr_hot;
151 if (nd) {
152 newtFormAddHotKey(self->b.form, NEWT_KEY_TAB);
153 newtFormAddHotKey(self->b.form, NEWT_KEY_UNTAB);
154 }
155
156 while (1) {
157 ui_browser__run(&self->b, es);
158
159 if (es->reason != NEWT_EXIT_HOTKEY)
160 break;
161
162 switch (es->u.key) {
163 case NEWT_KEY_TAB:
164 nd = rb_prev(nd);
165 if (nd == NULL)
166 nd = rb_last(&self->entries);
167 annotate_browser__set_top(self, nd);
168 break;
169 case NEWT_KEY_UNTAB:
170 nd = rb_next(nd);
171 if (nd == NULL)
172 nd = rb_first(&self->entries);
173 annotate_browser__set_top(self, nd);
174 break;
175 default:
176 goto out;
177 }
178 }
179out:
180 ui_browser__hide(&self->b);
181 return 0;
182}
183
184int hist_entry__tui_annotate(struct hist_entry *self)
185{
186 struct newtExitStruct es;
187 struct objdump_line *pos, *n;
188 struct objdump_line_rb_node *rbpos;
189 LIST_HEAD(head);
190 struct annotate_browser browser = {
191 .b = {
192 .entries = &head,
193 .refresh = ui_browser__list_head_refresh,
194 .seek = ui_browser__list_head_seek,
195 .write = annotate_browser__write,
196 .priv = self,
197 },
198 };
199 int ret;
200
201 if (self->ms.sym == NULL)
202 return -1;
203
204 if (self->ms.map->dso->annotate_warned)
205 return -1;
206
207 if (hist_entry__annotate(self, &head, sizeof(*rbpos)) < 0) {
208 ui__error_window(ui_helpline__last_msg);
209 return -1;
210 }
211
212 ui_helpline__push("Press <- or ESC to exit");
213
214 list_for_each_entry(pos, &head, node) {
215 size_t line_len = strlen(pos->line);
216 if (browser.b.width < line_len)
217 browser.b.width = line_len;
218 rbpos = objdump_line__rb(pos);
219 rbpos->idx = browser.b.nr_entries++;
220 rbpos->percent = objdump_line__calc_percent(pos, &head, self->ms.sym);
221 if (rbpos->percent < 0.01)
222 continue;
223 objdump__insert_line(&browser.entries, rbpos);
224 }
225
226 /*
227 * Position the browser at the hottest line.
228 */
229 browser.curr_hot = rb_last(&browser.entries);
230 if (browser.curr_hot)
231 annotate_browser__set_top(&browser, browser.curr_hot);
232
233 browser.b.width += 18; /* Percentage */
234 ret = annotate_browser__run(&browser, &es);
235 list_for_each_entry_safe(pos, n, &head, node) {
236 list_del(&pos->node);
237 objdump_line__free(pos);
238 }
239 return ret;
240}
diff --git a/tools/perf/util/ui/browsers/hists.c b/tools/perf/util/ui/browsers/hists.c
new file mode 100644
index 000000000000..dafdf6775d77
--- /dev/null
+++ b/tools/perf/util/ui/browsers/hists.c
@@ -0,0 +1,948 @@
1#define _GNU_SOURCE
2#include <stdio.h>
3#undef _GNU_SOURCE
4#include "../libslang.h"
5#include <stdlib.h>
6#include <string.h>
7#include <newt.h>
8#include <linux/rbtree.h>
9
10#include "../../hist.h"
11#include "../../pstack.h"
12#include "../../sort.h"
13#include "../../util.h"
14
15#include "../browser.h"
16#include "../helpline.h"
17#include "../util.h"
18#include "map.h"
19
20struct hist_browser {
21 struct ui_browser b;
22 struct hists *hists;
23 struct hist_entry *he_selection;
24 struct map_symbol *selection;
25};
26
27static void hist_browser__refresh_dimensions(struct hist_browser *self)
28{
29 /* 3 == +/- toggle symbol before actual hist_entry rendering */
30 self->b.width = 3 + (hists__sort_list_width(self->hists) +
31 sizeof("[k]"));
32}
33
34static void hist_browser__reset(struct hist_browser *self)
35{
36 self->b.nr_entries = self->hists->nr_entries;
37 hist_browser__refresh_dimensions(self);
38 ui_browser__reset_index(&self->b);
39}
40
41static char tree__folded_sign(bool unfolded)
42{
43 return unfolded ? '-' : '+';
44}
45
46static char map_symbol__folded(const struct map_symbol *self)
47{
48 return self->has_children ? tree__folded_sign(self->unfolded) : ' ';
49}
50
51static char hist_entry__folded(const struct hist_entry *self)
52{
53 return map_symbol__folded(&self->ms);
54}
55
56static char callchain_list__folded(const struct callchain_list *self)
57{
58 return map_symbol__folded(&self->ms);
59}
60
61static int callchain_node__count_rows_rb_tree(struct callchain_node *self)
62{
63 int n = 0;
64 struct rb_node *nd;
65
66 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
67 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
68 struct callchain_list *chain;
69 char folded_sign = ' '; /* No children */
70
71 list_for_each_entry(chain, &child->val, list) {
72 ++n;
73 /* We need this because we may not have children */
74 folded_sign = callchain_list__folded(chain);
75 if (folded_sign == '+')
76 break;
77 }
78
79 if (folded_sign == '-') /* Have children and they're unfolded */
80 n += callchain_node__count_rows_rb_tree(child);
81 }
82
83 return n;
84}
85
86static int callchain_node__count_rows(struct callchain_node *node)
87{
88 struct callchain_list *chain;
89 bool unfolded = false;
90 int n = 0;
91
92 list_for_each_entry(chain, &node->val, list) {
93 ++n;
94 unfolded = chain->ms.unfolded;
95 }
96
97 if (unfolded)
98 n += callchain_node__count_rows_rb_tree(node);
99
100 return n;
101}
102
103static int callchain__count_rows(struct rb_root *chain)
104{
105 struct rb_node *nd;
106 int n = 0;
107
108 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
109 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
110 n += callchain_node__count_rows(node);
111 }
112
113 return n;
114}
115
116static bool map_symbol__toggle_fold(struct map_symbol *self)
117{
118 if (!self->has_children)
119 return false;
120
121 self->unfolded = !self->unfolded;
122 return true;
123}
124
125static void callchain_node__init_have_children_rb_tree(struct callchain_node *self)
126{
127 struct rb_node *nd = rb_first(&self->rb_root);
128
129 for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) {
130 struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node);
131 struct callchain_list *chain;
132 int first = true;
133
134 list_for_each_entry(chain, &child->val, list) {
135 if (first) {
136 first = false;
137 chain->ms.has_children = chain->list.next != &child->val ||
138 rb_first(&child->rb_root) != NULL;
139 } else
140 chain->ms.has_children = chain->list.next == &child->val &&
141 rb_first(&child->rb_root) != NULL;
142 }
143
144 callchain_node__init_have_children_rb_tree(child);
145 }
146}
147
148static void callchain_node__init_have_children(struct callchain_node *self)
149{
150 struct callchain_list *chain;
151
152 list_for_each_entry(chain, &self->val, list)
153 chain->ms.has_children = rb_first(&self->rb_root) != NULL;
154
155 callchain_node__init_have_children_rb_tree(self);
156}
157
158static void callchain__init_have_children(struct rb_root *self)
159{
160 struct rb_node *nd;
161
162 for (nd = rb_first(self); nd; nd = rb_next(nd)) {
163 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
164 callchain_node__init_have_children(node);
165 }
166}
167
168static void hist_entry__init_have_children(struct hist_entry *self)
169{
170 if (!self->init_have_children) {
171 callchain__init_have_children(&self->sorted_chain);
172 self->init_have_children = true;
173 }
174}
175
176static bool hist_browser__toggle_fold(struct hist_browser *self)
177{
178 if (map_symbol__toggle_fold(self->selection)) {
179 struct hist_entry *he = self->he_selection;
180
181 hist_entry__init_have_children(he);
182 self->hists->nr_entries -= he->nr_rows;
183
184 if (he->ms.unfolded)
185 he->nr_rows = callchain__count_rows(&he->sorted_chain);
186 else
187 he->nr_rows = 0;
188 self->hists->nr_entries += he->nr_rows;
189 self->b.nr_entries = self->hists->nr_entries;
190
191 return true;
192 }
193
194 /* If it doesn't have children, no toggling performed */
195 return false;
196}
197
198static int hist_browser__run(struct hist_browser *self, const char *title,
199 struct newtExitStruct *es)
200{
201 char str[256], unit;
202 unsigned long nr_events = self->hists->stats.nr_events[PERF_RECORD_SAMPLE];
203
204 self->b.entries = &self->hists->entries;
205 self->b.nr_entries = self->hists->nr_entries;
206
207 hist_browser__refresh_dimensions(self);
208
209 nr_events = convert_unit(nr_events, &unit);
210 snprintf(str, sizeof(str), "Events: %lu%c ",
211 nr_events, unit);
212 newtDrawRootText(0, 0, str);
213
214 if (ui_browser__show(&self->b, title,
215 "Press '?' for help on key bindings") < 0)
216 return -1;
217
218 newtFormAddHotKey(self->b.form, 'a');
219 newtFormAddHotKey(self->b.form, '?');
220 newtFormAddHotKey(self->b.form, 'h');
221 newtFormAddHotKey(self->b.form, 'd');
222 newtFormAddHotKey(self->b.form, 'D');
223 newtFormAddHotKey(self->b.form, 't');
224
225 newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
226 newtFormAddHotKey(self->b.form, NEWT_KEY_RIGHT);
227 newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER);
228
229 while (1) {
230 ui_browser__run(&self->b, es);
231
232 if (es->reason != NEWT_EXIT_HOTKEY)
233 break;
234 switch (es->u.key) {
235 case 'D': { /* Debug */
236 static int seq;
237 struct hist_entry *h = rb_entry(self->b.top,
238 struct hist_entry, rb_node);
239 ui_helpline__pop();
240 ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d",
241 seq++, self->b.nr_entries,
242 self->hists->nr_entries,
243 self->b.height,
244 self->b.index,
245 self->b.top_idx,
246 h->row_offset, h->nr_rows);
247 }
248 continue;
249 case NEWT_KEY_ENTER:
250 if (hist_browser__toggle_fold(self))
251 break;
252 /* fall thru */
253 default:
254 return 0;
255 }
256 }
257
258 ui_browser__hide(&self->b);
259 return 0;
260}
261
262static char *callchain_list__sym_name(struct callchain_list *self,
263 char *bf, size_t bfsize)
264{
265 if (self->ms.sym)
266 return self->ms.sym->name;
267
268 snprintf(bf, bfsize, "%#Lx", self->ip);
269 return bf;
270}
271
272#define LEVEL_OFFSET_STEP 3
273
274static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self,
275 struct callchain_node *chain_node,
276 u64 total, int level,
277 unsigned short row,
278 off_t *row_offset,
279 bool *is_current_entry)
280{
281 struct rb_node *node;
282 int first_row = row, width, offset = level * LEVEL_OFFSET_STEP;
283 u64 new_total, remaining;
284
285 if (callchain_param.mode == CHAIN_GRAPH_REL)
286 new_total = chain_node->children_hit;
287 else
288 new_total = total;
289
290 remaining = new_total;
291 node = rb_first(&chain_node->rb_root);
292 while (node) {
293 struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node);
294 struct rb_node *next = rb_next(node);
295 u64 cumul = cumul_hits(child);
296 struct callchain_list *chain;
297 char folded_sign = ' ';
298 int first = true;
299 int extra_offset = 0;
300
301 remaining -= cumul;
302
303 list_for_each_entry(chain, &child->val, list) {
304 char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str;
305 const char *str;
306 int color;
307 bool was_first = first;
308
309 if (first) {
310 first = false;
311 chain->ms.has_children = chain->list.next != &child->val ||
312 rb_first(&child->rb_root) != NULL;
313 } else {
314 extra_offset = LEVEL_OFFSET_STEP;
315 chain->ms.has_children = chain->list.next == &child->val &&
316 rb_first(&child->rb_root) != NULL;
317 }
318
319 folded_sign = callchain_list__folded(chain);
320 if (*row_offset != 0) {
321 --*row_offset;
322 goto do_next;
323 }
324
325 alloc_str = NULL;
326 str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
327 if (was_first) {
328 double percent = cumul * 100.0 / new_total;
329
330 if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0)
331 str = "Not enough memory!";
332 else
333 str = alloc_str;
334 }
335
336 color = HE_COLORSET_NORMAL;
337 width = self->b.width - (offset + extra_offset + 2);
338 if (ui_browser__is_current_entry(&self->b, row)) {
339 self->selection = &chain->ms;
340 color = HE_COLORSET_SELECTED;
341 *is_current_entry = true;
342 }
343
344 SLsmg_set_color(color);
345 SLsmg_gotorc(self->b.y + row, self->b.x);
346 slsmg_write_nstring(" ", offset + extra_offset);
347 slsmg_printf("%c ", folded_sign);
348 slsmg_write_nstring(str, width);
349 free(alloc_str);
350
351 if (++row == self->b.height)
352 goto out;
353do_next:
354 if (folded_sign == '+')
355 break;
356 }
357
358 if (folded_sign == '-') {
359 const int new_level = level + (extra_offset ? 2 : 1);
360 row += hist_browser__show_callchain_node_rb_tree(self, child, new_total,
361 new_level, row, row_offset,
362 is_current_entry);
363 }
364 if (row == self->b.height)
365 goto out;
366 node = next;
367 }
368out:
369 return row - first_row;
370}
371
372static int hist_browser__show_callchain_node(struct hist_browser *self,
373 struct callchain_node *node,
374 int level, unsigned short row,
375 off_t *row_offset,
376 bool *is_current_entry)
377{
378 struct callchain_list *chain;
379 int first_row = row,
380 offset = level * LEVEL_OFFSET_STEP,
381 width = self->b.width - offset;
382 char folded_sign = ' ';
383
384 list_for_each_entry(chain, &node->val, list) {
385 char ipstr[BITS_PER_LONG / 4 + 1], *s;
386 int color;
387 /*
388 * FIXME: This should be moved to somewhere else,
389 * probably when the callchain is created, so as not to
390 * traverse it all over again
391 */
392 chain->ms.has_children = rb_first(&node->rb_root) != NULL;
393 folded_sign = callchain_list__folded(chain);
394
395 if (*row_offset != 0) {
396 --*row_offset;
397 continue;
398 }
399
400 color = HE_COLORSET_NORMAL;
401 if (ui_browser__is_current_entry(&self->b, row)) {
402 self->selection = &chain->ms;
403 color = HE_COLORSET_SELECTED;
404 *is_current_entry = true;
405 }
406
407 s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
408 SLsmg_gotorc(self->b.y + row, self->b.x);
409 SLsmg_set_color(color);
410 slsmg_write_nstring(" ", offset);
411 slsmg_printf("%c ", folded_sign);
412 slsmg_write_nstring(s, width - 2);
413
414 if (++row == self->b.height)
415 goto out;
416 }
417
418 if (folded_sign == '-')
419 row += hist_browser__show_callchain_node_rb_tree(self, node,
420 self->hists->stats.total_period,
421 level + 1, row,
422 row_offset,
423 is_current_entry);
424out:
425 return row - first_row;
426}
427
428static int hist_browser__show_callchain(struct hist_browser *self,
429 struct rb_root *chain,
430 int level, unsigned short row,
431 off_t *row_offset,
432 bool *is_current_entry)
433{
434 struct rb_node *nd;
435 int first_row = row;
436
437 for (nd = rb_first(chain); nd; nd = rb_next(nd)) {
438 struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node);
439
440 row += hist_browser__show_callchain_node(self, node, level,
441 row, row_offset,
442 is_current_entry);
443 if (row == self->b.height)
444 break;
445 }
446
447 return row - first_row;
448}
449
450static int hist_browser__show_entry(struct hist_browser *self,
451 struct hist_entry *entry,
452 unsigned short row)
453{
454 char s[256];
455 double percent;
456 int printed = 0;
457 int color, width = self->b.width;
458 char folded_sign = ' ';
459 bool current_entry = ui_browser__is_current_entry(&self->b, row);
460 off_t row_offset = entry->row_offset;
461
462 if (current_entry) {
463 self->he_selection = entry;
464 self->selection = &entry->ms;
465 }
466
467 if (symbol_conf.use_callchain) {
468 entry->ms.has_children = !RB_EMPTY_ROOT(&entry->sorted_chain);
469 folded_sign = hist_entry__folded(entry);
470 }
471
472 if (row_offset == 0) {
473 hist_entry__snprintf(entry, s, sizeof(s), self->hists, NULL, false,
474 0, false, self->hists->stats.total_period);
475 percent = (entry->period * 100.0) / self->hists->stats.total_period;
476
477 color = HE_COLORSET_SELECTED;
478 if (!current_entry) {
479 if (percent >= MIN_RED)
480 color = HE_COLORSET_TOP;
481 else if (percent >= MIN_GREEN)
482 color = HE_COLORSET_MEDIUM;
483 else
484 color = HE_COLORSET_NORMAL;
485 }
486
487 SLsmg_set_color(color);
488 SLsmg_gotorc(self->b.y + row, self->b.x);
489 if (symbol_conf.use_callchain) {
490 slsmg_printf("%c ", folded_sign);
491 width -= 2;
492 }
493 slsmg_write_nstring(s, width);
494 ++row;
495 ++printed;
496 } else
497 --row_offset;
498
499 if (folded_sign == '-' && row != self->b.height) {
500 printed += hist_browser__show_callchain(self, &entry->sorted_chain,
501 1, row, &row_offset,
502 &current_entry);
503 if (current_entry)
504 self->he_selection = entry;
505 }
506
507 return printed;
508}
509
510static unsigned int hist_browser__refresh(struct ui_browser *self)
511{
512 unsigned row = 0;
513 struct rb_node *nd;
514 struct hist_browser *hb = container_of(self, struct hist_browser, b);
515
516 if (self->top == NULL)
517 self->top = rb_first(&hb->hists->entries);
518
519 for (nd = self->top; nd; nd = rb_next(nd)) {
520 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
521
522 if (h->filtered)
523 continue;
524
525 row += hist_browser__show_entry(hb, h, row);
526 if (row == self->height)
527 break;
528 }
529
530 return row;
531}
532
533static struct rb_node *hists__filter_entries(struct rb_node *nd)
534{
535 while (nd != NULL) {
536 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
537 if (!h->filtered)
538 return nd;
539
540 nd = rb_next(nd);
541 }
542
543 return NULL;
544}
545
546static struct rb_node *hists__filter_prev_entries(struct rb_node *nd)
547{
548 while (nd != NULL) {
549 struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
550 if (!h->filtered)
551 return nd;
552
553 nd = rb_prev(nd);
554 }
555
556 return NULL;
557}
558
559static void ui_browser__hists_seek(struct ui_browser *self,
560 off_t offset, int whence)
561{
562 struct hist_entry *h;
563 struct rb_node *nd;
564 bool first = true;
565
566 switch (whence) {
567 case SEEK_SET:
568 nd = hists__filter_entries(rb_first(self->entries));
569 break;
570 case SEEK_CUR:
571 nd = self->top;
572 goto do_offset;
573 case SEEK_END:
574 nd = hists__filter_prev_entries(rb_last(self->entries));
575 first = false;
576 break;
577 default:
578 return;
579 }
580
581 /*
582 * Moves not relative to the first visible entry invalidates its
583 * row_offset:
584 */
585 h = rb_entry(self->top, struct hist_entry, rb_node);
586 h->row_offset = 0;
587
588 /*
589 * Here we have to check if nd is expanded (+), if it is we can't go
590 * the next top level hist_entry, instead we must compute an offset of
591 * what _not_ to show and not change the first visible entry.
592 *
593 * This offset increments when we are going from top to bottom and
594 * decreases when we're going from bottom to top.
595 *
596 * As we don't have backpointers to the top level in the callchains
597 * structure, we need to always print the whole hist_entry callchain,
598 * skipping the first ones that are before the first visible entry
599 * and stop when we printed enough lines to fill the screen.
600 */
601do_offset:
602 if (offset > 0) {
603 do {
604 h = rb_entry(nd, struct hist_entry, rb_node);
605 if (h->ms.unfolded) {
606 u16 remaining = h->nr_rows - h->row_offset;
607 if (offset > remaining) {
608 offset -= remaining;
609 h->row_offset = 0;
610 } else {
611 h->row_offset += offset;
612 offset = 0;
613 self->top = nd;
614 break;
615 }
616 }
617 nd = hists__filter_entries(rb_next(nd));
618 if (nd == NULL)
619 break;
620 --offset;
621 self->top = nd;
622 } while (offset != 0);
623 } else if (offset < 0) {
624 while (1) {
625 h = rb_entry(nd, struct hist_entry, rb_node);
626 if (h->ms.unfolded) {
627 if (first) {
628 if (-offset > h->row_offset) {
629 offset += h->row_offset;
630 h->row_offset = 0;
631 } else {
632 h->row_offset += offset;
633 offset = 0;
634 self->top = nd;
635 break;
636 }
637 } else {
638 if (-offset > h->nr_rows) {
639 offset += h->nr_rows;
640 h->row_offset = 0;
641 } else {
642 h->row_offset = h->nr_rows + offset;
643 offset = 0;
644 self->top = nd;
645 break;
646 }
647 }
648 }
649
650 nd = hists__filter_prev_entries(rb_prev(nd));
651 if (nd == NULL)
652 break;
653 ++offset;
654 self->top = nd;
655 if (offset == 0) {
656 /*
657 * Last unfiltered hist_entry, check if it is
658 * unfolded, if it is then we should have
659 * row_offset at its last entry.
660 */
661 h = rb_entry(nd, struct hist_entry, rb_node);
662 if (h->ms.unfolded)
663 h->row_offset = h->nr_rows;
664 break;
665 }
666 first = false;
667 }
668 } else {
669 self->top = nd;
670 h = rb_entry(nd, struct hist_entry, rb_node);
671 h->row_offset = 0;
672 }
673}
674
675static struct hist_browser *hist_browser__new(struct hists *hists)
676{
677 struct hist_browser *self = zalloc(sizeof(*self));
678
679 if (self) {
680 self->hists = hists;
681 self->b.refresh = hist_browser__refresh;
682 self->b.seek = ui_browser__hists_seek;
683 }
684
685 return self;
686}
687
688static void hist_browser__delete(struct hist_browser *self)
689{
690 newtFormDestroy(self->b.form);
691 newtPopWindow();
692 free(self);
693}
694
695static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self)
696{
697 return self->he_selection;
698}
699
700static struct thread *hist_browser__selected_thread(struct hist_browser *self)
701{
702 return self->he_selection->thread;
703}
704
705static int hist_browser__title(char *bf, size_t size, const char *ev_name,
706 const struct dso *dso, const struct thread *thread)
707{
708 int printed = 0;
709
710 if (thread)
711 printed += snprintf(bf + printed, size - printed,
712 "Thread: %s(%d)",
713 (thread->comm_set ? thread->comm : ""),
714 thread->pid);
715 if (dso)
716 printed += snprintf(bf + printed, size - printed,
717 "%sDSO: %s", thread ? " " : "",
718 dso->short_name);
719 return printed ?: snprintf(bf, size, "Event: %s", ev_name);
720}
721
722int hists__browse(struct hists *self, const char *helpline, const char *ev_name)
723{
724 struct hist_browser *browser = hist_browser__new(self);
725 struct pstack *fstack;
726 const struct thread *thread_filter = NULL;
727 const struct dso *dso_filter = NULL;
728 struct newtExitStruct es;
729 char msg[160];
730 int key = -1;
731
732 if (browser == NULL)
733 return -1;
734
735 fstack = pstack__new(2);
736 if (fstack == NULL)
737 goto out;
738
739 ui_helpline__push(helpline);
740
741 hist_browser__title(msg, sizeof(msg), ev_name,
742 dso_filter, thread_filter);
743
744 while (1) {
745 const struct thread *thread;
746 const struct dso *dso;
747 char *options[16];
748 int nr_options = 0, choice = 0, i,
749 annotate = -2, zoom_dso = -2, zoom_thread = -2,
750 browse_map = -2;
751
752 if (hist_browser__run(browser, msg, &es))
753 break;
754
755 thread = hist_browser__selected_thread(browser);
756 dso = browser->selection->map ? browser->selection->map->dso : NULL;
757
758 if (es.reason == NEWT_EXIT_HOTKEY) {
759 key = es.u.key;
760
761 switch (key) {
762 case NEWT_KEY_F1:
763 goto do_help;
764 case NEWT_KEY_TAB:
765 case NEWT_KEY_UNTAB:
766 /*
767 * Exit the browser, let hists__browser_tree
768 * go to the next or previous
769 */
770 goto out_free_stack;
771 default:;
772 }
773
774 switch (key) {
775 case 'a':
776 if (browser->selection->map == NULL &&
777 browser->selection->map->dso->annotate_warned)
778 continue;
779 goto do_annotate;
780 case 'd':
781 goto zoom_dso;
782 case 't':
783 goto zoom_thread;
784 case 'h':
785 case '?':
786do_help:
787 ui__help_window("-> Zoom into DSO/Threads & Annotate current symbol\n"
788 "<- Zoom out\n"
789 "a Annotate current symbol\n"
790 "h/?/F1 Show this window\n"
791 "d Zoom into current DSO\n"
792 "t Zoom into current Thread\n"
793 "q/CTRL+C Exit browser");
794 continue;
795 default:;
796 }
797 if (is_exit_key(key)) {
798 if (key == NEWT_KEY_ESCAPE &&
799 !ui__dialog_yesno("Do you really want to exit?"))
800 continue;
801 break;
802 }
803
804 if (es.u.key == NEWT_KEY_LEFT) {
805 const void *top;
806
807 if (pstack__empty(fstack))
808 continue;
809 top = pstack__pop(fstack);
810 if (top == &dso_filter)
811 goto zoom_out_dso;
812 if (top == &thread_filter)
813 goto zoom_out_thread;
814 continue;
815 }
816 }
817
818 if (browser->selection->sym != NULL &&
819 !browser->selection->map->dso->annotate_warned &&
820 asprintf(&options[nr_options], "Annotate %s",
821 browser->selection->sym->name) > 0)
822 annotate = nr_options++;
823
824 if (thread != NULL &&
825 asprintf(&options[nr_options], "Zoom %s %s(%d) thread",
826 (thread_filter ? "out of" : "into"),
827 (thread->comm_set ? thread->comm : ""),
828 thread->pid) > 0)
829 zoom_thread = nr_options++;
830
831 if (dso != NULL &&
832 asprintf(&options[nr_options], "Zoom %s %s DSO",
833 (dso_filter ? "out of" : "into"),
834 (dso->kernel ? "the Kernel" : dso->short_name)) > 0)
835 zoom_dso = nr_options++;
836
837 if (browser->selection->map != NULL &&
838 asprintf(&options[nr_options], "Browse map details") > 0)
839 browse_map = nr_options++;
840
841 options[nr_options++] = (char *)"Exit";
842
843 choice = ui__popup_menu(nr_options, options);
844
845 for (i = 0; i < nr_options - 1; ++i)
846 free(options[i]);
847
848 if (choice == nr_options - 1)
849 break;
850
851 if (choice == -1)
852 continue;
853
854 if (choice == annotate) {
855 struct hist_entry *he;
856do_annotate:
857 if (browser->selection->map->dso->origin == DSO__ORIG_KERNEL) {
858 browser->selection->map->dso->annotate_warned = 1;
859 ui_helpline__puts("No vmlinux file found, can't "
860 "annotate with just a "
861 "kallsyms file");
862 continue;
863 }
864
865 he = hist_browser__selected_entry(browser);
866 if (he == NULL)
867 continue;
868
869 hist_entry__tui_annotate(he);
870 } else if (choice == browse_map)
871 map__browse(browser->selection->map);
872 else if (choice == zoom_dso) {
873zoom_dso:
874 if (dso_filter) {
875 pstack__remove(fstack, &dso_filter);
876zoom_out_dso:
877 ui_helpline__pop();
878 dso_filter = NULL;
879 } else {
880 if (dso == NULL)
881 continue;
882 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s DSO\"",
883 dso->kernel ? "the Kernel" : dso->short_name);
884 dso_filter = dso;
885 pstack__push(fstack, &dso_filter);
886 }
887 hists__filter_by_dso(self, dso_filter);
888 hist_browser__title(msg, sizeof(msg), ev_name,
889 dso_filter, thread_filter);
890 hist_browser__reset(browser);
891 } else if (choice == zoom_thread) {
892zoom_thread:
893 if (thread_filter) {
894 pstack__remove(fstack, &thread_filter);
895zoom_out_thread:
896 ui_helpline__pop();
897 thread_filter = NULL;
898 } else {
899 ui_helpline__fpush("To zoom out press <- or -> + \"Zoom out of %s(%d) thread\"",
900 thread->comm_set ? thread->comm : "",
901 thread->pid);
902 thread_filter = thread;
903 pstack__push(fstack, &thread_filter);
904 }
905 hists__filter_by_thread(self, thread_filter);
906 hist_browser__title(msg, sizeof(msg), ev_name,
907 dso_filter, thread_filter);
908 hist_browser__reset(browser);
909 }
910 }
911out_free_stack:
912 pstack__delete(fstack);
913out:
914 hist_browser__delete(browser);
915 return key;
916}
917
918int hists__tui_browse_tree(struct rb_root *self, const char *help)
919{
920 struct rb_node *first = rb_first(self), *nd = first, *next;
921 int key = 0;
922
923 while (nd) {
924 struct hists *hists = rb_entry(nd, struct hists, rb_node);
925 const char *ev_name = __event_name(hists->type, hists->config);
926
927 key = hists__browse(hists, help, ev_name);
928
929 if (is_exit_key(key))
930 break;
931
932 switch (key) {
933 case NEWT_KEY_TAB:
934 next = rb_next(nd);
935 if (next)
936 nd = next;
937 break;
938 case NEWT_KEY_UNTAB:
939 if (nd == first)
940 continue;
941 nd = rb_prev(nd);
942 default:
943 break;
944 }
945 }
946
947 return key;
948}
diff --git a/tools/perf/util/ui/browsers/map.c b/tools/perf/util/ui/browsers/map.c
new file mode 100644
index 000000000000..142b825b42bf
--- /dev/null
+++ b/tools/perf/util/ui/browsers/map.c
@@ -0,0 +1,161 @@
1#include "../libslang.h"
2#include <elf.h>
3#include <newt.h>
4#include <sys/ttydefaults.h>
5#include <ctype.h>
6#include <string.h>
7#include <linux/bitops.h>
8#include "../../debug.h"
9#include "../../symbol.h"
10#include "../browser.h"
11#include "../helpline.h"
12#include "map.h"
13
14static int ui_entry__read(const char *title, char *bf, size_t size, int width)
15{
16 struct newtExitStruct es;
17 newtComponent form, entry;
18 const char *result;
19 int err = -1;
20
21 newtCenteredWindow(width, 1, title);
22 form = newtForm(NULL, NULL, 0);
23 if (form == NULL)
24 return -1;
25
26 entry = newtEntry(0, 0, "0x", width, &result, NEWT_FLAG_SCROLL);
27 if (entry == NULL)
28 goto out_free_form;
29
30 newtFormAddComponent(form, entry);
31 newtFormAddHotKey(form, NEWT_KEY_ENTER);
32 newtFormAddHotKey(form, NEWT_KEY_ESCAPE);
33 newtFormAddHotKey(form, NEWT_KEY_LEFT);
34 newtFormAddHotKey(form, CTRL('c'));
35 newtFormRun(form, &es);
36
37 if (result != NULL) {
38 strncpy(bf, result, size);
39 err = 0;
40 }
41out_free_form:
42 newtPopWindow();
43 newtFormDestroy(form);
44 return 0;
45}
46
47struct map_browser {
48 struct ui_browser b;
49 struct map *map;
50 u16 namelen;
51 u8 addrlen;
52};
53
54static void map_browser__write(struct ui_browser *self, void *nd, int row)
55{
56 struct symbol *sym = rb_entry(nd, struct symbol, rb_node);
57 struct map_browser *mb = container_of(self, struct map_browser, b);
58 bool current_entry = ui_browser__is_current_entry(self, row);
59 int color = ui_browser__percent_color(0, current_entry);
60
61 SLsmg_set_color(color);
62 slsmg_printf("%*llx %*llx %c ",
63 mb->addrlen, sym->start, mb->addrlen, sym->end,
64 sym->binding == STB_GLOBAL ? 'g' :
65 sym->binding == STB_LOCAL ? 'l' : 'w');
66 slsmg_write_nstring(sym->name, mb->namelen);
67}
68
69/* FIXME uber-kludgy, see comment on cmd_report... */
70static u32 *symbol__browser_index(struct symbol *self)
71{
72 return ((void *)self) - sizeof(struct rb_node) - sizeof(u32);
73}
74
75static int map_browser__search(struct map_browser *self)
76{
77 char target[512];
78 struct symbol *sym;
79 int err = ui_entry__read("Search by name/addr", target, sizeof(target), 40);
80
81 if (err)
82 return err;
83
84 if (target[0] == '0' && tolower(target[1]) == 'x') {
85 u64 addr = strtoull(target, NULL, 16);
86 sym = map__find_symbol(self->map, addr, NULL);
87 } else
88 sym = map__find_symbol_by_name(self->map, target, NULL);
89
90 if (sym != NULL) {
91 u32 *idx = symbol__browser_index(sym);
92
93 self->b.top = &sym->rb_node;
94 self->b.index = self->b.top_idx = *idx;
95 } else
96 ui_helpline__fpush("%s not found!", target);
97
98 return 0;
99}
100
101static int map_browser__run(struct map_browser *self, struct newtExitStruct *es)
102{
103 if (ui_browser__show(&self->b, self->map->dso->long_name,
104 "Press <- or ESC to exit, %s / to search",
105 verbose ? "" : "restart with -v to use") < 0)
106 return -1;
107
108 newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT);
109 newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER);
110 if (verbose)
111 newtFormAddHotKey(self->b.form, '/');
112
113 while (1) {
114 ui_browser__run(&self->b, es);
115
116 if (es->reason != NEWT_EXIT_HOTKEY)
117 break;
118 if (verbose && es->u.key == '/')
119 map_browser__search(self);
120 else
121 break;
122 }
123
124 ui_browser__hide(&self->b);
125 return 0;
126}
127
128int map__browse(struct map *self)
129{
130 struct map_browser mb = {
131 .b = {
132 .entries = &self->dso->symbols[self->type],
133 .refresh = ui_browser__rb_tree_refresh,
134 .seek = ui_browser__rb_tree_seek,
135 .write = map_browser__write,
136 },
137 .map = self,
138 };
139 struct newtExitStruct es;
140 struct rb_node *nd;
141 char tmp[BITS_PER_LONG / 4];
142 u64 maxaddr = 0;
143
144 for (nd = rb_first(mb.b.entries); nd; nd = rb_next(nd)) {
145 struct symbol *pos = rb_entry(nd, struct symbol, rb_node);
146
147 if (mb.namelen < pos->namelen)
148 mb.namelen = pos->namelen;
149 if (maxaddr < pos->end)
150 maxaddr = pos->end;
151 if (verbose) {
152 u32 *idx = symbol__browser_index(pos);
153 *idx = mb.b.nr_entries;
154 }
155 ++mb.b.nr_entries;
156 }
157
158 mb.addrlen = snprintf(tmp, sizeof(tmp), "%llx", maxaddr);
159 mb.b.width += mb.addrlen * 2 + 4 + mb.namelen;
160 return map_browser__run(&mb, &es);
161}
diff --git a/tools/perf/util/ui/browsers/map.h b/tools/perf/util/ui/browsers/map.h
new file mode 100644
index 000000000000..df8581a43e17
--- /dev/null
+++ b/tools/perf/util/ui/browsers/map.h
@@ -0,0 +1,6 @@
1#ifndef _PERF_UI_MAP_BROWSER_H_
2#define _PERF_UI_MAP_BROWSER_H_ 1
3struct map;
4
5int map__browse(struct map *self);
6#endif /* _PERF_UI_MAP_BROWSER_H_ */
diff --git a/tools/perf/util/ui/helpline.c b/tools/perf/util/ui/helpline.c
new file mode 100644
index 000000000000..8d79daa4458a
--- /dev/null
+++ b/tools/perf/util/ui/helpline.c
@@ -0,0 +1,69 @@
1#define _GNU_SOURCE
2#include <stdio.h>
3#include <stdlib.h>
4#include <newt.h>
5
6#include "../debug.h"
7#include "helpline.h"
8
9void ui_helpline__pop(void)
10{
11 newtPopHelpLine();
12}
13
14void ui_helpline__push(const char *msg)
15{
16 newtPushHelpLine(msg);
17}
18
19void ui_helpline__vpush(const char *fmt, va_list ap)
20{
21 char *s;
22
23 if (vasprintf(&s, fmt, ap) < 0)
24 vfprintf(stderr, fmt, ap);
25 else {
26 ui_helpline__push(s);
27 free(s);
28 }
29}
30
31void ui_helpline__fpush(const char *fmt, ...)
32{
33 va_list ap;
34
35 va_start(ap, fmt);
36 ui_helpline__vpush(fmt, ap);
37 va_end(ap);
38}
39
40void ui_helpline__puts(const char *msg)
41{
42 ui_helpline__pop();
43 ui_helpline__push(msg);
44}
45
46void ui_helpline__init(void)
47{
48 ui_helpline__puts(" ");
49}
50
51char ui_helpline__last_msg[1024];
52
53int ui_helpline__show_help(const char *format, va_list ap)
54{
55 int ret;
56 static int backlog;
57
58 ret = vsnprintf(ui_helpline__last_msg + backlog,
59 sizeof(ui_helpline__last_msg) - backlog, format, ap);
60 backlog += ret;
61
62 if (ui_helpline__last_msg[backlog - 1] == '\n') {
63 ui_helpline__puts(ui_helpline__last_msg);
64 newtRefresh();
65 backlog = 0;
66 }
67
68 return ret;
69}
diff --git a/tools/perf/util/ui/helpline.h b/tools/perf/util/ui/helpline.h
new file mode 100644
index 000000000000..ab6028d0c401
--- /dev/null
+++ b/tools/perf/util/ui/helpline.h
@@ -0,0 +1,11 @@
1#ifndef _PERF_UI_HELPLINE_H_
2#define _PERF_UI_HELPLINE_H_ 1
3
4void ui_helpline__init(void);
5void ui_helpline__pop(void);
6void ui_helpline__push(const char *msg);
7void ui_helpline__vpush(const char *fmt, va_list ap);
8void ui_helpline__fpush(const char *fmt, ...);
9void ui_helpline__puts(const char *msg);
10
11#endif /* _PERF_UI_HELPLINE_H_ */
diff --git a/tools/perf/util/ui/libslang.h b/tools/perf/util/ui/libslang.h
new file mode 100644
index 000000000000..5623da8e8080
--- /dev/null
+++ b/tools/perf/util/ui/libslang.h
@@ -0,0 +1,27 @@
1#ifndef _PERF_UI_SLANG_H_
2#define _PERF_UI_SLANG_H_ 1
3/*
4 * slang versions <= 2.0.6 have a "#if HAVE_LONG_LONG" that breaks
5 * the build if it isn't defined. Use the equivalent one that glibc
6 * has on features.h.
7 */
8#include <features.h>
9#ifndef HAVE_LONG_LONG
10#define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG
11#endif
12#include <slang.h>
13
14#if SLANG_VERSION < 20104
15#define slsmg_printf(msg, args...) \
16 SLsmg_printf((char *)msg, ##args)
17#define slsmg_write_nstring(msg, len) \
18 SLsmg_write_nstring((char *)msg, len)
19#define sltt_set_color(obj, name, fg, bg) \
20 SLtt_set_color(obj,(char *)name, (char *)fg, (char *)bg)
21#else
22#define slsmg_printf SLsmg_printf
23#define slsmg_write_nstring SLsmg_write_nstring
24#define sltt_set_color SLtt_set_color
25#endif
26
27#endif /* _PERF_UI_SLANG_H_ */
diff --git a/tools/perf/util/ui/progress.c b/tools/perf/util/ui/progress.c
new file mode 100644
index 000000000000..d7fc399d36b3
--- /dev/null
+++ b/tools/perf/util/ui/progress.c
@@ -0,0 +1,60 @@
1#include <stdlib.h>
2#include <newt.h>
3#include "../cache.h"
4#include "progress.h"
5
6struct ui_progress {
7 newtComponent form, scale;
8};
9
10struct ui_progress *ui_progress__new(const char *title, u64 total)
11{
12 struct ui_progress *self = malloc(sizeof(*self));
13
14 if (self != NULL) {
15 int cols;
16
17 if (use_browser <= 0)
18 return self;
19 newtGetScreenSize(&cols, NULL);
20 cols -= 4;
21 newtCenteredWindow(cols, 1, title);
22 self->form = newtForm(NULL, NULL, 0);
23 if (self->form == NULL)
24 goto out_free_self;
25 self->scale = newtScale(0, 0, cols, total);
26 if (self->scale == NULL)
27 goto out_free_form;
28 newtFormAddComponent(self->form, self->scale);
29 newtRefresh();
30 }
31
32 return self;
33
34out_free_form:
35 newtFormDestroy(self->form);
36out_free_self:
37 free(self);
38 return NULL;
39}
40
41void ui_progress__update(struct ui_progress *self, u64 curr)
42{
43 /*
44 * FIXME: We should have a per UI backend way of showing progress,
45 * stdio will just show a percentage as NN%, etc.
46 */
47 if (use_browser <= 0)
48 return;
49 newtScaleSet(self->scale, curr);
50 newtRefresh();
51}
52
53void ui_progress__delete(struct ui_progress *self)
54{
55 if (use_browser > 0) {
56 newtFormDestroy(self->form);
57 newtPopWindow();
58 }
59 free(self);
60}
diff --git a/tools/perf/util/ui/progress.h b/tools/perf/util/ui/progress.h
new file mode 100644
index 000000000000..a3820a0beb5b
--- /dev/null
+++ b/tools/perf/util/ui/progress.h
@@ -0,0 +1,11 @@
1#ifndef _PERF_UI_PROGRESS_H_
2#define _PERF_UI_PROGRESS_H_ 1
3
4struct ui_progress;
5
6struct ui_progress *ui_progress__new(const char *title, u64 total);
7void ui_progress__delete(struct ui_progress *self);
8
9void ui_progress__update(struct ui_progress *self, u64 curr);
10
11#endif
diff --git a/tools/perf/util/ui/setup.c b/tools/perf/util/ui/setup.c
new file mode 100644
index 000000000000..662085032eb7
--- /dev/null
+++ b/tools/perf/util/ui/setup.c
@@ -0,0 +1,42 @@
1#include <newt.h>
2#include <signal.h>
3#include <stdbool.h>
4
5#include "../cache.h"
6#include "../debug.h"
7#include "browser.h"
8#include "helpline.h"
9
10static void newt_suspend(void *d __used)
11{
12 newtSuspend();
13 raise(SIGTSTP);
14 newtResume();
15}
16
17void setup_browser(void)
18{
19 if (!isatty(1) || !use_browser || dump_trace) {
20 use_browser = 0;
21 setup_pager();
22 return;
23 }
24
25 use_browser = 1;
26 newtInit();
27 newtCls();
28 newtSetSuspendCallback(newt_suspend, NULL);
29 ui_helpline__init();
30 ui_browser__init();
31}
32
33void exit_browser(bool wait_for_ok)
34{
35 if (use_browser > 0) {
36 if (wait_for_ok) {
37 char title[] = "Fatal Error", ok[] = "Ok";
38 newtWinMessage(title, ok, ui_helpline__last_msg);
39 }
40 newtFinished();
41 }
42}
diff --git a/tools/perf/util/ui/util.c b/tools/perf/util/ui/util.c
new file mode 100644
index 000000000000..04600e26ceea
--- /dev/null
+++ b/tools/perf/util/ui/util.c
@@ -0,0 +1,114 @@
1#include <newt.h>
2#include <signal.h>
3#include <stdio.h>
4#include <stdbool.h>
5#include <string.h>
6#include <sys/ttydefaults.h>
7
8#include "../cache.h"
9#include "../debug.h"
10#include "browser.h"
11#include "helpline.h"
12#include "util.h"
13
14newtComponent newt_form__new(void);
15
16static void newt_form__set_exit_keys(newtComponent self)
17{
18 newtFormAddHotKey(self, NEWT_KEY_LEFT);
19 newtFormAddHotKey(self, NEWT_KEY_ESCAPE);
20 newtFormAddHotKey(self, 'Q');
21 newtFormAddHotKey(self, 'q');
22 newtFormAddHotKey(self, CTRL('c'));
23}
24
25newtComponent newt_form__new(void)
26{
27 newtComponent self = newtForm(NULL, NULL, 0);
28 if (self)
29 newt_form__set_exit_keys(self);
30 return self;
31}
32
33int ui__popup_menu(int argc, char * const argv[])
34{
35 struct newtExitStruct es;
36 int i, rc = -1, max_len = 5;
37 newtComponent listbox, form = newt_form__new();
38
39 if (form == NULL)
40 return -1;
41
42 listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT);
43 if (listbox == NULL)
44 goto out_destroy_form;
45
46 newtFormAddComponent(form, listbox);
47
48 for (i = 0; i < argc; ++i) {
49 int len = strlen(argv[i]);
50 if (len > max_len)
51 max_len = len;
52 if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i))
53 goto out_destroy_form;
54 }
55
56 newtCenteredWindow(max_len, argc, NULL);
57 newtFormRun(form, &es);
58 rc = newtListboxGetCurrent(listbox) - NULL;
59 if (es.reason == NEWT_EXIT_HOTKEY)
60 rc = -1;
61 newtPopWindow();
62out_destroy_form:
63 newtFormDestroy(form);
64 return rc;
65}
66
67int ui__help_window(const char *text)
68{
69 struct newtExitStruct es;
70 newtComponent tb, form = newt_form__new();
71 int rc = -1;
72 int max_len = 0, nr_lines = 0;
73 const char *t;
74
75 if (form == NULL)
76 return -1;
77
78 t = text;
79 while (1) {
80 const char *sep = strchr(t, '\n');
81 int len;
82
83 if (sep == NULL)
84 sep = strchr(t, '\0');
85 len = sep - t;
86 if (max_len < len)
87 max_len = len;
88 ++nr_lines;
89 if (*sep == '\0')
90 break;
91 t = sep + 1;
92 }
93
94 tb = newtTextbox(0, 0, max_len, nr_lines, 0);
95 if (tb == NULL)
96 goto out_destroy_form;
97
98 newtTextboxSetText(tb, text);
99 newtFormAddComponent(form, tb);
100 newtCenteredWindow(max_len, nr_lines, NULL);
101 newtFormRun(form, &es);
102 newtPopWindow();
103 rc = 0;
104out_destroy_form:
105 newtFormDestroy(form);
106 return rc;
107}
108
109bool ui__dialog_yesno(const char *msg)
110{
111 /* newtWinChoice should really be accepting const char pointers... */
112 char yes[] = "Yes", no[] = "No";
113 return newtWinChoice(NULL, yes, no, (char *)msg) == 1;
114}
diff --git a/tools/perf/util/ui/util.h b/tools/perf/util/ui/util.h
new file mode 100644
index 000000000000..afcbc1d99531
--- /dev/null
+++ b/tools/perf/util/ui/util.h
@@ -0,0 +1,10 @@
1#ifndef _PERF_UI_UTIL_H_
2#define _PERF_UI_UTIL_H_ 1
3
4#include <stdbool.h>
5
6int ui__popup_menu(int argc, char * const argv[]);
7int ui__help_window(const char *text);
8bool ui__dialog_yesno(const char *msg);
9
10#endif /* _PERF_UI_UTIL_H_ */
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h
index 4e8b6b0c551c..f380fed74359 100644
--- a/tools/perf/util/util.h
+++ b/tools/perf/util/util.h
@@ -89,6 +89,7 @@
89 89
90extern const char *graph_line; 90extern const char *graph_line;
91extern const char *graph_dotted_line; 91extern const char *graph_dotted_line;
92extern char buildid_dir[];
92 93
93/* On most systems <limits.h> would have given us this, but 94/* On most systems <limits.h> would have given us this, but
94 * not on some systems (e.g. GNU/Hurd). 95 * not on some systems (e.g. GNU/Hurd).
@@ -152,6 +153,8 @@ extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)))
152extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN); 153extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN);
153 154
154extern int prefixcmp(const char *str, const char *prefix); 155extern int prefixcmp(const char *str, const char *prefix);
156extern void set_buildid_dir(void);
157extern void disable_buildid_cache(void);
155 158
156static inline const char *skip_prefix(const char *str, const char *prefix) 159static inline const char *skip_prefix(const char *str, const char *prefix)
157{ 160{