diff options
-rw-r--r-- | tools/perf/Documentation/perf-buildid-cache.txt | 8 | ||||
-rw-r--r-- | tools/perf/builtin-buildid-list.c | 4 | ||||
-rw-r--r-- | tools/perf/builtin-diff.c | 2 | ||||
-rw-r--r-- | tools/perf/builtin-probe.c | 1 | ||||
-rw-r--r-- | tools/perf/builtin-record.c | 37 | ||||
-rw-r--r-- | tools/perf/builtin-report.c | 2 | ||||
-rw-r--r-- | tools/perf/util/build-id.c | 18 | ||||
-rw-r--r-- | tools/perf/util/event.c | 42 | ||||
-rw-r--r-- | tools/perf/util/hist.c | 160 | ||||
-rw-r--r-- | tools/perf/util/hist.h | 30 | ||||
-rw-r--r-- | tools/perf/util/map.c | 81 | ||||
-rw-r--r-- | tools/perf/util/map.h | 4 | ||||
-rw-r--r-- | tools/perf/util/newt.c | 991 | ||||
-rw-r--r-- | tools/perf/util/probe-event.c | 135 | ||||
-rw-r--r-- | tools/perf/util/probe-event.h | 27 | ||||
-rw-r--r-- | tools/perf/util/probe-finder.c | 34 | ||||
-rw-r--r-- | tools/perf/util/probe-finder.h | 10 | ||||
-rw-r--r-- | tools/perf/util/session.c | 48 | ||||
-rw-r--r-- | tools/perf/util/sort.c | 17 | ||||
-rw-r--r-- | tools/perf/util/sort.h | 18 | ||||
-rw-r--r-- | tools/perf/util/symbol.c | 219 | ||||
-rw-r--r-- | tools/perf/util/symbol.h | 10 | ||||
-rw-r--r-- | tools/perf/util/thread.c | 7 | ||||
-rw-r--r-- | tools/perf/util/thread.h | 2 |
24 files changed, 1253 insertions, 654 deletions
diff --git a/tools/perf/Documentation/perf-buildid-cache.txt b/tools/perf/Documentation/perf-buildid-cache.txt index 5d1a9500277f..c1057701a7dc 100644 --- a/tools/perf/Documentation/perf-buildid-cache.txt +++ b/tools/perf/Documentation/perf-buildid-cache.txt | |||
@@ -12,9 +12,9 @@ SYNOPSIS | |||
12 | 12 | ||
13 | DESCRIPTION | 13 | DESCRIPTION |
14 | ----------- | 14 | ----------- |
15 | This command manages the build-id cache. It can add and remove files to the | 15 | This command manages the build-id cache. It can add and remove files to/from |
16 | cache. In the future it should as well purge older entries, set upper limits | 16 | the cache. In the future it should as well purge older entries, set upper |
17 | for the space used by the cache, etc. | 17 | limits for the space used by the cache, etc. |
18 | 18 | ||
19 | OPTIONS | 19 | OPTIONS |
20 | ------- | 20 | ------- |
@@ -23,7 +23,7 @@ OPTIONS | |||
23 | Add specified file to the cache. | 23 | Add specified file to the cache. |
24 | -r:: | 24 | -r:: |
25 | --remove=:: | 25 | --remove=:: |
26 | Remove specified file to the cache. | 26 | Remove specified file from the cache. |
27 | -v:: | 27 | -v:: |
28 | --verbose:: | 28 | --verbose:: |
29 | Be more verbose. | 29 | Be more verbose. |
diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index 99890728409e..44a47e13bd67 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c | |||
@@ -43,10 +43,8 @@ static int __cmd_buildid_list(void) | |||
43 | if (session == NULL) | 43 | if (session == NULL) |
44 | return -1; | 44 | return -1; |
45 | 45 | ||
46 | if (with_hits) { | 46 | if (with_hits) |
47 | symbol_conf.full_paths = true; | ||
48 | perf_session__process_events(session, &build_id__mark_dso_hit_ops); | 47 | perf_session__process_events(session, &build_id__mark_dso_hit_ops); |
49 | } | ||
50 | 48 | ||
51 | perf_session__fprintf_dsos_buildid(session, stdout, with_hits); | 49 | perf_session__fprintf_dsos_buildid(session, stdout, with_hits); |
52 | 50 | ||
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 39e6627ebb96..fca1d4402910 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c | |||
@@ -180,8 +180,6 @@ static const struct option options[] = { | |||
180 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), | 180 | OPT_BOOLEAN('f', "force", &force, "don't complain, do it"), |
181 | OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules, | 181 | OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules, |
182 | "load module symbols - WARNING: use only with -k and LIVE kernel"), | 182 | "load module symbols - WARNING: use only with -k and LIVE kernel"), |
183 | OPT_BOOLEAN('P', "full-paths", &symbol_conf.full_paths, | ||
184 | "Don't shorten the pathnames taking into account the cwd"), | ||
185 | OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", | 183 | OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]", |
186 | "only consider symbols in these dsos"), | 184 | "only consider symbols in these dsos"), |
187 | OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]", | 185 | OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]", |
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c index 54551867e7e0..199d5e19554f 100644 --- a/tools/perf/builtin-probe.c +++ b/tools/perf/builtin-probe.c | |||
@@ -267,4 +267,3 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used) | |||
267 | } | 267 | } |
268 | return 0; | 268 | return 0; |
269 | } | 269 | } |
270 | |||
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index b93879677cca..ff77b805de71 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -439,6 +439,8 @@ static void atexit_header(void) | |||
439 | 439 | ||
440 | process_buildids(); | 440 | process_buildids(); |
441 | perf_header__write(&session->header, output, true); | 441 | perf_header__write(&session->header, output, true); |
442 | perf_session__delete(session); | ||
443 | symbol__exit(); | ||
442 | } | 444 | } |
443 | } | 445 | } |
444 | 446 | ||
@@ -558,12 +560,15 @@ static int __cmd_record(int argc, const char **argv) | |||
558 | if (!file_new) { | 560 | if (!file_new) { |
559 | err = perf_header__read(session, output); | 561 | err = perf_header__read(session, output); |
560 | if (err < 0) | 562 | if (err < 0) |
561 | return err; | 563 | goto out_delete_session; |
562 | } | 564 | } |
563 | 565 | ||
564 | if (have_tracepoints(attrs, nr_counters)) | 566 | if (have_tracepoints(attrs, nr_counters)) |
565 | perf_header__set_feat(&session->header, HEADER_TRACE_INFO); | 567 | perf_header__set_feat(&session->header, HEADER_TRACE_INFO); |
566 | 568 | ||
569 | /* | ||
570 | * perf_session__delete(session) will be called at atexit_header() | ||
571 | */ | ||
567 | atexit(atexit_header); | 572 | atexit(atexit_header); |
568 | 573 | ||
569 | if (forks) { | 574 | if (forks) { |
@@ -768,6 +773,10 @@ static int __cmd_record(int argc, const char **argv) | |||
768 | bytes_written / 24); | 773 | bytes_written / 24); |
769 | 774 | ||
770 | return 0; | 775 | return 0; |
776 | |||
777 | out_delete_session: | ||
778 | perf_session__delete(session); | ||
779 | return err; | ||
771 | } | 780 | } |
772 | 781 | ||
773 | static const char * const record_usage[] = { | 782 | static const char * const record_usage[] = { |
@@ -824,7 +833,7 @@ static const struct option options[] = { | |||
824 | 833 | ||
825 | int cmd_record(int argc, const char **argv, const char *prefix __used) | 834 | int cmd_record(int argc, const char **argv, const char *prefix __used) |
826 | { | 835 | { |
827 | int i,j; | 836 | int i, j, err = -ENOMEM; |
828 | 837 | ||
829 | argc = parse_options(argc, argv, options, record_usage, | 838 | argc = parse_options(argc, argv, options, record_usage, |
830 | PARSE_OPT_STOP_AT_NON_OPTION); | 839 | PARSE_OPT_STOP_AT_NON_OPTION); |
@@ -863,7 +872,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
863 | } else { | 872 | } else { |
864 | all_tids=malloc(sizeof(pid_t)); | 873 | all_tids=malloc(sizeof(pid_t)); |
865 | if (!all_tids) | 874 | if (!all_tids) |
866 | return -ENOMEM; | 875 | goto out_symbol_exit; |
867 | 876 | ||
868 | all_tids[0] = target_tid; | 877 | all_tids[0] = target_tid; |
869 | thread_num = 1; | 878 | thread_num = 1; |
@@ -873,13 +882,13 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
873 | for (j = 0; j < MAX_COUNTERS; j++) { | 882 | for (j = 0; j < MAX_COUNTERS; j++) { |
874 | fd[i][j] = malloc(sizeof(int)*thread_num); | 883 | fd[i][j] = malloc(sizeof(int)*thread_num); |
875 | if (!fd[i][j]) | 884 | if (!fd[i][j]) |
876 | return -ENOMEM; | 885 | goto out_free_fd; |
877 | } | 886 | } |
878 | } | 887 | } |
879 | event_array = malloc( | 888 | event_array = malloc( |
880 | sizeof(struct pollfd)*MAX_NR_CPUS*MAX_COUNTERS*thread_num); | 889 | sizeof(struct pollfd)*MAX_NR_CPUS*MAX_COUNTERS*thread_num); |
881 | if (!event_array) | 890 | if (!event_array) |
882 | return -ENOMEM; | 891 | goto out_free_fd; |
883 | 892 | ||
884 | if (user_interval != ULLONG_MAX) | 893 | if (user_interval != ULLONG_MAX) |
885 | default_interval = user_interval; | 894 | default_interval = user_interval; |
@@ -895,8 +904,22 @@ int cmd_record(int argc, const char **argv, const char *prefix __used) | |||
895 | default_interval = freq; | 904 | default_interval = freq; |
896 | } else { | 905 | } else { |
897 | fprintf(stderr, "frequency and count are zero, aborting\n"); | 906 | fprintf(stderr, "frequency and count are zero, aborting\n"); |
898 | exit(EXIT_FAILURE); | 907 | err = -EINVAL; |
908 | goto out_free_event_array; | ||
899 | } | 909 | } |
900 | 910 | ||
901 | return __cmd_record(argc, argv); | 911 | err = __cmd_record(argc, argv); |
912 | |||
913 | out_free_event_array: | ||
914 | free(event_array); | ||
915 | out_free_fd: | ||
916 | for (i = 0; i < MAX_NR_CPUS; i++) { | ||
917 | for (j = 0; j < MAX_COUNTERS; j++) | ||
918 | free(fd[i][j]); | ||
919 | } | ||
920 | free(all_tids); | ||
921 | all_tids = NULL; | ||
922 | out_symbol_exit: | ||
923 | symbol__exit(); | ||
924 | return err; | ||
902 | } | 925 | } |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index ce42bbaa252d..2f4b92925b26 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
@@ -441,8 +441,6 @@ static const struct option options[] = { | |||
441 | "pretty printing style key: normal raw"), | 441 | "pretty printing style key: normal raw"), |
442 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", | 442 | OPT_STRING('s', "sort", &sort_order, "key[,key2...]", |
443 | "sort by key(s): pid, comm, dso, symbol, parent"), | 443 | "sort by key(s): pid, comm, dso, symbol, parent"), |
444 | OPT_BOOLEAN('P', "full-paths", &symbol_conf.full_paths, | ||
445 | "Don't shorten the pathnames taking into account the cwd"), | ||
446 | OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, | 444 | OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization, |
447 | "Show sample percentage for different cpu modes"), | 445 | "Show sample percentage for different cpu modes"), |
448 | OPT_STRING('p', "parent", &parent_pattern, "regex", | 446 | OPT_STRING('p', "parent", &parent_pattern, "regex", |
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 5c26e2d314af..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,10 +35,27 @@ 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) |
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index d7f21d71eb69..6b0db5577929 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -340,30 +340,29 @@ int event__synthesize_kernel_mmap(event__handler_t process, | |||
340 | return process(&ev, session); | 340 | return process(&ev, session); |
341 | } | 341 | } |
342 | 342 | ||
343 | static void thread__comm_adjust(struct thread *self) | 343 | static void thread__comm_adjust(struct thread *self, struct hists *hists) |
344 | { | 344 | { |
345 | char *comm = self->comm; | 345 | char *comm = self->comm; |
346 | 346 | ||
347 | if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && | 347 | if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && |
348 | (!symbol_conf.comm_list || | 348 | (!symbol_conf.comm_list || |
349 | strlist__has_entry(symbol_conf.comm_list, comm))) { | 349 | strlist__has_entry(symbol_conf.comm_list, comm))) { |
350 | unsigned int slen = strlen(comm); | 350 | u16 slen = strlen(comm); |
351 | 351 | ||
352 | if (slen > comms__col_width) { | 352 | if (hists__new_col_len(hists, HISTC_COMM, slen)) |
353 | comms__col_width = slen; | 353 | hists__set_col_len(hists, HISTC_THREAD, slen + 6); |
354 | threads__col_width = slen + 6; | ||
355 | } | ||
356 | } | 354 | } |
357 | } | 355 | } |
358 | 356 | ||
359 | static int thread__set_comm_adjust(struct thread *self, const char *comm) | 357 | static int thread__set_comm_adjust(struct thread *self, const char *comm, |
358 | struct hists *hists) | ||
360 | { | 359 | { |
361 | int ret = thread__set_comm(self, comm); | 360 | int ret = thread__set_comm(self, comm); |
362 | 361 | ||
363 | if (ret) | 362 | if (ret) |
364 | return ret; | 363 | return ret; |
365 | 364 | ||
366 | thread__comm_adjust(self); | 365 | thread__comm_adjust(self, hists); |
367 | 366 | ||
368 | return 0; | 367 | return 0; |
369 | } | 368 | } |
@@ -374,7 +373,8 @@ int event__process_comm(event_t *self, struct perf_session *session) | |||
374 | 373 | ||
375 | dump_printf(": %s:%d\n", self->comm.comm, self->comm.tid); | 374 | dump_printf(": %s:%d\n", self->comm.comm, self->comm.tid); |
376 | 375 | ||
377 | if (thread == NULL || thread__set_comm_adjust(thread, self->comm.comm)) { | 376 | if (thread == NULL || thread__set_comm_adjust(thread, self->comm.comm, |
377 | &session->hists)) { | ||
378 | dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); | 378 | dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); |
379 | return -1; | 379 | return -1; |
380 | } | 380 | } |
@@ -456,6 +456,7 @@ static int event__process_kernel_mmap(event_t *self, | |||
456 | goto out_problem; | 456 | goto out_problem; |
457 | 457 | ||
458 | map->dso->short_name = name; | 458 | map->dso->short_name = name; |
459 | map->dso->sname_alloc = 1; | ||
459 | map->end = map->start + self->mmap.len; | 460 | map->end = map->start + self->mmap.len; |
460 | } else if (is_kernel_mmap) { | 461 | } else if (is_kernel_mmap) { |
461 | const char *symbol_name = (self->mmap.filename + | 462 | const char *symbol_name = (self->mmap.filename + |
@@ -514,12 +515,13 @@ int event__process_mmap(event_t *self, struct perf_session *session) | |||
514 | if (machine == NULL) | 515 | if (machine == NULL) |
515 | goto out_problem; | 516 | goto out_problem; |
516 | thread = perf_session__findnew(session, self->mmap.pid); | 517 | thread = perf_session__findnew(session, self->mmap.pid); |
518 | if (thread == NULL) | ||
519 | goto out_problem; | ||
517 | map = map__new(&machine->user_dsos, self->mmap.start, | 520 | map = map__new(&machine->user_dsos, self->mmap.start, |
518 | self->mmap.len, self->mmap.pgoff, | 521 | self->mmap.len, self->mmap.pgoff, |
519 | self->mmap.pid, self->mmap.filename, | 522 | self->mmap.pid, self->mmap.filename, |
520 | MAP__FUNCTION, session->cwd, session->cwdlen); | 523 | MAP__FUNCTION); |
521 | 524 | if (map == NULL) | |
522 | if (thread == NULL || map == NULL) | ||
523 | goto out_problem; | 525 | goto out_problem; |
524 | 526 | ||
525 | thread__insert_map(thread, map); | 527 | thread__insert_map(thread, map); |
@@ -641,16 +643,13 @@ void thread__find_addr_location(struct thread *self, | |||
641 | al->sym = NULL; | 643 | al->sym = NULL; |
642 | } | 644 | } |
643 | 645 | ||
644 | static void dso__calc_col_width(struct dso *self) | 646 | static void dso__calc_col_width(struct dso *self, struct hists *hists) |
645 | { | 647 | { |
646 | if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && | 648 | if (!symbol_conf.col_width_list_str && !symbol_conf.field_sep && |
647 | (!symbol_conf.dso_list || | 649 | (!symbol_conf.dso_list || |
648 | strlist__has_entry(symbol_conf.dso_list, self->name))) { | 650 | strlist__has_entry(symbol_conf.dso_list, self->name))) { |
649 | u16 slen = self->short_name_len; | 651 | u16 slen = dso__name_len(self); |
650 | if (verbose) | 652 | 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 | } | 653 | } |
655 | 654 | ||
656 | self->slen_calculated = 1; | 655 | self->slen_calculated = 1; |
@@ -729,16 +728,17 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, | |||
729 | * sampled. | 728 | * sampled. |
730 | */ | 729 | */ |
731 | if (!sort_dso.elide && !al->map->dso->slen_calculated) | 730 | if (!sort_dso.elide && !al->map->dso->slen_calculated) |
732 | dso__calc_col_width(al->map->dso); | 731 | dso__calc_col_width(al->map->dso, &session->hists); |
733 | 732 | ||
734 | al->sym = map__find_symbol(al->map, al->addr, filter); | 733 | al->sym = map__find_symbol(al->map, al->addr, filter); |
735 | } else { | 734 | } else { |
736 | const unsigned int unresolved_col_width = BITS_PER_LONG / 4; | 735 | const unsigned int unresolved_col_width = BITS_PER_LONG / 4; |
737 | 736 | ||
738 | if (dsos__col_width < unresolved_col_width && | 737 | if (hists__col_len(&session->hists, HISTC_DSO) < unresolved_col_width && |
739 | !symbol_conf.col_width_list_str && !symbol_conf.field_sep && | 738 | !symbol_conf.col_width_list_str && !symbol_conf.field_sep && |
740 | !symbol_conf.dso_list) | 739 | !symbol_conf.dso_list) |
741 | dsos__col_width = unresolved_col_width; | 740 | hists__set_col_len(&session->hists, HISTC_DSO, |
741 | unresolved_col_width); | ||
742 | } | 742 | } |
743 | 743 | ||
744 | if (symbol_conf.sym_list && al->sym && | 744 | if (symbol_conf.sym_list && al->sym && |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 7b5848ce1505..d0f07f7bdf16 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 | { |
@@ -50,11 +100,19 @@ static struct hist_entry *hist_entry__new(struct hist_entry *template) | |||
50 | return self; | 100 | return self; |
51 | } | 101 | } |
52 | 102 | ||
53 | static void hists__inc_nr_entries(struct hists *self, struct hist_entry *entry) | 103 | static void hists__inc_nr_entries(struct hists *self, struct hist_entry *h) |
54 | { | 104 | { |
55 | if (entry->ms.sym && self->max_sym_namelen < entry->ms.sym->namelen) | 105 | if (!h->filtered) { |
56 | self->max_sym_namelen = entry->ms.sym->namelen; | 106 | hists__calc_col_len(self, h); |
57 | ++self->nr_entries; | 107 | ++self->nr_entries; |
108 | } | ||
109 | } | ||
110 | |||
111 | static u8 symbol__parent_filter(const struct symbol *parent) | ||
112 | { | ||
113 | if (symbol_conf.exclude_other && parent == NULL) | ||
114 | return 1 << HIST_FILTER__PARENT; | ||
115 | return 0; | ||
58 | } | 116 | } |
59 | 117 | ||
60 | struct hist_entry *__hists__add_entry(struct hists *self, | 118 | struct hist_entry *__hists__add_entry(struct hists *self, |
@@ -75,6 +133,7 @@ struct hist_entry *__hists__add_entry(struct hists *self, | |||
75 | .level = al->level, | 133 | .level = al->level, |
76 | .period = period, | 134 | .period = period, |
77 | .parent = sym_parent, | 135 | .parent = sym_parent, |
136 | .filtered = symbol__parent_filter(sym_parent), | ||
78 | }; | 137 | }; |
79 | int cmp; | 138 | int cmp; |
80 | 139 | ||
@@ -192,7 +251,7 @@ void hists__collapse_resort(struct hists *self) | |||
192 | tmp = RB_ROOT; | 251 | tmp = RB_ROOT; |
193 | next = rb_first(&self->entries); | 252 | next = rb_first(&self->entries); |
194 | self->nr_entries = 0; | 253 | self->nr_entries = 0; |
195 | self->max_sym_namelen = 0; | 254 | hists__reset_col_len(self); |
196 | 255 | ||
197 | while (next) { | 256 | while (next) { |
198 | n = rb_entry(next, struct hist_entry, rb_node); | 257 | n = rb_entry(next, struct hist_entry, rb_node); |
@@ -249,7 +308,7 @@ void hists__output_resort(struct hists *self) | |||
249 | next = rb_first(&self->entries); | 308 | next = rb_first(&self->entries); |
250 | 309 | ||
251 | self->nr_entries = 0; | 310 | self->nr_entries = 0; |
252 | self->max_sym_namelen = 0; | 311 | hists__reset_col_len(self); |
253 | 312 | ||
254 | while (next) { | 313 | while (next) { |
255 | n = rb_entry(next, struct hist_entry, rb_node); | 314 | n = rb_entry(next, struct hist_entry, rb_node); |
@@ -516,8 +575,9 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, | |||
516 | } | 575 | } |
517 | 576 | ||
518 | int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, | 577 | int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, |
519 | struct hists *pair_hists, bool show_displacement, | 578 | struct hists *hists, struct hists *pair_hists, |
520 | long displacement, bool color, u64 session_total) | 579 | bool show_displacement, long displacement, |
580 | bool color, u64 session_total) | ||
521 | { | 581 | { |
522 | struct sort_entry *se; | 582 | struct sort_entry *se; |
523 | u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us; | 583 | u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us; |
@@ -621,24 +681,25 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, | |||
621 | 681 | ||
622 | ret += snprintf(s + ret, size - ret, "%s", sep ?: " "); | 682 | ret += snprintf(s + ret, size - ret, "%s", sep ?: " "); |
623 | ret += se->se_snprintf(self, s + ret, size - ret, | 683 | ret += se->se_snprintf(self, s + ret, size - ret, |
624 | se->se_width ? *se->se_width : 0); | 684 | hists__col_len(hists, se->se_width_idx)); |
625 | } | 685 | } |
626 | 686 | ||
627 | return ret; | 687 | return ret; |
628 | } | 688 | } |
629 | 689 | ||
630 | int hist_entry__fprintf(struct hist_entry *self, struct hists *pair_hists, | 690 | int hist_entry__fprintf(struct hist_entry *self, struct hists *hists, |
631 | bool show_displacement, long displacement, FILE *fp, | 691 | struct hists *pair_hists, bool show_displacement, |
632 | u64 session_total) | 692 | long displacement, FILE *fp, u64 session_total) |
633 | { | 693 | { |
634 | char bf[512]; | 694 | char bf[512]; |
635 | hist_entry__snprintf(self, bf, sizeof(bf), pair_hists, | 695 | hist_entry__snprintf(self, bf, sizeof(bf), hists, pair_hists, |
636 | show_displacement, displacement, | 696 | show_displacement, displacement, |
637 | true, session_total); | 697 | true, session_total); |
638 | return fprintf(fp, "%s\n", bf); | 698 | return fprintf(fp, "%s\n", bf); |
639 | } | 699 | } |
640 | 700 | ||
641 | static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp, | 701 | static size_t hist_entry__fprintf_callchain(struct hist_entry *self, |
702 | struct hists *hists, FILE *fp, | ||
642 | u64 session_total) | 703 | u64 session_total) |
643 | { | 704 | { |
644 | int left_margin = 0; | 705 | int left_margin = 0; |
@@ -646,7 +707,7 @@ static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp, | |||
646 | if (sort__first_dimension == SORT_COMM) { | 707 | if (sort__first_dimension == SORT_COMM) { |
647 | struct sort_entry *se = list_first_entry(&hist_entry__sort_list, | 708 | struct sort_entry *se = list_first_entry(&hist_entry__sort_list, |
648 | typeof(*se), list); | 709 | typeof(*se), list); |
649 | left_margin = se->se_width ? *se->se_width : 0; | 710 | left_margin = hists__col_len(hists, se->se_width_idx); |
650 | left_margin -= thread__comm_len(self->thread); | 711 | left_margin -= thread__comm_len(self->thread); |
651 | } | 712 | } |
652 | 713 | ||
@@ -717,17 +778,17 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, | |||
717 | continue; | 778 | continue; |
718 | } | 779 | } |
719 | width = strlen(se->se_header); | 780 | width = strlen(se->se_header); |
720 | if (se->se_width) { | 781 | if (symbol_conf.col_width_list_str) { |
721 | if (symbol_conf.col_width_list_str) { | 782 | if (col_width) { |
722 | if (col_width) { | 783 | hists__set_col_len(self, se->se_width_idx, |
723 | *se->se_width = atoi(col_width); | 784 | atoi(col_width)); |
724 | col_width = strchr(col_width, ','); | 785 | col_width = strchr(col_width, ','); |
725 | if (col_width) | 786 | if (col_width) |
726 | ++col_width; | 787 | ++col_width; |
727 | } | ||
728 | } | 788 | } |
729 | width = *se->se_width = max(*se->se_width, width); | ||
730 | } | 789 | } |
790 | if (!hists__new_col_len(self, se->se_width_idx, width)) | ||
791 | width = hists__col_len(self, se->se_width_idx); | ||
731 | fprintf(fp, " %*s", width, se->se_header); | 792 | fprintf(fp, " %*s", width, se->se_header); |
732 | } | 793 | } |
733 | fprintf(fp, "\n"); | 794 | fprintf(fp, "\n"); |
@@ -750,9 +811,8 @@ size_t hists__fprintf(struct hists *self, struct hists *pair, | |||
750 | continue; | 811 | continue; |
751 | 812 | ||
752 | fprintf(fp, " "); | 813 | fprintf(fp, " "); |
753 | if (se->se_width) | 814 | width = hists__col_len(self, se->se_width_idx); |
754 | width = *se->se_width; | 815 | if (width == 0) |
755 | else | ||
756 | width = strlen(se->se_header); | 816 | width = strlen(se->se_header); |
757 | for (i = 0; i < width; i++) | 817 | for (i = 0; i < width; i++) |
758 | fprintf(fp, "."); | 818 | fprintf(fp, "."); |
@@ -772,12 +832,12 @@ print_entries: | |||
772 | displacement = 0; | 832 | displacement = 0; |
773 | ++position; | 833 | ++position; |
774 | } | 834 | } |
775 | ret += hist_entry__fprintf(h, pair, show_displacement, | 835 | ret += hist_entry__fprintf(h, self, pair, show_displacement, |
776 | displacement, fp, self->stats.total_period); | 836 | displacement, fp, self->stats.total_period); |
777 | 837 | ||
778 | if (symbol_conf.use_callchain) | 838 | if (symbol_conf.use_callchain) |
779 | ret += hist_entry__fprintf_callchain(h, fp, self->stats.total_period); | 839 | ret += hist_entry__fprintf_callchain(h, self, fp, |
780 | 840 | self->stats.total_period); | |
781 | if (h->ms.map == NULL && verbose > 1) { | 841 | if (h->ms.map == NULL && verbose > 1) { |
782 | __map_groups__fprintf_maps(&h->thread->mg, | 842 | __map_groups__fprintf_maps(&h->thread->mg, |
783 | MAP__FUNCTION, verbose, fp); | 843 | MAP__FUNCTION, verbose, fp); |
@@ -790,10 +850,32 @@ print_entries: | |||
790 | return ret; | 850 | return ret; |
791 | } | 851 | } |
792 | 852 | ||
793 | enum hist_filter { | 853 | /* |
794 | HIST_FILTER__DSO, | 854 | * See hists__fprintf to match the column widths |
795 | HIST_FILTER__THREAD, | 855 | */ |
796 | }; | 856 | unsigned int hists__sort_list_width(struct hists *self) |
857 | { | ||
858 | struct sort_entry *se; | ||
859 | int ret = 9; /* total % */ | ||
860 | |||
861 | if (symbol_conf.show_cpu_utilization) { | ||
862 | ret += 7; /* count_sys % */ | ||
863 | ret += 6; /* count_us % */ | ||
864 | if (perf_guest) { | ||
865 | ret += 13; /* count_guest_sys % */ | ||
866 | ret += 12; /* count_guest_us % */ | ||
867 | } | ||
868 | } | ||
869 | |||
870 | if (symbol_conf.show_nr_samples) | ||
871 | ret += 11; | ||
872 | |||
873 | list_for_each_entry(se, &hist_entry__sort_list, list) | ||
874 | if (!se->elide) | ||
875 | ret += 2 + hists__col_len(self, se->se_width_idx); | ||
876 | |||
877 | return ret; | ||
878 | } | ||
797 | 879 | ||
798 | static void hists__remove_entry_filter(struct hists *self, struct hist_entry *h, | 880 | static void hists__remove_entry_filter(struct hists *self, struct hist_entry *h, |
799 | enum hist_filter filter) | 881 | enum hist_filter filter) |
@@ -803,11 +885,13 @@ static void hists__remove_entry_filter(struct hists *self, struct hist_entry *h, | |||
803 | return; | 885 | return; |
804 | 886 | ||
805 | ++self->nr_entries; | 887 | ++self->nr_entries; |
888 | if (h->ms.unfolded) | ||
889 | self->nr_entries += h->nr_rows; | ||
890 | h->row_offset = 0; | ||
806 | self->stats.total_period += h->period; | 891 | self->stats.total_period += h->period; |
807 | self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; | 892 | self->stats.nr_events[PERF_RECORD_SAMPLE] += h->nr_events; |
808 | 893 | ||
809 | if (h->ms.sym && self->max_sym_namelen < h->ms.sym->namelen) | 894 | hists__calc_col_len(self, h); |
810 | self->max_sym_namelen = h->ms.sym->namelen; | ||
811 | } | 895 | } |
812 | 896 | ||
813 | void hists__filter_by_dso(struct hists *self, const struct dso *dso) | 897 | void hists__filter_by_dso(struct hists *self, const struct dso *dso) |
@@ -816,7 +900,7 @@ void hists__filter_by_dso(struct hists *self, const struct dso *dso) | |||
816 | 900 | ||
817 | self->nr_entries = self->stats.total_period = 0; | 901 | self->nr_entries = self->stats.total_period = 0; |
818 | self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; | 902 | self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; |
819 | self->max_sym_namelen = 0; | 903 | hists__reset_col_len(self); |
820 | 904 | ||
821 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | 905 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { |
822 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 906 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
@@ -839,7 +923,7 @@ void hists__filter_by_thread(struct hists *self, const struct thread *thread) | |||
839 | 923 | ||
840 | self->nr_entries = self->stats.total_period = 0; | 924 | self->nr_entries = self->stats.total_period = 0; |
841 | self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; | 925 | self->stats.nr_events[PERF_RECORD_SAMPLE] = 0; |
842 | self->max_sym_namelen = 0; | 926 | hists__reset_col_len(self); |
843 | 927 | ||
844 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { | 928 | for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) { |
845 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 929 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
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..15d6a6dd50c5 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 | { |
@@ -43,7 +33,7 @@ void map__init(struct map *self, enum map_type type, | |||
43 | 33 | ||
44 | struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, | 34 | struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, |
45 | u64 pgoff, u32 pid, char *filename, | 35 | u64 pgoff, u32 pid, char *filename, |
46 | enum map_type type, char *cwd, int cwdlen) | 36 | enum map_type type) |
47 | { | 37 | { |
48 | struct map *self = malloc(sizeof(*self)); | 38 | struct map *self = malloc(sizeof(*self)); |
49 | 39 | ||
@@ -52,16 +42,6 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, | |||
52 | struct dso *dso; | 42 | struct dso *dso; |
53 | int anon; | 43 | int anon; |
54 | 44 | ||
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); | 45 | anon = is_anon_memory(filename); |
66 | 46 | ||
67 | if (anon) { | 47 | if (anon) { |
@@ -248,6 +228,39 @@ void map_groups__init(struct map_groups *self) | |||
248 | self->machine = NULL; | 228 | self->machine = NULL; |
249 | } | 229 | } |
250 | 230 | ||
231 | static void maps__delete(struct rb_root *self) | ||
232 | { | ||
233 | struct rb_node *next = rb_first(self); | ||
234 | |||
235 | while (next) { | ||
236 | struct map *pos = rb_entry(next, struct map, rb_node); | ||
237 | |||
238 | next = rb_next(&pos->rb_node); | ||
239 | rb_erase(&pos->rb_node, self); | ||
240 | map__delete(pos); | ||
241 | } | ||
242 | } | ||
243 | |||
244 | static void maps__delete_removed(struct list_head *self) | ||
245 | { | ||
246 | struct map *pos, *n; | ||
247 | |||
248 | list_for_each_entry_safe(pos, n, self, node) { | ||
249 | list_del(&pos->node); | ||
250 | map__delete(pos); | ||
251 | } | ||
252 | } | ||
253 | |||
254 | void map_groups__exit(struct map_groups *self) | ||
255 | { | ||
256 | int i; | ||
257 | |||
258 | for (i = 0; i < MAP__NR_TYPES; ++i) { | ||
259 | maps__delete(&self->maps[i]); | ||
260 | maps__delete_removed(&self->removed_maps[i]); | ||
261 | } | ||
262 | } | ||
263 | |||
251 | void map_groups__flush(struct map_groups *self) | 264 | void map_groups__flush(struct map_groups *self) |
252 | { | 265 | { |
253 | int type; | 266 | int type; |
@@ -526,6 +539,32 @@ int machine__init(struct machine *self, const char *root_dir, pid_t pid) | |||
526 | return self->root_dir == NULL ? -ENOMEM : 0; | 539 | return self->root_dir == NULL ? -ENOMEM : 0; |
527 | } | 540 | } |
528 | 541 | ||
542 | static void dsos__delete(struct list_head *self) | ||
543 | { | ||
544 | struct dso *pos, *n; | ||
545 | |||
546 | list_for_each_entry_safe(pos, n, self, node) { | ||
547 | list_del(&pos->node); | ||
548 | dso__delete(pos); | ||
549 | } | ||
550 | } | ||
551 | |||
552 | void machine__exit(struct machine *self) | ||
553 | { | ||
554 | struct kmap *kmap = map__kmap(self->vmlinux_maps[MAP__FUNCTION]); | ||
555 | |||
556 | if (kmap->ref_reloc_sym) { | ||
557 | free((char *)kmap->ref_reloc_sym->name); | ||
558 | free(kmap->ref_reloc_sym); | ||
559 | } | ||
560 | |||
561 | map_groups__exit(&self->kmaps); | ||
562 | dsos__delete(&self->user_dsos); | ||
563 | dsos__delete(&self->kernel_dsos); | ||
564 | free(self->root_dir); | ||
565 | self->root_dir = NULL; | ||
566 | } | ||
567 | |||
529 | struct machine *machines__add(struct rb_root *self, pid_t pid, | 568 | struct machine *machines__add(struct rb_root *self, pid_t pid, |
530 | const char *root_dir) | 569 | const char *root_dir) |
531 | { | 570 | { |
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index f39134512829..0e0984e86fce 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h | |||
@@ -106,7 +106,7 @@ void map__init(struct map *self, enum map_type type, | |||
106 | u64 start, u64 end, u64 pgoff, struct dso *dso); | 106 | u64 start, u64 end, u64 pgoff, struct dso *dso); |
107 | struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, | 107 | struct map *map__new(struct list_head *dsos__list, u64 start, u64 len, |
108 | u64 pgoff, u32 pid, char *filename, | 108 | u64 pgoff, u32 pid, char *filename, |
109 | enum map_type type, char *cwd, int cwdlen); | 109 | enum map_type type); |
110 | void map__delete(struct map *self); | 110 | void map__delete(struct map *self); |
111 | struct map *map__clone(struct map *self); | 111 | struct map *map__clone(struct map *self); |
112 | int map__overlap(struct map *l, struct map *r); | 112 | int map__overlap(struct map *l, struct map *r); |
@@ -127,6 +127,7 @@ size_t __map_groups__fprintf_maps(struct map_groups *self, | |||
127 | void maps__insert(struct rb_root *maps, struct map *map); | 127 | void maps__insert(struct rb_root *maps, struct map *map); |
128 | struct map *maps__find(struct rb_root *maps, u64 addr); | 128 | struct map *maps__find(struct rb_root *maps, u64 addr); |
129 | void map_groups__init(struct map_groups *self); | 129 | void map_groups__init(struct map_groups *self); |
130 | void map_groups__exit(struct map_groups *self); | ||
130 | int map_groups__clone(struct map_groups *self, | 131 | int map_groups__clone(struct map_groups *self, |
131 | struct map_groups *parent, enum map_type type); | 132 | struct map_groups *parent, enum map_type type); |
132 | size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp); | 133 | size_t map_groups__fprintf(struct map_groups *self, int verbose, FILE *fp); |
@@ -142,6 +143,7 @@ struct machine *machines__find(struct rb_root *self, pid_t pid); | |||
142 | struct machine *machines__findnew(struct rb_root *self, pid_t pid); | 143 | struct machine *machines__findnew(struct rb_root *self, pid_t pid); |
143 | char *machine__mmap_name(struct machine *self, char *bf, size_t size); | 144 | 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); | 145 | int machine__init(struct machine *self, const char *root_dir, pid_t pid); |
146 | void machine__exit(struct machine *self); | ||
145 | 147 | ||
146 | /* | 148 | /* |
147 | * Default guest kernel is defined by parameter --guestkallsyms | 149 | * Default guest kernel is defined by parameter --guestkallsyms |
diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c index 7979003adeaf..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> |
@@ -342,15 +343,17 @@ static void ui_browser__reset_index(struct ui_browser *self) | |||
342 | 343 | ||
343 | static int ui_browser__show(struct ui_browser *self, const char *title) | 344 | static int ui_browser__show(struct ui_browser *self, const char *title) |
344 | { | 345 | { |
345 | if (self->form != NULL) | 346 | if (self->form != NULL) { |
346 | return 0; | 347 | newtFormDestroy(self->form); |
348 | newtPopWindow(); | ||
349 | } | ||
347 | ui_browser__refresh_dimensions(self); | 350 | ui_browser__refresh_dimensions(self); |
348 | newtCenteredWindow(self->width + 2, self->height, title); | 351 | newtCenteredWindow(self->width, self->height, title); |
349 | self->form = newt_form__new(); | 352 | self->form = newt_form__new(); |
350 | if (self->form == NULL) | 353 | if (self->form == NULL) |
351 | return -1; | 354 | return -1; |
352 | 355 | ||
353 | self->sb = newtVerticalScrollbar(self->width + 1, 0, self->height, | 356 | self->sb = newtVerticalScrollbar(self->width, 0, self->height, |
354 | HE_COLORSET_NORMAL, | 357 | HE_COLORSET_NORMAL, |
355 | HE_COLORSET_SELECTED); | 358 | HE_COLORSET_SELECTED); |
356 | if (self->sb == NULL) | 359 | if (self->sb == NULL) |
@@ -507,38 +510,6 @@ static int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es) | |||
507 | return 0; | 510 | return 0; |
508 | } | 511 | } |
509 | 512 | ||
510 | /* | ||
511 | * When debugging newt problems it was useful to be able to "unroll" | ||
512 | * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate | ||
513 | * a source file with the sequence of calls to these methods, to then | ||
514 | * tweak the arrays to get the intended results, so I'm keeping this code | ||
515 | * here, may be useful again in the future. | ||
516 | */ | ||
517 | #undef NEWT_DEBUG | ||
518 | |||
519 | static void newt_checkbox_tree__add(newtComponent tree, const char *str, | ||
520 | void *priv, int *indexes) | ||
521 | { | ||
522 | #ifdef NEWT_DEBUG | ||
523 | /* Print the newtCheckboxTreeAddArray to tinker with its index arrays */ | ||
524 | int i = 0, len = 40 - strlen(str); | ||
525 | |||
526 | fprintf(stderr, | ||
527 | "\tnewtCheckboxTreeAddItem(tree, %*.*s\"%s\", (void *)%p, 0, ", | ||
528 | len, len, " ", str, priv); | ||
529 | while (indexes[i] != NEWT_ARG_LAST) { | ||
530 | if (indexes[i] != NEWT_ARG_APPEND) | ||
531 | fprintf(stderr, " %d,", indexes[i]); | ||
532 | else | ||
533 | fprintf(stderr, " %s,", "NEWT_ARG_APPEND"); | ||
534 | ++i; | ||
535 | } | ||
536 | fprintf(stderr, " %s", " NEWT_ARG_LAST);\n"); | ||
537 | fflush(stderr); | ||
538 | #endif | ||
539 | newtCheckboxTreeAddArray(tree, str, priv, 0, indexes); | ||
540 | } | ||
541 | |||
542 | static char *callchain_list__sym_name(struct callchain_list *self, | 513 | static char *callchain_list__sym_name(struct callchain_list *self, |
543 | char *bf, size_t bfsize) | 514 | char *bf, size_t bfsize) |
544 | { | 515 | { |
@@ -574,146 +545,6 @@ static unsigned int hist_entry__annotate_browser_refresh(struct ui_browser *self | |||
574 | return row; | 545 | return row; |
575 | } | 546 | } |
576 | 547 | ||
577 | static void __callchain__append_graph_browser(struct callchain_node *self, | ||
578 | newtComponent tree, u64 total, | ||
579 | int *indexes, int depth) | ||
580 | { | ||
581 | struct rb_node *node; | ||
582 | u64 new_total, remaining; | ||
583 | int idx = 0; | ||
584 | |||
585 | if (callchain_param.mode == CHAIN_GRAPH_REL) | ||
586 | new_total = self->children_hit; | ||
587 | else | ||
588 | new_total = total; | ||
589 | |||
590 | remaining = new_total; | ||
591 | node = rb_first(&self->rb_root); | ||
592 | while (node) { | ||
593 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); | ||
594 | struct rb_node *next = rb_next(node); | ||
595 | u64 cumul = cumul_hits(child); | ||
596 | struct callchain_list *chain; | ||
597 | int first = true, printed = 0; | ||
598 | int chain_idx = -1; | ||
599 | remaining -= cumul; | ||
600 | |||
601 | indexes[depth] = NEWT_ARG_APPEND; | ||
602 | indexes[depth + 1] = NEWT_ARG_LAST; | ||
603 | |||
604 | list_for_each_entry(chain, &child->val, list) { | ||
605 | char ipstr[BITS_PER_LONG / 4 + 1], | ||
606 | *alloc_str = NULL; | ||
607 | const char *str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); | ||
608 | |||
609 | if (first) { | ||
610 | double percent = cumul * 100.0 / new_total; | ||
611 | |||
612 | first = false; | ||
613 | if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) | ||
614 | str = "Not enough memory!"; | ||
615 | else | ||
616 | str = alloc_str; | ||
617 | } else { | ||
618 | indexes[depth] = idx; | ||
619 | indexes[depth + 1] = NEWT_ARG_APPEND; | ||
620 | indexes[depth + 2] = NEWT_ARG_LAST; | ||
621 | ++chain_idx; | ||
622 | } | ||
623 | newt_checkbox_tree__add(tree, str, &chain->ms, indexes); | ||
624 | free(alloc_str); | ||
625 | ++printed; | ||
626 | } | ||
627 | |||
628 | indexes[depth] = idx; | ||
629 | if (chain_idx != -1) | ||
630 | indexes[depth + 1] = chain_idx; | ||
631 | if (printed != 0) | ||
632 | ++idx; | ||
633 | __callchain__append_graph_browser(child, tree, new_total, indexes, | ||
634 | depth + (chain_idx != -1 ? 2 : 1)); | ||
635 | node = next; | ||
636 | } | ||
637 | } | ||
638 | |||
639 | static void callchain__append_graph_browser(struct callchain_node *self, | ||
640 | newtComponent tree, u64 total, | ||
641 | int *indexes, int parent_idx) | ||
642 | { | ||
643 | struct callchain_list *chain; | ||
644 | int i = 0; | ||
645 | |||
646 | indexes[1] = NEWT_ARG_APPEND; | ||
647 | indexes[2] = NEWT_ARG_LAST; | ||
648 | |||
649 | list_for_each_entry(chain, &self->val, list) { | ||
650 | char ipstr[BITS_PER_LONG / 4 + 1], *str; | ||
651 | |||
652 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
653 | continue; | ||
654 | |||
655 | if (!i++ && sort__first_dimension == SORT_SYM) | ||
656 | continue; | ||
657 | |||
658 | str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); | ||
659 | newt_checkbox_tree__add(tree, str, &chain->ms, indexes); | ||
660 | } | ||
661 | |||
662 | indexes[1] = parent_idx; | ||
663 | indexes[2] = NEWT_ARG_APPEND; | ||
664 | indexes[3] = NEWT_ARG_LAST; | ||
665 | __callchain__append_graph_browser(self, tree, total, indexes, 2); | ||
666 | } | ||
667 | |||
668 | static void hist_entry__append_callchain_browser(struct hist_entry *self, | ||
669 | newtComponent tree, u64 total, int parent_idx) | ||
670 | { | ||
671 | struct rb_node *rb_node; | ||
672 | int indexes[1024] = { [0] = parent_idx, }; | ||
673 | int idx = 0; | ||
674 | struct callchain_node *chain; | ||
675 | |||
676 | rb_node = rb_first(&self->sorted_chain); | ||
677 | while (rb_node) { | ||
678 | chain = rb_entry(rb_node, struct callchain_node, rb_node); | ||
679 | switch (callchain_param.mode) { | ||
680 | case CHAIN_FLAT: | ||
681 | break; | ||
682 | case CHAIN_GRAPH_ABS: /* falldown */ | ||
683 | case CHAIN_GRAPH_REL: | ||
684 | callchain__append_graph_browser(chain, tree, total, indexes, idx++); | ||
685 | break; | ||
686 | case CHAIN_NONE: | ||
687 | default: | ||
688 | break; | ||
689 | } | ||
690 | rb_node = rb_next(rb_node); | ||
691 | } | ||
692 | } | ||
693 | |||
694 | static size_t hist_entry__append_browser(struct hist_entry *self, | ||
695 | newtComponent tree, u64 total) | ||
696 | { | ||
697 | char s[256]; | ||
698 | size_t ret; | ||
699 | |||
700 | if (symbol_conf.exclude_other && !self->parent) | ||
701 | return 0; | ||
702 | |||
703 | ret = hist_entry__snprintf(self, s, sizeof(s), NULL, | ||
704 | false, 0, false, total); | ||
705 | if (symbol_conf.use_callchain) { | ||
706 | int indexes[2]; | ||
707 | |||
708 | indexes[0] = NEWT_ARG_APPEND; | ||
709 | indexes[1] = NEWT_ARG_LAST; | ||
710 | newt_checkbox_tree__add(tree, s, &self->ms, indexes); | ||
711 | } else | ||
712 | newtListboxAppendEntry(tree, s, &self->ms); | ||
713 | |||
714 | return ret; | ||
715 | } | ||
716 | |||
717 | int hist_entry__tui_annotate(struct hist_entry *self) | 548 | int hist_entry__tui_annotate(struct hist_entry *self) |
718 | { | 549 | { |
719 | struct ui_browser browser; | 550 | struct ui_browser browser; |
@@ -749,6 +580,7 @@ int hist_entry__tui_annotate(struct hist_entry *self) | |||
749 | 580 | ||
750 | browser.width += 18; /* Percentage */ | 581 | browser.width += 18; /* Percentage */ |
751 | ui_browser__show(&browser, self->ms.sym->name); | 582 | ui_browser__show(&browser, self->ms.sym->name); |
583 | newtFormAddHotKey(browser.form, ' '); | ||
752 | ret = ui_browser__run(&browser, &es); | 584 | ret = ui_browser__run(&browser, &es); |
753 | newtFormDestroy(browser.form); | 585 | newtFormDestroy(browser.form); |
754 | newtPopWindow(); | 586 | newtPopWindow(); |
@@ -760,157 +592,48 @@ int hist_entry__tui_annotate(struct hist_entry *self) | |||
760 | return ret; | 592 | return ret; |
761 | } | 593 | } |
762 | 594 | ||
763 | static const void *newt__symbol_tree_get_current(newtComponent self) | ||
764 | { | ||
765 | if (symbol_conf.use_callchain) | ||
766 | return newtCheckboxTreeGetCurrent(self); | ||
767 | return newtListboxGetCurrent(self); | ||
768 | } | ||
769 | |||
770 | static void hist_browser__selection(newtComponent self, void *data) | ||
771 | { | ||
772 | const struct map_symbol **symbol_ptr = data; | ||
773 | *symbol_ptr = newt__symbol_tree_get_current(self); | ||
774 | } | ||
775 | |||
776 | struct hist_browser { | 595 | struct hist_browser { |
777 | newtComponent form, tree; | 596 | struct ui_browser b; |
778 | const struct map_symbol *selection; | 597 | struct hists *hists; |
598 | struct hist_entry *he_selection; | ||
599 | struct map_symbol *selection; | ||
779 | }; | 600 | }; |
780 | 601 | ||
781 | 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) | ||
782 | { | 610 | { |
783 | struct hist_browser *self = malloc(sizeof(*self)); | 611 | struct hist_browser *self = zalloc(sizeof(*self)); |
784 | 612 | ||
785 | if (self != NULL) | 613 | if (self) { |
786 | 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 | } | ||
787 | 618 | ||
788 | return self; | 619 | return self; |
789 | } | 620 | } |
790 | 621 | ||
791 | static void hist_browser__delete(struct hist_browser *self) | 622 | static void hist_browser__delete(struct hist_browser *self) |
792 | { | 623 | { |
793 | newtFormDestroy(self->form); | 624 | newtFormDestroy(self->b.form); |
794 | newtPopWindow(); | 625 | newtPopWindow(); |
795 | free(self); | 626 | free(self); |
796 | } | 627 | } |
797 | 628 | ||
798 | static int hist_browser__populate(struct hist_browser *self, struct hists *hists, | ||
799 | const char *title) | ||
800 | { | ||
801 | int max_len = 0, idx, cols, rows; | ||
802 | struct ui_progress *progress; | ||
803 | struct rb_node *nd; | ||
804 | u64 curr_hist = 0; | ||
805 | char seq[] = ".", unit; | ||
806 | char str[256]; | ||
807 | unsigned long nr_events = hists->stats.nr_events[PERF_RECORD_SAMPLE]; | ||
808 | |||
809 | if (self->form) { | ||
810 | newtFormDestroy(self->form); | ||
811 | newtPopWindow(); | ||
812 | } | ||
813 | |||
814 | nr_events = convert_unit(nr_events, &unit); | ||
815 | snprintf(str, sizeof(str), "Events: %lu%c ", | ||
816 | nr_events, unit); | ||
817 | newtDrawRootText(0, 0, str); | ||
818 | |||
819 | newtGetScreenSize(NULL, &rows); | ||
820 | |||
821 | if (symbol_conf.use_callchain) | ||
822 | self->tree = newtCheckboxTreeMulti(0, 0, rows - 5, seq, | ||
823 | NEWT_FLAG_SCROLL); | ||
824 | else | ||
825 | self->tree = newtListbox(0, 0, rows - 5, | ||
826 | (NEWT_FLAG_SCROLL | | ||
827 | NEWT_FLAG_RETURNEXIT)); | ||
828 | |||
829 | newtComponentAddCallback(self->tree, hist_browser__selection, | ||
830 | &self->selection); | ||
831 | |||
832 | progress = ui_progress__new("Adding entries to the browser...", | ||
833 | hists->nr_entries); | ||
834 | if (progress == NULL) | ||
835 | return -1; | ||
836 | |||
837 | idx = 0; | ||
838 | for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) { | ||
839 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
840 | int len; | ||
841 | |||
842 | if (h->filtered) | ||
843 | continue; | ||
844 | |||
845 | len = hist_entry__append_browser(h, self->tree, hists->stats.total_period); | ||
846 | if (len > max_len) | ||
847 | max_len = len; | ||
848 | if (symbol_conf.use_callchain) | ||
849 | hist_entry__append_callchain_browser(h, self->tree, | ||
850 | hists->stats.total_period, idx++); | ||
851 | ++curr_hist; | ||
852 | if (curr_hist % 5) | ||
853 | ui_progress__update(progress, curr_hist); | ||
854 | } | ||
855 | |||
856 | ui_progress__delete(progress); | ||
857 | |||
858 | newtGetScreenSize(&cols, &rows); | ||
859 | |||
860 | if (max_len > cols) | ||
861 | max_len = cols - 3; | ||
862 | |||
863 | if (!symbol_conf.use_callchain) | ||
864 | newtListboxSetWidth(self->tree, max_len); | ||
865 | |||
866 | newtCenteredWindow(max_len + (symbol_conf.use_callchain ? 5 : 0), | ||
867 | rows - 5, title); | ||
868 | self->form = newt_form__new(); | ||
869 | if (self->form == NULL) | ||
870 | return -1; | ||
871 | |||
872 | newtFormAddHotKey(self->form, 'A'); | ||
873 | newtFormAddHotKey(self->form, 'a'); | ||
874 | newtFormAddHotKey(self->form, 'D'); | ||
875 | newtFormAddHotKey(self->form, 'd'); | ||
876 | newtFormAddHotKey(self->form, 'T'); | ||
877 | newtFormAddHotKey(self->form, 't'); | ||
878 | newtFormAddHotKey(self->form, '?'); | ||
879 | newtFormAddHotKey(self->form, 'H'); | ||
880 | newtFormAddHotKey(self->form, 'h'); | ||
881 | newtFormAddHotKey(self->form, NEWT_KEY_F1); | ||
882 | newtFormAddHotKey(self->form, NEWT_KEY_RIGHT); | ||
883 | newtFormAddHotKey(self->form, NEWT_KEY_TAB); | ||
884 | newtFormAddHotKey(self->form, NEWT_KEY_UNTAB); | ||
885 | newtFormAddComponents(self->form, self->tree, NULL); | ||
886 | self->selection = newt__symbol_tree_get_current(self->tree); | ||
887 | |||
888 | return 0; | ||
889 | } | ||
890 | |||
891 | 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) |
892 | { | 630 | { |
893 | int *indexes; | 631 | return self->he_selection; |
894 | |||
895 | if (!symbol_conf.use_callchain) | ||
896 | goto out; | ||
897 | |||
898 | indexes = newtCheckboxTreeFindItem(self->tree, (void *)self->selection); | ||
899 | if (indexes) { | ||
900 | bool is_hist_entry = indexes[1] == NEWT_ARG_LAST; | ||
901 | free(indexes); | ||
902 | if (is_hist_entry) | ||
903 | goto out; | ||
904 | } | ||
905 | return NULL; | ||
906 | out: | ||
907 | return container_of(self->selection, struct hist_entry, ms); | ||
908 | } | 632 | } |
909 | 633 | ||
910 | static struct thread *hist_browser__selected_thread(struct hist_browser *self) | 634 | static struct thread *hist_browser__selected_thread(struct hist_browser *self) |
911 | { | 635 | { |
912 | struct hist_entry *he = hist_browser__selected_entry(self); | 636 | return self->he_selection->thread; |
913 | return he ? he->thread : NULL; | ||
914 | } | 637 | } |
915 | 638 | ||
916 | 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, |
@@ -932,7 +655,7 @@ static int hist_browser__title(char *bf, size_t size, const char *ev_name, | |||
932 | 655 | ||
933 | 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) |
934 | { | 657 | { |
935 | struct hist_browser *browser = hist_browser__new(); | 658 | struct hist_browser *browser = hist_browser__new(self); |
936 | struct pstack *fstack; | 659 | struct pstack *fstack; |
937 | const struct thread *thread_filter = NULL; | 660 | const struct thread *thread_filter = NULL; |
938 | const struct dso *dso_filter = NULL; | 661 | const struct dso *dso_filter = NULL; |
@@ -951,8 +674,6 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name) | |||
951 | 674 | ||
952 | hist_browser__title(msg, sizeof(msg), ev_name, | 675 | hist_browser__title(msg, sizeof(msg), ev_name, |
953 | dso_filter, thread_filter); | 676 | dso_filter, thread_filter); |
954 | if (hist_browser__populate(browser, self, msg) < 0) | ||
955 | goto out_free_stack; | ||
956 | 677 | ||
957 | while (1) { | 678 | while (1) { |
958 | const struct thread *thread; | 679 | const struct thread *thread; |
@@ -961,7 +682,8 @@ int hists__browse(struct hists *self, const char *helpline, const char *ev_name) | |||
961 | int nr_options = 0, choice = 0, i, | 682 | int nr_options = 0, choice = 0, i, |
962 | annotate = -2, zoom_dso = -2, zoom_thread = -2; | 683 | annotate = -2, zoom_dso = -2, zoom_thread = -2; |
963 | 684 | ||
964 | newtFormRun(browser->form, &es); | 685 | if (hist_browser__run(browser, msg, &es)) |
686 | break; | ||
965 | 687 | ||
966 | thread = hist_browser__selected_thread(browser); | 688 | thread = hist_browser__selected_thread(browser); |
967 | dso = browser->selection->map ? browser->selection->map->dso : NULL; | 689 | dso = browser->selection->map ? browser->selection->map->dso : NULL; |
@@ -1096,8 +818,7 @@ zoom_out_dso: | |||
1096 | hists__filter_by_dso(self, dso_filter); | 818 | hists__filter_by_dso(self, dso_filter); |
1097 | hist_browser__title(msg, sizeof(msg), ev_name, | 819 | hist_browser__title(msg, sizeof(msg), ev_name, |
1098 | dso_filter, thread_filter); | 820 | dso_filter, thread_filter); |
1099 | if (hist_browser__populate(browser, self, msg) < 0) | 821 | hist_browser__reset(browser); |
1100 | goto out; | ||
1101 | } else if (choice == zoom_thread) { | 822 | } else if (choice == zoom_thread) { |
1102 | zoom_thread: | 823 | zoom_thread: |
1103 | if (thread_filter) { | 824 | if (thread_filter) { |
@@ -1115,8 +836,7 @@ zoom_out_thread: | |||
1115 | hists__filter_by_thread(self, thread_filter); | 836 | hists__filter_by_thread(self, thread_filter); |
1116 | hist_browser__title(msg, sizeof(msg), ev_name, | 837 | hist_browser__title(msg, sizeof(msg), ev_name, |
1117 | dso_filter, thread_filter); | 838 | dso_filter, thread_filter); |
1118 | if (hist_browser__populate(browser, self, msg) < 0) | 839 | hist_browser__reset(browser); |
1119 | goto out; | ||
1120 | } | 840 | } |
1121 | } | 841 | } |
1122 | out_free_stack: | 842 | out_free_stack: |
@@ -1172,6 +892,13 @@ static struct newtPercentTreeColors { | |||
1172 | "blue", "lightgray", | 892 | "blue", "lightgray", |
1173 | }; | 893 | }; |
1174 | 894 | ||
895 | static void newt_suspend(void *d __used) | ||
896 | { | ||
897 | newtSuspend(); | ||
898 | raise(SIGTSTP); | ||
899 | newtResume(); | ||
900 | } | ||
901 | |||
1175 | void setup_browser(void) | 902 | void setup_browser(void) |
1176 | { | 903 | { |
1177 | struct newtPercentTreeColors *c = &defaultPercentTreeColors; | 904 | struct newtPercentTreeColors *c = &defaultPercentTreeColors; |
@@ -1185,6 +912,7 @@ void setup_browser(void) | |||
1185 | use_browser = 1; | 912 | use_browser = 1; |
1186 | newtInit(); | 913 | newtInit(); |
1187 | newtCls(); | 914 | newtCls(); |
915 | newtSetSuspendCallback(newt_suspend, NULL); | ||
1188 | ui_helpline__puts(" "); | 916 | ui_helpline__puts(" "); |
1189 | sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg); | 917 | sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg); |
1190 | sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg); | 918 | sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg); |
@@ -1203,3 +931,638 @@ void exit_browser(bool wait_for_ok) | |||
1203 | newtFinished(); | 931 | newtFinished(); |
1204 | } | 932 | } |
1205 | } | 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/probe-event.c b/tools/perf/util/probe-event.c index 4445a1e7052f..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 | ||
@@ -377,8 +380,8 @@ end: | |||
377 | 380 | ||
378 | #else /* !DWARF_SUPPORT */ | 381 | #else /* !DWARF_SUPPORT */ |
379 | 382 | ||
380 | 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, |
381 | struct perf_probe_point *pp) | 384 | struct perf_probe_point *pp) |
382 | { | 385 | { |
383 | pp->function = strdup(tp->symbol); | 386 | pp->function = strdup(tp->symbol); |
384 | if (pp->function == NULL) | 387 | if (pp->function == NULL) |
@@ -389,8 +392,8 @@ static int convert_to_perf_probe_point(struct kprobe_trace_point *tp, | |||
389 | return 0; | 392 | return 0; |
390 | } | 393 | } |
391 | 394 | ||
392 | 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, |
393 | struct kprobe_trace_event **tevs __unused, | 396 | struct probe_trace_event **tevs __unused, |
394 | int max_tevs __unused) | 397 | int max_tevs __unused) |
395 | { | 398 | { |
396 | if (perf_probe_event_need_dwarf(pev)) { | 399 | if (perf_probe_event_need_dwarf(pev)) { |
@@ -781,16 +784,17 @@ bool perf_probe_event_need_dwarf(struct perf_probe_event *pev) | |||
781 | return false; | 784 | return false; |
782 | } | 785 | } |
783 | 786 | ||
784 | /* Parse kprobe_events event into struct probe_point */ | 787 | /* Parse probe_events event into struct probe_point */ |
785 | 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) | ||
786 | { | 790 | { |
787 | struct kprobe_trace_point *tp = &tev->point; | 791 | struct probe_trace_point *tp = &tev->point; |
788 | char pr; | 792 | char pr; |
789 | char *p; | 793 | char *p; |
790 | int ret, i, argc; | 794 | int ret, i, argc; |
791 | char **argv; | 795 | char **argv; |
792 | 796 | ||
793 | pr_debug("Parsing kprobe_events: %s\n", cmd); | 797 | pr_debug("Parsing probe_events: %s\n", cmd); |
794 | argv = argv_split(cmd, &argc); | 798 | argv = argv_split(cmd, &argc); |
795 | if (!argv) { | 799 | if (!argv) { |
796 | pr_debug("Failed to split arguments.\n"); | 800 | pr_debug("Failed to split arguments.\n"); |
@@ -822,7 +826,7 @@ int parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev) | |||
822 | tp->offset = 0; | 826 | tp->offset = 0; |
823 | 827 | ||
824 | tev->nargs = argc - 2; | 828 | tev->nargs = argc - 2; |
825 | tev->args = zalloc(sizeof(struct kprobe_trace_arg) * tev->nargs); | 829 | tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); |
826 | if (tev->args == NULL) { | 830 | if (tev->args == NULL) { |
827 | ret = -ENOMEM; | 831 | ret = -ENOMEM; |
828 | goto out; | 832 | goto out; |
@@ -968,13 +972,13 @@ char *synthesize_perf_probe_command(struct perf_probe_event *pev) | |||
968 | } | 972 | } |
969 | #endif | 973 | #endif |
970 | 974 | ||
971 | 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, |
972 | char **buf, size_t *buflen, | 976 | char **buf, size_t *buflen, |
973 | int depth) | 977 | int depth) |
974 | { | 978 | { |
975 | int ret; | 979 | int ret; |
976 | if (ref->next) { | 980 | if (ref->next) { |
977 | depth = __synthesize_kprobe_trace_arg_ref(ref->next, buf, | 981 | depth = __synthesize_probe_trace_arg_ref(ref->next, buf, |
978 | buflen, depth + 1); | 982 | buflen, depth + 1); |
979 | if (depth < 0) | 983 | if (depth < 0) |
980 | goto out; | 984 | goto out; |
@@ -992,10 +996,10 @@ out: | |||
992 | 996 | ||
993 | } | 997 | } |
994 | 998 | ||
995 | static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg, | 999 | static int synthesize_probe_trace_arg(struct probe_trace_arg *arg, |
996 | char *buf, size_t buflen) | 1000 | char *buf, size_t buflen) |
997 | { | 1001 | { |
998 | struct kprobe_trace_arg_ref *ref = arg->ref; | 1002 | struct probe_trace_arg_ref *ref = arg->ref; |
999 | int ret, depth = 0; | 1003 | int ret, depth = 0; |
1000 | char *tmp = buf; | 1004 | char *tmp = buf; |
1001 | 1005 | ||
@@ -1015,7 +1019,7 @@ static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg, | |||
1015 | 1019 | ||
1016 | /* Dereferencing arguments */ | 1020 | /* Dereferencing arguments */ |
1017 | if (ref) { | 1021 | if (ref) { |
1018 | depth = __synthesize_kprobe_trace_arg_ref(ref, &buf, | 1022 | depth = __synthesize_probe_trace_arg_ref(ref, &buf, |
1019 | &buflen, 1); | 1023 | &buflen, 1); |
1020 | if (depth < 0) | 1024 | if (depth < 0) |
1021 | return depth; | 1025 | return depth; |
@@ -1051,9 +1055,9 @@ static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg, | |||
1051 | return buf - tmp; | 1055 | return buf - tmp; |
1052 | } | 1056 | } |
1053 | 1057 | ||
1054 | char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev) | 1058 | char *synthesize_probe_trace_command(struct probe_trace_event *tev) |
1055 | { | 1059 | { |
1056 | struct kprobe_trace_point *tp = &tev->point; | 1060 | struct probe_trace_point *tp = &tev->point; |
1057 | char *buf; | 1061 | char *buf; |
1058 | int i, len, ret; | 1062 | int i, len, ret; |
1059 | 1063 | ||
@@ -1069,7 +1073,7 @@ char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev) | |||
1069 | goto error; | 1073 | goto error; |
1070 | 1074 | ||
1071 | for (i = 0; i < tev->nargs; i++) { | 1075 | for (i = 0; i < tev->nargs; i++) { |
1072 | ret = synthesize_kprobe_trace_arg(&tev->args[i], buf + len, | 1076 | ret = synthesize_probe_trace_arg(&tev->args[i], buf + len, |
1073 | MAX_CMDLEN - len); | 1077 | MAX_CMDLEN - len); |
1074 | if (ret <= 0) | 1078 | if (ret <= 0) |
1075 | goto error; | 1079 | goto error; |
@@ -1082,7 +1086,7 @@ error: | |||
1082 | return NULL; | 1086 | return NULL; |
1083 | } | 1087 | } |
1084 | 1088 | ||
1085 | int convert_to_perf_probe_event(struct kprobe_trace_event *tev, | 1089 | static int convert_to_perf_probe_event(struct probe_trace_event *tev, |
1086 | struct perf_probe_event *pev) | 1090 | struct perf_probe_event *pev) |
1087 | { | 1091 | { |
1088 | char buf[64] = ""; | 1092 | char buf[64] = ""; |
@@ -1095,7 +1099,7 @@ int convert_to_perf_probe_event(struct kprobe_trace_event *tev, | |||
1095 | return -ENOMEM; | 1099 | return -ENOMEM; |
1096 | 1100 | ||
1097 | /* Convert trace_point to probe_point */ | 1101 | /* Convert trace_point to probe_point */ |
1098 | ret = convert_to_perf_probe_point(&tev->point, &pev->point); | 1102 | ret = kprobe_convert_to_perf_probe(&tev->point, &pev->point); |
1099 | if (ret < 0) | 1103 | if (ret < 0) |
1100 | return ret; | 1104 | return ret; |
1101 | 1105 | ||
@@ -1108,7 +1112,7 @@ int convert_to_perf_probe_event(struct kprobe_trace_event *tev, | |||
1108 | if (tev->args[i].name) | 1112 | if (tev->args[i].name) |
1109 | pev->args[i].name = strdup(tev->args[i].name); | 1113 | pev->args[i].name = strdup(tev->args[i].name); |
1110 | else { | 1114 | else { |
1111 | ret = synthesize_kprobe_trace_arg(&tev->args[i], | 1115 | ret = synthesize_probe_trace_arg(&tev->args[i], |
1112 | buf, 64); | 1116 | buf, 64); |
1113 | pev->args[i].name = strdup(buf); | 1117 | pev->args[i].name = strdup(buf); |
1114 | } | 1118 | } |
@@ -1159,9 +1163,9 @@ void clear_perf_probe_event(struct perf_probe_event *pev) | |||
1159 | memset(pev, 0, sizeof(*pev)); | 1163 | memset(pev, 0, sizeof(*pev)); |
1160 | } | 1164 | } |
1161 | 1165 | ||
1162 | void clear_kprobe_trace_event(struct kprobe_trace_event *tev) | 1166 | static void clear_probe_trace_event(struct probe_trace_event *tev) |
1163 | { | 1167 | { |
1164 | struct kprobe_trace_arg_ref *ref, *next; | 1168 | struct probe_trace_arg_ref *ref, *next; |
1165 | int i; | 1169 | int i; |
1166 | 1170 | ||
1167 | if (tev->event) | 1171 | if (tev->event) |
@@ -1222,7 +1226,7 @@ static int open_kprobe_events(bool readwrite) | |||
1222 | } | 1226 | } |
1223 | 1227 | ||
1224 | /* Get raw string list of current kprobe_events */ | 1228 | /* Get raw string list of current kprobe_events */ |
1225 | static struct strlist *get_kprobe_trace_command_rawlist(int fd) | 1229 | static struct strlist *get_probe_trace_command_rawlist(int fd) |
1226 | { | 1230 | { |
1227 | int ret, idx; | 1231 | int ret, idx; |
1228 | FILE *fp; | 1232 | FILE *fp; |
@@ -1290,7 +1294,7 @@ static int show_perf_probe_event(struct perf_probe_event *pev) | |||
1290 | int show_perf_probe_events(void) | 1294 | int show_perf_probe_events(void) |
1291 | { | 1295 | { |
1292 | int fd, ret; | 1296 | int fd, ret; |
1293 | struct kprobe_trace_event tev; | 1297 | struct probe_trace_event tev; |
1294 | struct perf_probe_event pev; | 1298 | struct perf_probe_event pev; |
1295 | struct strlist *rawlist; | 1299 | struct strlist *rawlist; |
1296 | struct str_node *ent; | 1300 | struct str_node *ent; |
@@ -1307,20 +1311,20 @@ int show_perf_probe_events(void) | |||
1307 | if (fd < 0) | 1311 | if (fd < 0) |
1308 | return fd; | 1312 | return fd; |
1309 | 1313 | ||
1310 | rawlist = get_kprobe_trace_command_rawlist(fd); | 1314 | rawlist = get_probe_trace_command_rawlist(fd); |
1311 | close(fd); | 1315 | close(fd); |
1312 | if (!rawlist) | 1316 | if (!rawlist) |
1313 | return -ENOENT; | 1317 | return -ENOENT; |
1314 | 1318 | ||
1315 | strlist__for_each(ent, rawlist) { | 1319 | strlist__for_each(ent, rawlist) { |
1316 | ret = parse_kprobe_trace_command(ent->s, &tev); | 1320 | ret = parse_probe_trace_command(ent->s, &tev); |
1317 | if (ret >= 0) { | 1321 | if (ret >= 0) { |
1318 | ret = convert_to_perf_probe_event(&tev, &pev); | 1322 | ret = convert_to_perf_probe_event(&tev, &pev); |
1319 | if (ret >= 0) | 1323 | if (ret >= 0) |
1320 | ret = show_perf_probe_event(&pev); | 1324 | ret = show_perf_probe_event(&pev); |
1321 | } | 1325 | } |
1322 | clear_perf_probe_event(&pev); | 1326 | clear_perf_probe_event(&pev); |
1323 | clear_kprobe_trace_event(&tev); | 1327 | clear_probe_trace_event(&tev); |
1324 | if (ret < 0) | 1328 | if (ret < 0) |
1325 | break; | 1329 | break; |
1326 | } | 1330 | } |
@@ -1330,20 +1334,19 @@ int show_perf_probe_events(void) | |||
1330 | } | 1334 | } |
1331 | 1335 | ||
1332 | /* Get current perf-probe event names */ | 1336 | /* Get current perf-probe event names */ |
1333 | 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) |
1334 | { | 1338 | { |
1335 | char buf[128]; | 1339 | char buf[128]; |
1336 | struct strlist *sl, *rawlist; | 1340 | struct strlist *sl, *rawlist; |
1337 | struct str_node *ent; | 1341 | struct str_node *ent; |
1338 | struct kprobe_trace_event tev; | 1342 | struct probe_trace_event tev; |
1339 | int ret = 0; | 1343 | int ret = 0; |
1340 | 1344 | ||
1341 | memset(&tev, 0, sizeof(tev)); | 1345 | memset(&tev, 0, sizeof(tev)); |
1342 | 1346 | rawlist = get_probe_trace_command_rawlist(fd); | |
1343 | rawlist = get_kprobe_trace_command_rawlist(fd); | ||
1344 | sl = strlist__new(true, NULL); | 1347 | sl = strlist__new(true, NULL); |
1345 | strlist__for_each(ent, rawlist) { | 1348 | strlist__for_each(ent, rawlist) { |
1346 | ret = parse_kprobe_trace_command(ent->s, &tev); | 1349 | ret = parse_probe_trace_command(ent->s, &tev); |
1347 | if (ret < 0) | 1350 | if (ret < 0) |
1348 | break; | 1351 | break; |
1349 | if (include_group) { | 1352 | if (include_group) { |
@@ -1353,7 +1356,7 @@ static struct strlist *get_kprobe_trace_event_names(int fd, bool include_group) | |||
1353 | ret = strlist__add(sl, buf); | 1356 | ret = strlist__add(sl, buf); |
1354 | } else | 1357 | } else |
1355 | ret = strlist__add(sl, tev.event); | 1358 | ret = strlist__add(sl, tev.event); |
1356 | clear_kprobe_trace_event(&tev); | 1359 | clear_probe_trace_event(&tev); |
1357 | if (ret < 0) | 1360 | if (ret < 0) |
1358 | break; | 1361 | break; |
1359 | } | 1362 | } |
@@ -1366,13 +1369,13 @@ static struct strlist *get_kprobe_trace_event_names(int fd, bool include_group) | |||
1366 | return sl; | 1369 | return sl; |
1367 | } | 1370 | } |
1368 | 1371 | ||
1369 | 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) |
1370 | { | 1373 | { |
1371 | int ret = 0; | 1374 | int ret = 0; |
1372 | char *buf = synthesize_kprobe_trace_command(tev); | 1375 | char *buf = synthesize_probe_trace_command(tev); |
1373 | 1376 | ||
1374 | if (!buf) { | 1377 | if (!buf) { |
1375 | pr_debug("Failed to synthesize kprobe trace event.\n"); | 1378 | pr_debug("Failed to synthesize probe trace event.\n"); |
1376 | return -EINVAL; | 1379 | return -EINVAL; |
1377 | } | 1380 | } |
1378 | 1381 | ||
@@ -1425,12 +1428,12 @@ static int get_new_event_name(char *buf, size_t len, const char *base, | |||
1425 | return ret; | 1428 | return ret; |
1426 | } | 1429 | } |
1427 | 1430 | ||
1428 | static int __add_kprobe_trace_events(struct perf_probe_event *pev, | 1431 | static int __add_probe_trace_events(struct perf_probe_event *pev, |
1429 | struct kprobe_trace_event *tevs, | 1432 | struct probe_trace_event *tevs, |
1430 | int ntevs, bool allow_suffix) | 1433 | int ntevs, bool allow_suffix) |
1431 | { | 1434 | { |
1432 | int i, fd, ret; | 1435 | int i, fd, ret; |
1433 | struct kprobe_trace_event *tev = NULL; | 1436 | struct probe_trace_event *tev = NULL; |
1434 | char buf[64]; | 1437 | char buf[64]; |
1435 | const char *event, *group; | 1438 | const char *event, *group; |
1436 | struct strlist *namelist; | 1439 | struct strlist *namelist; |
@@ -1439,7 +1442,7 @@ static int __add_kprobe_trace_events(struct perf_probe_event *pev, | |||
1439 | if (fd < 0) | 1442 | if (fd < 0) |
1440 | return fd; | 1443 | return fd; |
1441 | /* Get current event names */ | 1444 | /* Get current event names */ |
1442 | namelist = get_kprobe_trace_event_names(fd, false); | 1445 | namelist = get_probe_trace_event_names(fd, false); |
1443 | if (!namelist) { | 1446 | if (!namelist) { |
1444 | pr_debug("Failed to get current event list.\n"); | 1447 | pr_debug("Failed to get current event list.\n"); |
1445 | return -EIO; | 1448 | return -EIO; |
@@ -1474,7 +1477,7 @@ static int __add_kprobe_trace_events(struct perf_probe_event *pev, | |||
1474 | ret = -ENOMEM; | 1477 | ret = -ENOMEM; |
1475 | break; | 1478 | break; |
1476 | } | 1479 | } |
1477 | ret = write_kprobe_trace_event(fd, tev); | 1480 | ret = write_probe_trace_event(fd, tev); |
1478 | if (ret < 0) | 1481 | if (ret < 0) |
1479 | break; | 1482 | break; |
1480 | /* Add added event name to namelist */ | 1483 | /* Add added event name to namelist */ |
@@ -1511,21 +1514,21 @@ static int __add_kprobe_trace_events(struct perf_probe_event *pev, | |||
1511 | return ret; | 1514 | return ret; |
1512 | } | 1515 | } |
1513 | 1516 | ||
1514 | 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, |
1515 | struct kprobe_trace_event **tevs, | 1518 | struct probe_trace_event **tevs, |
1516 | int max_tevs) | 1519 | int max_tevs) |
1517 | { | 1520 | { |
1518 | struct symbol *sym; | 1521 | struct symbol *sym; |
1519 | int ret = 0, i; | 1522 | int ret = 0, i; |
1520 | struct kprobe_trace_event *tev; | 1523 | struct probe_trace_event *tev; |
1521 | 1524 | ||
1522 | /* Convert perf_probe_event with debuginfo */ | 1525 | /* Convert perf_probe_event with debuginfo */ |
1523 | ret = try_to_find_kprobe_trace_events(pev, tevs, max_tevs); | 1526 | ret = try_to_find_probe_trace_events(pev, tevs, max_tevs); |
1524 | if (ret != 0) | 1527 | if (ret != 0) |
1525 | return ret; | 1528 | return ret; |
1526 | 1529 | ||
1527 | /* Allocate trace event buffer */ | 1530 | /* Allocate trace event buffer */ |
1528 | tev = *tevs = zalloc(sizeof(struct kprobe_trace_event)); | 1531 | tev = *tevs = zalloc(sizeof(struct probe_trace_event)); |
1529 | if (tev == NULL) | 1532 | if (tev == NULL) |
1530 | return -ENOMEM; | 1533 | return -ENOMEM; |
1531 | 1534 | ||
@@ -1538,7 +1541,7 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, | |||
1538 | tev->point.offset = pev->point.offset; | 1541 | tev->point.offset = pev->point.offset; |
1539 | tev->nargs = pev->nargs; | 1542 | tev->nargs = pev->nargs; |
1540 | if (tev->nargs) { | 1543 | if (tev->nargs) { |
1541 | tev->args = zalloc(sizeof(struct kprobe_trace_arg) | 1544 | tev->args = zalloc(sizeof(struct probe_trace_arg) |
1542 | * tev->nargs); | 1545 | * tev->nargs); |
1543 | if (tev->args == NULL) { | 1546 | if (tev->args == NULL) { |
1544 | ret = -ENOMEM; | 1547 | ret = -ENOMEM; |
@@ -1579,7 +1582,7 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, | |||
1579 | 1582 | ||
1580 | return 1; | 1583 | return 1; |
1581 | error: | 1584 | error: |
1582 | clear_kprobe_trace_event(tev); | 1585 | clear_probe_trace_event(tev); |
1583 | free(tev); | 1586 | free(tev); |
1584 | *tevs = NULL; | 1587 | *tevs = NULL; |
1585 | return ret; | 1588 | return ret; |
@@ -1587,7 +1590,7 @@ error: | |||
1587 | 1590 | ||
1588 | struct __event_package { | 1591 | struct __event_package { |
1589 | struct perf_probe_event *pev; | 1592 | struct perf_probe_event *pev; |
1590 | struct kprobe_trace_event *tevs; | 1593 | struct probe_trace_event *tevs; |
1591 | int ntevs; | 1594 | int ntevs; |
1592 | }; | 1595 | }; |
1593 | 1596 | ||
@@ -1610,7 +1613,7 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, | |||
1610 | for (i = 0; i < npevs; i++) { | 1613 | for (i = 0; i < npevs; i++) { |
1611 | pkgs[i].pev = &pevs[i]; | 1614 | pkgs[i].pev = &pevs[i]; |
1612 | /* Convert with or without debuginfo */ | 1615 | /* Convert with or without debuginfo */ |
1613 | ret = convert_to_kprobe_trace_events(pkgs[i].pev, | 1616 | ret = convert_to_probe_trace_events(pkgs[i].pev, |
1614 | &pkgs[i].tevs, max_tevs); | 1617 | &pkgs[i].tevs, max_tevs); |
1615 | if (ret < 0) | 1618 | if (ret < 0) |
1616 | goto end; | 1619 | goto end; |
@@ -1619,24 +1622,24 @@ int add_perf_probe_events(struct perf_probe_event *pevs, int npevs, | |||
1619 | 1622 | ||
1620 | /* Loop 2: add all events */ | 1623 | /* Loop 2: add all events */ |
1621 | for (i = 0; i < npevs && ret >= 0; i++) | 1624 | for (i = 0; i < npevs && ret >= 0; i++) |
1622 | ret = __add_kprobe_trace_events(pkgs[i].pev, pkgs[i].tevs, | 1625 | ret = __add_probe_trace_events(pkgs[i].pev, pkgs[i].tevs, |
1623 | pkgs[i].ntevs, force_add); | 1626 | pkgs[i].ntevs, force_add); |
1624 | end: | 1627 | end: |
1625 | /* Loop 3: cleanup trace events */ | 1628 | /* Loop 3: cleanup trace events */ |
1626 | for (i = 0; i < npevs; i++) | 1629 | for (i = 0; i < npevs; i++) |
1627 | for (j = 0; j < pkgs[i].ntevs; j++) | 1630 | for (j = 0; j < pkgs[i].ntevs; j++) |
1628 | clear_kprobe_trace_event(&pkgs[i].tevs[j]); | 1631 | clear_probe_trace_event(&pkgs[i].tevs[j]); |
1629 | 1632 | ||
1630 | return ret; | 1633 | return ret; |
1631 | } | 1634 | } |
1632 | 1635 | ||
1633 | 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) |
1634 | { | 1637 | { |
1635 | char *p; | 1638 | char *p; |
1636 | char buf[128]; | 1639 | char buf[128]; |
1637 | int ret; | 1640 | int ret; |
1638 | 1641 | ||
1639 | /* Convert from perf-probe event to trace-kprobe event */ | 1642 | /* Convert from perf-probe event to trace-probe event */ |
1640 | ret = e_snprintf(buf, 128, "-:%s", ent->s); | 1643 | ret = e_snprintf(buf, 128, "-:%s", ent->s); |
1641 | if (ret < 0) | 1644 | if (ret < 0) |
1642 | goto error; | 1645 | goto error; |
@@ -1662,7 +1665,7 @@ error: | |||
1662 | return ret; | 1665 | return ret; |
1663 | } | 1666 | } |
1664 | 1667 | ||
1665 | static int del_trace_kprobe_event(int fd, const char *group, | 1668 | static int del_trace_probe_event(int fd, const char *group, |
1666 | const char *event, struct strlist *namelist) | 1669 | const char *event, struct strlist *namelist) |
1667 | { | 1670 | { |
1668 | char buf[128]; | 1671 | char buf[128]; |
@@ -1679,7 +1682,7 @@ static int del_trace_kprobe_event(int fd, const char *group, | |||
1679 | strlist__for_each_safe(ent, n, namelist) | 1682 | strlist__for_each_safe(ent, n, namelist) |
1680 | if (strglobmatch(ent->s, buf)) { | 1683 | if (strglobmatch(ent->s, buf)) { |
1681 | found++; | 1684 | found++; |
1682 | ret = __del_trace_kprobe_event(fd, ent); | 1685 | ret = __del_trace_probe_event(fd, ent); |
1683 | if (ret < 0) | 1686 | if (ret < 0) |
1684 | break; | 1687 | break; |
1685 | strlist__remove(namelist, ent); | 1688 | strlist__remove(namelist, ent); |
@@ -1688,7 +1691,7 @@ static int del_trace_kprobe_event(int fd, const char *group, | |||
1688 | ent = strlist__find(namelist, buf); | 1691 | ent = strlist__find(namelist, buf); |
1689 | if (ent) { | 1692 | if (ent) { |
1690 | found++; | 1693 | found++; |
1691 | ret = __del_trace_kprobe_event(fd, ent); | 1694 | ret = __del_trace_probe_event(fd, ent); |
1692 | if (ret >= 0) | 1695 | if (ret >= 0) |
1693 | strlist__remove(namelist, ent); | 1696 | strlist__remove(namelist, ent); |
1694 | } | 1697 | } |
@@ -1712,7 +1715,7 @@ int del_perf_probe_events(struct strlist *dellist) | |||
1712 | return fd; | 1715 | return fd; |
1713 | 1716 | ||
1714 | /* Get current event names */ | 1717 | /* Get current event names */ |
1715 | namelist = get_kprobe_trace_event_names(fd, true); | 1718 | namelist = get_probe_trace_event_names(fd, true); |
1716 | if (namelist == NULL) | 1719 | if (namelist == NULL) |
1717 | return -EINVAL; | 1720 | return -EINVAL; |
1718 | 1721 | ||
@@ -1733,7 +1736,7 @@ int del_perf_probe_events(struct strlist *dellist) | |||
1733 | event = str; | 1736 | event = str; |
1734 | } | 1737 | } |
1735 | pr_debug("Group: %s, Event: %s\n", group, event); | 1738 | pr_debug("Group: %s, Event: %s\n", group, event); |
1736 | ret = del_trace_kprobe_event(fd, group, event, namelist); | 1739 | ret = del_trace_probe_event(fd, group, event, namelist); |
1737 | free(str); | 1740 | free(str); |
1738 | if (ret < 0) | 1741 | if (ret < 0) |
1739 | break; | 1742 | break; |
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index ed362acff4b6..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 */ |
@@ -93,25 +93,18 @@ struct line_range { | |||
93 | /* Command string to events */ | 93 | /* Command string to events */ |
94 | extern int parse_perf_probe_command(const char *cmd, | 94 | extern int parse_perf_probe_command(const char *cmd, |
95 | struct perf_probe_event *pev); | 95 | struct perf_probe_event *pev); |
96 | extern int parse_kprobe_trace_command(const char *cmd, | ||
97 | struct kprobe_trace_event *tev); | ||
98 | 96 | ||
99 | /* Events to command string */ | 97 | /* Events to command string */ |
100 | extern char *synthesize_perf_probe_command(struct perf_probe_event *pev); | 98 | extern char *synthesize_perf_probe_command(struct perf_probe_event *pev); |
101 | extern char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev); | 99 | extern char *synthesize_probe_trace_command(struct probe_trace_event *tev); |
102 | 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, |
103 | size_t len); | 101 | size_t len); |
104 | 102 | ||
105 | /* Check the perf_probe_event needs debuginfo */ | 103 | /* Check the perf_probe_event needs debuginfo */ |
106 | 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); |
107 | 105 | ||
108 | /* Convert from kprobe_trace_event to perf_probe_event */ | ||
109 | extern int convert_to_perf_probe_event(struct kprobe_trace_event *tev, | ||
110 | struct perf_probe_event *pev); | ||
111 | |||
112 | /* Release event contents */ | 106 | /* Release event contents */ |
113 | extern void clear_perf_probe_event(struct perf_probe_event *pev); | 107 | extern void clear_perf_probe_event(struct perf_probe_event *pev); |
114 | extern void clear_kprobe_trace_event(struct kprobe_trace_event *tev); | ||
115 | 108 | ||
116 | /* Command string to line-range */ | 109 | /* Command string to line-range */ |
117 | 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 f88070ea5b90..840f1aabbb74 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -366,10 +366,10 @@ static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name, | |||
366 | * Probe finder related functions | 366 | * Probe finder related functions |
367 | */ | 367 | */ |
368 | 368 | ||
369 | static struct kprobe_trace_arg_ref *alloc_trace_arg_ref(long offs) | 369 | static struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs) |
370 | { | 370 | { |
371 | struct kprobe_trace_arg_ref *ref; | 371 | struct probe_trace_arg_ref *ref; |
372 | ref = zalloc(sizeof(struct kprobe_trace_arg_ref)); | 372 | ref = zalloc(sizeof(struct probe_trace_arg_ref)); |
373 | if (ref != NULL) | 373 | if (ref != NULL) |
374 | ref->offset = offs; | 374 | ref->offset = offs; |
375 | return ref; | 375 | return ref; |
@@ -385,7 +385,7 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf) | |||
385 | Dwarf_Word offs = 0; | 385 | Dwarf_Word offs = 0; |
386 | bool ref = false; | 386 | bool ref = false; |
387 | const char *regs; | 387 | const char *regs; |
388 | struct kprobe_trace_arg *tvar = pf->tvar; | 388 | struct probe_trace_arg *tvar = pf->tvar; |
389 | int ret; | 389 | int ret; |
390 | 390 | ||
391 | /* TODO: handle more than 1 exprs */ | 391 | /* TODO: handle more than 1 exprs */ |
@@ -459,10 +459,10 @@ static int convert_variable_location(Dwarf_Die *vr_die, struct probe_finder *pf) | |||
459 | } | 459 | } |
460 | 460 | ||
461 | static int convert_variable_type(Dwarf_Die *vr_die, | 461 | static int convert_variable_type(Dwarf_Die *vr_die, |
462 | struct kprobe_trace_arg *tvar, | 462 | struct probe_trace_arg *tvar, |
463 | const char *cast) | 463 | const char *cast) |
464 | { | 464 | { |
465 | struct kprobe_trace_arg_ref **ref_ptr = &tvar->ref; | 465 | struct probe_trace_arg_ref **ref_ptr = &tvar->ref; |
466 | Dwarf_Die type; | 466 | Dwarf_Die type; |
467 | char buf[16]; | 467 | char buf[16]; |
468 | int ret; | 468 | int ret; |
@@ -500,7 +500,7 @@ static int convert_variable_type(Dwarf_Die *vr_die, | |||
500 | while (*ref_ptr) | 500 | while (*ref_ptr) |
501 | ref_ptr = &(*ref_ptr)->next; | 501 | ref_ptr = &(*ref_ptr)->next; |
502 | /* Add new reference with offset +0 */ | 502 | /* Add new reference with offset +0 */ |
503 | *ref_ptr = zalloc(sizeof(struct kprobe_trace_arg_ref)); | 503 | *ref_ptr = zalloc(sizeof(struct probe_trace_arg_ref)); |
504 | if (*ref_ptr == NULL) { | 504 | if (*ref_ptr == NULL) { |
505 | pr_warning("Out of memory error\n"); | 505 | pr_warning("Out of memory error\n"); |
506 | return -ENOMEM; | 506 | return -ENOMEM; |
@@ -545,10 +545,10 @@ static int convert_variable_type(Dwarf_Die *vr_die, | |||
545 | 545 | ||
546 | 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, |
547 | struct perf_probe_arg_field *field, | 547 | struct perf_probe_arg_field *field, |
548 | struct kprobe_trace_arg_ref **ref_ptr, | 548 | struct probe_trace_arg_ref **ref_ptr, |
549 | Dwarf_Die *die_mem) | 549 | Dwarf_Die *die_mem) |
550 | { | 550 | { |
551 | struct kprobe_trace_arg_ref *ref = *ref_ptr; | 551 | struct probe_trace_arg_ref *ref = *ref_ptr; |
552 | Dwarf_Die type; | 552 | Dwarf_Die type; |
553 | Dwarf_Word offs; | 553 | Dwarf_Word offs; |
554 | int ret, tag; | 554 | int ret, tag; |
@@ -574,7 +574,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, | |||
574 | pr_debug2("Array real type: (%x)\n", | 574 | pr_debug2("Array real type: (%x)\n", |
575 | (unsigned)dwarf_dieoffset(&type)); | 575 | (unsigned)dwarf_dieoffset(&type)); |
576 | if (tag == DW_TAG_pointer_type) { | 576 | if (tag == DW_TAG_pointer_type) { |
577 | ref = zalloc(sizeof(struct kprobe_trace_arg_ref)); | 577 | ref = zalloc(sizeof(struct probe_trace_arg_ref)); |
578 | if (ref == NULL) | 578 | if (ref == NULL) |
579 | return -ENOMEM; | 579 | return -ENOMEM; |
580 | if (*ref_ptr) | 580 | if (*ref_ptr) |
@@ -605,7 +605,7 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname, | |||
605 | return -EINVAL; | 605 | return -EINVAL; |
606 | } | 606 | } |
607 | 607 | ||
608 | ref = zalloc(sizeof(struct kprobe_trace_arg_ref)); | 608 | ref = zalloc(sizeof(struct probe_trace_arg_ref)); |
609 | if (ref == NULL) | 609 | if (ref == NULL) |
610 | return -ENOMEM; | 610 | return -ENOMEM; |
611 | if (*ref_ptr) | 611 | if (*ref_ptr) |
@@ -738,7 +738,7 @@ static int find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
738 | /* Show a probe point to output buffer */ | 738 | /* Show a probe point to output buffer */ |
739 | 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) |
740 | { | 740 | { |
741 | struct kprobe_trace_event *tev; | 741 | struct probe_trace_event *tev; |
742 | Dwarf_Addr eaddr; | 742 | Dwarf_Addr eaddr; |
743 | Dwarf_Die die_mem; | 743 | Dwarf_Die die_mem; |
744 | const char *name; | 744 | const char *name; |
@@ -803,7 +803,7 @@ static int convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
803 | 803 | ||
804 | /* Find each argument */ | 804 | /* Find each argument */ |
805 | tev->nargs = pf->pev->nargs; | 805 | tev->nargs = pf->pev->nargs; |
806 | tev->args = zalloc(sizeof(struct kprobe_trace_arg) * tev->nargs); | 806 | tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs); |
807 | if (tev->args == NULL) | 807 | if (tev->args == NULL) |
808 | return -ENOMEM; | 808 | return -ENOMEM; |
809 | for (i = 0; i < pf->pev->nargs; i++) { | 809 | for (i = 0; i < pf->pev->nargs; i++) { |
@@ -1060,9 +1060,9 @@ static int find_probe_point_by_func(struct probe_finder *pf) | |||
1060 | return _param.retval; | 1060 | return _param.retval; |
1061 | } | 1061 | } |
1062 | 1062 | ||
1063 | /* Find kprobe_trace_events specified by perf_probe_event from debuginfo */ | 1063 | /* Find probe_trace_events specified by perf_probe_event from debuginfo */ |
1064 | 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, |
1065 | struct kprobe_trace_event **tevs, int max_tevs) | 1065 | struct probe_trace_event **tevs, int max_tevs) |
1066 | { | 1066 | { |
1067 | struct probe_finder pf = {.pev = pev, .max_tevs = max_tevs}; | 1067 | struct probe_finder pf = {.pev = pev, .max_tevs = max_tevs}; |
1068 | struct perf_probe_point *pp = &pev->point; | 1068 | struct perf_probe_point *pp = &pev->point; |
@@ -1072,7 +1072,7 @@ int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, | |||
1072 | Dwarf *dbg; | 1072 | Dwarf *dbg; |
1073 | int ret = 0; | 1073 | int ret = 0; |
1074 | 1074 | ||
1075 | pf.tevs = zalloc(sizeof(struct kprobe_trace_event) * max_tevs); | 1075 | pf.tevs = zalloc(sizeof(struct probe_trace_event) * max_tevs); |
1076 | if (pf.tevs == NULL) | 1076 | if (pf.tevs == NULL) |
1077 | return -ENOMEM; | 1077 | return -ENOMEM; |
1078 | *tevs = pf.tevs; | 1078 | *tevs = pf.tevs; |
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 030791870e33..04a3b3db9e90 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -96,8 +96,6 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc | |||
96 | self->hists_tree = RB_ROOT; | 96 | self->hists_tree = RB_ROOT; |
97 | self->last_match = NULL; | 97 | self->last_match = NULL; |
98 | self->mmap_window = 32; | 98 | self->mmap_window = 32; |
99 | self->cwd = NULL; | ||
100 | self->cwdlen = 0; | ||
101 | self->machines = RB_ROOT; | 99 | self->machines = RB_ROOT; |
102 | self->repipe = repipe; | 100 | self->repipe = repipe; |
103 | INIT_LIST_HEAD(&self->ordered_samples.samples_head); | 101 | INIT_LIST_HEAD(&self->ordered_samples.samples_head); |
@@ -126,11 +124,36 @@ out_delete: | |||
126 | return NULL; | 124 | return NULL; |
127 | } | 125 | } |
128 | 126 | ||
127 | static void perf_session__delete_dead_threads(struct perf_session *self) | ||
128 | { | ||
129 | struct thread *n, *t; | ||
130 | |||
131 | list_for_each_entry_safe(t, n, &self->dead_threads, node) { | ||
132 | list_del(&t->node); | ||
133 | thread__delete(t); | ||
134 | } | ||
135 | } | ||
136 | |||
137 | static void perf_session__delete_threads(struct perf_session *self) | ||
138 | { | ||
139 | struct rb_node *nd = rb_first(&self->threads); | ||
140 | |||
141 | while (nd) { | ||
142 | struct thread *t = rb_entry(nd, struct thread, rb_node); | ||
143 | |||
144 | rb_erase(&t->rb_node, &self->threads); | ||
145 | nd = rb_next(nd); | ||
146 | thread__delete(t); | ||
147 | } | ||
148 | } | ||
149 | |||
129 | void perf_session__delete(struct perf_session *self) | 150 | void perf_session__delete(struct perf_session *self) |
130 | { | 151 | { |
131 | perf_header__exit(&self->header); | 152 | perf_header__exit(&self->header); |
153 | perf_session__delete_dead_threads(self); | ||
154 | perf_session__delete_threads(self); | ||
155 | machine__exit(&self->host_machine); | ||
132 | close(self->fd); | 156 | close(self->fd); |
133 | free(self->cwd); | ||
134 | free(self); | 157 | free(self); |
135 | } | 158 | } |
136 | 159 | ||
@@ -832,23 +855,6 @@ int perf_session__process_events(struct perf_session *self, | |||
832 | if (perf_session__register_idle_thread(self) == NULL) | 855 | if (perf_session__register_idle_thread(self) == NULL) |
833 | return -ENOMEM; | 856 | return -ENOMEM; |
834 | 857 | ||
835 | if (!symbol_conf.full_paths) { | ||
836 | char bf[PATH_MAX]; | ||
837 | |||
838 | if (getcwd(bf, sizeof(bf)) == NULL) { | ||
839 | err = -errno; | ||
840 | out_getcwd_err: | ||
841 | pr_err("failed to get the current directory\n"); | ||
842 | goto out_err; | ||
843 | } | ||
844 | self->cwd = strdup(bf); | ||
845 | if (self->cwd == NULL) { | ||
846 | err = -ENOMEM; | ||
847 | goto out_getcwd_err; | ||
848 | } | ||
849 | self->cwdlen = strlen(self->cwd); | ||
850 | } | ||
851 | |||
852 | if (!self->fd_pipe) | 858 | if (!self->fd_pipe) |
853 | err = __perf_session__process_events(self, | 859 | err = __perf_session__process_events(self, |
854 | self->header.data_offset, | 860 | self->header.data_offset, |
@@ -856,7 +862,7 @@ out_getcwd_err: | |||
856 | self->size, ops); | 862 | self->size, ops); |
857 | else | 863 | else |
858 | err = __perf_session__process_pipe_events(self, ops); | 864 | err = __perf_session__process_pipe_events(self, ops); |
859 | out_err: | 865 | |
860 | return err; | 866 | return err; |
861 | } | 867 | } |
862 | 868 | ||
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index c27b4b03fbc1..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,11 +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 | unsigned int cpus__col_width; | ||
17 | static unsigned int parent_symbol__col_width; | ||
18 | char * field_sep; | 14 | char * field_sep; |
19 | 15 | ||
20 | LIST_HEAD(hist_entry__sort_list); | 16 | LIST_HEAD(hist_entry__sort_list); |
@@ -36,7 +32,7 @@ struct sort_entry sort_thread = { | |||
36 | .se_header = "Command: Pid", | 32 | .se_header = "Command: Pid", |
37 | .se_cmp = sort__thread_cmp, | 33 | .se_cmp = sort__thread_cmp, |
38 | .se_snprintf = hist_entry__thread_snprintf, | 34 | .se_snprintf = hist_entry__thread_snprintf, |
39 | .se_width = &threads__col_width, | 35 | .se_width_idx = HISTC_THREAD, |
40 | }; | 36 | }; |
41 | 37 | ||
42 | struct sort_entry sort_comm = { | 38 | struct sort_entry sort_comm = { |
@@ -44,34 +40,35 @@ struct sort_entry sort_comm = { | |||
44 | .se_cmp = sort__comm_cmp, | 40 | .se_cmp = sort__comm_cmp, |
45 | .se_collapse = sort__comm_collapse, | 41 | .se_collapse = sort__comm_collapse, |
46 | .se_snprintf = hist_entry__comm_snprintf, | 42 | .se_snprintf = hist_entry__comm_snprintf, |
47 | .se_width = &comms__col_width, | 43 | .se_width_idx = HISTC_COMM, |
48 | }; | 44 | }; |
49 | 45 | ||
50 | struct sort_entry sort_dso = { | 46 | struct sort_entry sort_dso = { |
51 | .se_header = "Shared Object", | 47 | .se_header = "Shared Object", |
52 | .se_cmp = sort__dso_cmp, | 48 | .se_cmp = sort__dso_cmp, |
53 | .se_snprintf = hist_entry__dso_snprintf, | 49 | .se_snprintf = hist_entry__dso_snprintf, |
54 | .se_width = &dsos__col_width, | 50 | .se_width_idx = HISTC_DSO, |
55 | }; | 51 | }; |
56 | 52 | ||
57 | struct sort_entry sort_sym = { | 53 | struct sort_entry sort_sym = { |
58 | .se_header = "Symbol", | 54 | .se_header = "Symbol", |
59 | .se_cmp = sort__sym_cmp, | 55 | .se_cmp = sort__sym_cmp, |
60 | .se_snprintf = hist_entry__sym_snprintf, | 56 | .se_snprintf = hist_entry__sym_snprintf, |
57 | .se_width_idx = HISTC_SYMBOL, | ||
61 | }; | 58 | }; |
62 | 59 | ||
63 | struct sort_entry sort_parent = { | 60 | struct sort_entry sort_parent = { |
64 | .se_header = "Parent symbol", | 61 | .se_header = "Parent symbol", |
65 | .se_cmp = sort__parent_cmp, | 62 | .se_cmp = sort__parent_cmp, |
66 | .se_snprintf = hist_entry__parent_snprintf, | 63 | .se_snprintf = hist_entry__parent_snprintf, |
67 | .se_width = &parent_symbol__col_width, | 64 | .se_width_idx = HISTC_PARENT, |
68 | }; | 65 | }; |
69 | 66 | ||
70 | struct sort_entry sort_cpu = { | 67 | struct sort_entry sort_cpu = { |
71 | .se_header = "CPU", | 68 | .se_header = "CPU", |
72 | .se_cmp = sort__cpu_cmp, | 69 | .se_cmp = sort__cpu_cmp, |
73 | .se_snprintf = hist_entry__cpu_snprintf, | 70 | .se_snprintf = hist_entry__cpu_snprintf, |
74 | .se_width = &cpus__col_width, | 71 | .se_width_idx = HISTC_CPU, |
75 | }; | 72 | }; |
76 | 73 | ||
77 | struct sort_dimension { | 74 | struct sort_dimension { |
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 560c855417e4..46e531d09e8b 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
@@ -36,12 +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 unsigned int cpus__col_width; | ||
43 | extern enum sort_type sort__first_dimension; | 39 | extern enum sort_type sort__first_dimension; |
44 | 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 | */ | ||
45 | struct hist_entry { | 47 | struct hist_entry { |
46 | struct rb_node rb_node; | 48 | struct rb_node rb_node; |
47 | u64 period; | 49 | u64 period; |
@@ -54,6 +56,12 @@ struct hist_entry { | |||
54 | u64 ip; | 56 | u64 ip; |
55 | s32 cpu; | 57 | s32 cpu; |
56 | 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; | ||
57 | char level; | 65 | char level; |
58 | u8 filtered; | 66 | u8 filtered; |
59 | struct symbol *parent; | 67 | struct symbol *parent; |
@@ -87,7 +95,7 @@ struct sort_entry { | |||
87 | int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *); | 95 | int64_t (*se_collapse)(struct hist_entry *, struct hist_entry *); |
88 | 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, |
89 | unsigned int width); | 97 | unsigned int width); |
90 | unsigned int *se_width; | 98 | u8 se_width_idx; |
91 | bool elide; | 99 | bool elide; |
92 | }; | 100 | }; |
93 | 101 | ||
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 971d0a05d6b4..94cdf68440cd 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 | } |
@@ -953,7 +966,8 @@ static size_t elf_addr_to_index(Elf *elf, GElf_Addr addr) | |||
953 | } | 966 | } |
954 | 967 | ||
955 | 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, |
956 | int fd, symbol_filter_t filter, int kmodule) | 969 | int fd, symbol_filter_t filter, int kmodule, |
970 | int want_symtab) | ||
957 | { | 971 | { |
958 | struct kmap *kmap = self->kernel ? map__kmap(map) : NULL; | 972 | struct kmap *kmap = self->kernel ? map__kmap(map) : NULL; |
959 | struct map *curr_map = map; | 973 | struct map *curr_map = map; |
@@ -973,17 +987,32 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, | |||
973 | 987 | ||
974 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | 988 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); |
975 | if (elf == NULL) { | 989 | if (elf == NULL) { |
976 | pr_err("%s: cannot read %s ELF file.\n", __func__, name); | 990 | pr_debug("%s: cannot read %s ELF file.\n", __func__, name); |
977 | goto out_close; | 991 | goto out_close; |
978 | } | 992 | } |
979 | 993 | ||
980 | if (gelf_getehdr(elf, &ehdr) == NULL) { | 994 | if (gelf_getehdr(elf, &ehdr) == NULL) { |
981 | pr_err("%s: cannot get elf header.\n", __func__); | 995 | pr_debug("%s: cannot get elf header.\n", __func__); |
982 | goto out_elf_end; | 996 | goto out_elf_end; |
983 | } | 997 | } |
984 | 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 | |||
985 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL); | 1011 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL); |
986 | if (sec == NULL) { | 1012 | if (sec == NULL) { |
1013 | if (want_symtab) | ||
1014 | goto out_elf_end; | ||
1015 | |||
987 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL); | 1016 | sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL); |
988 | if (sec == NULL) | 1017 | if (sec == NULL) |
989 | goto out_elf_end; | 1018 | goto out_elf_end; |
@@ -1182,37 +1211,26 @@ bool __dsos__read_build_ids(struct list_head *head, bool with_hits) | |||
1182 | */ | 1211 | */ |
1183 | #define NOTE_ALIGN(n) (((n) + 3) & -4U) | 1212 | #define NOTE_ALIGN(n) (((n) + 3) & -4U) |
1184 | 1213 | ||
1185 | 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) |
1186 | { | 1215 | { |
1187 | int fd, err = -1; | 1216 | int err = -1; |
1188 | GElf_Ehdr ehdr; | 1217 | GElf_Ehdr ehdr; |
1189 | GElf_Shdr shdr; | 1218 | GElf_Shdr shdr; |
1190 | Elf_Data *data; | 1219 | Elf_Data *data; |
1191 | Elf_Scn *sec; | 1220 | Elf_Scn *sec; |
1192 | Elf_Kind ek; | 1221 | Elf_Kind ek; |
1193 | void *ptr; | 1222 | void *ptr; |
1194 | Elf *elf; | ||
1195 | 1223 | ||
1196 | if (size < BUILD_ID_SIZE) | 1224 | if (size < BUILD_ID_SIZE) |
1197 | goto out; | 1225 | goto out; |
1198 | 1226 | ||
1199 | fd = open(filename, O_RDONLY); | ||
1200 | if (fd < 0) | ||
1201 | goto out; | ||
1202 | |||
1203 | elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); | ||
1204 | if (elf == NULL) { | ||
1205 | pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename); | ||
1206 | goto out_close; | ||
1207 | } | ||
1208 | |||
1209 | ek = elf_kind(elf); | 1227 | ek = elf_kind(elf); |
1210 | if (ek != ELF_K_ELF) | 1228 | if (ek != ELF_K_ELF) |
1211 | goto out_elf_end; | 1229 | goto out; |
1212 | 1230 | ||
1213 | if (gelf_getehdr(elf, &ehdr) == NULL) { | 1231 | if (gelf_getehdr(elf, &ehdr) == NULL) { |
1214 | pr_err("%s: cannot get elf header.\n", __func__); | 1232 | pr_err("%s: cannot get elf header.\n", __func__); |
1215 | goto out_elf_end; | 1233 | goto out; |
1216 | } | 1234 | } |
1217 | 1235 | ||
1218 | sec = elf_section_by_name(elf, &ehdr, &shdr, | 1236 | sec = elf_section_by_name(elf, &ehdr, &shdr, |
@@ -1221,12 +1239,12 @@ int filename__read_build_id(const char *filename, void *bf, size_t size) | |||
1221 | sec = elf_section_by_name(elf, &ehdr, &shdr, | 1239 | sec = elf_section_by_name(elf, &ehdr, &shdr, |
1222 | ".notes", NULL); | 1240 | ".notes", NULL); |
1223 | if (sec == NULL) | 1241 | if (sec == NULL) |
1224 | goto out_elf_end; | 1242 | goto out; |
1225 | } | 1243 | } |
1226 | 1244 | ||
1227 | data = elf_getdata(sec, NULL); | 1245 | data = elf_getdata(sec, NULL); |
1228 | if (data == NULL) | 1246 | if (data == NULL) |
1229 | goto out_elf_end; | 1247 | goto out; |
1230 | 1248 | ||
1231 | ptr = data->d_buf; | 1249 | ptr = data->d_buf; |
1232 | while (ptr < (data->d_buf + data->d_size)) { | 1250 | while (ptr < (data->d_buf + data->d_size)) { |
@@ -1248,7 +1266,31 @@ int filename__read_build_id(const char *filename, void *bf, size_t size) | |||
1248 | } | 1266 | } |
1249 | ptr += descsz; | 1267 | ptr += descsz; |
1250 | } | 1268 | } |
1251 | 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 | |||
1252 | elf_end(elf); | 1294 | elf_end(elf); |
1253 | out_close: | 1295 | out_close: |
1254 | close(fd); | 1296 | close(fd); |
@@ -1324,11 +1366,11 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) | |||
1324 | { | 1366 | { |
1325 | int size = PATH_MAX; | 1367 | int size = PATH_MAX; |
1326 | char *name; | 1368 | char *name; |
1327 | u8 build_id[BUILD_ID_SIZE]; | ||
1328 | int ret = -1; | 1369 | int ret = -1; |
1329 | int fd; | 1370 | int fd; |
1330 | struct machine *machine; | 1371 | struct machine *machine; |
1331 | const char *root_dir; | 1372 | const char *root_dir; |
1373 | int want_symtab; | ||
1332 | 1374 | ||
1333 | dso__set_loaded(self, map->type); | 1375 | dso__set_loaded(self, map->type); |
1334 | 1376 | ||
@@ -1355,13 +1397,18 @@ int dso__load(struct dso *self, struct map *map, symbol_filter_t filter) | |||
1355 | return ret; | 1397 | return ret; |
1356 | } | 1398 | } |
1357 | 1399 | ||
1358 | self->origin = DSO__ORIG_BUILD_ID_CACHE; | 1400 | /* Iterate over candidate debug images. |
1359 | if (dso__build_id_filename(self, name, size) != NULL) | 1401 | * On the first pass, only load images if they have a full symtab. |
1360 | goto open_file; | 1402 | * Failing that, do a second pass where we accept .dynsym also |
1361 | more: | 1403 | */ |
1362 | do { | 1404 | for (self->origin = DSO__ORIG_BUILD_ID_CACHE, want_symtab = 1; |
1363 | self->origin++; | 1405 | self->origin != DSO__ORIG_NOT_FOUND; |
1406 | self->origin++) { | ||
1364 | 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; | ||
1365 | case DSO__ORIG_FEDORA: | 1412 | case DSO__ORIG_FEDORA: |
1366 | snprintf(name, size, "/usr/lib/debug%s.debug", | 1413 | snprintf(name, size, "/usr/lib/debug%s.debug", |
1367 | self->long_name); | 1414 | self->long_name); |
@@ -1370,21 +1417,20 @@ more: | |||
1370 | snprintf(name, size, "/usr/lib/debug%s", | 1417 | snprintf(name, size, "/usr/lib/debug%s", |
1371 | self->long_name); | 1418 | self->long_name); |
1372 | break; | 1419 | break; |
1373 | case DSO__ORIG_BUILDID: | 1420 | case DSO__ORIG_BUILDID: { |
1374 | if (filename__read_build_id(self->long_name, build_id, | 1421 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; |
1375 | sizeof(build_id))) { | 1422 | |
1376 | char build_id_hex[BUILD_ID_SIZE * 2 + 1]; | 1423 | if (!self->has_build_id) |
1377 | build_id__sprintf(build_id, sizeof(build_id), | 1424 | continue; |
1378 | build_id_hex); | 1425 | |
1379 | snprintf(name, size, | 1426 | build_id__sprintf(self->build_id, |
1380 | "/usr/lib/debug/.build-id/%.2s/%s.debug", | 1427 | sizeof(self->build_id), |
1381 | build_id_hex, build_id_hex + 2); | 1428 | build_id_hex); |
1382 | if (self->has_build_id) | 1429 | snprintf(name, size, |
1383 | goto compare_build_id; | 1430 | "/usr/lib/debug/.build-id/%.2s/%s.debug", |
1384 | break; | 1431 | build_id_hex, build_id_hex + 2); |
1385 | } | 1432 | } |
1386 | self->origin++; | 1433 | break; |
1387 | /* Fall thru */ | ||
1388 | case DSO__ORIG_DSO: | 1434 | case DSO__ORIG_DSO: |
1389 | snprintf(name, size, "%s", self->long_name); | 1435 | snprintf(name, size, "%s", self->long_name); |
1390 | break; | 1436 | break; |
@@ -1397,36 +1443,41 @@ more: | |||
1397 | break; | 1443 | break; |
1398 | 1444 | ||
1399 | default: | 1445 | default: |
1400 | 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; | ||
1401 | } | 1455 | } |
1402 | 1456 | ||
1403 | if (self->has_build_id) { | 1457 | /* Name is now the name of the next image to try */ |
1404 | if (filename__read_build_id(name, build_id, | ||
1405 | sizeof(build_id)) < 0) | ||
1406 | goto more; | ||
1407 | compare_build_id: | ||
1408 | if (!dso__build_id_equal(self, build_id)) | ||
1409 | goto more; | ||
1410 | } | ||
1411 | open_file: | ||
1412 | fd = open(name, O_RDONLY); | 1458 | fd = open(name, O_RDONLY); |
1413 | } while (fd < 0); | 1459 | if (fd < 0) |
1460 | continue; | ||
1414 | 1461 | ||
1415 | ret = dso__load_sym(self, map, name, fd, filter, 0); | 1462 | ret = dso__load_sym(self, map, name, fd, filter, 0, |
1416 | close(fd); | 1463 | want_symtab); |
1464 | close(fd); | ||
1417 | 1465 | ||
1418 | /* | 1466 | /* |
1419 | * Some people seem to have debuginfo files _WITHOUT_ debug info!?!? | 1467 | * Some people seem to have debuginfo files _WITHOUT_ debug |
1420 | */ | 1468 | * info!?!? |
1421 | if (!ret) | 1469 | */ |
1422 | goto more; | 1470 | if (!ret) |
1471 | continue; | ||
1423 | 1472 | ||
1424 | if (ret > 0) { | 1473 | if (ret > 0) { |
1425 | int nr_plt = dso__synthesize_plt_symbols(self, map, filter); | 1474 | int nr_plt = dso__synthesize_plt_symbols(self, map, filter); |
1426 | if (nr_plt > 0) | 1475 | if (nr_plt > 0) |
1427 | ret += nr_plt; | 1476 | ret += nr_plt; |
1477 | break; | ||
1478 | } | ||
1428 | } | 1479 | } |
1429 | out: | 1480 | |
1430 | free(name); | 1481 | free(name); |
1431 | if (ret < 0 && strstr(self->name, " (deleted)") != NULL) | 1482 | if (ret < 0 && strstr(self->name, " (deleted)") != NULL) |
1432 | return 0; | 1483 | return 0; |
@@ -1521,6 +1572,7 @@ static int map_groups__set_modules_path_dir(struct map_groups *self, | |||
1521 | if (long_name == NULL) | 1572 | if (long_name == NULL) |
1522 | goto failure; | 1573 | goto failure; |
1523 | dso__set_long_name(map->dso, long_name); | 1574 | dso__set_long_name(map->dso, long_name); |
1575 | map->dso->lname_alloc = 1; | ||
1524 | dso__kernel_module_get_build_id(map->dso, ""); | 1576 | dso__kernel_module_get_build_id(map->dso, ""); |
1525 | } | 1577 | } |
1526 | } | 1578 | } |
@@ -1684,36 +1736,12 @@ static int dso__load_vmlinux(struct dso *self, struct map *map, | |||
1684 | { | 1736 | { |
1685 | int err = -1, fd; | 1737 | int err = -1, fd; |
1686 | 1738 | ||
1687 | if (self->has_build_id) { | ||
1688 | u8 build_id[BUILD_ID_SIZE]; | ||
1689 | |||
1690 | if (filename__read_build_id(vmlinux, build_id, | ||
1691 | sizeof(build_id)) < 0) { | ||
1692 | pr_debug("No build_id in %s, ignoring it\n", vmlinux); | ||
1693 | return -1; | ||
1694 | } | ||
1695 | if (!dso__build_id_equal(self, build_id)) { | ||
1696 | char expected_build_id[BUILD_ID_SIZE * 2 + 1], | ||
1697 | vmlinux_build_id[BUILD_ID_SIZE * 2 + 1]; | ||
1698 | |||
1699 | build_id__sprintf(self->build_id, | ||
1700 | sizeof(self->build_id), | ||
1701 | expected_build_id); | ||
1702 | build_id__sprintf(build_id, sizeof(build_id), | ||
1703 | vmlinux_build_id); | ||
1704 | pr_debug("build_id in %s is %s while expected is %s, " | ||
1705 | "ignoring it\n", vmlinux, vmlinux_build_id, | ||
1706 | expected_build_id); | ||
1707 | return -1; | ||
1708 | } | ||
1709 | } | ||
1710 | |||
1711 | fd = open(vmlinux, O_RDONLY); | 1739 | fd = open(vmlinux, O_RDONLY); |
1712 | if (fd < 0) | 1740 | if (fd < 0) |
1713 | return -1; | 1741 | return -1; |
1714 | 1742 | ||
1715 | dso__set_loaded(self, map->type); | 1743 | dso__set_loaded(self, map->type); |
1716 | err = dso__load_sym(self, map, vmlinux, fd, filter, 0); | 1744 | err = dso__load_sym(self, map, vmlinux, fd, filter, 0, 0); |
1717 | close(fd); | 1745 | close(fd); |
1718 | 1746 | ||
1719 | if (err > 0) | 1747 | if (err > 0) |
@@ -2217,6 +2245,15 @@ out_free_comm_list: | |||
2217 | return -1; | 2245 | return -1; |
2218 | } | 2246 | } |
2219 | 2247 | ||
2248 | void symbol__exit(void) | ||
2249 | { | ||
2250 | strlist__delete(symbol_conf.sym_list); | ||
2251 | strlist__delete(symbol_conf.dso_list); | ||
2252 | strlist__delete(symbol_conf.comm_list); | ||
2253 | vmlinux_path__exit(); | ||
2254 | symbol_conf.sym_list = symbol_conf.dso_list = symbol_conf.comm_list = NULL; | ||
2255 | } | ||
2256 | |||
2220 | int machines__create_kernel_maps(struct rb_root *self, pid_t pid) | 2257 | int machines__create_kernel_maps(struct rb_root *self, pid_t pid) |
2221 | { | 2258 | { |
2222 | struct machine *machine = machines__findnew(self, pid); | 2259 | struct machine *machine = machines__findnew(self, pid); |
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 80e569bbdecc..33d53ce28958 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -68,7 +68,6 @@ struct symbol_conf { | |||
68 | show_nr_samples, | 68 | show_nr_samples, |
69 | use_callchain, | 69 | use_callchain, |
70 | exclude_other, | 70 | exclude_other, |
71 | full_paths, | ||
72 | show_cpu_utilization; | 71 | show_cpu_utilization; |
73 | const char *vmlinux_name, | 72 | const char *vmlinux_name, |
74 | *source_prefix, | 73 | *source_prefix, |
@@ -102,6 +101,8 @@ struct ref_reloc_sym { | |||
102 | struct map_symbol { | 101 | struct map_symbol { |
103 | struct map *map; | 102 | struct map *map; |
104 | struct symbol *sym; | 103 | struct symbol *sym; |
104 | bool unfolded; | ||
105 | bool has_children; | ||
105 | }; | 106 | }; |
106 | 107 | ||
107 | struct addr_location { | 108 | struct addr_location { |
@@ -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 | ||
@@ -214,6 +219,7 @@ int machines__create_kernel_maps(struct rb_root *self, pid_t pid); | |||
214 | int machines__create_guest_kernel_maps(struct rb_root *self); | 219 | int machines__create_guest_kernel_maps(struct rb_root *self); |
215 | 220 | ||
216 | int symbol__init(void); | 221 | int symbol__init(void); |
222 | void symbol__exit(void); | ||
217 | bool symbol_type__is_a(char symbol_type, enum map_type map_type); | 223 | bool symbol_type__is_a(char symbol_type, enum map_type map_type); |
218 | 224 | ||
219 | size_t machine__fprintf_vmlinux_path(struct machine *self, FILE *fp); | 225 | 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); |