diff options
Diffstat (limited to 'tools/perf/util')
29 files changed, 2001 insertions, 851 deletions
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 | ||
16 | static int build_id__mark_dso_hit(event_t *event, struct perf_session *session) | 17 | static 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 | ||
38 | static 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 | |||
37 | struct perf_event_ops build_id__mark_dso_hit_ops = { | 54 | struct 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 | ||
43 | char *dso__build_id_filename(struct dso *self, char *bf, size_t size) | 61 | char *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 *); | |||
23 | extern int perf_config_int(const char *, const char *); | 23 | extern int perf_config_int(const char *, const char *); |
24 | extern int perf_config_bool(const char *, const char *); | 24 | extern int perf_config_bool(const char *, const char *); |
25 | extern int config_error_nonbool(const char *); | 25 | extern int config_error_nonbool(const char *); |
26 | extern const char *perf_config_dirname(const char *, const char *); | ||
26 | 27 | ||
27 | /* pager.c */ | 28 | /* pager.c */ |
28 | extern void setup_pager(void); | 29 | extern void setup_pager(void); |
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 52c777e451ed..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 | ||
21 | bool ip_callchain__valid(struct ip_callchain *chain, event_t *event) | 21 | bool 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; |
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index f2e9ee164bd8..624a96c636fd 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
@@ -63,5 +63,5 @@ int register_callchain_param(struct callchain_param *param); | |||
63 | int append_chain(struct callchain_node *root, struct ip_callchain *chain, | 63 | int append_chain(struct callchain_node *root, struct ip_callchain *chain, |
64 | struct map_symbol *syms, u64 period); | 64 | struct map_symbol *syms, u64 period); |
65 | 65 | ||
66 | bool ip_callchain__valid(struct ip_callchain *chain, event_t *event); | 66 | bool ip_callchain__valid(struct ip_callchain *chain, const event_t *event); |
67 | #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 | |||
17 | char buildid_dir[MAXPATHLEN]; /* root dir for buildid, binary cache */ | ||
18 | |||
14 | static FILE *config_file; | 19 | static FILE *config_file; |
15 | static const char *config_file_name; | 20 | static const char *config_file_name; |
16 | static int config_linenr; | 21 | static 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 | ||
335 | const char *perf_config_dirname(const char *name, const char *value) | ||
336 | { | ||
337 | if (!name) | ||
338 | return NULL; | ||
339 | return value; | ||
340 | } | ||
341 | |||
330 | static int perf_default_core_config(const char *var __used, const char *value __used) | 342 | static 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 | |||
444 | struct buildid_dir_config { | ||
445 | char *dir; | ||
446 | }; | ||
447 | |||
448 | static 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 | |||
465 | static 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 | |||
472 | void 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 | ||
23 | int read_cpu_map(void) | 23 | static 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 | |||
61 | int 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(); | ||
112 | invalid: | ||
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 | ||
4 | extern int read_cpu_map(void); | 4 | extern int read_cpu_map(const char *cpu_list); |
5 | extern int cpumap[]; | 5 | extern 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..318dab15d177 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c | |||
@@ -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/event.c b/tools/perf/util/event.c index 2fbf6a463c81..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,30 +334,29 @@ int event__synthesize_kernel_mmap(event__handler_t process, | |||
340 | return process(&ev, session); | 334 | return process(&ev, session); |
341 | } | 335 | } |
342 | 336 | ||
343 | static void thread__comm_adjust(struct thread *self) | 337 | static 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 | ||
359 | static int thread__set_comm_adjust(struct thread *self, const char *comm) | 351 | static 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 | } |
@@ -374,7 +367,8 @@ int event__process_comm(event_t *self, struct perf_session *session) | |||
374 | 367 | ||
375 | dump_printf(": %s:%d\n", self->comm.comm, self->comm.tid); | 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); |
@@ -552,6 +548,26 @@ int event__process_task(event_t *self, struct perf_session *session) | |||
552 | return 0; | 548 | return 0; |
553 | } | 549 | } |
554 | 550 | ||
551 | int 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 | |||
555 | void thread__find_addr_map(struct thread *self, | 571 | void thread__find_addr_map(struct thread *self, |
556 | struct perf_session *session, u8 cpumode, | 572 | struct perf_session *session, u8 cpumode, |
557 | enum map_type type, pid_t pid, u64 addr, | 573 | enum map_type type, pid_t pid, u64 addr, |
@@ -641,27 +657,49 @@ void thread__find_addr_location(struct thread *self, | |||
641 | al->sym = NULL; | 657 | al->sym = NULL; |
642 | } | 658 | } |
643 | 659 | ||
644 | static void dso__calc_col_width(struct dso *self) | 660 | static void dso__calc_col_width(struct dso *self, struct hists *hists) |
645 | { | 661 | { |
646 | if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && | 662 | if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && |
647 | (!symbol_conf.dso_list || | 663 | (!symbol_conf.dso_list || |
648 | strlist__has_entry(symbol_conf.dso_list, self->name))) { | 664 | strlist__has_entry(symbol_conf.dso_list, self->name))) { |
649 | u16 slen = self->short_name_len; | 665 | u16 slen = dso__name_len(self); |
650 | if (verbose) | 666 | hists__new_col_len(hists, HISTC_DSO, slen); |
651 | slen = self->long_name_len; | ||
652 | if (dsos__col_width < slen) | ||
653 | dsos__col_width = slen; | ||
654 | } | 667 | } |
655 | 668 | ||
656 | self->slen_calculated = 1; | 669 | self->slen_calculated = 1; |
657 | } | 670 | } |
658 | 671 | ||
659 | int event__preprocess_sample(const event_t *self, struct perf_session *session, | 672 | int event__preprocess_sample(const event_t *self, struct perf_session *session, |
660 | struct addr_location *al, symbol_filter_t filter) | 673 | struct addr_location *al, struct sample_data *data, |
674 | symbol_filter_t filter) | ||
661 | { | 675 | { |
662 | u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; | 676 | u8 cpumode = self->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; |
663 | struct thread *thread = perf_session__findnew(session, self->ip.pid); | 677 | struct thread *thread; |
664 | 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); | ||
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); | ||
665 | if (thread == NULL) | 703 | if (thread == NULL) |
666 | return -1; | 704 | return -1; |
667 | 705 | ||
@@ -687,6 +725,7 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, | |||
687 | al->map ? al->map->dso->long_name : | 725 | al->map ? al->map->dso->long_name : |
688 | al->level == 'H' ? "[hypervisor]" : "<not found>"); | 726 | al->level == 'H' ? "[hypervisor]" : "<not found>"); |
689 | al->sym = NULL; | 727 | al->sym = NULL; |
728 | al->cpu = data->cpu; | ||
690 | 729 | ||
691 | if (al->map) { | 730 | if (al->map) { |
692 | if (symbol_conf.dso_list && | 731 | if (symbol_conf.dso_list && |
@@ -703,16 +742,17 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, | |||
703 | * sampled. | 742 | * sampled. |
704 | */ | 743 | */ |
705 | if (!sort_dso.elide && !al->map->dso->slen_calculated) | 744 | if (!sort_dso.elide && !al->map->dso->slen_calculated) |
706 | dso__calc_col_width(al->map->dso); | 745 | dso__calc_col_width(al->map->dso, &session->hists); |
707 | 746 | ||
708 | al->sym = map__find_symbol(al->map, al->addr, filter); | 747 | al->sym = map__find_symbol(al->map, al->addr, filter); |
709 | } else { | 748 | } else { |
710 | const unsigned int unresolved_col_width = BITS_PER_LONG / 4; | 749 | const unsigned int unresolved_col_width = BITS_PER_LONG / 4; |
711 | 750 | ||
712 | if (dsos__col_width < unresolved_col_width && | 751 | if (hists__col_len(&session->hists, HISTC_DSO) < unresolved_col_width && |
713 | !symbol_conf.col_width_list_str && !symbol_conf.field_sep && | 752 | !symbol_conf.col_width_list_str && !symbol_conf.field_sep && |
714 | !symbol_conf.dso_list) | 753 | !symbol_conf.dso_list) |
715 | dsos__col_width = unresolved_col_width; | 754 | hists__set_col_len(&session->hists, HISTC_DSO, |
755 | unresolved_col_width); | ||
716 | } | 756 | } |
717 | 757 | ||
718 | if (symbol_conf.sym_list && al->sym && | 758 | if (symbol_conf.sym_list && al->sym && |
@@ -726,9 +766,9 @@ out_filtered: | |||
726 | return 0; | 766 | return 0; |
727 | } | 767 | } |
728 | 768 | ||
729 | int event__parse_sample(event_t *event, u64 type, struct sample_data *data) | 769 | int event__parse_sample(const event_t *event, u64 type, struct sample_data *data) |
730 | { | 770 | { |
731 | u64 *array = event->sample.array; | 771 | const u64 *array = event->sample.array; |
732 | 772 | ||
733 | if (type & PERF_SAMPLE_IP) { | 773 | if (type & PERF_SAMPLE_IP) { |
734 | data->ip = event->ip.ip; | 774 | data->ip = event->ip.ip; |
@@ -767,7 +807,8 @@ int event__parse_sample(event_t *event, u64 type, struct sample_data *data) | |||
767 | u32 *p = (u32 *)array; | 807 | u32 *p = (u32 *)array; |
768 | data->cpu = *p; | 808 | data->cpu = *p; |
769 | array++; | 809 | array++; |
770 | } | 810 | } else |
811 | data->cpu = -1; | ||
771 | 812 | ||
772 | if (type & PERF_SAMPLE_PERIOD) { | 813 | if (type & PERF_SAMPLE_PERIOD) { |
773 | 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); | |||
154 | int event__process_lost(event_t *self, struct perf_session *session); | 154 | int event__process_lost(event_t *self, struct perf_session *session); |
155 | int event__process_mmap(event_t *self, struct perf_session *session); | 155 | int event__process_mmap(event_t *self, struct perf_session *session); |
156 | int event__process_task(event_t *self, struct perf_session *session); | 156 | int event__process_task(event_t *self, struct perf_session *session); |
157 | int event__process(event_t *event, struct perf_session *session); | ||
157 | 158 | ||
158 | struct addr_location; | 159 | struct addr_location; |
159 | int event__preprocess_sample(const event_t *self, struct perf_session *session, | 160 | int 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, |
161 | int event__parse_sample(event_t *event, u64 type, struct sample_data *data); | 162 | symbol_filter_t filter); |
163 | int event__parse_sample(const event_t *event, u64 type, struct sample_data *data); | ||
162 | 164 | ||
163 | extern const char *event__name[]; | 165 | extern 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 | ||
19 | static 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 | |||
1196 | void 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 784ee0bdda77..e7263d49bcf0 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 | ||
8 | enum hist_filter { | ||
9 | HIST_FILTER__DSO, | ||
10 | HIST_FILTER__THREAD, | ||
11 | HIST_FILTER__PARENT, | ||
12 | }; | ||
13 | |||
8 | struct callchain_param callchain_param = { | 14 | struct 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 | ||
19 | u16 hists__col_len(struct hists *self, enum hist_column col) | ||
20 | { | ||
21 | return self->col_len[col]; | ||
22 | } | ||
23 | |||
24 | void hists__set_col_len(struct hists *self, enum hist_column col, u16 len) | ||
25 | { | ||
26 | self->col_len[col] = len; | ||
27 | } | ||
28 | |||
29 | bool 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 | |||
38 | static 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 | |||
46 | static 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 | |||
13 | static void hist_entry__add_cpumode_period(struct hist_entry *self, | 63 | static 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 | ||
53 | static void hists__inc_nr_entries(struct hists *self, struct hist_entry *entry) | 105 | static 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 | |||
113 | static 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 | ||
60 | struct hist_entry *__hists__add_entry(struct hists *self, | 120 | struct 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 | ||
517 | int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, | 579 | int 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,29 +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 | ||
629 | int hist_entry__fprintf(struct hist_entry *self, struct hists *pair_hists, | 692 | int 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 | int ret; | 697 | hist_entry__snprintf(self, bf, sizeof(bf), hists, pair_hists, |
635 | 698 | show_displacement, displacement, | |
636 | ret = hist_entry__snprintf(self, bf, sizeof(bf), pair_hists, | 699 | true, session_total); |
637 | show_displacement, displacement, | ||
638 | true, session_total); | ||
639 | if (!ret) | ||
640 | return 0; | ||
641 | |||
642 | return fprintf(fp, "%s\n", bf); | 700 | return fprintf(fp, "%s\n", bf); |
643 | } | 701 | } |
644 | 702 | ||
645 | static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp, | 703 | static size_t hist_entry__fprintf_callchain(struct hist_entry *self, |
704 | struct hists *hists, FILE *fp, | ||
646 | u64 session_total) | 705 | u64 session_total) |
647 | { | 706 | { |
648 | int left_margin = 0; | 707 | int left_margin = 0; |
@@ -650,7 +709,7 @@ static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp, | |||
650 | if (sort__first_dimension == SORT_COMM) { | 709 | if (sort__first_dimension == SORT_COMM) { |
651 | struct sort_entry *se = list_first_entry(&hist_entry__sort_list, | 710 | struct sort_entry *se = list_first_entry(&hist_entry__sort_list, |
652 | typeof(*se), list); | 711 | typeof(*se), list); |
653 | left_margin = se->se_width ? *se->se_width : 0; | 712 | left_margin = hists__col_len(hists, se->se_width_idx); |
654 | left_margin -= thread__comm_len(self->thread); | 713 | left_margin -= thread__comm_len(self->thread); |
655 | } | 714 | } |
656 | 715 | ||
@@ -721,17 +780,17 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, | |||
721 | continue; | 780 | continue; |
722 | } | 781 | } |
723 | width = strlen(se->se_header); | 782 | width = strlen(se->se_header); |
724 | if (se->se_width) { | 783 | if (symbol_conf.col_width_list_str) { |
725 | if (symbol_conf.col_width_list_str) { | 784 | if (col_width) { |
726 | if (col_width) { | 785 | hists__set_col_len(self, se->se_width_idx, |
727 | *se->se_width = atoi(col_width); | 786 | atoi(col_width)); |
728 | col_width = strchr(col_width, ','); | 787 | col_width = strchr(col_width, ','); |
729 | if (col_width) | 788 | if (col_width) |
730 | ++col_width; | 789 | ++col_width; |
731 | } | ||
732 | } | 790 | } |
733 | width = *se->se_width = max(*se->se_width, width); | ||
734 | } | 791 | } |
792 | if (!hists__new_col_len(self, se->se_width_idx, width)) | ||
793 | width = hists__col_len(self, se->se_width_idx); | ||
735 | fprintf(fp, " %*s", width, se->se_header); | 794 | fprintf(fp, " %*s", width, se->se_header); |
736 | } | 795 | } |
737 | fprintf(fp, "\n"); | 796 | fprintf(fp, "\n"); |
@@ -754,9 +813,8 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, | |||
754 | continue; | 813 | continue; |
755 | 814 | ||
756 | fprintf(fp, " "); | 815 | fprintf(fp, " "); |
757 | if (se->se_width) | 816 | width = hists__col_len(self, se->se_width_idx); |
758 | width = *se->se_width; | 817 | if (width == 0) |
759 | else | ||
760 | width = strlen(se->se_header); | 818 | width = strlen(se->se_header); |
761 | for (i = 0; i < width; i++) | 819 | for (i = 0; i < width; i++) |
762 | fprintf(fp, "."); | 820 | fprintf(fp, "."); |
@@ -767,7 +825,6 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, | |||
767 | print_entries: | 825 | print_entries: |
768 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | 826 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { |
769 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 827 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
770 | int cnt; | ||
771 | 828 | ||
772 | if (show_displacement) { | 829 | if (show_displacement) { |
773 | if (h->pair != NULL) | 830 | if (h->pair != NULL) |
@@ -777,17 +834,12 @@ print_entries: | |||
777 | displacement = 0; | 834 | displacement = 0; |
778 | ++position; | 835 | ++position; |
779 | } | 836 | } |
780 | cnt = hist_entry__fprintf(h, pair, show_displacement, | 837 | ret += hist_entry__fprintf(h, self, pair, show_displacement, |
781 | displacement, fp, self->stats.total_period); | 838 | displacement, fp, self->stats.total_period); |
782 | /* Ignore those that didn't match the parent filter */ | ||
783 | if (!cnt) | ||
784 | continue; | ||
785 | |||
786 | ret += cnt; | ||
787 | 839 | ||
788 | if (symbol_conf.use_callchain) | 840 | if (symbol_conf.use_callchain) |
789 | ret += hist_entry__fprintf_callchain(h, fp, self->stats.total_period); | 841 | ret += hist_entry__fprintf_callchain(h, self, fp, |
790 | 842 | self->stats.total_period); | |
791 | if (h->ms.map == NULL && verbose > 1) { | 843 | if (h->ms.map == NULL && verbose > 1) { |
792 | __map_groups__fprintf_maps(&h->thread->mg, | 844 | __map_groups__fprintf_maps(&h->thread->mg, |
793 | MAP__FUNCTION, verbose, fp); | 845 | MAP__FUNCTION, verbose, fp); |
@@ -800,10 +852,49 @@ print_entries: | |||
800 | return ret; | 852 | return ret; |
801 | } | 853 | } |
802 | 854 | ||
803 | enum hist_filter { | 855 | /* |
804 | HIST_FILTER__DSO, | 856 | * See hists__fprintf to match the column widths |
805 | HIST_FILTER__THREAD, | 857 | */ |
806 | }; | 858 | unsigned 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 | return ret; | ||
880 | } | ||
881 | |||
882 | static void hists__remove_entry_filter(struct hists *self, struct hist_entry *h, | ||
883 | enum hist_filter filter) | ||
884 | { | ||
885 | h->filtered &= ~(1 << filter); | ||
886 | if (h->filtered) | ||
887 | return; | ||
888 | |||
889 | ++self->nr_entries; | ||
890 | if (h->ms.unfolded) | ||
891 | self->nr_entries += h->nr_rows; | ||
892 | h->row_offset = 0; | ||
893 | self->stats.total_period += h->period; | ||
894 | self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; | ||
895 | |||
896 | hists__calc_col_len(self, h); | ||
897 | } | ||
807 | 898 | ||
808 | void hists__filter_by_dso(struct hists *self, const struct dso *dso) | 899 | void hists__filter_by_dso(struct hists *self, const struct dso *dso) |
809 | { | 900 | { |
@@ -811,7 +902,7 @@ void hists__filter_by_dso(struct hists *self, const struct dso *dso) | |||
811 | 902 | ||
812 | self->nr_entries = self->stats.total_period = 0; | 903 | self->nr_entries = self->stats.total_period = 0; |
813 | self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; | 904 | self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; |
814 | self->max_sym_namelen = 0; | 905 | hists__reset_col_len(self); |
815 | 906 | ||
816 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | 907 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { |
817 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 908 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
@@ -824,15 +915,7 @@ void hists__filter_by_dso(struct hists *self, const struct dso *dso) | |||
824 | continue; | 915 | continue; |
825 | } | 916 | } |
826 | 917 | ||
827 | h->filtered &= ~(1 << HIST_FILTER__DSO); | 918 | hists__remove_entry_filter(self, h, HIST_FILTER__DSO); |
828 | if (!h->filtered) { | ||
829 | ++self->nr_entries; | ||
830 | self->stats.total_period += h->period; | ||
831 | self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; | ||
832 | if (h->ms.sym && | ||
833 | self->max_sym_namelen < h->ms.sym->namelen) | ||
834 | self->max_sym_namelen = h->ms.sym->namelen; | ||
835 | } | ||
836 | } | 919 | } |
837 | } | 920 | } |
838 | 921 | ||
@@ -842,7 +925,7 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread) | |||
842 | 925 | ||
843 | self->nr_entries = self->stats.total_period = 0; | 926 | self->nr_entries = self->stats.total_period = 0; |
844 | self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; | 927 | self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; |
845 | self->max_sym_namelen = 0; | 928 | hists__reset_col_len(self); |
846 | 929 | ||
847 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | 930 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { |
848 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 931 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
@@ -851,15 +934,8 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread) | |||
851 | h->filtered |= (1 << HIST_FILTER__THREAD); | 934 | h->filtered |= (1 << HIST_FILTER__THREAD); |
852 | continue; | 935 | continue; |
853 | } | 936 | } |
854 | h->filtered &= ~(1 << HIST_FILTER__THREAD); | 937 | |
855 | if (!h->filtered) { | 938 | hists__remove_entry_filter(self, h, HIST_FILTER__THREAD); |
856 | ++self->nr_entries; | ||
857 | self->stats.total_period += h->period; | ||
858 | self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; | ||
859 | if (h->ms.sym && | ||
860 | self->max_sym_namelen < h->ms.sym->namelen) | ||
861 | self->max_sym_namelen = h->ms.sym->namelen; | ||
862 | } | ||
863 | } | 939 | } |
864 | } | 940 | } |
865 | 941 | ||
@@ -1052,7 +1128,7 @@ fallback: | |||
1052 | dso, dso->long_name, sym, sym->name); | 1128 | dso, dso->long_name, sym, sym->name); |
1053 | 1129 | ||
1054 | snprintf(command, sizeof(command), | 1130 | snprintf(command, sizeof(command), |
1055 | "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS %s|grep -v %s|expand", | 1131 | "objdump --start-address=0x%016Lx --stop-address=0x%016Lx -dS -C %s|grep -v %s|expand", |
1056 | map__rip_2objdump(map, sym->start), | 1132 | map__rip_2objdump(map, sym->start), |
1057 | map__rip_2objdump(map, sym->end), | 1133 | map__rip_2objdump(map, sym->end), |
1058 | filename, filename); | 1134 | filename, filename); |
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 83fa33a7b38b..65a48db46a29 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 | ||
59 | enum 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 | |||
59 | struct hists { | 69 | struct 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 | ||
70 | struct hist_entry *__hists__add_entry(struct hists *self, | 80 | struct 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); |
73 | extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); | 83 | extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); |
74 | extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); | 84 | extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); |
75 | int hist_entry__fprintf(struct hist_entry *self, struct hists *pair_hists, | 85 | int 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); |
78 | int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size, | 88 | int 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); | ||
81 | void hist_entry__free(struct hist_entry *); | 92 | void hist_entry__free(struct hist_entry *); |
82 | 93 | ||
83 | void hists__output_resort(struct hists *self); | 94 | void hists__output_resort(struct hists *self); |
@@ -95,6 +106,10 @@ int hist_entry__annotate(struct hist_entry *self, struct list_head *head); | |||
95 | void hists__filter_by_dso(struct hists *self, const struct dso *dso); | 106 | void hists__filter_by_dso(struct hists *self, const struct dso *dso); |
96 | void hists__filter_by_thread(struct hists *self, const struct thread *thread); | 107 | void hists__filter_by_thread(struct hists *self, const struct thread *thread); |
97 | 108 | ||
109 | u16 hists__col_len(struct hists *self, enum hist_column col); | ||
110 | void hists__set_col_len(struct hists *self, enum hist_column col, u16 len); | ||
111 | bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len); | ||
112 | |||
98 | #ifdef NO_NEWT_SUPPORT | 113 | #ifdef NO_NEWT_SUPPORT |
99 | static inline int hists__browse(struct hists *self __used, | 114 | static inline int hists__browse(struct hists *self __used, |
100 | const char *helpline __used, | 115 | const char *helpline __used, |
@@ -126,4 +141,7 @@ int hist_entry__tui_annotate(struct hist_entry *self); | |||
126 | 141 | ||
127 | int hists__tui_browse_tree(struct rb_root *self, const char *help); | 142 | int hists__tui_browse_tree(struct rb_root *self, const char *help); |
128 | #endif | 143 | #endif |
144 | |||
145 | unsigned int hists__sort_list_width(struct hists *self); | ||
146 | |||
129 | #endif /* __PERF_HIST_H */ | 147 | #endif /* __PERF_HIST_H */ |
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 | ||
20 | static 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 | |||
30 | void map__init(struct map *self, enum map_type type, | 20 | void 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 | ||
44 | struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, | 35 | struct 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 | ||
232 | static 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 | |||
245 | static 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 | |||
255 | void 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 | |||
251 | void map_groups__flush(struct map_groups *self) | 265 | void 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 | } |
438 | move_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 | ||
520 | void maps__remove(struct rb_root *self, struct map *map) | ||
521 | { | ||
522 | rb_erase(&map->rb_node, self); | ||
523 | } | ||
524 | |||
496 | struct map *maps__find(struct rb_root *maps, u64 ip) | 525 | struct 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 | ||
558 | static 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 | |||
568 | void 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 | |||
577 | void machine__delete(struct machine *self) | ||
578 | { | ||
579 | machine__exit(self); | ||
580 | free(self); | ||
581 | } | ||
582 | |||
529 | struct machine *machines__add(struct rb_root *self, pid_t pid, | 583 | struct 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); |
107 | struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, | 108 | struct 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); |
110 | void map__delete(struct map *self); | 111 | void map__delete(struct map *self); |
111 | struct map *map__clone(struct map *self); | 112 | struct map *map__clone(struct map *self); |
112 | int map__overlap(struct map *l, struct map *r); | 113 | int map__overlap(struct map *l, struct map *r); |
@@ -125,8 +126,10 @@ void map__reloc_vmlinux(struct map *self); | |||
125 | size_t __map_groups__fprintf_maps(struct map_groups *self, | 126 | size_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); |
127 | void maps__insert(struct rb_root *maps, struct map *map); | 128 | void maps__insert(struct rb_root *maps, struct map *map); |
129 | void maps__remove(struct rb_root *self, struct map *map); | ||
128 | struct map *maps__find(struct rb_root *maps, u64 addr); | 130 | struct map *maps__find(struct rb_root *maps, u64 addr); |
129 | void map_groups__init(struct map_groups *self); | 131 | void map_groups__init(struct map_groups *self); |
132 | void map_groups__exit(struct map_groups *self); | ||
130 | int map_groups__clone(struct map_groups *self, | 133 | int 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); |
132 | size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp); | 135 | size_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); | |||
142 | struct machine *machines__findnew(struct rb_root *self, pid_t pid); | 145 | struct machine *machines__findnew(struct rb_root *self, pid_t pid); |
143 | char *machine__mmap_name(struct machine *self, char *bf, size_t size); | 146 | char *machine__mmap_name(struct machine *self, char *bf, size_t size); |
144 | int machine__init(struct machine *self, const char *root_dir, pid_t pid); | 147 | int machine__init(struct machine *self, const char *root_dir, pid_t pid); |
148 | void machine__exit(struct machine *self); | ||
149 | void 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 | ||
171 | static inline void map_groups__remove(struct map_groups *self, struct map *map) | ||
172 | { | ||
173 | maps__remove(&self->maps[map->type], map); | ||
174 | } | ||
175 | |||
166 | static inline struct map *map_groups__find(struct map_groups *self, | 176 | static 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 index 7537ca15900b..91de99b58445 100644 --- a/tools/perf/util/newt.c +++ b/tools/perf/util/newt.c | |||
@@ -11,6 +11,7 @@ | |||
11 | #define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG | 11 | #define HAVE_LONG_LONG __GLIBC_HAVE_LONG_LONG |
12 | #endif | 12 | #endif |
13 | #include <slang.h> | 13 | #include <slang.h> |
14 | #include <signal.h> | ||
14 | #include <stdlib.h> | 15 | #include <stdlib.h> |
15 | #include <newt.h> | 16 | #include <newt.h> |
16 | #include <sys/ttydefaults.h> | 17 | #include <sys/ttydefaults.h> |
@@ -278,9 +279,48 @@ struct ui_browser { | |||
278 | void *first_visible_entry, *entries; | 279 | void *first_visible_entry, *entries; |
279 | u16 top, left, width, height; | 280 | u16 top, left, width, height; |
280 | void *priv; | 281 | void *priv; |
282 | unsigned int (*refresh_entries)(struct ui_browser *self); | ||
283 | void (*seek)(struct ui_browser *self, | ||
284 | off_t offset, int whence); | ||
281 | u32 nr_entries; | 285 | u32 nr_entries; |
282 | }; | 286 | }; |
283 | 287 | ||
288 | static void ui_browser__list_head_seek(struct ui_browser *self, | ||
289 | off_t offset, int whence) | ||
290 | { | ||
291 | struct list_head *head = self->entries; | ||
292 | struct list_head *pos; | ||
293 | |||
294 | switch (whence) { | ||
295 | case SEEK_SET: | ||
296 | pos = head->next; | ||
297 | break; | ||
298 | case SEEK_CUR: | ||
299 | pos = self->first_visible_entry; | ||
300 | break; | ||
301 | case SEEK_END: | ||
302 | pos = head->prev; | ||
303 | break; | ||
304 | default: | ||
305 | return; | ||
306 | } | ||
307 | |||
308 | if (offset > 0) { | ||
309 | while (offset-- != 0) | ||
310 | pos = pos->next; | ||
311 | } else { | ||
312 | while (offset++ != 0) | ||
313 | pos = pos->prev; | ||
314 | } | ||
315 | |||
316 | self->first_visible_entry = pos; | ||
317 | } | ||
318 | |||
319 | static bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row) | ||
320 | { | ||
321 | return (self->first_visible_entry_idx + row) == self->index; | ||
322 | } | ||
323 | |||
284 | static void ui_browser__refresh_dimensions(struct ui_browser *self) | 324 | static void ui_browser__refresh_dimensions(struct ui_browser *self) |
285 | { | 325 | { |
286 | int cols, rows; | 326 | int cols, rows; |
@@ -297,8 +337,36 @@ static void ui_browser__refresh_dimensions(struct ui_browser *self) | |||
297 | 337 | ||
298 | static void ui_browser__reset_index(struct ui_browser *self) | 338 | static void ui_browser__reset_index(struct ui_browser *self) |
299 | { | 339 | { |
300 | self->index = self->first_visible_entry_idx = 0; | 340 | self->index = self->first_visible_entry_idx = 0; |
301 | self->first_visible_entry = NULL; | 341 | self->seek(self, 0, SEEK_SET); |
342 | } | ||
343 | |||
344 | static int ui_browser__show(struct ui_browser *self, const char *title) | ||
345 | { | ||
346 | if (self->form != NULL) { | ||
347 | newtFormDestroy(self->form); | ||
348 | newtPopWindow(); | ||
349 | } | ||
350 | ui_browser__refresh_dimensions(self); | ||
351 | newtCenteredWindow(self->width, self->height, title); | ||
352 | self->form = newt_form__new(); | ||
353 | if (self->form == NULL) | ||
354 | return -1; | ||
355 | |||
356 | self->sb = newtVerticalScrollbar(self->width, 0, self->height, | ||
357 | HE_COLORSET_NORMAL, | ||
358 | HE_COLORSET_SELECTED); | ||
359 | if (self->sb == NULL) | ||
360 | return -1; | ||
361 | |||
362 | newtFormAddHotKey(self->form, NEWT_KEY_UP); | ||
363 | newtFormAddHotKey(self->form, NEWT_KEY_DOWN); | ||
364 | newtFormAddHotKey(self->form, NEWT_KEY_PGUP); | ||
365 | newtFormAddHotKey(self->form, NEWT_KEY_PGDN); | ||
366 | newtFormAddHotKey(self->form, NEWT_KEY_HOME); | ||
367 | newtFormAddHotKey(self->form, NEWT_KEY_END); | ||
368 | newtFormAddComponent(self->form, self->sb); | ||
369 | return 0; | ||
302 | } | 370 | } |
303 | 371 | ||
304 | static int objdump_line__show(struct objdump_line *self, struct list_head *head, | 372 | static int objdump_line__show(struct objdump_line *self, struct list_head *head, |
@@ -352,26 +420,10 @@ static int objdump_line__show(struct objdump_line *self, struct list_head *head, | |||
352 | 420 | ||
353 | static int ui_browser__refresh_entries(struct ui_browser *self) | 421 | static int ui_browser__refresh_entries(struct ui_browser *self) |
354 | { | 422 | { |
355 | struct objdump_line *pos; | 423 | int row; |
356 | struct list_head *head = self->entries; | ||
357 | struct hist_entry *he = self->priv; | ||
358 | int row = 0; | ||
359 | int len = he->ms.sym->end - he->ms.sym->start; | ||
360 | |||
361 | if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries) | ||
362 | self->first_visible_entry = head->next; | ||
363 | |||
364 | pos = list_entry(self->first_visible_entry, struct objdump_line, node); | ||
365 | |||
366 | list_for_each_entry_from(pos, head, node) { | ||
367 | bool current_entry = (self->first_visible_entry_idx + row) == self->index; | ||
368 | SLsmg_gotorc(self->top + row, self->left); | ||
369 | objdump_line__show(pos, head, self->width, | ||
370 | he, len, current_entry); | ||
371 | if (++row == self->height) | ||
372 | break; | ||
373 | } | ||
374 | 424 | ||
425 | newtScrollbarSet(self->sb, self->index, self->nr_entries - 1); | ||
426 | row = self->refresh_entries(self); | ||
375 | SLsmg_set_color(HE_COLORSET_NORMAL); | 427 | SLsmg_set_color(HE_COLORSET_NORMAL); |
376 | SLsmg_fill_region(self->top + row, self->left, | 428 | SLsmg_fill_region(self->top + row, self->left, |
377 | self->height - row, self->width, ' '); | 429 | self->height - row, self->width, ' '); |
@@ -379,42 +431,13 @@ static int ui_browser__refresh_entries(struct ui_browser *self) | |||
379 | return 0; | 431 | return 0; |
380 | } | 432 | } |
381 | 433 | ||
382 | static int ui_browser__run(struct ui_browser *self, const char *title, | 434 | static int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es) |
383 | struct newtExitStruct *es) | ||
384 | { | 435 | { |
385 | if (self->form) { | ||
386 | newtFormDestroy(self->form); | ||
387 | newtPopWindow(); | ||
388 | } | ||
389 | |||
390 | ui_browser__refresh_dimensions(self); | ||
391 | newtCenteredWindow(self->width + 2, self->height, title); | ||
392 | self->form = newt_form__new(); | ||
393 | if (self->form == NULL) | ||
394 | return -1; | ||
395 | |||
396 | self->sb = newtVerticalScrollbar(self->width + 1, 0, self->height, | ||
397 | HE_COLORSET_NORMAL, | ||
398 | HE_COLORSET_SELECTED); | ||
399 | if (self->sb == NULL) | ||
400 | return -1; | ||
401 | |||
402 | newtFormAddHotKey(self->form, NEWT_KEY_UP); | ||
403 | newtFormAddHotKey(self->form, NEWT_KEY_DOWN); | ||
404 | newtFormAddHotKey(self->form, NEWT_KEY_PGUP); | ||
405 | newtFormAddHotKey(self->form, NEWT_KEY_PGDN); | ||
406 | newtFormAddHotKey(self->form, ' '); | ||
407 | newtFormAddHotKey(self->form, NEWT_KEY_HOME); | ||
408 | newtFormAddHotKey(self->form, NEWT_KEY_END); | ||
409 | newtFormAddHotKey(self->form, NEWT_KEY_TAB); | ||
410 | newtFormAddHotKey(self->form, NEWT_KEY_RIGHT); | ||
411 | |||
412 | if (ui_browser__refresh_entries(self) < 0) | 436 | if (ui_browser__refresh_entries(self) < 0) |
413 | return -1; | 437 | return -1; |
414 | newtFormAddComponent(self->form, self->sb); | ||
415 | 438 | ||
416 | while (1) { | 439 | while (1) { |
417 | unsigned int offset; | 440 | off_t offset; |
418 | 441 | ||
419 | newtFormRun(self->form, es); | 442 | newtFormRun(self->form, es); |
420 | 443 | ||
@@ -428,9 +451,8 @@ static int ui_browser__run(struct ui_browser *self, const char *title, | |||
428 | break; | 451 | break; |
429 | ++self->index; | 452 | ++self->index; |
430 | if (self->index == self->first_visible_entry_idx + self->height) { | 453 | if (self->index == self->first_visible_entry_idx + self->height) { |
431 | struct list_head *pos = self->first_visible_entry; | ||
432 | ++self->first_visible_entry_idx; | 454 | ++self->first_visible_entry_idx; |
433 | self->first_visible_entry = pos->next; | 455 | self->seek(self, +1, SEEK_CUR); |
434 | } | 456 | } |
435 | break; | 457 | break; |
436 | case NEWT_KEY_UP: | 458 | case NEWT_KEY_UP: |
@@ -438,9 +460,8 @@ static int ui_browser__run(struct ui_browser *self, const char *title, | |||
438 | break; | 460 | break; |
439 | --self->index; | 461 | --self->index; |
440 | if (self->index < self->first_visible_entry_idx) { | 462 | if (self->index < self->first_visible_entry_idx) { |
441 | struct list_head *pos = self->first_visible_entry; | ||
442 | --self->first_visible_entry_idx; | 463 | --self->first_visible_entry_idx; |
443 | self->first_visible_entry = pos->prev; | 464 | self->seek(self, -1, SEEK_CUR); |
444 | } | 465 | } |
445 | break; | 466 | break; |
446 | case NEWT_KEY_PGDN: | 467 | case NEWT_KEY_PGDN: |
@@ -453,12 +474,7 @@ static int ui_browser__run(struct ui_browser *self, const char *title, | |||
453 | offset = self->nr_entries - 1 - self->index; | 474 | offset = self->nr_entries - 1 - self->index; |
454 | self->index += offset; | 475 | self->index += offset; |
455 | self->first_visible_entry_idx += offset; | 476 | self->first_visible_entry_idx += offset; |
456 | 477 | self->seek(self, +offset, SEEK_CUR); | |
457 | while (offset--) { | ||
458 | struct list_head *pos = self->first_visible_entry; | ||
459 | self->first_visible_entry = pos->next; | ||
460 | } | ||
461 | |||
462 | break; | 478 | break; |
463 | case NEWT_KEY_PGUP: | 479 | case NEWT_KEY_PGUP: |
464 | if (self->first_visible_entry_idx == 0) | 480 | if (self->first_visible_entry_idx == 0) |
@@ -471,36 +487,22 @@ static int ui_browser__run(struct ui_browser *self, const char *title, | |||
471 | 487 | ||
472 | self->index -= offset; | 488 | self->index -= offset; |
473 | self->first_visible_entry_idx -= offset; | 489 | self->first_visible_entry_idx -= offset; |
474 | 490 | self->seek(self, -offset, SEEK_CUR); | |
475 | while (offset--) { | ||
476 | struct list_head *pos = self->first_visible_entry; | ||
477 | self->first_visible_entry = pos->prev; | ||
478 | } | ||
479 | break; | 491 | break; |
480 | case NEWT_KEY_HOME: | 492 | case NEWT_KEY_HOME: |
481 | ui_browser__reset_index(self); | 493 | ui_browser__reset_index(self); |
482 | break; | 494 | break; |
483 | case NEWT_KEY_END: { | 495 | case NEWT_KEY_END: |
484 | struct list_head *head = self->entries; | ||
485 | offset = self->height - 1; | 496 | offset = self->height - 1; |
497 | if (offset >= self->nr_entries) | ||
498 | offset = self->nr_entries - 1; | ||
486 | 499 | ||
487 | if (offset > self->nr_entries) | 500 | self->index = self->nr_entries - 1; |
488 | offset = self->nr_entries; | 501 | self->first_visible_entry_idx = self->index - offset; |
489 | 502 | self->seek(self, -offset, SEEK_END); | |
490 | self->index = self->first_visible_entry_idx = self->nr_entries - 1 - offset; | ||
491 | self->first_visible_entry = head->prev; | ||
492 | while (offset-- != 0) { | ||
493 | struct list_head *pos = self->first_visible_entry; | ||
494 | self->first_visible_entry = pos->prev; | ||
495 | } | ||
496 | } | ||
497 | break; | 503 | break; |
498 | case NEWT_KEY_RIGHT: | ||
499 | case NEWT_KEY_LEFT: | ||
500 | case NEWT_KEY_TAB: | ||
501 | return es->u.key; | ||
502 | default: | 504 | default: |
503 | continue; | 505 | return es->u.key; |
504 | } | 506 | } |
505 | if (ui_browser__refresh_entries(self) < 0) | 507 | if (ui_browser__refresh_entries(self) < 0) |
506 | return -1; | 508 | return -1; |
@@ -508,38 +510,6 @@ static int ui_browser__run(struct ui_browser *self, const char *title, | |||
508 | return 0; | 510 | return 0; |
509 | } | 511 | } |
510 | 512 | ||
511 | /* | ||
512 | * When debugging newt problems it was useful to be able to "unroll" | ||
513 | * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate | ||
514 | * a source file with the sequence of calls to these methods, to then | ||
515 | * tweak the arrays to get the intended results, so I'm keeping this code | ||
516 | * here, may be useful again in the future. | ||
517 | */ | ||
518 | #undef NEWT_DEBUG | ||
519 | |||
520 | static void newt_checkbox_tree__add(newtComponent tree, const char *str, | ||
521 | void *priv, int *indexes) | ||
522 | { | ||
523 | #ifdef NEWT_DEBUG | ||
524 | /* Print the newtCheckboxTreeAddArray to tinker with its index arrays */ | ||
525 | int i = 0, len = 40 - strlen(str); | ||
526 | |||
527 | fprintf(stderr, | ||
528 | "\tnewtCheckboxTreeAddItem(tree, %*.*s\"%s\", (void *)%p, 0, ", | ||
529 | len, len, " ", str, priv); | ||
530 | while (indexes[i] != NEWT_ARG_LAST) { | ||
531 | if (indexes[i] != NEWT_ARG_APPEND) | ||
532 | fprintf(stderr, " %d,", indexes[i]); | ||
533 | else | ||
534 | fprintf(stderr, " %s,", "NEWT_ARG_APPEND"); | ||
535 | ++i; | ||
536 | } | ||
537 | fprintf(stderr, " %s", " NEWT_ARG_LAST);\n"); | ||
538 | fflush(stderr); | ||
539 | #endif | ||
540 | newtCheckboxTreeAddArray(tree, str, priv, 0, indexes); | ||
541 | } | ||
542 | |||
543 | static char *callchain_list__sym_name(struct callchain_list *self, | 513 | static char *callchain_list__sym_name(struct callchain_list *self, |
544 | char *bf, size_t bfsize) | 514 | char *bf, size_t bfsize) |
545 | { | 515 | { |
@@ -550,144 +520,29 @@ static char *callchain_list__sym_name(struct callchain_list *self, | |||
550 | return bf; | 520 | return bf; |
551 | } | 521 | } |
552 | 522 | ||
553 | static void __callchain__append_graph_browser(struct callchain_node *self, | 523 | static unsigned int hist_entry__annotate_browser_refresh(struct ui_browser *self) |
554 | newtComponent tree, u64 total, | ||
555 | int *indexes, int depth) | ||
556 | { | 524 | { |
557 | struct rb_node *node; | 525 | struct objdump_line *pos; |
558 | u64 new_total, remaining; | 526 | struct list_head *head = self->entries; |
559 | int idx = 0; | 527 | struct hist_entry *he = self->priv; |
560 | 528 | int row = 0; | |
561 | if (callchain_param.mode == CHAIN_GRAPH_REL) | 529 | int len = he->ms.sym->end - he->ms.sym->start; |
562 | new_total = self->children_hit; | ||
563 | else | ||
564 | new_total = total; | ||
565 | |||
566 | remaining = new_total; | ||
567 | node = rb_first(&self->rb_root); | ||
568 | while (node) { | ||
569 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); | ||
570 | struct rb_node *next = rb_next(node); | ||
571 | u64 cumul = cumul_hits(child); | ||
572 | struct callchain_list *chain; | ||
573 | int first = true, printed = 0; | ||
574 | int chain_idx = -1; | ||
575 | remaining -= cumul; | ||
576 | |||
577 | indexes[depth] = NEWT_ARG_APPEND; | ||
578 | indexes[depth + 1] = NEWT_ARG_LAST; | ||
579 | |||
580 | list_for_each_entry(chain, &child->val, list) { | ||
581 | char ipstr[BITS_PER_LONG / 4 + 1], | ||
582 | *alloc_str = NULL; | ||
583 | const char *str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); | ||
584 | |||
585 | if (first) { | ||
586 | double percent = cumul * 100.0 / new_total; | ||
587 | |||
588 | first = false; | ||
589 | if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) | ||
590 | str = "Not enough memory!"; | ||
591 | else | ||
592 | str = alloc_str; | ||
593 | } else { | ||
594 | indexes[depth] = idx; | ||
595 | indexes[depth + 1] = NEWT_ARG_APPEND; | ||
596 | indexes[depth + 2] = NEWT_ARG_LAST; | ||
597 | ++chain_idx; | ||
598 | } | ||
599 | newt_checkbox_tree__add(tree, str, &chain->ms, indexes); | ||
600 | free(alloc_str); | ||
601 | ++printed; | ||
602 | } | ||
603 | |||
604 | indexes[depth] = idx; | ||
605 | if (chain_idx != -1) | ||
606 | indexes[depth + 1] = chain_idx; | ||
607 | if (printed != 0) | ||
608 | ++idx; | ||
609 | __callchain__append_graph_browser(child, tree, new_total, indexes, | ||
610 | depth + (chain_idx != -1 ? 2 : 1)); | ||
611 | node = next; | ||
612 | } | ||
613 | } | ||
614 | |||
615 | static void callchain__append_graph_browser(struct callchain_node *self, | ||
616 | newtComponent tree, u64 total, | ||
617 | int *indexes, int parent_idx) | ||
618 | { | ||
619 | struct callchain_list *chain; | ||
620 | int i = 0; | ||
621 | |||
622 | indexes[1] = NEWT_ARG_APPEND; | ||
623 | indexes[2] = NEWT_ARG_LAST; | ||
624 | |||
625 | list_for_each_entry(chain, &self->val, list) { | ||
626 | char ipstr[BITS_PER_LONG / 4 + 1], *str; | ||
627 | |||
628 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
629 | continue; | ||
630 | |||
631 | if (!i++ && sort__first_dimension == SORT_SYM) | ||
632 | continue; | ||
633 | 530 | ||
634 | str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); | 531 | if (self->first_visible_entry == NULL || self->first_visible_entry == self->entries) |
635 | newt_checkbox_tree__add(tree, str, &chain->ms, indexes); | 532 | self->first_visible_entry = head->next; |
636 | } | ||
637 | 533 | ||
638 | indexes[1] = parent_idx; | 534 | pos = list_entry(self->first_visible_entry, struct objdump_line, node); |
639 | indexes[2] = NEWT_ARG_APPEND; | ||
640 | indexes[3] = NEWT_ARG_LAST; | ||
641 | __callchain__append_graph_browser(self, tree, total, indexes, 2); | ||
642 | } | ||
643 | 535 | ||
644 | static void hist_entry__append_callchain_browser(struct hist_entry *self, | 536 | list_for_each_entry_from(pos, head, node) { |
645 | newtComponent tree, u64 total, int parent_idx) | 537 | bool current_entry = ui_browser__is_current_entry(self, row); |
646 | { | 538 | SLsmg_gotorc(self->top + row, self->left); |
647 | struct rb_node *rb_node; | 539 | objdump_line__show(pos, head, self->width, |
648 | int indexes[1024] = { [0] = parent_idx, }; | 540 | he, len, current_entry); |
649 | int idx = 0; | 541 | if (++row == self->height) |
650 | struct callchain_node *chain; | ||
651 | |||
652 | rb_node = rb_first(&self->sorted_chain); | ||
653 | while (rb_node) { | ||
654 | chain = rb_entry(rb_node, struct callchain_node, rb_node); | ||
655 | switch (callchain_param.mode) { | ||
656 | case CHAIN_FLAT: | ||
657 | break; | ||
658 | case CHAIN_GRAPH_ABS: /* falldown */ | ||
659 | case CHAIN_GRAPH_REL: | ||
660 | callchain__append_graph_browser(chain, tree, total, indexes, idx++); | ||
661 | break; | ||
662 | case CHAIN_NONE: | ||
663 | default: | ||
664 | break; | 542 | break; |
665 | } | ||
666 | rb_node = rb_next(rb_node); | ||
667 | } | 543 | } |
668 | } | ||
669 | |||
670 | static size_t hist_entry__append_browser(struct hist_entry *self, | ||
671 | newtComponent tree, u64 total) | ||
672 | { | ||
673 | char s[256]; | ||
674 | size_t ret; | ||
675 | |||
676 | if (symbol_conf.exclude_other && !self->parent) | ||
677 | return 0; | ||
678 | 544 | ||
679 | ret = hist_entry__snprintf(self, s, sizeof(s), NULL, | 545 | return row; |
680 | false, 0, false, total); | ||
681 | if (symbol_conf.use_callchain) { | ||
682 | int indexes[2]; | ||
683 | |||
684 | indexes[0] = NEWT_ARG_APPEND; | ||
685 | indexes[1] = NEWT_ARG_LAST; | ||
686 | newt_checkbox_tree__add(tree, s, &self->ms, indexes); | ||
687 | } else | ||
688 | newtListboxAppendEntry(tree, s, &self->ms); | ||
689 | |||
690 | return ret; | ||
691 | } | 546 | } |
692 | 547 | ||
693 | int hist_entry__tui_annotate(struct hist_entry *self) | 548 | int hist_entry__tui_annotate(struct hist_entry *self) |
@@ -712,7 +567,9 @@ int hist_entry__tui_annotate(struct hist_entry *self) | |||
712 | ui_helpline__push("Press <- or ESC to exit"); | 567 | ui_helpline__push("Press <- or ESC to exit"); |
713 | 568 | ||
714 | memset(&browser, 0, sizeof(browser)); | 569 | memset(&browser, 0, sizeof(browser)); |
715 | browser.entries = &head; | 570 | browser.entries = &head; |
571 | browser.refresh_entries = hist_entry__annotate_browser_refresh; | ||
572 | browser.seek = ui_browser__list_head_seek; | ||
716 | browser.priv = self; | 573 | browser.priv = self; |
717 | list_for_each_entry(pos, &head, node) { | 574 | list_for_each_entry(pos, &head, node) { |
718 | size_t line_len = strlen(pos->line); | 575 | size_t line_len = strlen(pos->line); |
@@ -722,7 +579,9 @@ int hist_entry__tui_annotate(struct hist_entry *self) | |||
722 | } | 579 | } |
723 | 580 | ||
724 | browser.width += 18; /* Percentage */ | 581 | browser.width += 18; /* Percentage */ |
725 | ret = ui_browser__run(&browser, self->ms.sym->name, &es); | 582 | ui_browser__show(&browser, self->ms.sym->name); |
583 | newtFormAddHotKey(browser.form, ' '); | ||
584 | ret = ui_browser__run(&browser, &es); | ||
726 | newtFormDestroy(browser.form); | 585 | newtFormDestroy(browser.form); |
727 | newtPopWindow(); | 586 | newtPopWindow(); |
728 | list_for_each_entry_safe(pos, n, &head, node) { | 587 | list_for_each_entry_safe(pos, n, &head, node) { |
@@ -733,157 +592,48 @@ int hist_entry__tui_annotate(struct hist_entry *self) | |||
733 | return ret; | 592 | return ret; |
734 | } | 593 | } |
735 | 594 | ||
736 | static const void *newt__symbol_tree_get_current(newtComponent self) | ||
737 | { | ||
738 | if (symbol_conf.use_callchain) | ||
739 | return newtCheckboxTreeGetCurrent(self); | ||
740 | return newtListboxGetCurrent(self); | ||
741 | } | ||
742 | |||
743 | static void hist_browser__selection(newtComponent self, void *data) | ||
744 | { | ||
745 | const struct map_symbol **symbol_ptr = data; | ||
746 | *symbol_ptr = newt__symbol_tree_get_current(self); | ||
747 | } | ||
748 | |||
749 | struct hist_browser { | 595 | struct hist_browser { |
750 | newtComponent form, tree; | 596 | struct ui_browser b; |
751 | const struct map_symbol *selection; | 597 | struct hists *hists; |
598 | struct hist_entry *he_selection; | ||
599 | struct map_symbol *selection; | ||
752 | }; | 600 | }; |
753 | 601 | ||
754 | static struct hist_browser *hist_browser__new(void) | 602 | static void hist_browser__reset(struct hist_browser *self); |
603 | static int hist_browser__run(struct hist_browser *self, const char *title, | ||
604 | struct newtExitStruct *es); | ||
605 | static unsigned int hist_browser__refresh_entries(struct ui_browser *self); | ||
606 | static void ui_browser__hists_seek(struct ui_browser *self, | ||
607 | off_t offset, int whence); | ||
608 | |||
609 | static struct hist_browser *hist_browser__new(struct hists *hists) | ||
755 | { | 610 | { |
756 | struct hist_browser *self = malloc(sizeof(*self)); | 611 | struct hist_browser *self = zalloc(sizeof(*self)); |
757 | 612 | ||
758 | if (self != NULL) | 613 | if (self) { |
759 | self->form = NULL; | 614 | self->hists = hists; |
615 | self->b.refresh_entries = hist_browser__refresh_entries; | ||
616 | self->b.seek = ui_browser__hists_seek; | ||
617 | } | ||
760 | 618 | ||
761 | return self; | 619 | return self; |
762 | } | 620 | } |
763 | 621 | ||
764 | static void hist_browser__delete(struct hist_browser *self) | 622 | static void hist_browser__delete(struct hist_browser *self) |
765 | { | 623 | { |
766 | newtFormDestroy(self->form); | 624 | newtFormDestroy(self->b.form); |
767 | newtPopWindow(); | 625 | newtPopWindow(); |
768 | free(self); | 626 | free(self); |
769 | } | 627 | } |
770 | 628 | ||
771 | static int hist_browser__populate(struct hist_browser *self, struct hists *hists, | ||
772 | const char *title) | ||
773 | { | ||
774 | int max_len = 0, idx, cols, rows; | ||
775 | struct ui_progress *progress; | ||
776 | struct rb_node *nd; | ||
777 | u64 curr_hist = 0; | ||
778 | char seq[] = ".", unit; | ||
779 | char str[256]; | ||
780 | unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE]; | ||
781 | |||
782 | if (self->form) { | ||
783 | newtFormDestroy(self->form); | ||
784 | newtPopWindow(); | ||
785 | } | ||
786 | |||
787 | nr_events = convert_unit(nr_events, &unit); | ||
788 | snprintf(str, sizeof(str), "Events: %lu%c ", | ||
789 | nr_events, unit); | ||
790 | newtDrawRootText(0, 0, str); | ||
791 | |||
792 | newtGetScreenSize(NULL, &rows); | ||
793 | |||
794 | if (symbol_conf.use_callchain) | ||
795 | self->tree = newtCheckboxTreeMulti(0, 0, rows - 5, seq, | ||
796 | NEWT_FLAG_SCROLL); | ||
797 | else | ||
798 | self->tree = newtListbox(0, 0, rows - 5, | ||
799 | (NEWT_FLAG_SCROLL | | ||
800 | NEWT_FLAG_RETURNEXIT)); | ||
801 | |||
802 | newtComponentAddCallback(self->tree, hist_browser__selection, | ||
803 | &self->selection); | ||
804 | |||
805 | progress = ui_progress__new("Adding entries to the browser...", | ||
806 | hists->nr_entries); | ||
807 | if (progress == NULL) | ||
808 | return -1; | ||
809 | |||
810 | idx = 0; | ||
811 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { | ||
812 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
813 | int len; | ||
814 | |||
815 | if (h->filtered) | ||
816 | continue; | ||
817 | |||
818 | len = hist_entry__append_browser(h, self->tree, hists->stats.total_period); | ||
819 | if (len > max_len) | ||
820 | max_len = len; | ||
821 | if (symbol_conf.use_callchain) | ||
822 | hist_entry__append_callchain_browser(h, self->tree, | ||
823 | hists->stats.total_period, idx++); | ||
824 | ++curr_hist; | ||
825 | if (curr_hist % 5) | ||
826 | ui_progress__update(progress, curr_hist); | ||
827 | } | ||
828 | |||
829 | ui_progress__delete(progress); | ||
830 | |||
831 | newtGetScreenSize(&cols, &rows); | ||
832 | |||
833 | if (max_len > cols) | ||
834 | max_len = cols - 3; | ||
835 | |||
836 | if (!symbol_conf.use_callchain) | ||
837 | newtListboxSetWidth(self->tree, max_len); | ||
838 | |||
839 | newtCenteredWindow(max_len + (symbol_conf.use_callchain ? 5 : 0), | ||
840 | rows - 5, title); | ||
841 | self->form = newt_form__new(); | ||
842 | if (self->form == NULL) | ||
843 | return -1; | ||
844 | |||
845 | newtFormAddHotKey(self->form, 'A'); | ||
846 | newtFormAddHotKey(self->form, 'a'); | ||
847 | newtFormAddHotKey(self->form, 'D'); | ||
848 | newtFormAddHotKey(self->form, 'd'); | ||
849 | newtFormAddHotKey(self->form, 'T'); | ||
850 | newtFormAddHotKey(self->form, 't'); | ||
851 | newtFormAddHotKey(self->form, '?'); | ||
852 | newtFormAddHotKey(self->form, 'H'); | ||
853 | newtFormAddHotKey(self->form, 'h'); | ||
854 | newtFormAddHotKey(self->form, NEWT_KEY_F1); | ||
855 | newtFormAddHotKey(self->form, NEWT_KEY_RIGHT); | ||
856 | newtFormAddHotKey(self->form, NEWT_KEY_TAB); | ||
857 | newtFormAddHotKey(self->form, NEWT_KEY_UNTAB); | ||
858 | newtFormAddComponents(self->form, self->tree, NULL); | ||
859 | self->selection = newt__symbol_tree_get_current(self->tree); | ||
860 | |||
861 | return 0; | ||
862 | } | ||
863 | |||
864 | static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self) | 629 | static struct hist_entry *hist_browser__selected_entry(struct hist_browser *self) |
865 | { | 630 | { |
866 | int *indexes; | 631 | return self->he_selection; |
867 | |||
868 | if (!symbol_conf.use_callchain) | ||
869 | goto out; | ||
870 | |||
871 | indexes = newtCheckboxTreeFindItem(self->tree, (void *)self->selection); | ||
872 | if (indexes) { | ||
873 | bool is_hist_entry = indexes[1] == NEWT_ARG_LAST; | ||
874 | free(indexes); | ||
875 | if (is_hist_entry) | ||
876 | goto out; | ||
877 | } | ||
878 | return NULL; | ||
879 | out: | ||
880 | return container_of(self->selection, struct hist_entry, ms); | ||
881 | } | 632 | } |
882 | 633 | ||
883 | static struct thread *hist_browser__selected_thread(struct hist_browser *self) | 634 | static struct thread *hist_browser__selected_thread(struct hist_browser *self) |
884 | { | 635 | { |
885 | struct hist_entry *he = hist_browser__selected_entry(self); | 636 | return self->he_selection->thread; |
886 | return he ? he->thread : NULL; | ||
887 | } | 637 | } |
888 | 638 | ||
889 | static int hist_browser__title(char *bf, size_t size, const char *ev_name, | 639 | static int hist_browser__title(char *bf, size_t size, const char *ev_name, |
@@ -905,7 +655,7 @@ static int hist_browser__title(char *bf, size_t size, const char *ev_name, | |||
905 | 655 | ||
906 | int hists__browse(struct hists *self, const char *helpline, const char *ev_name) | 656 | int hists__browse(struct hists *self, const char *helpline, const char *ev_name) |
907 | { | 657 | { |
908 | struct hist_browser *browser = hist_browser__new(); | 658 | struct hist_browser *browser = hist_browser__new(self); |
909 | struct pstack *fstack; | 659 | struct pstack *fstack; |
910 | const struct thread *thread_filter = NULL; | 660 | const struct thread *thread_filter = NULL; |
911 | const struct dso *dso_filter = NULL; | 661 | const struct dso *dso_filter = NULL; |
@@ -924,8 +674,6 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name) | |||
924 | 674 | ||
925 | hist_browser__title(msg, sizeof(msg), ev_name, | 675 | hist_browser__title(msg, sizeof(msg), ev_name, |
926 | dso_filter, thread_filter); | 676 | dso_filter, thread_filter); |
927 | if (hist_browser__populate(browser, self, msg) < 0) | ||
928 | goto out_free_stack; | ||
929 | 677 | ||
930 | while (1) { | 678 | while (1) { |
931 | const struct thread *thread; | 679 | const struct thread *thread; |
@@ -934,7 +682,8 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name) | |||
934 | int nr_options = 0, choice = 0, i, | 682 | int nr_options = 0, choice = 0, i, |
935 | annotate = -2, zoom_dso = -2, zoom_thread = -2; | 683 | annotate = -2, zoom_dso = -2, zoom_thread = -2; |
936 | 684 | ||
937 | newtFormRun(browser->form, &es); | 685 | if (hist_browser__run(browser, msg, &es)) |
686 | break; | ||
938 | 687 | ||
939 | thread = hist_browser__selected_thread(browser); | 688 | thread = hist_browser__selected_thread(browser); |
940 | dso = browser->selection->map ? browser->selection->map->dso : NULL; | 689 | dso = browser->selection->map ? browser->selection->map->dso : NULL; |
@@ -1069,8 +818,7 @@ zoom_out_dso: | |||
1069 | hists__filter_by_dso(self, dso_filter); | 818 | hists__filter_by_dso(self, dso_filter); |
1070 | hist_browser__title(msg, sizeof(msg), ev_name, | 819 | hist_browser__title(msg, sizeof(msg), ev_name, |
1071 | dso_filter, thread_filter); | 820 | dso_filter, thread_filter); |
1072 | if (hist_browser__populate(browser, self, msg) < 0) | 821 | hist_browser__reset(browser); |
1073 | goto out; | ||
1074 | } else if (choice == zoom_thread) { | 822 | } else if (choice == zoom_thread) { |
1075 | zoom_thread: | 823 | zoom_thread: |
1076 | if (thread_filter) { | 824 | if (thread_filter) { |
@@ -1088,8 +836,7 @@ zoom_out_thread: | |||
1088 | hists__filter_by_thread(self, thread_filter); | 836 | hists__filter_by_thread(self, thread_filter); |
1089 | hist_browser__title(msg, sizeof(msg), ev_name, | 837 | hist_browser__title(msg, sizeof(msg), ev_name, |
1090 | dso_filter, thread_filter); | 838 | dso_filter, thread_filter); |
1091 | if (hist_browser__populate(browser, self, msg) < 0) | 839 | hist_browser__reset(browser); |
1092 | goto out; | ||
1093 | } | 840 | } |
1094 | } | 841 | } |
1095 | out_free_stack: | 842 | out_free_stack: |
@@ -1145,6 +892,13 @@ static struct newtPercentTreeColors { | |||
1145 | "blue", "lightgray", | 892 | "blue", "lightgray", |
1146 | }; | 893 | }; |
1147 | 894 | ||
895 | static void newt_suspend(void *d __used) | ||
896 | { | ||
897 | newtSuspend(); | ||
898 | raise(SIGTSTP); | ||
899 | newtResume(); | ||
900 | } | ||
901 | |||
1148 | void setup_browser(void) | 902 | void setup_browser(void) |
1149 | { | 903 | { |
1150 | struct newtPercentTreeColors *c = &defaultPercentTreeColors; | 904 | struct newtPercentTreeColors *c = &defaultPercentTreeColors; |
@@ -1158,6 +912,7 @@ void setup_browser(void) | |||
1158 | use_browser = 1; | 912 | use_browser = 1; |
1159 | newtInit(); | 913 | newtInit(); |
1160 | newtCls(); | 914 | newtCls(); |
915 | newtSetSuspendCallback(newt_suspend, NULL); | ||
1161 | ui_helpline__puts(" "); | 916 | ui_helpline__puts(" "); |
1162 | sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg); | 917 | sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg); |
1163 | sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg); | 918 | sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg); |
@@ -1176,3 +931,638 @@ void exit_browser(bool wait_for_ok) | |||
1176 | newtFinished(); | 931 | newtFinished(); |
1177 | } | 932 | } |
1178 | } | 933 | } |
934 | |||
935 | static void hist_browser__refresh_dimensions(struct hist_browser *self) | ||
936 | { | ||
937 | /* 3 == +/- toggle symbol before actual hist_entry rendering */ | ||
938 | self->b.width = 3 + (hists__sort_list_width(self->hists) + | ||
939 | sizeof("[k]")); | ||
940 | } | ||
941 | |||
942 | static void hist_browser__reset(struct hist_browser *self) | ||
943 | { | ||
944 | self->b.nr_entries = self->hists->nr_entries; | ||
945 | hist_browser__refresh_dimensions(self); | ||
946 | ui_browser__reset_index(&self->b); | ||
947 | } | ||
948 | |||
949 | static char tree__folded_sign(bool unfolded) | ||
950 | { | ||
951 | return unfolded ? '-' : '+'; | ||
952 | } | ||
953 | |||
954 | static char map_symbol__folded(const struct map_symbol *self) | ||
955 | { | ||
956 | return self->has_children ? tree__folded_sign(self->unfolded) : ' '; | ||
957 | } | ||
958 | |||
959 | static char hist_entry__folded(const struct hist_entry *self) | ||
960 | { | ||
961 | return map_symbol__folded(&self->ms); | ||
962 | } | ||
963 | |||
964 | static char callchain_list__folded(const struct callchain_list *self) | ||
965 | { | ||
966 | return map_symbol__folded(&self->ms); | ||
967 | } | ||
968 | |||
969 | static bool map_symbol__toggle_fold(struct map_symbol *self) | ||
970 | { | ||
971 | if (!self->has_children) | ||
972 | return false; | ||
973 | |||
974 | self->unfolded = !self->unfolded; | ||
975 | return true; | ||
976 | } | ||
977 | |||
978 | #define LEVEL_OFFSET_STEP 3 | ||
979 | |||
980 | static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *self, | ||
981 | struct callchain_node *chain_node, | ||
982 | u64 total, int level, | ||
983 | unsigned short row, | ||
984 | off_t *row_offset, | ||
985 | bool *is_current_entry) | ||
986 | { | ||
987 | struct rb_node *node; | ||
988 | int first_row = row, width, offset = level * LEVEL_OFFSET_STEP; | ||
989 | u64 new_total, remaining; | ||
990 | |||
991 | if (callchain_param.mode == CHAIN_GRAPH_REL) | ||
992 | new_total = chain_node->children_hit; | ||
993 | else | ||
994 | new_total = total; | ||
995 | |||
996 | remaining = new_total; | ||
997 | node = rb_first(&chain_node->rb_root); | ||
998 | while (node) { | ||
999 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); | ||
1000 | struct rb_node *next = rb_next(node); | ||
1001 | u64 cumul = cumul_hits(child); | ||
1002 | struct callchain_list *chain; | ||
1003 | char folded_sign = ' '; | ||
1004 | int first = true; | ||
1005 | int extra_offset = 0; | ||
1006 | |||
1007 | remaining -= cumul; | ||
1008 | |||
1009 | list_for_each_entry(chain, &child->val, list) { | ||
1010 | char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str; | ||
1011 | const char *str; | ||
1012 | int color; | ||
1013 | bool was_first = first; | ||
1014 | |||
1015 | if (first) { | ||
1016 | first = false; | ||
1017 | chain->ms.has_children = chain->list.next != &child->val || | ||
1018 | rb_first(&child->rb_root) != NULL; | ||
1019 | } else { | ||
1020 | extra_offset = LEVEL_OFFSET_STEP; | ||
1021 | chain->ms.has_children = chain->list.next == &child->val && | ||
1022 | rb_first(&child->rb_root) != NULL; | ||
1023 | } | ||
1024 | |||
1025 | folded_sign = callchain_list__folded(chain); | ||
1026 | if (*row_offset != 0) { | ||
1027 | --*row_offset; | ||
1028 | goto do_next; | ||
1029 | } | ||
1030 | |||
1031 | alloc_str = NULL; | ||
1032 | str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); | ||
1033 | if (was_first) { | ||
1034 | double percent = cumul * 100.0 / new_total; | ||
1035 | |||
1036 | if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) | ||
1037 | str = "Not enough memory!"; | ||
1038 | else | ||
1039 | str = alloc_str; | ||
1040 | } | ||
1041 | |||
1042 | color = HE_COLORSET_NORMAL; | ||
1043 | width = self->b.width - (offset + extra_offset + 2); | ||
1044 | if (ui_browser__is_current_entry(&self->b, row)) { | ||
1045 | self->selection = &chain->ms; | ||
1046 | color = HE_COLORSET_SELECTED; | ||
1047 | *is_current_entry = true; | ||
1048 | } | ||
1049 | |||
1050 | SLsmg_set_color(color); | ||
1051 | SLsmg_gotorc(self->b.top + row, self->b.left); | ||
1052 | slsmg_write_nstring(" ", offset + extra_offset); | ||
1053 | slsmg_printf("%c ", folded_sign); | ||
1054 | slsmg_write_nstring(str, width); | ||
1055 | free(alloc_str); | ||
1056 | |||
1057 | if (++row == self->b.height) | ||
1058 | goto out; | ||
1059 | do_next: | ||
1060 | if (folded_sign == '+') | ||
1061 | break; | ||
1062 | } | ||
1063 | |||
1064 | if (folded_sign == '-') { | ||
1065 | const int new_level = level + (extra_offset ? 2 : 1); | ||
1066 | row += hist_browser__show_callchain_node_rb_tree(self, child, new_total, | ||
1067 | new_level, row, row_offset, | ||
1068 | is_current_entry); | ||
1069 | } | ||
1070 | if (row == self->b.height) | ||
1071 | goto out; | ||
1072 | node = next; | ||
1073 | } | ||
1074 | out: | ||
1075 | return row - first_row; | ||
1076 | } | ||
1077 | |||
1078 | static int hist_browser__show_callchain_node(struct hist_browser *self, | ||
1079 | struct callchain_node *node, | ||
1080 | int level, unsigned short row, | ||
1081 | off_t *row_offset, | ||
1082 | bool *is_current_entry) | ||
1083 | { | ||
1084 | struct callchain_list *chain; | ||
1085 | int first_row = row, | ||
1086 | offset = level * LEVEL_OFFSET_STEP, | ||
1087 | width = self->b.width - offset; | ||
1088 | char folded_sign = ' '; | ||
1089 | |||
1090 | list_for_each_entry(chain, &node->val, list) { | ||
1091 | char ipstr[BITS_PER_LONG / 4 + 1], *s; | ||
1092 | int color; | ||
1093 | /* | ||
1094 | * FIXME: This should be moved to somewhere else, | ||
1095 | * probably when the callchain is created, so as not to | ||
1096 | * traverse it all over again | ||
1097 | */ | ||
1098 | chain->ms.has_children = rb_first(&node->rb_root) != NULL; | ||
1099 | folded_sign = callchain_list__folded(chain); | ||
1100 | |||
1101 | if (*row_offset != 0) { | ||
1102 | --*row_offset; | ||
1103 | continue; | ||
1104 | } | ||
1105 | |||
1106 | color = HE_COLORSET_NORMAL; | ||
1107 | if (ui_browser__is_current_entry(&self->b, row)) { | ||
1108 | self->selection = &chain->ms; | ||
1109 | color = HE_COLORSET_SELECTED; | ||
1110 | *is_current_entry = true; | ||
1111 | } | ||
1112 | |||
1113 | s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); | ||
1114 | SLsmg_gotorc(self->b.top + row, self->b.left); | ||
1115 | SLsmg_set_color(color); | ||
1116 | slsmg_write_nstring(" ", offset); | ||
1117 | slsmg_printf("%c ", folded_sign); | ||
1118 | slsmg_write_nstring(s, width - 2); | ||
1119 | |||
1120 | if (++row == self->b.height) | ||
1121 | goto out; | ||
1122 | } | ||
1123 | |||
1124 | if (folded_sign == '-') | ||
1125 | row += hist_browser__show_callchain_node_rb_tree(self, node, | ||
1126 | self->hists->stats.total_period, | ||
1127 | level + 1, row, | ||
1128 | row_offset, | ||
1129 | is_current_entry); | ||
1130 | out: | ||
1131 | return row - first_row; | ||
1132 | } | ||
1133 | |||
1134 | static int hist_browser__show_callchain(struct hist_browser *self, | ||
1135 | struct rb_root *chain, | ||
1136 | int level, unsigned short row, | ||
1137 | off_t *row_offset, | ||
1138 | bool *is_current_entry) | ||
1139 | { | ||
1140 | struct rb_node *nd; | ||
1141 | int first_row = row; | ||
1142 | |||
1143 | for (nd = rb_first(chain); nd; nd = rb_next(nd)) { | ||
1144 | struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); | ||
1145 | |||
1146 | row += hist_browser__show_callchain_node(self, node, level, | ||
1147 | row, row_offset, | ||
1148 | is_current_entry); | ||
1149 | if (row == self->b.height) | ||
1150 | break; | ||
1151 | } | ||
1152 | |||
1153 | return row - first_row; | ||
1154 | } | ||
1155 | |||
1156 | static int hist_browser__show_entry(struct hist_browser *self, | ||
1157 | struct hist_entry *entry, | ||
1158 | unsigned short row) | ||
1159 | { | ||
1160 | char s[256]; | ||
1161 | double percent; | ||
1162 | int printed = 0; | ||
1163 | int color, width = self->b.width; | ||
1164 | char folded_sign = ' '; | ||
1165 | bool current_entry = ui_browser__is_current_entry(&self->b, row); | ||
1166 | off_t row_offset = entry->row_offset; | ||
1167 | |||
1168 | if (current_entry) { | ||
1169 | self->he_selection = entry; | ||
1170 | self->selection = &entry->ms; | ||
1171 | } | ||
1172 | |||
1173 | if (symbol_conf.use_callchain) { | ||
1174 | entry->ms.has_children = !RB_EMPTY_ROOT(&entry->sorted_chain); | ||
1175 | folded_sign = hist_entry__folded(entry); | ||
1176 | } | ||
1177 | |||
1178 | if (row_offset == 0) { | ||
1179 | hist_entry__snprintf(entry, s, sizeof(s), self->hists, NULL, false, | ||
1180 | 0, false, self->hists->stats.total_period); | ||
1181 | percent = (entry->period * 100.0) / self->hists->stats.total_period; | ||
1182 | |||
1183 | color = HE_COLORSET_SELECTED; | ||
1184 | if (!current_entry) { | ||
1185 | if (percent >= MIN_RED) | ||
1186 | color = HE_COLORSET_TOP; | ||
1187 | else if (percent >= MIN_GREEN) | ||
1188 | color = HE_COLORSET_MEDIUM; | ||
1189 | else | ||
1190 | color = HE_COLORSET_NORMAL; | ||
1191 | } | ||
1192 | |||
1193 | SLsmg_set_color(color); | ||
1194 | SLsmg_gotorc(self->b.top + row, self->b.left); | ||
1195 | if (symbol_conf.use_callchain) { | ||
1196 | slsmg_printf("%c ", folded_sign); | ||
1197 | width -= 2; | ||
1198 | } | ||
1199 | slsmg_write_nstring(s, width); | ||
1200 | ++row; | ||
1201 | ++printed; | ||
1202 | } else | ||
1203 | --row_offset; | ||
1204 | |||
1205 | if (folded_sign == '-' && row != self->b.height) { | ||
1206 | printed += hist_browser__show_callchain(self, &entry->sorted_chain, | ||
1207 | 1, row, &row_offset, | ||
1208 | ¤t_entry); | ||
1209 | if (current_entry) | ||
1210 | self->he_selection = entry; | ||
1211 | } | ||
1212 | |||
1213 | return printed; | ||
1214 | } | ||
1215 | |||
1216 | static unsigned int hist_browser__refresh_entries(struct ui_browser *self) | ||
1217 | { | ||
1218 | unsigned row = 0; | ||
1219 | struct rb_node *nd; | ||
1220 | struct hist_browser *hb = container_of(self, struct hist_browser, b); | ||
1221 | |||
1222 | if (self->first_visible_entry == NULL) | ||
1223 | self->first_visible_entry = rb_first(&hb->hists->entries); | ||
1224 | |||
1225 | for (nd = self->first_visible_entry; nd; nd = rb_next(nd)) { | ||
1226 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
1227 | |||
1228 | if (h->filtered) | ||
1229 | continue; | ||
1230 | |||
1231 | row += hist_browser__show_entry(hb, h, row); | ||
1232 | if (row == self->height) | ||
1233 | break; | ||
1234 | } | ||
1235 | |||
1236 | return row; | ||
1237 | } | ||
1238 | |||
1239 | static void callchain_node__init_have_children_rb_tree(struct callchain_node *self) | ||
1240 | { | ||
1241 | struct rb_node *nd = rb_first(&self->rb_root); | ||
1242 | |||
1243 | for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) { | ||
1244 | struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); | ||
1245 | struct callchain_list *chain; | ||
1246 | int first = true; | ||
1247 | |||
1248 | list_for_each_entry(chain, &child->val, list) { | ||
1249 | if (first) { | ||
1250 | first = false; | ||
1251 | chain->ms.has_children = chain->list.next != &child->val || | ||
1252 | rb_first(&child->rb_root) != NULL; | ||
1253 | } else | ||
1254 | chain->ms.has_children = chain->list.next == &child->val && | ||
1255 | rb_first(&child->rb_root) != NULL; | ||
1256 | } | ||
1257 | |||
1258 | callchain_node__init_have_children_rb_tree(child); | ||
1259 | } | ||
1260 | } | ||
1261 | |||
1262 | static void callchain_node__init_have_children(struct callchain_node *self) | ||
1263 | { | ||
1264 | struct callchain_list *chain; | ||
1265 | |||
1266 | list_for_each_entry(chain, &self->val, list) | ||
1267 | chain->ms.has_children = rb_first(&self->rb_root) != NULL; | ||
1268 | |||
1269 | callchain_node__init_have_children_rb_tree(self); | ||
1270 | } | ||
1271 | |||
1272 | static void callchain__init_have_children(struct rb_root *self) | ||
1273 | { | ||
1274 | struct rb_node *nd; | ||
1275 | |||
1276 | for (nd = rb_first(self); nd; nd = rb_next(nd)) { | ||
1277 | struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); | ||
1278 | callchain_node__init_have_children(node); | ||
1279 | } | ||
1280 | } | ||
1281 | |||
1282 | static void hist_entry__init_have_children(struct hist_entry *self) | ||
1283 | { | ||
1284 | if (!self->init_have_children) { | ||
1285 | callchain__init_have_children(&self->sorted_chain); | ||
1286 | self->init_have_children = true; | ||
1287 | } | ||
1288 | } | ||
1289 | |||
1290 | static struct rb_node *hists__filter_entries(struct rb_node *nd) | ||
1291 | { | ||
1292 | while (nd != NULL) { | ||
1293 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
1294 | if (!h->filtered) | ||
1295 | return nd; | ||
1296 | |||
1297 | nd = rb_next(nd); | ||
1298 | } | ||
1299 | |||
1300 | return NULL; | ||
1301 | } | ||
1302 | |||
1303 | static struct rb_node *hists__filter_prev_entries(struct rb_node *nd) | ||
1304 | { | ||
1305 | while (nd != NULL) { | ||
1306 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
1307 | if (!h->filtered) | ||
1308 | return nd; | ||
1309 | |||
1310 | nd = rb_prev(nd); | ||
1311 | } | ||
1312 | |||
1313 | return NULL; | ||
1314 | } | ||
1315 | |||
1316 | static void ui_browser__hists_seek(struct ui_browser *self, | ||
1317 | off_t offset, int whence) | ||
1318 | { | ||
1319 | struct hist_entry *h; | ||
1320 | struct rb_node *nd; | ||
1321 | bool first = true; | ||
1322 | |||
1323 | switch (whence) { | ||
1324 | case SEEK_SET: | ||
1325 | nd = hists__filter_entries(rb_first(self->entries)); | ||
1326 | break; | ||
1327 | case SEEK_CUR: | ||
1328 | nd = self->first_visible_entry; | ||
1329 | goto do_offset; | ||
1330 | case SEEK_END: | ||
1331 | nd = hists__filter_prev_entries(rb_last(self->entries)); | ||
1332 | first = false; | ||
1333 | break; | ||
1334 | default: | ||
1335 | return; | ||
1336 | } | ||
1337 | |||
1338 | /* | ||
1339 | * Moves not relative to the first visible entry invalidates its | ||
1340 | * row_offset: | ||
1341 | */ | ||
1342 | h = rb_entry(self->first_visible_entry, struct hist_entry, rb_node); | ||
1343 | h->row_offset = 0; | ||
1344 | |||
1345 | /* | ||
1346 | * Here we have to check if nd is expanded (+), if it is we can't go | ||
1347 | * the next top level hist_entry, instead we must compute an offset of | ||
1348 | * what _not_ to show and not change the first visible entry. | ||
1349 | * | ||
1350 | * This offset increments when we are going from top to bottom and | ||
1351 | * decreases when we're going from bottom to top. | ||
1352 | * | ||
1353 | * As we don't have backpointers to the top level in the callchains | ||
1354 | * structure, we need to always print the whole hist_entry callchain, | ||
1355 | * skipping the first ones that are before the first visible entry | ||
1356 | * and stop when we printed enough lines to fill the screen. | ||
1357 | */ | ||
1358 | do_offset: | ||
1359 | if (offset > 0) { | ||
1360 | do { | ||
1361 | h = rb_entry(nd, struct hist_entry, rb_node); | ||
1362 | if (h->ms.unfolded) { | ||
1363 | u16 remaining = h->nr_rows - h->row_offset; | ||
1364 | if (offset > remaining) { | ||
1365 | offset -= remaining; | ||
1366 | h->row_offset = 0; | ||
1367 | } else { | ||
1368 | h->row_offset += offset; | ||
1369 | offset = 0; | ||
1370 | self->first_visible_entry = nd; | ||
1371 | break; | ||
1372 | } | ||
1373 | } | ||
1374 | nd = hists__filter_entries(rb_next(nd)); | ||
1375 | if (nd == NULL) | ||
1376 | break; | ||
1377 | --offset; | ||
1378 | self->first_visible_entry = nd; | ||
1379 | } while (offset != 0); | ||
1380 | } else if (offset < 0) { | ||
1381 | while (1) { | ||
1382 | h = rb_entry(nd, struct hist_entry, rb_node); | ||
1383 | if (h->ms.unfolded) { | ||
1384 | if (first) { | ||
1385 | if (-offset > h->row_offset) { | ||
1386 | offset += h->row_offset; | ||
1387 | h->row_offset = 0; | ||
1388 | } else { | ||
1389 | h->row_offset += offset; | ||
1390 | offset = 0; | ||
1391 | self->first_visible_entry = nd; | ||
1392 | break; | ||
1393 | } | ||
1394 | } else { | ||
1395 | if (-offset > h->nr_rows) { | ||
1396 | offset += h->nr_rows; | ||
1397 | h->row_offset = 0; | ||
1398 | } else { | ||
1399 | h->row_offset = h->nr_rows + offset; | ||
1400 | offset = 0; | ||
1401 | self->first_visible_entry = nd; | ||
1402 | break; | ||
1403 | } | ||
1404 | } | ||
1405 | } | ||
1406 | |||
1407 | nd = hists__filter_prev_entries(rb_prev(nd)); | ||
1408 | if (nd == NULL) | ||
1409 | break; | ||
1410 | ++offset; | ||
1411 | self->first_visible_entry = nd; | ||
1412 | if (offset == 0) { | ||
1413 | /* | ||
1414 | * Last unfiltered hist_entry, check if it is | ||
1415 | * unfolded, if it is then we should have | ||
1416 | * row_offset at its last entry. | ||
1417 | */ | ||
1418 | h = rb_entry(nd, struct hist_entry, rb_node); | ||
1419 | if (h->ms.unfolded) | ||
1420 | h->row_offset = h->nr_rows; | ||
1421 | break; | ||
1422 | } | ||
1423 | first = false; | ||
1424 | } | ||
1425 | } else { | ||
1426 | self->first_visible_entry = nd; | ||
1427 | h = rb_entry(nd, struct hist_entry, rb_node); | ||
1428 | h->row_offset = 0; | ||
1429 | } | ||
1430 | } | ||
1431 | |||
1432 | static int callchain_node__count_rows_rb_tree(struct callchain_node *self) | ||
1433 | { | ||
1434 | int n = 0; | ||
1435 | struct rb_node *nd; | ||
1436 | |||
1437 | for (nd = rb_first(&self->rb_root); nd; nd = rb_next(nd)) { | ||
1438 | struct callchain_node *child = rb_entry(nd, struct callchain_node, rb_node); | ||
1439 | struct callchain_list *chain; | ||
1440 | char folded_sign = ' '; /* No children */ | ||
1441 | |||
1442 | list_for_each_entry(chain, &child->val, list) { | ||
1443 | ++n; | ||
1444 | /* We need this because we may not have children */ | ||
1445 | folded_sign = callchain_list__folded(chain); | ||
1446 | if (folded_sign == '+') | ||
1447 | break; | ||
1448 | } | ||
1449 | |||
1450 | if (folded_sign == '-') /* Have children and they're unfolded */ | ||
1451 | n += callchain_node__count_rows_rb_tree(child); | ||
1452 | } | ||
1453 | |||
1454 | return n; | ||
1455 | } | ||
1456 | |||
1457 | static int callchain_node__count_rows(struct callchain_node *node) | ||
1458 | { | ||
1459 | struct callchain_list *chain; | ||
1460 | bool unfolded = false; | ||
1461 | int n = 0; | ||
1462 | |||
1463 | list_for_each_entry(chain, &node->val, list) { | ||
1464 | ++n; | ||
1465 | unfolded = chain->ms.unfolded; | ||
1466 | } | ||
1467 | |||
1468 | if (unfolded) | ||
1469 | n += callchain_node__count_rows_rb_tree(node); | ||
1470 | |||
1471 | return n; | ||
1472 | } | ||
1473 | |||
1474 | static int callchain__count_rows(struct rb_root *chain) | ||
1475 | { | ||
1476 | struct rb_node *nd; | ||
1477 | int n = 0; | ||
1478 | |||
1479 | for (nd = rb_first(chain); nd; nd = rb_next(nd)) { | ||
1480 | struct callchain_node *node = rb_entry(nd, struct callchain_node, rb_node); | ||
1481 | n += callchain_node__count_rows(node); | ||
1482 | } | ||
1483 | |||
1484 | return n; | ||
1485 | } | ||
1486 | |||
1487 | static bool hist_browser__toggle_fold(struct hist_browser *self) | ||
1488 | { | ||
1489 | if (map_symbol__toggle_fold(self->selection)) { | ||
1490 | struct hist_entry *he = self->he_selection; | ||
1491 | |||
1492 | hist_entry__init_have_children(he); | ||
1493 | self->hists->nr_entries -= he->nr_rows; | ||
1494 | |||
1495 | if (he->ms.unfolded) | ||
1496 | he->nr_rows = callchain__count_rows(&he->sorted_chain); | ||
1497 | else | ||
1498 | he->nr_rows = 0; | ||
1499 | self->hists->nr_entries += he->nr_rows; | ||
1500 | self->b.nr_entries = self->hists->nr_entries; | ||
1501 | |||
1502 | return true; | ||
1503 | } | ||
1504 | |||
1505 | /* If it doesn't have children, no toggling performed */ | ||
1506 | return false; | ||
1507 | } | ||
1508 | |||
1509 | static int hist_browser__run(struct hist_browser *self, const char *title, | ||
1510 | struct newtExitStruct *es) | ||
1511 | { | ||
1512 | char str[256], unit; | ||
1513 | unsigned long nr_events = self->hists->stats.nr_events[PERF_RECORD_SAMPLE]; | ||
1514 | |||
1515 | self->b.entries = &self->hists->entries; | ||
1516 | self->b.nr_entries = self->hists->nr_entries; | ||
1517 | |||
1518 | hist_browser__refresh_dimensions(self); | ||
1519 | |||
1520 | nr_events = convert_unit(nr_events, &unit); | ||
1521 | snprintf(str, sizeof(str), "Events: %lu%c ", | ||
1522 | nr_events, unit); | ||
1523 | newtDrawRootText(0, 0, str); | ||
1524 | |||
1525 | if (ui_browser__show(&self->b, title) < 0) | ||
1526 | return -1; | ||
1527 | |||
1528 | newtFormAddHotKey(self->b.form, 'A'); | ||
1529 | newtFormAddHotKey(self->b.form, 'a'); | ||
1530 | newtFormAddHotKey(self->b.form, '?'); | ||
1531 | newtFormAddHotKey(self->b.form, 'h'); | ||
1532 | newtFormAddHotKey(self->b.form, 'H'); | ||
1533 | newtFormAddHotKey(self->b.form, 'd'); | ||
1534 | |||
1535 | newtFormAddHotKey(self->b.form, NEWT_KEY_LEFT); | ||
1536 | newtFormAddHotKey(self->b.form, NEWT_KEY_RIGHT); | ||
1537 | newtFormAddHotKey(self->b.form, NEWT_KEY_ENTER); | ||
1538 | |||
1539 | while (1) { | ||
1540 | ui_browser__run(&self->b, es); | ||
1541 | |||
1542 | if (es->reason != NEWT_EXIT_HOTKEY) | ||
1543 | break; | ||
1544 | switch (es->u.key) { | ||
1545 | case 'd': { /* Debug */ | ||
1546 | static int seq; | ||
1547 | struct hist_entry *h = rb_entry(self->b.first_visible_entry, | ||
1548 | struct hist_entry, rb_node); | ||
1549 | ui_helpline__pop(); | ||
1550 | ui_helpline__fpush("%d: nr_ent=(%d,%d), height=%d, idx=%d, fve: idx=%d, row_off=%d, nrows=%d", | ||
1551 | seq++, self->b.nr_entries, | ||
1552 | self->hists->nr_entries, | ||
1553 | self->b.height, | ||
1554 | self->b.index, | ||
1555 | self->b.first_visible_entry_idx, | ||
1556 | h->row_offset, h->nr_rows); | ||
1557 | } | ||
1558 | continue; | ||
1559 | case NEWT_KEY_ENTER: | ||
1560 | if (hist_browser__toggle_fold(self)) | ||
1561 | break; | ||
1562 | /* fall thru */ | ||
1563 | default: | ||
1564 | return 0; | ||
1565 | } | ||
1566 | } | ||
1567 | return 0; | ||
1568 | } | ||
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..2e665cb84055 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 | /* |
124 | static 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 | */ | ||
127 | static 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 */ |
154 | static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, | 157 | static 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 | */ | ||
207 | static 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 | ||
311 | static int convert_to_perf_probe_point(struct kprobe_trace_point *tp, | 383 | static 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 | ||
323 | static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, | 395 | static 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 */ |
558 | static int parse_perf_probe_arg(char *str, struct perf_probe_arg *arg) | 630 | static 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 */ |
697 | int parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev) | 788 | static 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 | ||
880 | static int __synthesize_kprobe_trace_arg_ref(struct kprobe_trace_arg_ref *ref, | 975 | static 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 | ||
904 | static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg, | 999 | static 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 | ||
954 | char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev) | 1058 | char *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 | ||
985 | int convert_to_perf_probe_event(struct kprobe_trace_event *tev, | 1089 | static 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 | ||
1062 | void clear_kprobe_trace_event(struct kprobe_trace_event *tev) | 1166 | static 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 */ |
1125 | static struct strlist *get_kprobe_trace_command_rawlist(int fd) | 1229 | static 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) | |||
1190 | int show_perf_probe_events(void) | 1294 | int 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 */ |
1233 | static struct strlist *get_kprobe_trace_event_names(int fd, bool include_group) | 1337 | static 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 | ||
1269 | static int write_kprobe_trace_event(int fd, struct kprobe_trace_event *tev) | 1372 | static 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 | ||
1328 | static int __add_kprobe_trace_events(struct perf_probe_event *pev, | 1431 | static 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 | ||
1414 | static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, | 1517 | static 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; |
1481 | error: | 1584 | error: |
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 | ||
1488 | struct __event_package { | 1591 | struct __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 | ||
@@ -1510,7 +1613,7 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, | |||
1510 | for (i = 0; i < npevs; i++) { | 1613 | for (i = 0; i < npevs; i++) { |
1511 | pkgs[i].pev = &pevs[i]; | 1614 | pkgs[i].pev = &pevs[i]; |
1512 | /* Convert with or without debuginfo */ | 1615 | /* Convert with or without debuginfo */ |
1513 | ret = convert_to_kprobe_trace_events(pkgs[i].pev, | 1616 | ret = convert_to_probe_trace_events(pkgs[i].pev, |
1514 | &pkgs[i].tevs, max_tevs); | 1617 | &pkgs[i].tevs, max_tevs); |
1515 | if (ret < 0) | 1618 | if (ret < 0) |
1516 | goto end; | 1619 | goto end; |
@@ -1519,24 +1622,24 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, | |||
1519 | 1622 | ||
1520 | /* Loop 2: add all events */ | 1623 | /* Loop 2: add all events */ |
1521 | for (i = 0; i < npevs && ret >= 0; i++) | 1624 | for (i = 0; i < npevs && ret >= 0; i++) |
1522 | ret = __add_kprobe_trace_events(pkgs[i].pev, pkgs[i].tevs, | 1625 | ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs, |
1523 | pkgs[i].ntevs, force_add); | 1626 | pkgs[i].ntevs, force_add); |
1524 | end: | 1627 | end: |
1525 | /* Loop 3: cleanup trace events */ | 1628 | /* Loop 3: cleanup trace events */ |
1526 | for (i = 0; i < npevs; i++) | 1629 | for (i = 0; i < npevs; i++) |
1527 | for (j = 0; j < pkgs[i].ntevs; j++) | 1630 | for (j = 0; j < pkgs[i].ntevs; j++) |
1528 | clear_kprobe_trace_event(&pkgs[i].tevs[j]); | 1631 | clear_probe_trace_event(&pkgs[i].tevs[j]); |
1529 | 1632 | ||
1530 | return ret; | 1633 | return ret; |
1531 | } | 1634 | } |
1532 | 1635 | ||
1533 | static int __del_trace_kprobe_event(int fd, struct str_node *ent) | 1636 | static int __del_trace_probe_event(int fd, struct str_node *ent) |
1534 | { | 1637 | { |
1535 | char *p; | 1638 | char *p; |
1536 | char buf[128]; | 1639 | char buf[128]; |
1537 | int ret; | 1640 | int ret; |
1538 | 1641 | ||
1539 | /* Convert from perf-probe event to trace-kprobe event */ | 1642 | /* Convert from perf-probe event to trace-probe event */ |
1540 | ret = e_snprintf(buf, 128, "-:%s", ent->s); | 1643 | ret = e_snprintf(buf, 128, "-:%s", ent->s); |
1541 | if (ret < 0) | 1644 | if (ret < 0) |
1542 | goto error; | 1645 | goto error; |
@@ -1562,7 +1665,7 @@ error: | |||
1562 | return ret; | 1665 | return ret; |
1563 | } | 1666 | } |
1564 | 1667 | ||
1565 | static int del_trace_kprobe_event(int fd, const char *group, | 1668 | static int del_trace_probe_event(int fd, const char *group, |
1566 | const char *event, struct strlist *namelist) | 1669 | const char *event, struct strlist *namelist) |
1567 | { | 1670 | { |
1568 | char buf[128]; | 1671 | char buf[128]; |
@@ -1579,7 +1682,7 @@ static int del_trace_kprobe_event(int fd, const char *group, | |||
1579 | strlist__for_each_safe(ent, n, namelist) | 1682 | strlist__for_each_safe(ent, n, namelist) |
1580 | if (strglobmatch(ent->s, buf)) { | 1683 | if (strglobmatch(ent->s, buf)) { |
1581 | found++; | 1684 | found++; |
1582 | ret = __del_trace_kprobe_event(fd, ent); | 1685 | ret = __del_trace_probe_event(fd, ent); |
1583 | if (ret < 0) | 1686 | if (ret < 0) |
1584 | break; | 1687 | break; |
1585 | strlist__remove(namelist, ent); | 1688 | strlist__remove(namelist, ent); |
@@ -1588,7 +1691,7 @@ static int del_trace_kprobe_event(int fd, const char *group, | |||
1588 | ent = strlist__find(namelist, buf); | 1691 | ent = strlist__find(namelist, buf); |
1589 | if (ent) { | 1692 | if (ent) { |
1590 | found++; | 1693 | found++; |
1591 | ret = __del_trace_kprobe_event(fd, ent); | 1694 | ret = __del_trace_probe_event(fd, ent); |
1592 | if (ret >= 0) | 1695 | if (ret >= 0) |
1593 | strlist__remove(namelist, ent); | 1696 | strlist__remove(namelist, ent); |
1594 | } | 1697 | } |
@@ -1612,7 +1715,7 @@ int del_perf_probe_events(struct strlist *dellist) | |||
1612 | return fd; | 1715 | return fd; |
1613 | 1716 | ||
1614 | /* Get current event names */ | 1717 | /* Get current event names */ |
1615 | namelist = get_kprobe_trace_event_names(fd, true); | 1718 | namelist = get_probe_trace_event_names(fd, true); |
1616 | if (namelist == NULL) | 1719 | if (namelist == NULL) |
1617 | return -EINVAL; | 1720 | return -EINVAL; |
1618 | 1721 | ||
@@ -1633,7 +1736,7 @@ int del_perf_probe_events(struct strlist *dellist) | |||
1633 | event = str; | 1736 | event = str; |
1634 | } | 1737 | } |
1635 | pr_debug("Group: %s, Event: %s\n", group, event); | 1738 | pr_debug("Group: %s, Event: %s\n", group, event); |
1636 | ret = del_trace_kprobe_event(fd, group, event, namelist); | 1739 | ret = del_trace_probe_event(fd, group, event, namelist); |
1637 | free(str); | 1740 | free(str); |
1638 | if (ret < 0) | 1741 | if (ret < 0) |
1639 | break; | 1742 | 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 @@ | |||
7 | extern bool probe_event_dry_run; | 7 | extern bool probe_event_dry_run; |
8 | 8 | ||
9 | /* kprobe-tracer tracing point */ | 9 | /* kprobe-tracer tracing point */ |
10 | struct kprobe_trace_point { | 10 | struct 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 */ |
17 | struct kprobe_trace_arg_ref { | 17 | struct 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 */ |
23 | struct kprobe_trace_arg { | 23 | struct 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) */ |
31 | struct kprobe_trace_event { | 31 | struct 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 { | |||
50 | struct perf_probe_arg_field { | 50 | struct 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 */ |
92 | extern int parse_perf_probe_command(const char *cmd, | 94 | extern int parse_perf_probe_command(const char *cmd, |
93 | struct perf_probe_event *pev); | 95 | struct perf_probe_event *pev); |
94 | extern 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 */ |
98 | extern char *synthesize_perf_probe_command(struct perf_probe_event *pev); | 98 | extern char *synthesize_perf_probe_command(struct perf_probe_event *pev); |
99 | extern char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev); | 99 | extern char *synthesize_probe_trace_command(struct probe_trace_event *tev); |
100 | extern int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, | 100 | extern 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 */ |
104 | extern bool perf_probe_event_need_dwarf(struct perf_probe_event *pev); | 104 | extern bool perf_probe_event_need_dwarf(struct perf_probe_event *pev); |
105 | 105 | ||
106 | /* Convert from kprobe_trace_event to perf_probe_event */ | ||
107 | extern 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 */ |
111 | extern void clear_perf_probe_event(struct perf_probe_event *pev); | 107 | extern void clear_perf_probe_event(struct perf_probe_event *pev); |
112 | extern void clear_kprobe_trace_event(struct kprobe_trace_event *tev); | ||
113 | 108 | ||
114 | /* Command string to line-range */ | 109 | /* Command string to line-range */ |
115 | extern int parse_line_range_desc(const char *cmd, struct line_range *lr); | 110 | extern 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..840f1aabbb74 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -37,6 +37,7 @@ | |||
37 | #include "event.h" | 37 | #include "event.h" |
38 | #include "debug.h" | 38 | #include "debug.h" |
39 | #include "util.h" | 39 | #include "util.h" |
40 | #include "symbol.h" | ||
40 | #include "probe-finder.h" | 41 | #include "probe-finder.h" |
41 | 42 | ||
42 | /* Kprobe tracer basic type is up to u64 */ | 43 | /* Kprobe tracer basic type is up to u64 */ |
@@ -143,12 +144,21 @@ static const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname) | |||
143 | return src; | 144 | return src; |
144 | } | 145 | } |
145 | 146 | ||
147 | /* Get DW_AT_comp_dir (should be NULL with older gcc) */ | ||
148 | static const char *cu_get_comp_dir(Dwarf_Die *cu_die) | ||
149 | { | ||
150 | Dwarf_Attribute attr; | ||
151 | if (dwarf_attr(cu_die, DW_AT_comp_dir, &attr) == NULL) | ||
152 | return NULL; | ||
153 | return dwarf_formstring(&attr); | ||
154 | } | ||
155 | |||
146 | /* Compare diename and tname */ | 156 | /* Compare diename and tname */ |
147 | static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) | 157 | static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) |
148 | { | 158 | { |
149 | const char *name; | 159 | const char *name; |
150 | name = dwarf_diename(dw_die); | 160 | name = dwarf_diename(dw_die); |
151 | return name ? strcmp(tname, name) : -1; | 161 | return name ? (strcmp(tname, name) == 0) : false; |
152 | } | 162 | } |
153 | 163 | ||
154 | /* Get type die, but skip qualifiers and typedef */ | 164 | /* Get type die, but skip qualifiers and typedef */ |
@@ -319,7 +329,7 @@ static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data) | |||
319 | tag = dwarf_tag(die_mem); | 329 | tag = dwarf_tag(die_mem); |
320 | if ((tag == DW_TAG_formal_parameter || | 330 | if ((tag == DW_TAG_formal_parameter || |
321 | tag == DW_TAG_variable) && | 331 | tag == DW_TAG_variable) && |
322 | (die_compare_name(die_mem, name) == 0)) | 332 | die_compare_name(die_mem, name)) |
323 | return DIE_FIND_CB_FOUND; | 333 | return DIE_FIND_CB_FOUND; |
324 | 334 | ||
325 | return DIE_FIND_CB_CONTINUE; | 335 | return DIE_FIND_CB_CONTINUE; |
@@ -338,7 +348,7 @@ static int __die_find_member_cb(Dwarf_Die *die_mem, void *data) | |||
338 | const char *name = data; | 348 | const char *name = data; |
339 | 349 | ||
340 | if ((dwarf_tag(die_mem) == DW_TAG_member) && | 350 | if ((dwarf_tag(die_mem) == DW_TAG_member) && |
341 | (die_compare_name(die_mem, name) == 0)) | 351 | die_compare_name(die_mem, name)) |
342 | return DIE_FIND_CB_FOUND; | 352 | return DIE_FIND_CB_FOUND; |
343 | 353 | ||
344 | return DIE_FIND_CB_SIBLING; | 354 | return DIE_FIND_CB_SIBLING; |
@@ -356,14 +366,50 @@ static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name, | |||
356 | * Probe finder related functions | 366 | * Probe finder related functions |
357 | */ | 367 | */ |
358 | 368 | ||
369 | static struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs) | ||
370 | { | ||
371 | struct probe_trace_arg_ref *ref; | ||
372 | ref = zalloc(sizeof(struct probe_trace_arg_ref)); | ||
373 | if (ref != NULL) | ||
374 | ref->offset = offs; | ||
375 | return ref; | ||
376 | } | ||
377 | |||
359 | /* Show a location */ | 378 | /* Show a location */ |
360 | static int convert_location(Dwarf_Op *op, struct probe_finder *pf) | 379 | static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf) |
361 | { | 380 | { |
381 | Dwarf_Attribute attr; | ||
382 | Dwarf_Op *op; | ||
383 | size_t nops; | ||
362 | unsigned int regn; | 384 | unsigned int regn; |
363 | Dwarf_Word offs = 0; | 385 | Dwarf_Word offs = 0; |
364 | bool ref = false; | 386 | bool ref = false; |
365 | const char *regs; | 387 | const char *regs; |
366 | struct kprobe_trace_arg *tvar = pf->tvar; | 388 | struct probe_trace_arg *tvar = pf->tvar; |
389 | int ret; | ||
390 | |||
391 | /* TODO: handle more than 1 exprs */ | ||
392 | if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL || | ||
393 | dwarf_getlocation_addr(&attr, pf->addr, &op, &nops, 1) <= 0 || | ||
394 | nops == 0) { | ||
395 | /* TODO: Support const_value */ | ||
396 | pr_err("Failed to find the location of %s at this address.\n" | ||
397 | " Perhaps, it has been optimized out.\n", pf->pvar->var); | ||
398 | return -ENOENT; | ||
399 | } | ||
400 | |||
401 | if (op->atom == DW_OP_addr) { | ||
402 | /* Static variables on memory (not stack), make @varname */ | ||
403 | ret = strlen(dwarf_diename(vr_die)); | ||
404 | tvar->value = zalloc(ret + 2); | ||
405 | if (tvar->value == NULL) | ||
406 | return -ENOMEM; | ||
407 | snprintf(tvar->value, ret + 2, "@%s", dwarf_diename(vr_die)); | ||
408 | tvar->ref = alloc_trace_arg_ref((long)offs); | ||
409 | if (tvar->ref == NULL) | ||
410 | return -ENOMEM; | ||
411 | return 0; | ||
412 | } | ||
367 | 413 | ||
368 | /* If this is based on frame buffer, set the offset */ | 414 | /* If this is based on frame buffer, set the offset */ |
369 | if (op->atom == DW_OP_fbreg) { | 415 | if (op->atom == DW_OP_fbreg) { |
@@ -405,27 +451,72 @@ static int convert_location(Dwarf_Op *op, struct probe_finder *pf) | |||
405 | return -ENOMEM; | 451 | return -ENOMEM; |
406 | 452 | ||
407 | if (ref) { | 453 | if (ref) { |
408 | tvar->ref = zalloc(sizeof(struct kprobe_trace_arg_ref)); | 454 | tvar->ref = alloc_trace_arg_ref((long)offs); |
409 | if (tvar->ref == NULL) | 455 | if (tvar->ref == NULL) |
410 | return -ENOMEM; | 456 | return -ENOMEM; |
411 | tvar->ref->offset = (long)offs; | ||
412 | } | 457 | } |
413 | return 0; | 458 | return 0; |
414 | } | 459 | } |
415 | 460 | ||
416 | static int convert_variable_type(Dwarf_Die *vr_die, | 461 | static int convert_variable_type(Dwarf_Die *vr_die, |
417 | struct kprobe_trace_arg *targ) | 462 | struct probe_trace_arg *tvar, |
463 | const char *cast) | ||
418 | { | 464 | { |
465 | struct probe_trace_arg_ref **ref_ptr = &tvar->ref; | ||
419 | Dwarf_Die type; | 466 | Dwarf_Die type; |
420 | char buf[16]; | 467 | char buf[16]; |
421 | int ret; | 468 | int ret; |
422 | 469 | ||
470 | /* TODO: check all types */ | ||
471 | if (cast && strcmp(cast, "string") != 0) { | ||
472 | /* Non string type is OK */ | ||
473 | tvar->type = strdup(cast); | ||
474 | return (tvar->type == NULL) ? -ENOMEM : 0; | ||
475 | } | ||
476 | |||
423 | if (die_get_real_type(vr_die, &type) == NULL) { | 477 | if (die_get_real_type(vr_die, &type) == NULL) { |
424 | pr_warning("Failed to get a type information of %s.\n", | 478 | pr_warning("Failed to get a type information of %s.\n", |
425 | dwarf_diename(vr_die)); | 479 | dwarf_diename(vr_die)); |
426 | return -ENOENT; | 480 | return -ENOENT; |
427 | } | 481 | } |
428 | 482 | ||
483 | pr_debug("%s type is %s.\n", | ||
484 | dwarf_diename(vr_die), dwarf_diename(&type)); | ||
485 | |||
486 | if (cast && strcmp(cast, "string") == 0) { /* String type */ | ||
487 | ret = dwarf_tag(&type); | ||
488 | if (ret != DW_TAG_pointer_type && | ||
489 | ret != DW_TAG_array_type) { | ||
490 | pr_warning("Failed to cast into string: " | ||
491 | "%s(%s) is not a pointer nor array.", | ||
492 | dwarf_diename(vr_die), dwarf_diename(&type)); | ||
493 | return -EINVAL; | ||
494 | } | ||
495 | if (ret == DW_TAG_pointer_type) { | ||
496 | if (die_get_real_type(&type, &type) == NULL) { | ||
497 | pr_warning("Failed to get a type information."); | ||
498 | return -ENOENT; | ||
499 | } | ||
500 | while (*ref_ptr) | ||
501 | ref_ptr = &(*ref_ptr)->next; | ||
502 | /* Add new reference with offset +0 */ | ||
503 | *ref_ptr = zalloc(sizeof(struct probe_trace_arg_ref)); | ||
504 | if (*ref_ptr == NULL) { | ||
505 | pr_warning("Out of memory error\n"); | ||
506 | return -ENOMEM; | ||
507 | } | ||
508 | } | ||
509 | if (!die_compare_name(&type, "char") && | ||
510 | !die_compare_name(&type, "unsigned char")) { | ||
511 | pr_warning("Failed to cast into string: " | ||
512 | "%s is not (unsigned) char *.", | ||
513 | dwarf_diename(vr_die)); | ||
514 | return -EINVAL; | ||
515 | } | ||
516 | tvar->type = strdup(cast); | ||
517 | return (tvar->type == NULL) ? -ENOMEM : 0; | ||
518 | } | ||
519 | |||
429 | ret = die_get_byte_size(&type) * 8; | 520 | ret = die_get_byte_size(&type) * 8; |
430 | if (ret) { | 521 | if (ret) { |
431 | /* Check the bitwidth */ | 522 | /* Check the bitwidth */ |
@@ -445,8 +536,8 @@ static int convert_variable_type(Dwarf_Die *vr_die, | |||
445 | strerror(-ret)); | 536 | strerror(-ret)); |
446 | return ret; | 537 | return ret; |
447 | } | 538 | } |
448 | targ->type = strdup(buf); | 539 | tvar->type = strdup(buf); |
449 | if (targ->type == NULL) | 540 | if (tvar->type == NULL) |
450 | return -ENOMEM; | 541 | return -ENOMEM; |
451 | } | 542 | } |
452 | return 0; | 543 | return 0; |
@@ -454,22 +545,50 @@ static int convert_variable_type(Dwarf_Die *vr_die, | |||
454 | 545 | ||
455 | static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, | 546 | static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, |
456 | struct perf_probe_arg_field *field, | 547 | struct perf_probe_arg_field *field, |
457 | struct kprobe_trace_arg_ref **ref_ptr, | 548 | struct probe_trace_arg_ref **ref_ptr, |
458 | Dwarf_Die *die_mem) | 549 | Dwarf_Die *die_mem) |
459 | { | 550 | { |
460 | struct kprobe_trace_arg_ref *ref = *ref_ptr; | 551 | struct probe_trace_arg_ref *ref = *ref_ptr; |
461 | Dwarf_Die type; | 552 | Dwarf_Die type; |
462 | Dwarf_Word offs; | 553 | Dwarf_Word offs; |
463 | int ret; | 554 | int ret, tag; |
464 | 555 | ||
465 | pr_debug("converting %s in %s\n", field->name, varname); | 556 | pr_debug("converting %s in %s\n", field->name, varname); |
466 | if (die_get_real_type(vr_die, &type) == NULL) { | 557 | if (die_get_real_type(vr_die, &type) == NULL) { |
467 | pr_warning("Failed to get the type of %s.\n", varname); | 558 | pr_warning("Failed to get the type of %s.\n", varname); |
468 | return -ENOENT; | 559 | return -ENOENT; |
469 | } | 560 | } |
470 | 561 | pr_debug2("Var real type: (%x)\n", (unsigned)dwarf_dieoffset(&type)); | |
471 | /* Check the pointer and dereference */ | 562 | tag = dwarf_tag(&type); |
472 | if (dwarf_tag(&type) == DW_TAG_pointer_type) { | 563 | |
564 | if (field->name[0] == '[' && | ||
565 | (tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)) { | ||
566 | if (field->next) | ||
567 | /* Save original type for next field */ | ||
568 | memcpy(die_mem, &type, sizeof(*die_mem)); | ||
569 | /* Get the type of this array */ | ||
570 | if (die_get_real_type(&type, &type) == NULL) { | ||
571 | pr_warning("Failed to get the type of %s.\n", varname); | ||
572 | return -ENOENT; | ||
573 | } | ||
574 | pr_debug2("Array real type: (%x)\n", | ||
575 | (unsigned)dwarf_dieoffset(&type)); | ||
576 | if (tag == DW_TAG_pointer_type) { | ||
577 | ref = zalloc(sizeof(struct probe_trace_arg_ref)); | ||
578 | if (ref == NULL) | ||
579 | return -ENOMEM; | ||
580 | if (*ref_ptr) | ||
581 | (*ref_ptr)->next = ref; | ||
582 | else | ||
583 | *ref_ptr = ref; | ||
584 | } | ||
585 | ref->offset += die_get_byte_size(&type) * field->index; | ||
586 | if (!field->next) | ||
587 | /* Save vr_die for converting types */ | ||
588 | memcpy(die_mem, vr_die, sizeof(*die_mem)); | ||
589 | goto next; | ||
590 | } else if (tag == DW_TAG_pointer_type) { | ||
591 | /* Check the pointer and dereference */ | ||
473 | if (!field->ref) { | 592 | if (!field->ref) { |
474 | pr_err("Semantic error: %s must be referred by '->'\n", | 593 | pr_err("Semantic error: %s must be referred by '->'\n", |
475 | field->name); | 594 | field->name); |
@@ -486,7 +605,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, | |||
486 | return -EINVAL; | 605 | return -EINVAL; |
487 | } | 606 | } |
488 | 607 | ||
489 | ref = zalloc(sizeof(struct kprobe_trace_arg_ref)); | 608 | ref = zalloc(sizeof(struct probe_trace_arg_ref)); |
490 | if (ref == NULL) | 609 | if (ref == NULL) |
491 | return -ENOMEM; | 610 | return -ENOMEM; |
492 | if (*ref_ptr) | 611 | if (*ref_ptr) |
@@ -495,10 +614,15 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, | |||
495 | *ref_ptr = ref; | 614 | *ref_ptr = ref; |
496 | } else { | 615 | } else { |
497 | /* Verify it is a data structure */ | 616 | /* Verify it is a data structure */ |
498 | if (dwarf_tag(&type) != DW_TAG_structure_type) { | 617 | if (tag != DW_TAG_structure_type) { |
499 | pr_warning("%s is not a data structure.\n", varname); | 618 | pr_warning("%s is not a data structure.\n", varname); |
500 | return -EINVAL; | 619 | return -EINVAL; |
501 | } | 620 | } |
621 | if (field->name[0] == '[') { | ||
622 | pr_err("Semantic error: %s is not a pointor nor array.", | ||
623 | varname); | ||
624 | return -EINVAL; | ||
625 | } | ||
502 | if (field->ref) { | 626 | if (field->ref) { |
503 | pr_err("Semantic error: %s must be referred by '.'\n", | 627 | pr_err("Semantic error: %s must be referred by '.'\n", |
504 | field->name); | 628 | field->name); |
@@ -525,6 +649,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, | |||
525 | } | 649 | } |
526 | ref->offset += (long)offs; | 650 | ref->offset += (long)offs; |
527 | 651 | ||
652 | next: | ||
528 | /* Converting next field */ | 653 | /* Converting next field */ |
529 | if (field->next) | 654 | if (field->next) |
530 | return convert_variable_fields(die_mem, field->name, | 655 | return convert_variable_fields(die_mem, field->name, |
@@ -536,51 +661,32 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, | |||
536 | /* Show a variables in kprobe event format */ | 661 | /* Show a variables in kprobe event format */ |
537 | static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) | 662 | static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) |
538 | { | 663 | { |
539 | Dwarf_Attribute attr; | ||
540 | Dwarf_Die die_mem; | 664 | Dwarf_Die die_mem; |
541 | Dwarf_Op *expr; | ||
542 | size_t nexpr; | ||
543 | int ret; | 665 | int ret; |
544 | 666 | ||
545 | if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL) | 667 | pr_debug("Converting variable %s into trace event.\n", |
546 | goto error; | 668 | 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 | 669 | ||
552 | ret = convert_location(expr, pf); | 670 | ret = convert_variable_location(vr_die, pf); |
553 | if (ret == 0 && pf->pvar->field) { | 671 | if (ret == 0 && pf->pvar->field) { |
554 | ret = convert_variable_fields(vr_die, pf->pvar->var, | 672 | ret = convert_variable_fields(vr_die, pf->pvar->var, |
555 | pf->pvar->field, &pf->tvar->ref, | 673 | pf->pvar->field, &pf->tvar->ref, |
556 | &die_mem); | 674 | &die_mem); |
557 | vr_die = &die_mem; | 675 | vr_die = &die_mem; |
558 | } | 676 | } |
559 | if (ret == 0) { | 677 | if (ret == 0) |
560 | if (pf->pvar->type) { | 678 | 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. */ | 679 | /* *expr will be cached in libdw. Don't free it. */ |
568 | return ret; | 680 | return ret; |
569 | error: | ||
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 | } | 681 | } |
575 | 682 | ||
576 | /* Find a variable in a subprogram die */ | 683 | /* Find a variable in a subprogram die */ |
577 | static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) | 684 | static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) |
578 | { | 685 | { |
579 | Dwarf_Die vr_die; | 686 | Dwarf_Die vr_die, *scopes; |
580 | char buf[32], *ptr; | 687 | char buf[32], *ptr; |
581 | int ret; | 688 | int ret, nscopes; |
582 | 689 | ||
583 | /* TODO: Support arrays */ | ||
584 | if (pf->pvar->name) | 690 | if (pf->pvar->name) |
585 | pf->tvar->name = strdup(pf->pvar->name); | 691 | pf->tvar->name = strdup(pf->pvar->name); |
586 | else { | 692 | else { |
@@ -607,18 +713,32 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
607 | pr_debug("Searching '%s' variable in context.\n", | 713 | pr_debug("Searching '%s' variable in context.\n", |
608 | pf->pvar->var); | 714 | pf->pvar->var); |
609 | /* Search child die for local variables and parameters. */ | 715 | /* Search child die for local variables and parameters. */ |
610 | if (!die_find_variable(sp_die, pf->pvar->var, &vr_die)) { | 716 | if (die_find_variable(sp_die, pf->pvar->var, &vr_die)) |
717 | ret = convert_variable(&vr_die, pf); | ||
718 | else { | ||
719 | /* Search upper class */ | ||
720 | nscopes = dwarf_getscopes_die(sp_die, &scopes); | ||
721 | if (nscopes > 0) { | ||
722 | ret = dwarf_getscopevar(scopes, nscopes, pf->pvar->var, | ||
723 | 0, NULL, 0, 0, &vr_die); | ||
724 | if (ret >= 0) | ||
725 | ret = convert_variable(&vr_die, pf); | ||
726 | else | ||
727 | ret = -ENOENT; | ||
728 | free(scopes); | ||
729 | } else | ||
730 | ret = -ENOENT; | ||
731 | } | ||
732 | if (ret < 0) | ||
611 | pr_warning("Failed to find '%s' in this function.\n", | 733 | pr_warning("Failed to find '%s' in this function.\n", |
612 | pf->pvar->var); | 734 | pf->pvar->var); |
613 | return -ENOENT; | 735 | return ret; |
614 | } | ||
615 | return convert_variable(&vr_die, pf); | ||
616 | } | 736 | } |
617 | 737 | ||
618 | /* Show a probe point to output buffer */ | 738 | /* Show a probe point to output buffer */ |
619 | static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) | 739 | static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) |
620 | { | 740 | { |
621 | struct kprobe_trace_event *tev; | 741 | struct probe_trace_event *tev; |
622 | Dwarf_Addr eaddr; | 742 | Dwarf_Addr eaddr; |
623 | Dwarf_Die die_mem; | 743 | Dwarf_Die die_mem; |
624 | const char *name; | 744 | const char *name; |
@@ -683,7 +803,7 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
683 | 803 | ||
684 | /* Find each argument */ | 804 | /* Find each argument */ |
685 | tev->nargs = pf->pev->nargs; | 805 | tev->nargs = pf->pev->nargs; |
686 | tev->args = zalloc(sizeof(struct kprobe_trace_arg) * tev->nargs); | 806 | tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); |
687 | if (tev->args == NULL) | 807 | if (tev->args == NULL) |
688 | return -ENOMEM; | 808 | return -ENOMEM; |
689 | for (i = 0; i < pf->pev->nargs; i++) { | 809 | for (i = 0; i < pf->pev->nargs; i++) { |
@@ -897,7 +1017,7 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) | |||
897 | 1017 | ||
898 | /* Check tag and diename */ | 1018 | /* Check tag and diename */ |
899 | if (dwarf_tag(sp_die) != DW_TAG_subprogram || | 1019 | if (dwarf_tag(sp_die) != DW_TAG_subprogram || |
900 | die_compare_name(sp_die, pp->function) != 0) | 1020 | !die_compare_name(sp_die, pp->function)) |
901 | return DWARF_CB_OK; | 1021 | return DWARF_CB_OK; |
902 | 1022 | ||
903 | pf->fname = dwarf_decl_file(sp_die); | 1023 | pf->fname = dwarf_decl_file(sp_die); |
@@ -940,9 +1060,9 @@ static int find_probe_point_by_func(struct probe_finder *pf) | |||
940 | return _param.retval; | 1060 | return _param.retval; |
941 | } | 1061 | } |
942 | 1062 | ||
943 | /* Find kprobe_trace_events specified by perf_probe_event from debuginfo */ | 1063 | /* Find probe_trace_events specified by perf_probe_event from debuginfo */ |
944 | int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, | 1064 | int find_probe_trace_events(int fd, struct perf_probe_event *pev, |
945 | struct kprobe_trace_event **tevs, int max_tevs) | 1065 | struct probe_trace_event **tevs, int max_tevs) |
946 | { | 1066 | { |
947 | struct probe_finder pf = {.pev = pev, .max_tevs = max_tevs}; | 1067 | struct probe_finder pf = {.pev = pev, .max_tevs = max_tevs}; |
948 | struct perf_probe_point *pp = &pev->point; | 1068 | struct perf_probe_point *pp = &pev->point; |
@@ -952,7 +1072,7 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, | |||
952 | Dwarf *dbg; | 1072 | Dwarf *dbg; |
953 | int ret = 0; | 1073 | int ret = 0; |
954 | 1074 | ||
955 | pf.tevs = zalloc(sizeof(struct kprobe_trace_event) * max_tevs); | 1075 | pf.tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs); |
956 | if (pf.tevs == NULL) | 1076 | if (pf.tevs == NULL) |
957 | return -ENOMEM; | 1077 | return -ENOMEM; |
958 | *tevs = pf.tevs; | 1078 | *tevs = pf.tevs; |
@@ -1096,7 +1216,7 @@ end: | |||
1096 | static int line_range_add_line(const char *src, unsigned int lineno, | 1216 | static int line_range_add_line(const char *src, unsigned int lineno, |
1097 | struct line_range *lr) | 1217 | struct line_range *lr) |
1098 | { | 1218 | { |
1099 | /* Copy real path */ | 1219 | /* Copy source path */ |
1100 | if (!lr->path) { | 1220 | if (!lr->path) { |
1101 | lr->path = strdup(src); | 1221 | lr->path = strdup(src); |
1102 | if (lr->path == NULL) | 1222 | if (lr->path == NULL) |
@@ -1220,7 +1340,7 @@ static int line_range_search_cb(Dwarf_Die *sp_die, void *data) | |||
1220 | struct line_range *lr = lf->lr; | 1340 | struct line_range *lr = lf->lr; |
1221 | 1341 | ||
1222 | if (dwarf_tag(sp_die) == DW_TAG_subprogram && | 1342 | if (dwarf_tag(sp_die) == DW_TAG_subprogram && |
1223 | die_compare_name(sp_die, lr->function) == 0) { | 1343 | die_compare_name(sp_die, lr->function)) { |
1224 | lf->fname = dwarf_decl_file(sp_die); | 1344 | lf->fname = dwarf_decl_file(sp_die); |
1225 | dwarf_decl_line(sp_die, &lr->offset); | 1345 | dwarf_decl_line(sp_die, &lr->offset); |
1226 | pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset); | 1346 | pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset); |
@@ -1263,6 +1383,7 @@ int find_line_range(int fd, struct line_range *lr) | |||
1263 | size_t cuhl; | 1383 | size_t cuhl; |
1264 | Dwarf_Die *diep; | 1384 | Dwarf_Die *diep; |
1265 | Dwarf *dbg; | 1385 | Dwarf *dbg; |
1386 | const char *comp_dir; | ||
1266 | 1387 | ||
1267 | dbg = dwarf_begin(fd, DWARF_C_READ); | 1388 | dbg = dwarf_begin(fd, DWARF_C_READ); |
1268 | if (!dbg) { | 1389 | if (!dbg) { |
@@ -1298,7 +1419,18 @@ int find_line_range(int fd, struct line_range *lr) | |||
1298 | } | 1419 | } |
1299 | off = noff; | 1420 | off = noff; |
1300 | } | 1421 | } |
1301 | pr_debug("path: %lx\n", (unsigned long)lr->path); | 1422 | |
1423 | /* Store comp_dir */ | ||
1424 | if (lf.found) { | ||
1425 | comp_dir = cu_get_comp_dir(&lf.cu_die); | ||
1426 | if (comp_dir) { | ||
1427 | lr->comp_dir = strdup(comp_dir); | ||
1428 | if (!lr->comp_dir) | ||
1429 | ret = -ENOMEM; | ||
1430 | } | ||
1431 | } | ||
1432 | |||
1433 | pr_debug("path: %s\n", lr->path); | ||
1302 | dwarf_end(dbg); | 1434 | dwarf_end(dbg); |
1303 | 1435 | ||
1304 | return (ret < 0) ? ret : lf.found; | 1436 | 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 */ |
20 | extern int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, | 20 | extern 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 | ||
34 | struct probe_finder { | 34 | struct 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 | ||
56 | struct line_finder { | 56 | struct line_finder { |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index c422cd676313..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 | ||
82 | static 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 | |||
80 | struct perf_session *perf_session__new(const char *filename, int mode, bool force, bool repipe) | 88 | struct 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; |
@@ -94,8 +102,6 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc | |||
94 | self->hists_tree = RB_ROOT; | 102 | self->hists_tree = RB_ROOT; |
95 | self->last_match = NULL; | 103 | self->last_match = NULL; |
96 | self->mmap_window = 32; | 104 | self->mmap_window = 32; |
97 | self->cwd = NULL; | ||
98 | self->cwdlen = 0; | ||
99 | self->machines = RB_ROOT; | 105 | self->machines = RB_ROOT; |
100 | self->repipe = repipe; | 106 | self->repipe = repipe; |
101 | INIT_LIST_HEAD(&self->ordered_samples.samples_head); | 107 | INIT_LIST_HEAD(&self->ordered_samples.samples_head); |
@@ -124,16 +130,43 @@ out_delete: | |||
124 | return NULL; | 130 | return NULL; |
125 | } | 131 | } |
126 | 132 | ||
133 | static 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 | |||
143 | static 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 | |||
127 | void perf_session__delete(struct perf_session *self) | 156 | void perf_session__delete(struct perf_session *self) |
128 | { | 157 | { |
129 | 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); | ||
130 | close(self->fd); | 163 | close(self->fd); |
131 | free(self->cwd); | ||
132 | free(self); | 164 | free(self); |
133 | } | 165 | } |
134 | 166 | ||
135 | void perf_session__remove_thread(struct perf_session *self, struct thread *th) | 167 | void perf_session__remove_thread(struct perf_session *self, struct thread *th) |
136 | { | 168 | { |
169 | self->last_match = NULL; | ||
137 | rb_erase(&th->rb_node, &self->threads); | 170 | rb_erase(&th->rb_node, &self->threads); |
138 | /* | 171 | /* |
139 | * We may have references to this thread, for instance in some hist_entry | 172 | * We may have references to this thread, for instance in some hist_entry |
@@ -830,23 +863,6 @@ int perf_session__process_events(struct perf_session *self, | |||
830 | if (perf_session__register_idle_thread(self) == NULL) | 863 | if (perf_session__register_idle_thread(self) == NULL) |
831 | return -ENOMEM; | 864 | return -ENOMEM; |
832 | 865 | ||
833 | if (!symbol_conf.full_paths) { | ||
834 | char bf[PATH_MAX]; | ||
835 | |||
836 | if (getcwd(bf, sizeof(bf)) == NULL) { | ||
837 | err = -errno; | ||
838 | out_getcwd_err: | ||
839 | pr_err("failed to get the current directory\n"); | ||
840 | goto out_err; | ||
841 | } | ||
842 | self->cwd = strdup(bf); | ||
843 | if (self->cwd == NULL) { | ||
844 | err = -ENOMEM; | ||
845 | goto out_getcwd_err; | ||
846 | } | ||
847 | self->cwdlen = strlen(self->cwd); | ||
848 | } | ||
849 | |||
850 | if (!self->fd_pipe) | 866 | if (!self->fd_pipe) |
851 | err = __perf_session__process_events(self, | 867 | err = __perf_session__process_events(self, |
852 | self->header.data_offset, | 868 | self->header.data_offset, |
@@ -854,7 +870,7 @@ out_getcwd_err: | |||
854 | self->size, ops); | 870 | self->size, ops); |
855 | else | 871 | else |
856 | err = __perf_session__process_pipe_events(self, ops); | 872 | err = __perf_session__process_pipe_events(self, ops); |
857 | out_err: | 873 | |
858 | return err; | 874 | return err; |
859 | } | 875 | } |
860 | 876 | ||
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 2316cb5a4116..1c61a4f4aa8a 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 | ||
3 | regex_t parent_regex; | 4 | regex_t parent_regex; |
4 | const char default_parent_pattern[] = "^sys_|^do_page_fault"; | 5 | const char default_parent_pattern[] = "^sys_|^do_page_fault"; |
@@ -10,10 +11,6 @@ int sort__has_parent = 0; | |||
10 | 11 | ||
11 | enum sort_type sort__first_dimension; | 12 | enum sort_type sort__first_dimension; |
12 | 13 | ||
13 | unsigned int dsos__col_width; | ||
14 | unsigned int comms__col_width; | ||
15 | unsigned int threads__col_width; | ||
16 | static unsigned int parent_symbol__col_width; | ||
17 | char * field_sep; | 14 | char * field_sep; |
18 | 15 | ||
19 | LIST_HEAD(hist_entry__sort_list); | 16 | LIST_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); |
29 | static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, | 26 | static 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); |
28 | static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf, | ||
29 | size_t size, unsigned int width); | ||
31 | 30 | ||
32 | struct sort_entry sort_thread = { | 31 | struct 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 | ||
39 | struct sort_entry sort_comm = { | 38 | struct 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 | ||
47 | struct sort_entry sort_dso = { | 46 | struct 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 | ||
54 | struct sort_entry sort_sym = { | 53 | struct 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 | ||
60 | struct sort_entry sort_parent = { | 60 | struct 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 | |||
67 | struct 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 | ||
67 | struct sort_dimension { | 74 | struct 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 | ||
81 | int64_t cmp_null(void *l, void *r) | 89 | int64_t cmp_null(void *l, void *r) |
@@ -242,6 +250,20 @@ static int hist_entry__parent_snprintf(struct hist_entry *self, char *bf, | |||
242 | self->parent ? self->parent->name : "[other]"); | 250 | self->parent ? self->parent->name : "[other]"); |
243 | } | 251 | } |
244 | 252 | ||
253 | /* --sort cpu */ | ||
254 | |||
255 | int64_t | ||
256 | sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right) | ||
257 | { | ||
258 | return right->cpu - left->cpu; | ||
259 | } | ||
260 | |||
261 | static int hist_entry__cpu_snprintf(struct hist_entry *self, char *bf, | ||
262 | size_t size, unsigned int width) | ||
263 | { | ||
264 | return repsep_snprintf(bf, size, "%-*d", width, self->cpu); | ||
265 | } | ||
266 | |||
245 | int sort_dimension__add(const char *tok) | 267 | int sort_dimension__add(const char *tok) |
246 | { | 268 | { |
247 | unsigned int i; | 269 | unsigned int i; |
@@ -281,6 +303,8 @@ int sort_dimension__add(const char *tok) | |||
281 | sort__first_dimension = SORT_SYM; | 303 | sort__first_dimension = SORT_SYM; |
282 | else if (!strcmp(sd->name, "parent")) | 304 | else if (!strcmp(sd->name, "parent")) |
283 | sort__first_dimension = SORT_PARENT; | 305 | sort__first_dimension = SORT_PARENT; |
306 | else if (!strcmp(sd->name, "cpu")) | ||
307 | sort__first_dimension = SORT_CPU; | ||
284 | } | 308 | } |
285 | 309 | ||
286 | list_add_tail(&sd->entry->list, &hist_entry__sort_list); | 310 | 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; | |||
36 | extern struct sort_entry sort_dso; | 36 | extern struct sort_entry sort_dso; |
37 | extern struct sort_entry sort_sym; | 37 | extern struct sort_entry sort_sym; |
38 | extern struct sort_entry sort_parent; | 38 | extern struct sort_entry sort_parent; |
39 | extern unsigned int dsos__col_width; | ||
40 | extern unsigned int comms__col_width; | ||
41 | extern unsigned int threads__col_width; | ||
42 | extern enum sort_type sort__first_dimension; | 39 | extern 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 | */ | ||
44 | struct hist_entry { | 47 | struct 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 *); | |||
104 | extern int64_t sort__dso_cmp(struct hist_entry *, struct hist_entry *); | 115 | extern int64_t sort__dso_cmp(struct hist_entry *, struct hist_entry *); |
105 | extern int64_t sort__sym_cmp(struct hist_entry *, struct hist_entry *); | 116 | extern int64_t sort__sym_cmp(struct hist_entry *, struct hist_entry *); |
106 | extern int64_t sort__parent_cmp(struct hist_entry *, struct hist_entry *); | 117 | extern int64_t sort__parent_cmp(struct hist_entry *, struct hist_entry *); |
118 | int64_t sort__cpu_cmp(struct hist_entry *left, struct hist_entry *right); | ||
107 | extern size_t sort__parent_print(FILE *, struct hist_entry *, unsigned int); | 119 | extern size_t sort__parent_print(FILE *, struct hist_entry *, unsigned int); |
108 | extern int sort_dimension__add(const char *); | 120 | extern int sort_dimension__add(const char *); |
109 | void sort_entry__setup_elide(struct sort_entry *self, struct strlist *list, | 121 | void 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 5b276833e2bf..6f0dd90c36ce 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 | ||
29 | static bool dso__build_id_equal(const struct dso *self, u8 *build_id); | ||
30 | static int elf_read_build_id(Elf *elf, void *bf, size_t size); | ||
28 | static void dsos__add(struct list_head *head, struct dso *dso); | 31 | static void dsos__add(struct list_head *head, struct dso *dso); |
29 | static struct map *map__new2(u64 start, struct dso *dso, enum map_type type); | 32 | static struct map *map__new2(u64 start, struct dso *dso, enum map_type type); |
30 | static int dso__load_kernel_sym(struct dso *self, struct map *map, | 33 | static 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 | ||
46 | int 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 | |||
43 | bool dso__loaded(const struct dso *self, enum map_type type) | 54 | bool dso__loaded(const struct dso *self, enum map_type type) |
44 | { | 55 | { |
45 | return self->loaded & (1 << type); | 56 | return self->loaded & (1 << type); |
@@ -215,7 +226,9 @@ void dso__delete(struct dso *self) | |||
215 | int i; | 226 | int i; |
216 | for (i = 0; i < MAP__NR_TYPES; ++i) | 227 | for (i = 0; i < MAP__NR_TYPES; ++i) |
217 | symbols__delete(&self->symbols[i]); | 228 | symbols__delete(&self->symbols[i]); |
218 | if (self->long_name != self->name) | 229 | if (self->sname_alloc) |
230 | free((char *)self->short_name); | ||
231 | if (self->lname_alloc) | ||
219 | free(self->long_name); | 232 | free(self->long_name); |
220 | free(self); | 233 | free(self); |
221 | } | 234 | } |
@@ -933,8 +946,28 @@ static bool elf_sec__is_a(GElf_Shdr *self, Elf_Data *secstrs, enum map_type type | |||
933 | } | 946 | } |
934 | } | 947 | } |
935 | 948 | ||
949 | static size_t elf_addr_to_index(Elf *elf, GElf_Addr addr) | ||
950 | { | ||
951 | Elf_Scn *sec = NULL; | ||
952 | GElf_Shdr shdr; | ||
953 | size_t cnt = 1; | ||
954 | |||
955 | while ((sec = elf_nextscn(elf, sec)) != NULL) { | ||
956 | gelf_getshdr(sec, &shdr); | ||
957 | |||
958 | if ((addr >= shdr.sh_addr) && | ||
959 | (addr < (shdr.sh_addr + shdr.sh_size))) | ||
960 | return cnt; | ||
961 | |||
962 | ++cnt; | ||
963 | } | ||
964 | |||
965 | return -1; | ||
966 | } | ||
967 | |||
936 | static int dso__load_sym(struct dso *self, struct map *map, const char *name, | 968 | static int dso__load_sym(struct dso *self, struct map *map, const char *name, |
937 | int fd, symbol_filter_t filter, int kmodule) | 969 | int fd, symbol_filter_t filter, int kmodule, |
970 | int want_symtab) | ||
938 | { | 971 | { |
939 | struct kmap *kmap = self->kernel ? map__kmap(map) : NULL; | 972 | struct kmap *kmap = self->kernel ? map__kmap(map) : NULL; |
940 | struct map *curr_map = map; | 973 | struct map *curr_map = map; |
@@ -944,31 +977,51 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, | |||
944 | int err = -1; | 977 | int err = -1; |
945 | uint32_t idx; | 978 | uint32_t idx; |
946 | GElf_Ehdr ehdr; | 979 | GElf_Ehdr ehdr; |
947 | GElf_Shdr shdr; | 980 | GElf_Shdr shdr, opdshdr; |
948 | Elf_Data *syms; | 981 | Elf_Data *syms, *opddata = NULL; |
949 | GElf_Sym sym; | 982 | GElf_Sym sym; |
950 | Elf_Scn *sec, *sec_strndx; | 983 | Elf_Scn *sec, *sec_strndx, *opdsec; |
951 | Elf *elf; | 984 | Elf *elf; |
952 | int nr = 0; | 985 | int nr = 0; |
986 | size_t opdidx = 0; | ||
953 | 987 | ||
954 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | 988 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); |
955 | if (elf == NULL) { | 989 | if (elf == NULL) { |
956 | pr_err("%s: cannot read %s ELF file.\n", __func__, name); | 990 | pr_debug("%s: cannot read %s ELF file.\n", __func__, name); |
957 | goto out_close; | 991 | goto out_close; |
958 | } | 992 | } |
959 | 993 | ||
960 | if (gelf_getehdr(elf, &ehdr) == NULL) { | 994 | if (gelf_getehdr(elf, &ehdr) == NULL) { |
961 | pr_err("%s: cannot get elf header.\n", __func__); | 995 | pr_debug("%s: cannot get elf header.\n", __func__); |
962 | goto out_elf_end; | 996 | goto out_elf_end; |
963 | } | 997 | } |
964 | 998 | ||
999 | /* Always reject images with a mismatched build-id: */ | ||
1000 | if (self->has_build_id) { | ||
1001 | u8 build_id[BUILD_ID_SIZE]; | ||
1002 | |||
1003 | if (elf_read_build_id(elf, build_id, | ||
1004 | BUILD_ID_SIZE) != BUILD_ID_SIZE) | ||
1005 | goto out_elf_end; | ||
1006 | |||
1007 | if (!dso__build_id_equal(self, build_id)) | ||
1008 | goto out_elf_end; | ||
1009 | } | ||
1010 | |||
965 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL); | 1011 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL); |
966 | if (sec == NULL) { | 1012 | if (sec == NULL) { |
1013 | if (want_symtab) | ||
1014 | goto out_elf_end; | ||
1015 | |||
967 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL); | 1016 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL); |
968 | if (sec == NULL) | 1017 | if (sec == NULL) |
969 | goto out_elf_end; | 1018 | goto out_elf_end; |
970 | } | 1019 | } |
971 | 1020 | ||
1021 | opdsec = elf_section_by_name(elf, &ehdr, &opdshdr, ".opd", &opdidx); | ||
1022 | if (opdsec) | ||
1023 | opddata = elf_rawdata(opdsec, NULL); | ||
1024 | |||
972 | syms = elf_getdata(sec, NULL); | 1025 | syms = elf_getdata(sec, NULL); |
973 | if (syms == NULL) | 1026 | if (syms == NULL) |
974 | goto out_elf_end; | 1027 | goto out_elf_end; |
@@ -1013,6 +1066,13 @@ 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)) | 1066 | if (!is_label && !elf_sym__is_a(&sym, map->type)) |
1014 | continue; | 1067 | continue; |
1015 | 1068 | ||
1069 | if (opdsec && sym.st_shndx == opdidx) { | ||
1070 | u32 offset = sym.st_value - opdshdr.sh_addr; | ||
1071 | u64 *opd = opddata->d_buf + offset; | ||
1072 | sym.st_value = *opd; | ||
1073 | sym.st_shndx = elf_addr_to_index(elf, sym.st_value); | ||
1074 | } | ||
1075 | |||
1016 | sec = elf_getscn(elf, sym.st_shndx); | 1076 | sec = elf_getscn(elf, sym.st_shndx); |
1017 | if (!sec) | 1077 | if (!sec) |
1018 | goto out_elf_end; | 1078 | goto out_elf_end; |
@@ -1151,37 +1211,26 @@ bool __dsos__read_build_ids(struct list_head *head, bool with_hits) | |||
1151 | */ | 1211 | */ |
1152 | #define NOTE_ALIGN(n) (((n) + 3) & -4U) | 1212 | #define NOTE_ALIGN(n) (((n) + 3) & -4U) |
1153 | 1213 | ||
1154 | int filename__read_build_id(const char *filename, void *bf, size_t size) | 1214 | static int elf_read_build_id(Elf *elf, void *bf, size_t size) |
1155 | { | 1215 | { |
1156 | int fd, err = -1; | 1216 | int err = -1; |
1157 | GElf_Ehdr ehdr; | 1217 | GElf_Ehdr ehdr; |
1158 | GElf_Shdr shdr; | 1218 | GElf_Shdr shdr; |
1159 | Elf_Data *data; | 1219 | Elf_Data *data; |
1160 | Elf_Scn *sec; | 1220 | Elf_Scn *sec; |
1161 | Elf_Kind ek; | 1221 | Elf_Kind ek; |
1162 | void *ptr; | 1222 | void *ptr; |
1163 | Elf *elf; | ||
1164 | 1223 | ||
1165 | if (size < BUILD_ID_SIZE) | 1224 | if (size < BUILD_ID_SIZE) |
1166 | goto out; | 1225 | goto out; |
1167 | 1226 | ||
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); | 1227 | ek = elf_kind(elf); |
1179 | if (ek != ELF_K_ELF) | 1228 | if (ek != ELF_K_ELF) |
1180 | goto out_elf_end; | 1229 | goto out; |
1181 | 1230 | ||
1182 | if (gelf_getehdr(elf, &ehdr) == NULL) { | 1231 | if (gelf_getehdr(elf, &ehdr) == NULL) { |
1183 | pr_err("%s: cannot get elf header.\n", __func__); | 1232 | pr_err("%s: cannot get elf header.\n", __func__); |
1184 | goto out_elf_end; | 1233 | goto out; |
1185 | } | 1234 | } |
1186 | 1235 | ||
1187 | sec = elf_section_by_name(elf, &ehdr, &shdr, | 1236 | sec = elf_section_by_name(elf, &ehdr, &shdr, |
@@ -1190,12 +1239,12 @@ int filename__read_build_id(const char *filename, void *bf, size_t size) | |||
1190 | sec = elf_section_by_name(elf, &ehdr, &shdr, | 1239 | sec = elf_section_by_name(elf, &ehdr, &shdr, |
1191 | ".notes", NULL); | 1240 | ".notes", NULL); |
1192 | if (sec == NULL) | 1241 | if (sec == NULL) |
1193 | goto out_elf_end; | 1242 | goto out; |
1194 | } | 1243 | } |
1195 | 1244 | ||
1196 | data = elf_getdata(sec, NULL); | 1245 | data = elf_getdata(sec, NULL); |
1197 | if (data == NULL) | 1246 | if (data == NULL) |
1198 | goto out_elf_end; | 1247 | goto out; |
1199 | 1248 | ||
1200 | ptr = data->d_buf; | 1249 | ptr = data->d_buf; |
1201 | while (ptr < (data->d_buf + data->d_size)) { | 1250 | while (ptr < (data->d_buf + data->d_size)) { |
@@ -1217,7 +1266,31 @@ int filename__read_build_id(const char *filename, void *bf, size_t size) | |||
1217 | } | 1266 | } |
1218 | ptr += descsz; | 1267 | ptr += descsz; |
1219 | } | 1268 | } |
1220 | out_elf_end: | 1269 | |
1270 | out: | ||
1271 | return err; | ||
1272 | } | ||
1273 | |||
1274 | int filename__read_build_id(const char *filename, void *bf, size_t size) | ||
1275 | { | ||
1276 | int fd, err = -1; | ||
1277 | Elf *elf; | ||
1278 | |||
1279 | if (size < BUILD_ID_SIZE) | ||
1280 | goto out; | ||
1281 | |||
1282 | fd = open(filename, O_RDONLY); | ||
1283 | if (fd < 0) | ||
1284 | goto out; | ||
1285 | |||
1286 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | ||
1287 | if (elf == NULL) { | ||
1288 | pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename); | ||
1289 | goto out_close; | ||
1290 | } | ||
1291 | |||
1292 | err = elf_read_build_id(elf, bf, size); | ||
1293 | |||
1221 | elf_end(elf); | 1294 | elf_end(elf); |
1222 | out_close: | 1295 | out_close: |
1223 | close(fd); | 1296 | close(fd); |
@@ -1293,11 +1366,11 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) | |||
1293 | { | 1366 | { |
1294 | int size = PATH_MAX; | 1367 | int size = PATH_MAX; |
1295 | char *name; | 1368 | char *name; |
1296 | u8 build_id[BUILD_ID_SIZE]; | ||
1297 | int ret = -1; | 1369 | int ret = -1; |
1298 | int fd; | 1370 | int fd; |
1299 | struct machine *machine; | 1371 | struct machine *machine; |
1300 | const char *root_dir; | 1372 | const char *root_dir; |
1373 | int want_symtab; | ||
1301 | 1374 | ||
1302 | dso__set_loaded(self, map->type); | 1375 | dso__set_loaded(self, map->type); |
1303 | 1376 | ||
@@ -1324,13 +1397,18 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) | |||
1324 | return ret; | 1397 | return ret; |
1325 | } | 1398 | } |
1326 | 1399 | ||
1327 | self->origin = DSO__ORIG_BUILD_ID_CACHE; | 1400 | /* Iterate over candidate debug images. |
1328 | if (dso__build_id_filename(self, name, size) != NULL) | 1401 | * On the first pass, only load images if they have a full symtab. |
1329 | goto open_file; | 1402 | * Failing that, do a second pass where we accept .dynsym also |
1330 | more: | 1403 | */ |
1331 | do { | 1404 | for (self->origin = DSO__ORIG_BUILD_ID_CACHE, want_symtab = 1; |
1332 | self->origin++; | 1405 | self->origin != DSO__ORIG_NOT_FOUND; |
1406 | self->origin++) { | ||
1333 | switch (self->origin) { | 1407 | switch (self->origin) { |
1408 | case DSO__ORIG_BUILD_ID_CACHE: | ||
1409 | if (dso__build_id_filename(self, name, size) == NULL) | ||
1410 | continue; | ||
1411 | break; | ||
1334 | case DSO__ORIG_FEDORA: | 1412 | case DSO__ORIG_FEDORA: |
1335 | snprintf(name, size, "/usr/lib/debug%s.debug", | 1413 | snprintf(name, size, "/usr/lib/debug%s.debug", |
1336 | self->long_name); | 1414 | self->long_name); |
@@ -1339,21 +1417,20 @@ more: | |||
1339 | snprintf(name, size, "/usr/lib/debug%s", | 1417 | snprintf(name, size, "/usr/lib/debug%s", |
1340 | self->long_name); | 1418 | self->long_name); |
1341 | break; | 1419 | break; |
1342 | case DSO__ORIG_BUILDID: | 1420 | case DSO__ORIG_BUILDID: { |
1343 | if (filename__read_build_id(self->long_name, build_id, | 1421 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; |
1344 | sizeof(build_id))) { | 1422 | |
1345 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; | 1423 | if (!self->has_build_id) |
1346 | build_id__sprintf(build_id, sizeof(build_id), | 1424 | continue; |
1347 | build_id_hex); | 1425 | |
1348 | snprintf(name, size, | 1426 | build_id__sprintf(self->build_id, |
1349 | "/usr/lib/debug/.build-id/%.2s/%s.debug", | 1427 | sizeof(self->build_id), |
1350 | build_id_hex, build_id_hex + 2); | 1428 | build_id_hex); |
1351 | if (self->has_build_id) | 1429 | snprintf(name, size, |
1352 | goto compare_build_id; | 1430 | "/usr/lib/debug/.build-id/%.2s/%s.debug", |
1353 | break; | 1431 | build_id_hex, build_id_hex + 2); |
1354 | } | 1432 | } |
1355 | self->origin++; | 1433 | break; |
1356 | /* Fall thru */ | ||
1357 | case DSO__ORIG_DSO: | 1434 | case DSO__ORIG_DSO: |
1358 | snprintf(name, size, "%s", self->long_name); | 1435 | snprintf(name, size, "%s", self->long_name); |
1359 | break; | 1436 | break; |
@@ -1366,36 +1443,41 @@ more: | |||
1366 | break; | 1443 | break; |
1367 | 1444 | ||
1368 | default: | 1445 | default: |
1369 | goto out; | 1446 | /* |
1447 | * If we wanted a full symtab but no image had one, | ||
1448 | * relax our requirements and repeat the search. | ||
1449 | */ | ||
1450 | if (want_symtab) { | ||
1451 | want_symtab = 0; | ||
1452 | self->origin = DSO__ORIG_BUILD_ID_CACHE; | ||
1453 | } else | ||
1454 | continue; | ||
1370 | } | 1455 | } |
1371 | 1456 | ||
1372 | if (self->has_build_id) { | 1457 | /* 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; | ||
1376 | compare_build_id: | ||
1377 | if (!dso__build_id_equal(self, build_id)) | ||
1378 | goto more; | ||
1379 | } | ||
1380 | open_file: | ||
1381 | fd = open(name, O_RDONLY); | 1458 | fd = open(name, O_RDONLY); |
1382 | } while (fd < 0); | 1459 | if (fd < 0) |
1460 | continue; | ||
1383 | 1461 | ||
1384 | ret = dso__load_sym(self, map, name, fd, filter, 0); | 1462 | ret = dso__load_sym(self, map, name, fd, filter, 0, |
1385 | close(fd); | 1463 | want_symtab); |
1464 | close(fd); | ||
1386 | 1465 | ||
1387 | /* | 1466 | /* |
1388 | * Some people seem to have debuginfo files _WITHOUT_ debug info!?!? | 1467 | * Some people seem to have debuginfo files _WITHOUT_ debug |
1389 | */ | 1468 | * info!?!? |
1390 | if (!ret) | 1469 | */ |
1391 | goto more; | 1470 | if (!ret) |
1471 | continue; | ||
1392 | 1472 | ||
1393 | if (ret > 0) { | 1473 | if (ret > 0) { |
1394 | int nr_plt = dso__synthesize_plt_symbols(self, map, filter); | 1474 | int nr_plt = dso__synthesize_plt_symbols(self, map, filter); |
1395 | if (nr_plt > 0) | 1475 | if (nr_plt > 0) |
1396 | ret += nr_plt; | 1476 | ret += nr_plt; |
1477 | break; | ||
1478 | } | ||
1397 | } | 1479 | } |
1398 | out: | 1480 | |
1399 | free(name); | 1481 | free(name); |
1400 | if (ret < 0 && strstr(self->name, " (deleted)") != NULL) | 1482 | if (ret < 0 && strstr(self->name, " (deleted)") != NULL) |
1401 | return 0; | 1483 | return 0; |
@@ -1494,6 +1576,7 @@ static int map_groups__set_modules_path_dir(struct map_groups *self, | |||
1494 | goto out; | 1576 | goto out; |
1495 | } | 1577 | } |
1496 | dso__set_long_name(map->dso, long_name); | 1578 | dso__set_long_name(map->dso, long_name); |
1579 | map->dso->lname_alloc = 1; | ||
1497 | dso__kernel_module_get_build_id(map->dso, ""); | 1580 | dso__kernel_module_get_build_id(map->dso, ""); |
1498 | } | 1581 | } |
1499 | } | 1582 | } |
@@ -1656,36 +1739,12 @@ static int dso__load_vmlinux(struct dso *self, struct map *map, | |||
1656 | { | 1739 | { |
1657 | int err = -1, fd; | 1740 | int err = -1, fd; |
1658 | 1741 | ||
1659 | if (self->has_build_id) { | ||
1660 | u8 build_id[BUILD_ID_SIZE]; | ||
1661 | |||
1662 | if (filename__read_build_id(vmlinux, build_id, | ||
1663 | sizeof(build_id)) < 0) { | ||
1664 | pr_debug("No build_id in %s, ignoring it\n", vmlinux); | ||
1665 | return -1; | ||
1666 | } | ||
1667 | if (!dso__build_id_equal(self, build_id)) { | ||
1668 | char expected_build_id[BUILD_ID_SIZE * 2 + 1], | ||
1669 | vmlinux_build_id[BUILD_ID_SIZE * 2 + 1]; | ||
1670 | |||
1671 | build_id__sprintf(self->build_id, | ||
1672 | sizeof(self->build_id), | ||
1673 | expected_build_id); | ||
1674 | build_id__sprintf(build_id, sizeof(build_id), | ||
1675 | vmlinux_build_id); | ||
1676 | pr_debug("build_id in %s is %s while expected is %s, " | ||
1677 | "ignoring it\n", vmlinux, vmlinux_build_id, | ||
1678 | expected_build_id); | ||
1679 | return -1; | ||
1680 | } | ||
1681 | } | ||
1682 | |||
1683 | fd = open(vmlinux, O_RDONLY); | 1742 | fd = open(vmlinux, O_RDONLY); |
1684 | if (fd < 0) | 1743 | if (fd < 0) |
1685 | return -1; | 1744 | return -1; |
1686 | 1745 | ||
1687 | dso__set_loaded(self, map->type); | 1746 | dso__set_loaded(self, map->type); |
1688 | err = dso__load_sym(self, map, vmlinux, fd, filter, 0); | 1747 | err = dso__load_sym(self, map, vmlinux, fd, filter, 0, 0); |
1689 | close(fd); | 1748 | close(fd); |
1690 | 1749 | ||
1691 | if (err > 0) | 1750 | if (err > 0) |
@@ -2048,6 +2107,36 @@ int __machine__create_kernel_maps(struct machine *self, struct dso *kernel) | |||
2048 | return 0; | 2107 | return 0; |
2049 | } | 2108 | } |
2050 | 2109 | ||
2110 | void machine__destroy_kernel_maps(struct machine *self) | ||
2111 | { | ||
2112 | enum map_type type; | ||
2113 | |||
2114 | for (type = 0; type < MAP__NR_TYPES; ++type) { | ||
2115 | struct kmap *kmap; | ||
2116 | |||
2117 | if (self->vmlinux_maps[type] == NULL) | ||
2118 | continue; | ||
2119 | |||
2120 | kmap = map__kmap(self->vmlinux_maps[type]); | ||
2121 | map_groups__remove(&self->kmaps, self->vmlinux_maps[type]); | ||
2122 | if (kmap->ref_reloc_sym) { | ||
2123 | /* | ||
2124 | * ref_reloc_sym is shared among all maps, so free just | ||
2125 | * on one of them. | ||
2126 | */ | ||
2127 | if (type == MAP__FUNCTION) { | ||
2128 | free((char *)kmap->ref_reloc_sym->name); | ||
2129 | kmap->ref_reloc_sym->name = NULL; | ||
2130 | free(kmap->ref_reloc_sym); | ||
2131 | } | ||
2132 | kmap->ref_reloc_sym = NULL; | ||
2133 | } | ||
2134 | |||
2135 | map__delete(self->vmlinux_maps[type]); | ||
2136 | self->vmlinux_maps[type] = NULL; | ||
2137 | } | ||
2138 | } | ||
2139 | |||
2051 | int machine__create_kernel_maps(struct machine *self) | 2140 | int machine__create_kernel_maps(struct machine *self) |
2052 | { | 2141 | { |
2053 | struct dso *kernel = machine__create_kernel(self); | 2142 | struct dso *kernel = machine__create_kernel(self); |
@@ -2189,6 +2278,15 @@ out_free_comm_list: | |||
2189 | return -1; | 2278 | return -1; |
2190 | } | 2279 | } |
2191 | 2280 | ||
2281 | void symbol__exit(void) | ||
2282 | { | ||
2283 | strlist__delete(symbol_conf.sym_list); | ||
2284 | strlist__delete(symbol_conf.dso_list); | ||
2285 | strlist__delete(symbol_conf.comm_list); | ||
2286 | vmlinux_path__exit(); | ||
2287 | symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL; | ||
2288 | } | ||
2289 | |||
2192 | int machines__create_kernel_maps(struct rb_root *self, pid_t pid) | 2290 | int machines__create_kernel_maps(struct rb_root *self, pid_t pid) |
2193 | { | 2291 | { |
2194 | struct machine *machine = machines__findnew(self, pid); | 2292 | struct machine *machine = machines__findnew(self, pid); |
@@ -2283,6 +2381,19 @@ failure: | |||
2283 | return ret; | 2381 | return ret; |
2284 | } | 2382 | } |
2285 | 2383 | ||
2384 | void machines__destroy_guest_kernel_maps(struct rb_root *self) | ||
2385 | { | ||
2386 | struct rb_node *next = rb_first(self); | ||
2387 | |||
2388 | while (next) { | ||
2389 | struct machine *pos = rb_entry(next, struct machine, rb_node); | ||
2390 | |||
2391 | next = rb_next(&pos->rb_node); | ||
2392 | rb_erase(&pos->rb_node, self); | ||
2393 | machine__delete(pos); | ||
2394 | } | ||
2395 | } | ||
2396 | |||
2286 | int machine__load_kallsyms(struct machine *self, const char *filename, | 2397 | int machine__load_kallsyms(struct machine *self, const char *filename, |
2287 | enum map_type type, symbol_filter_t filter) | 2398 | enum map_type type, symbol_filter_t filter) |
2288 | { | 2399 | { |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 5e02d2c17154..906be20011d9 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 |
15 | extern char *cplus_demangle(const char *, int); | 13 | extern char *cplus_demangle(const char *, int); |
16 | 14 | ||
@@ -70,9 +68,9 @@ struct symbol_conf { | |||
70 | show_nr_samples, | 68 | show_nr_samples, |
71 | use_callchain, | 69 | use_callchain, |
72 | exclude_other, | 70 | exclude_other, |
73 | full_paths, | ||
74 | show_cpu_utilization; | 71 | show_cpu_utilization; |
75 | const char *vmlinux_name, | 72 | const char *vmlinux_name, |
73 | *source_prefix, | ||
76 | *field_sep; | 74 | *field_sep; |
77 | const char *default_guest_vmlinux_name, | 75 | const char *default_guest_vmlinux_name, |
78 | *default_guest_kallsyms, | 76 | *default_guest_kallsyms, |
@@ -103,6 +101,8 @@ struct ref_reloc_sym { | |||
103 | struct map_symbol { | 101 | struct map_symbol { |
104 | struct map *map; | 102 | struct map *map; |
105 | struct symbol *sym; | 103 | struct symbol *sym; |
104 | bool unfolded; | ||
105 | bool has_children; | ||
106 | }; | 106 | }; |
107 | 107 | ||
108 | struct addr_location { | 108 | struct addr_location { |
@@ -112,7 +112,8 @@ struct addr_location { | |||
112 | u64 addr; | 112 | u64 addr; |
113 | char level; | 113 | char level; |
114 | bool filtered; | 114 | bool filtered; |
115 | unsigned int cpumode; | 115 | u8 cpumode; |
116 | s32 cpu; | ||
116 | }; | 117 | }; |
117 | 118 | ||
118 | enum dso_kernel_type { | 119 | enum dso_kernel_type { |
@@ -125,12 +126,14 @@ struct dso { | |||
125 | struct list_head node; | 126 | struct list_head node; |
126 | struct rb_root symbols[MAP__NR_TYPES]; | 127 | struct rb_root symbols[MAP__NR_TYPES]; |
127 | struct rb_root symbol_names[MAP__NR_TYPES]; | 128 | struct rb_root symbol_names[MAP__NR_TYPES]; |
129 | enum dso_kernel_type kernel; | ||
128 | u8 adjust_symbols:1; | 130 | u8 adjust_symbols:1; |
129 | u8 slen_calculated:1; | 131 | u8 slen_calculated:1; |
130 | u8 has_build_id:1; | 132 | u8 has_build_id:1; |
131 | enum dso_kernel_type kernel; | ||
132 | u8 hit:1; | 133 | u8 hit:1; |
133 | u8 annotate_warned:1; | 134 | u8 annotate_warned:1; |
135 | u8 sname_alloc:1; | ||
136 | u8 lname_alloc:1; | ||
134 | unsigned char origin; | 137 | unsigned char origin; |
135 | u8 sorted_by_name; | 138 | u8 sorted_by_name; |
136 | u8 loaded; | 139 | u8 loaded; |
@@ -146,6 +149,8 @@ struct dso *dso__new(const char *name); | |||
146 | struct dso *dso__new_kernel(const char *name); | 149 | struct dso *dso__new_kernel(const char *name); |
147 | void dso__delete(struct dso *self); | 150 | void dso__delete(struct dso *self); |
148 | 151 | ||
152 | int dso__name_len(const struct dso *self); | ||
153 | |||
149 | bool dso__loaded(const struct dso *self, enum map_type type); | 154 | bool dso__loaded(const struct dso *self, enum map_type type); |
150 | bool dso__sorted_by_name(const struct dso *self, enum map_type type); | 155 | bool dso__sorted_by_name(const struct dso *self, enum map_type type); |
151 | 156 | ||
@@ -207,13 +212,16 @@ int kallsyms__parse(const char *filename, void *arg, | |||
207 | int (*process_symbol)(void *arg, const char *name, | 212 | int (*process_symbol)(void *arg, const char *name, |
208 | char type, u64 start)); | 213 | char type, u64 start)); |
209 | 214 | ||
215 | void machine__destroy_kernel_maps(struct machine *self); | ||
210 | int __machine__create_kernel_maps(struct machine *self, struct dso *kernel); | 216 | int __machine__create_kernel_maps(struct machine *self, struct dso *kernel); |
211 | int machine__create_kernel_maps(struct machine *self); | 217 | int machine__create_kernel_maps(struct machine *self); |
212 | 218 | ||
213 | int machines__create_kernel_maps(struct rb_root *self, pid_t pid); | 219 | int machines__create_kernel_maps(struct rb_root *self, pid_t pid); |
214 | int machines__create_guest_kernel_maps(struct rb_root *self); | 220 | int machines__create_guest_kernel_maps(struct rb_root *self); |
221 | void machines__destroy_guest_kernel_maps(struct rb_root *self); | ||
215 | 222 | ||
216 | int symbol__init(void); | 223 | int symbol__init(void); |
224 | void symbol__exit(void); | ||
217 | bool symbol_type__is_a(char symbol_type, enum map_type map_type); | 225 | bool symbol_type__is_a(char symbol_type, enum map_type map_type); |
218 | 226 | ||
219 | size_t machine__fprintf_vmlinux_path(struct machine *self, FILE *fp); | 227 | size_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 9a448b47400c..8c72d888e449 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
@@ -62,6 +62,13 @@ static struct thread *thread__new(pid_t pid) | |||
62 | return self; | 62 | return self; |
63 | } | 63 | } |
64 | 64 | ||
65 | void thread__delete(struct thread *self) | ||
66 | { | ||
67 | map_groups__exit(&self->mg); | ||
68 | free(self->comm); | ||
69 | free(self); | ||
70 | } | ||
71 | |||
65 | int thread__set_comm(struct thread *self, const char *comm) | 72 | int thread__set_comm(struct thread *self, const char *comm) |
66 | { | 73 | { |
67 | int err; | 74 | int err; |
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index ee6bbcf277ca..688500ff826f 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
@@ -20,6 +20,8 @@ struct thread { | |||
20 | 20 | ||
21 | struct perf_session; | 21 | struct perf_session; |
22 | 22 | ||
23 | void thread__delete(struct thread *self); | ||
24 | |||
23 | int find_all_tid(int pid, pid_t ** all_tid); | 25 | int find_all_tid(int pid, pid_t ** all_tid); |
24 | int thread__set_comm(struct thread *self, const char *comm); | 26 | int thread__set_comm(struct thread *self, const char *comm); |
25 | int thread__comm_len(struct thread *self); | 27 | int thread__comm_len(struct thread *self); |
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 | ||
90 | extern const char *graph_line; | 90 | extern const char *graph_line; |
91 | extern const char *graph_dotted_line; | 91 | extern const char *graph_dotted_line; |
92 | extern 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))) | |||
152 | extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN); | 153 | extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN); |
153 | 154 | ||
154 | extern int prefixcmp(const char *str, const char *prefix); | 155 | extern int prefixcmp(const char *str, const char *prefix); |
156 | extern void set_buildid_dir(void); | ||
157 | extern void disable_buildid_cache(void); | ||
155 | 158 | ||
156 | static inline const char *skip_prefix(const char *str, const char *prefix) | 159 | static inline const char *skip_prefix(const char *str, const char *prefix) |
157 | { | 160 | { |