diff options
Diffstat (limited to 'tools/perf/util')
| -rw-r--r-- | tools/perf/util/cpumap.c | 59 | ||||
| -rw-r--r-- | tools/perf/util/cpumap.h | 7 | ||||
| -rw-r--r-- | tools/perf/util/event.h | 9 | ||||
| -rw-r--r-- | tools/perf/util/hist.c | 52 | ||||
| -rw-r--r-- | tools/perf/util/hist.h | 12 | ||||
| -rw-r--r-- | tools/perf/util/probe-event.c | 59 | ||||
| -rw-r--r-- | tools/perf/util/probe-finder.c | 1004 | ||||
| -rw-r--r-- | tools/perf/util/probe-finder.h | 52 | ||||
| -rw-r--r-- | tools/perf/util/session.c | 1 | ||||
| -rw-r--r-- | tools/perf/util/session.h | 1 | ||||
| -rw-r--r-- | tools/perf/util/string.c | 55 | ||||
| -rw-r--r-- | tools/perf/util/string.h | 1 | ||||
| -rw-r--r-- | tools/perf/util/symbol.c | 18 | ||||
| -rw-r--r-- | tools/perf/util/symbol.h | 3 | ||||
| -rw-r--r-- | tools/perf/util/thread.c | 41 | ||||
| -rw-r--r-- | tools/perf/util/thread.h | 3 |
16 files changed, 732 insertions, 645 deletions
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c new file mode 100644 index 000000000000..4e01490e51e5 --- /dev/null +++ b/tools/perf/util/cpumap.c | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | #include "util.h" | ||
| 2 | #include "../perf.h" | ||
| 3 | #include "cpumap.h" | ||
| 4 | #include <assert.h> | ||
| 5 | #include <stdio.h> | ||
| 6 | |||
| 7 | int cpumap[MAX_NR_CPUS]; | ||
| 8 | |||
| 9 | static int default_cpu_map(void) | ||
| 10 | { | ||
| 11 | int nr_cpus, i; | ||
| 12 | |||
| 13 | nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); | ||
| 14 | assert(nr_cpus <= MAX_NR_CPUS); | ||
| 15 | assert((int)nr_cpus >= 0); | ||
| 16 | |||
| 17 | for (i = 0; i < nr_cpus; ++i) | ||
| 18 | cpumap[i] = i; | ||
| 19 | |||
| 20 | return nr_cpus; | ||
| 21 | } | ||
| 22 | |||
| 23 | int read_cpu_map(void) | ||
| 24 | { | ||
| 25 | FILE *onlnf; | ||
| 26 | int nr_cpus = 0; | ||
| 27 | int n, cpu, prev; | ||
| 28 | char sep; | ||
| 29 | |||
| 30 | onlnf = fopen("/sys/devices/system/cpu/online", "r"); | ||
| 31 | if (!onlnf) | ||
| 32 | return default_cpu_map(); | ||
| 33 | |||
| 34 | sep = 0; | ||
| 35 | prev = -1; | ||
| 36 | for (;;) { | ||
| 37 | n = fscanf(onlnf, "%u%c", &cpu, &sep); | ||
| 38 | if (n <= 0) | ||
| 39 | break; | ||
| 40 | if (prev >= 0) { | ||
| 41 | assert(nr_cpus + cpu - prev - 1 < MAX_NR_CPUS); | ||
| 42 | while (++prev < cpu) | ||
| 43 | cpumap[nr_cpus++] = prev; | ||
| 44 | } | ||
| 45 | assert (nr_cpus < MAX_NR_CPUS); | ||
| 46 | cpumap[nr_cpus++] = cpu; | ||
| 47 | if (n == 2 && sep == '-') | ||
| 48 | prev = cpu; | ||
| 49 | else | ||
| 50 | prev = -1; | ||
| 51 | if (n == 1 || sep == '\n') | ||
| 52 | break; | ||
| 53 | } | ||
| 54 | fclose(onlnf); | ||
| 55 | if (nr_cpus > 0) | ||
| 56 | return nr_cpus; | ||
| 57 | |||
| 58 | return default_cpu_map(); | ||
| 59 | } | ||
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h new file mode 100644 index 000000000000..86c78bb33098 --- /dev/null +++ b/tools/perf/util/cpumap.h | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | #ifndef __PERF_CPUMAP_H | ||
| 2 | #define __PERF_CPUMAP_H | ||
| 3 | |||
| 4 | extern int read_cpu_map(void); | ||
| 5 | extern int cpumap[]; | ||
| 6 | |||
| 7 | #endif /* __PERF_CPUMAP_H */ | ||
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 50a7132887f5..a33b94952e34 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
| @@ -99,6 +99,15 @@ struct events_stats { | |||
| 99 | u64 lost; | 99 | u64 lost; |
| 100 | }; | 100 | }; |
| 101 | 101 | ||
| 102 | struct event_stat_id { | ||
| 103 | struct rb_node rb_node; | ||
| 104 | struct rb_root hists; | ||
| 105 | struct events_stats stats; | ||
| 106 | u64 config; | ||
| 107 | u64 event_stream; | ||
| 108 | u32 type; | ||
| 109 | }; | ||
| 110 | |||
| 102 | void event__print_totals(void); | 111 | void event__print_totals(void); |
| 103 | 112 | ||
| 104 | struct perf_session; | 113 | struct perf_session; |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index e8daf5ca6fd2..2be33c7dbf03 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
| @@ -12,12 +12,12 @@ struct callchain_param callchain_param = { | |||
| 12 | * histogram, sorted on item, collects counts | 12 | * histogram, sorted on item, collects counts |
| 13 | */ | 13 | */ |
| 14 | 14 | ||
| 15 | struct hist_entry *__perf_session__add_hist_entry(struct perf_session *self, | 15 | struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, |
| 16 | struct addr_location *al, | 16 | struct addr_location *al, |
| 17 | struct symbol *sym_parent, | 17 | struct symbol *sym_parent, |
| 18 | u64 count, bool *hit) | 18 | u64 count, bool *hit) |
| 19 | { | 19 | { |
| 20 | struct rb_node **p = &self->hists.rb_node; | 20 | struct rb_node **p = &hists->rb_node; |
| 21 | struct rb_node *parent = NULL; | 21 | struct rb_node *parent = NULL; |
| 22 | struct hist_entry *he; | 22 | struct hist_entry *he; |
| 23 | struct hist_entry entry = { | 23 | struct hist_entry entry = { |
| @@ -53,7 +53,7 @@ struct hist_entry *__perf_session__add_hist_entry(struct perf_session *self, | |||
| 53 | return NULL; | 53 | return NULL; |
| 54 | *he = entry; | 54 | *he = entry; |
| 55 | rb_link_node(&he->rb_node, parent, p); | 55 | rb_link_node(&he->rb_node, parent, p); |
| 56 | rb_insert_color(&he->rb_node, &self->hists); | 56 | rb_insert_color(&he->rb_node, hists); |
| 57 | *hit = false; | 57 | *hit = false; |
| 58 | return he; | 58 | return he; |
| 59 | } | 59 | } |
| @@ -130,7 +130,7 @@ static void collapse__insert_entry(struct rb_root *root, struct hist_entry *he) | |||
| 130 | rb_insert_color(&he->rb_node, root); | 130 | rb_insert_color(&he->rb_node, root); |
| 131 | } | 131 | } |
| 132 | 132 | ||
| 133 | void perf_session__collapse_resort(struct perf_session *self) | 133 | void perf_session__collapse_resort(struct rb_root *hists) |
| 134 | { | 134 | { |
| 135 | struct rb_root tmp; | 135 | struct rb_root tmp; |
| 136 | struct rb_node *next; | 136 | struct rb_node *next; |
| @@ -140,17 +140,17 @@ void perf_session__collapse_resort(struct perf_session *self) | |||
| 140 | return; | 140 | return; |
| 141 | 141 | ||
| 142 | tmp = RB_ROOT; | 142 | tmp = RB_ROOT; |
| 143 | next = rb_first(&self->hists); | 143 | next = rb_first(hists); |
| 144 | 144 | ||
| 145 | while (next) { | 145 | while (next) { |
| 146 | n = rb_entry(next, struct hist_entry, rb_node); | 146 | n = rb_entry(next, struct hist_entry, rb_node); |
| 147 | next = rb_next(&n->rb_node); | 147 | next = rb_next(&n->rb_node); |
| 148 | 148 | ||
| 149 | rb_erase(&n->rb_node, &self->hists); | 149 | rb_erase(&n->rb_node, hists); |
| 150 | collapse__insert_entry(&tmp, n); | 150 | collapse__insert_entry(&tmp, n); |
| 151 | } | 151 | } |
| 152 | 152 | ||
| 153 | self->hists = tmp; | 153 | *hists = tmp; |
| 154 | } | 154 | } |
| 155 | 155 | ||
| 156 | /* | 156 | /* |
| @@ -183,7 +183,7 @@ static void perf_session__insert_output_hist_entry(struct rb_root *root, | |||
| 183 | rb_insert_color(&he->rb_node, root); | 183 | rb_insert_color(&he->rb_node, root); |
| 184 | } | 184 | } |
| 185 | 185 | ||
| 186 | void perf_session__output_resort(struct perf_session *self, u64 total_samples) | 186 | void perf_session__output_resort(struct rb_root *hists, u64 total_samples) |
| 187 | { | 187 | { |
| 188 | struct rb_root tmp; | 188 | struct rb_root tmp; |
| 189 | struct rb_node *next; | 189 | struct rb_node *next; |
| @@ -194,18 +194,18 @@ void perf_session__output_resort(struct perf_session *self, u64 total_samples) | |||
| 194 | total_samples * (callchain_param.min_percent / 100); | 194 | total_samples * (callchain_param.min_percent / 100); |
| 195 | 195 | ||
| 196 | tmp = RB_ROOT; | 196 | tmp = RB_ROOT; |
| 197 | next = rb_first(&self->hists); | 197 | next = rb_first(hists); |
| 198 | 198 | ||
| 199 | while (next) { | 199 | while (next) { |
| 200 | n = rb_entry(next, struct hist_entry, rb_node); | 200 | n = rb_entry(next, struct hist_entry, rb_node); |
| 201 | next = rb_next(&n->rb_node); | 201 | next = rb_next(&n->rb_node); |
| 202 | 202 | ||
| 203 | rb_erase(&n->rb_node, &self->hists); | 203 | rb_erase(&n->rb_node, hists); |
| 204 | perf_session__insert_output_hist_entry(&tmp, n, | 204 | perf_session__insert_output_hist_entry(&tmp, n, |
| 205 | min_callchain_hits); | 205 | min_callchain_hits); |
| 206 | } | 206 | } |
| 207 | 207 | ||
| 208 | self->hists = tmp; | 208 | *hists = tmp; |
| 209 | } | 209 | } |
| 210 | 210 | ||
| 211 | static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) | 211 | static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) |
| @@ -321,7 +321,7 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
| 321 | new_depth_mask &= ~(1 << (depth - 1)); | 321 | new_depth_mask &= ~(1 << (depth - 1)); |
| 322 | 322 | ||
| 323 | /* | 323 | /* |
| 324 | * But we keep the older depth mask for the line seperator | 324 | * But we keep the older depth mask for the line separator |
| 325 | * to keep the level link until we reach the last child | 325 | * to keep the level link until we reach the last child |
| 326 | */ | 326 | */ |
| 327 | ret += ipchain__fprintf_graph_line(fp, depth, depth_mask, | 327 | ret += ipchain__fprintf_graph_line(fp, depth, depth_mask, |
| @@ -456,10 +456,10 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, | |||
| 456 | } | 456 | } |
| 457 | 457 | ||
| 458 | static size_t hist_entry__fprintf(struct hist_entry *self, | 458 | static size_t hist_entry__fprintf(struct hist_entry *self, |
| 459 | struct perf_session *session, | ||
| 460 | struct perf_session *pair_session, | 459 | struct perf_session *pair_session, |
| 461 | bool show_displacement, | 460 | bool show_displacement, |
| 462 | long displacement, FILE *fp) | 461 | long displacement, FILE *fp, |
| 462 | u64 session_total) | ||
| 463 | { | 463 | { |
| 464 | struct sort_entry *se; | 464 | struct sort_entry *se; |
| 465 | u64 count, total; | 465 | u64 count, total; |
| @@ -474,7 +474,7 @@ static size_t hist_entry__fprintf(struct hist_entry *self, | |||
| 474 | total = pair_session->events_stats.total; | 474 | total = pair_session->events_stats.total; |
| 475 | } else { | 475 | } else { |
| 476 | count = self->count; | 476 | count = self->count; |
| 477 | total = session->events_stats.total; | 477 | total = session_total; |
| 478 | } | 478 | } |
| 479 | 479 | ||
| 480 | if (total) | 480 | if (total) |
| @@ -496,8 +496,8 @@ static size_t hist_entry__fprintf(struct hist_entry *self, | |||
| 496 | 496 | ||
| 497 | if (total > 0) | 497 | if (total > 0) |
| 498 | old_percent = (count * 100.0) / total; | 498 | old_percent = (count * 100.0) / total; |
| 499 | if (session->events_stats.total > 0) | 499 | if (session_total > 0) |
| 500 | new_percent = (self->count * 100.0) / session->events_stats.total; | 500 | new_percent = (self->count * 100.0) / session_total; |
| 501 | 501 | ||
| 502 | diff = new_percent - old_percent; | 502 | diff = new_percent - old_percent; |
| 503 | 503 | ||
| @@ -544,16 +544,17 @@ static size_t hist_entry__fprintf(struct hist_entry *self, | |||
| 544 | left_margin -= thread__comm_len(self->thread); | 544 | left_margin -= thread__comm_len(self->thread); |
| 545 | } | 545 | } |
| 546 | 546 | ||
| 547 | hist_entry_callchain__fprintf(fp, self, session->events_stats.total, | 547 | hist_entry_callchain__fprintf(fp, self, session_total, |
| 548 | left_margin); | 548 | left_margin); |
| 549 | } | 549 | } |
| 550 | 550 | ||
| 551 | return ret; | 551 | return ret; |
| 552 | } | 552 | } |
| 553 | 553 | ||
| 554 | size_t perf_session__fprintf_hists(struct perf_session *self, | 554 | size_t perf_session__fprintf_hists(struct rb_root *hists, |
| 555 | struct perf_session *pair, | 555 | struct perf_session *pair, |
| 556 | bool show_displacement, FILE *fp) | 556 | bool show_displacement, FILE *fp, |
| 557 | u64 session_total) | ||
| 557 | { | 558 | { |
| 558 | struct sort_entry *se; | 559 | struct sort_entry *se; |
| 559 | struct rb_node *nd; | 560 | struct rb_node *nd; |
| @@ -641,7 +642,7 @@ size_t perf_session__fprintf_hists(struct perf_session *self, | |||
| 641 | fprintf(fp, "\n#\n"); | 642 | fprintf(fp, "\n#\n"); |
| 642 | 643 | ||
| 643 | print_entries: | 644 | print_entries: |
| 644 | for (nd = rb_first(&self->hists); nd; nd = rb_next(nd)) { | 645 | for (nd = rb_first(hists); nd; nd = rb_next(nd)) { |
| 645 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | 646 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); |
| 646 | 647 | ||
| 647 | if (show_displacement) { | 648 | if (show_displacement) { |
| @@ -652,8 +653,13 @@ print_entries: | |||
| 652 | displacement = 0; | 653 | displacement = 0; |
| 653 | ++position; | 654 | ++position; |
| 654 | } | 655 | } |
| 655 | ret += hist_entry__fprintf(h, self, pair, show_displacement, | 656 | ret += hist_entry__fprintf(h, pair, show_displacement, |
| 656 | displacement, fp); | 657 | displacement, fp, session_total); |
| 658 | if (h->map == NULL && verbose > 1) { | ||
| 659 | __map_groups__fprintf_maps(&h->thread->mg, | ||
| 660 | MAP__FUNCTION, fp); | ||
| 661 | fprintf(fp, "%.10s end\n", graph_dotted_line); | ||
| 662 | } | ||
| 657 | } | 663 | } |
| 658 | 664 | ||
| 659 | free(rem_sq_bracket); | 665 | free(rem_sq_bracket); |
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index e5f99b24048b..16f360cce5bf 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
| @@ -10,8 +10,9 @@ struct perf_session; | |||
| 10 | struct hist_entry; | 10 | struct hist_entry; |
| 11 | struct addr_location; | 11 | struct addr_location; |
| 12 | struct symbol; | 12 | struct symbol; |
| 13 | struct rb_root; | ||
| 13 | 14 | ||
| 14 | struct hist_entry *__perf_session__add_hist_entry(struct perf_session *self, | 15 | struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, |
| 15 | struct addr_location *al, | 16 | struct addr_location *al, |
| 16 | struct symbol *parent, | 17 | struct symbol *parent, |
| 17 | u64 count, bool *hit); | 18 | u64 count, bool *hit); |
| @@ -19,9 +20,10 @@ extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); | |||
| 19 | extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); | 20 | extern int64_t hist_entry__collapse(struct hist_entry *, struct hist_entry *); |
| 20 | void hist_entry__free(struct hist_entry *); | 21 | void hist_entry__free(struct hist_entry *); |
| 21 | 22 | ||
| 22 | void perf_session__output_resort(struct perf_session *self, u64 total_samples); | 23 | void perf_session__output_resort(struct rb_root *hists, u64 total_samples); |
| 23 | void perf_session__collapse_resort(struct perf_session *self); | 24 | void perf_session__collapse_resort(struct rb_root *hists); |
| 24 | size_t perf_session__fprintf_hists(struct perf_session *self, | 25 | size_t perf_session__fprintf_hists(struct rb_root *hists, |
| 25 | struct perf_session *pair, | 26 | struct perf_session *pair, |
| 26 | bool show_displacement, FILE *fp); | 27 | bool show_displacement, FILE *fp, |
| 28 | u64 session_total); | ||
| 27 | #endif /* __PERF_HIST_H */ | 29 | #endif /* __PERF_HIST_H */ |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 8f0568849691..7c004b6ef24f 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
| @@ -119,14 +119,14 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) | |||
| 119 | char c, nc = 0; | 119 | char c, nc = 0; |
| 120 | /* | 120 | /* |
| 121 | * <Syntax> | 121 | * <Syntax> |
| 122 | * perf probe [EVENT=]SRC:LN | 122 | * perf probe [EVENT=]SRC[:LN|;PTN] |
| 123 | * perf probe [EVENT=]FUNC[+OFFS|%return][@SRC] | 123 | * perf probe [EVENT=]FUNC[@SRC][+OFFS|%return|:LN|;PAT] |
| 124 | * | 124 | * |
| 125 | * TODO:Group name support | 125 | * TODO:Group name support |
| 126 | */ | 126 | */ |
| 127 | 127 | ||
| 128 | ptr = strchr(arg, '='); | 128 | ptr = strpbrk(arg, ";=@+%"); |
| 129 | if (ptr) { /* Event name */ | 129 | if (ptr && *ptr == '=') { /* Event name */ |
| 130 | *ptr = '\0'; | 130 | *ptr = '\0'; |
| 131 | tmp = ptr + 1; | 131 | tmp = ptr + 1; |
| 132 | ptr = strchr(arg, ':'); | 132 | ptr = strchr(arg, ':'); |
| @@ -139,7 +139,7 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) | |||
| 139 | arg = tmp; | 139 | arg = tmp; |
| 140 | } | 140 | } |
| 141 | 141 | ||
| 142 | ptr = strpbrk(arg, ":+@%"); | 142 | ptr = strpbrk(arg, ";:+@%"); |
| 143 | if (ptr) { | 143 | if (ptr) { |
| 144 | nc = *ptr; | 144 | nc = *ptr; |
| 145 | *ptr++ = '\0'; | 145 | *ptr++ = '\0'; |
| @@ -156,7 +156,11 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) | |||
| 156 | while (ptr) { | 156 | while (ptr) { |
| 157 | arg = ptr; | 157 | arg = ptr; |
| 158 | c = nc; | 158 | c = nc; |
| 159 | ptr = strpbrk(arg, ":+@%"); | 159 | if (c == ';') { /* Lazy pattern must be the last part */ |
| 160 | pp->lazy_line = strdup(arg); | ||
| 161 | break; | ||
| 162 | } | ||
| 163 | ptr = strpbrk(arg, ";:+@%"); | ||
| 160 | if (ptr) { | 164 | if (ptr) { |
| 161 | nc = *ptr; | 165 | nc = *ptr; |
| 162 | *ptr++ = '\0'; | 166 | *ptr++ = '\0'; |
| @@ -165,13 +169,13 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) | |||
| 165 | case ':': /* Line number */ | 169 | case ':': /* Line number */ |
| 166 | pp->line = strtoul(arg, &tmp, 0); | 170 | pp->line = strtoul(arg, &tmp, 0); |
| 167 | if (*tmp != '\0') | 171 | if (*tmp != '\0') |
| 168 | semantic_error("There is non-digit charactor" | 172 | semantic_error("There is non-digit char" |
| 169 | " in line number."); | 173 | " in line number."); |
| 170 | break; | 174 | break; |
| 171 | case '+': /* Byte offset from a symbol */ | 175 | case '+': /* Byte offset from a symbol */ |
| 172 | pp->offset = strtoul(arg, &tmp, 0); | 176 | pp->offset = strtoul(arg, &tmp, 0); |
| 173 | if (*tmp != '\0') | 177 | if (*tmp != '\0') |
| 174 | semantic_error("There is non-digit charactor" | 178 | semantic_error("There is non-digit character" |
| 175 | " in offset."); | 179 | " in offset."); |
| 176 | break; | 180 | break; |
| 177 | case '@': /* File name */ | 181 | case '@': /* File name */ |
| @@ -179,9 +183,6 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) | |||
| 179 | semantic_error("SRC@SRC is not allowed."); | 183 | semantic_error("SRC@SRC is not allowed."); |
| 180 | pp->file = strdup(arg); | 184 | pp->file = strdup(arg); |
| 181 | DIE_IF(pp->file == NULL); | 185 | DIE_IF(pp->file == NULL); |
| 182 | if (ptr) | ||
| 183 | semantic_error("@SRC must be the last " | ||
| 184 | "option."); | ||
| 185 | break; | 186 | break; |
| 186 | case '%': /* Probe places */ | 187 | case '%': /* Probe places */ |
| 187 | if (strcmp(arg, "return") == 0) { | 188 | if (strcmp(arg, "return") == 0) { |
| @@ -196,11 +197,18 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) | |||
| 196 | } | 197 | } |
| 197 | 198 | ||
| 198 | /* Exclusion check */ | 199 | /* Exclusion check */ |
| 200 | if (pp->lazy_line && pp->line) | ||
| 201 | semantic_error("Lazy pattern can't be used with line number."); | ||
| 202 | |||
| 203 | if (pp->lazy_line && pp->offset) | ||
| 204 | semantic_error("Lazy pattern can't be used with offset."); | ||
| 205 | |||
| 199 | if (pp->line && pp->offset) | 206 | if (pp->line && pp->offset) |
| 200 | semantic_error("Offset can't be used with line number."); | 207 | semantic_error("Offset can't be used with line number."); |
| 201 | 208 | ||
| 202 | if (!pp->line && pp->file && !pp->function) | 209 | if (!pp->line && !pp->lazy_line && pp->file && !pp->function) |
| 203 | semantic_error("File always requires line number."); | 210 | semantic_error("File always requires line number or " |
| 211 | "lazy pattern."); | ||
| 204 | 212 | ||
| 205 | if (pp->offset && !pp->function) | 213 | if (pp->offset && !pp->function) |
| 206 | semantic_error("Offset requires an entry function."); | 214 | semantic_error("Offset requires an entry function."); |
| @@ -208,11 +216,13 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) | |||
| 208 | if (pp->retprobe && !pp->function) | 216 | if (pp->retprobe && !pp->function) |
| 209 | semantic_error("Return probe requires an entry function."); | 217 | semantic_error("Return probe requires an entry function."); |
| 210 | 218 | ||
| 211 | if ((pp->offset || pp->line) && pp->retprobe) | 219 | if ((pp->offset || pp->line || pp->lazy_line) && pp->retprobe) |
| 212 | semantic_error("Offset/Line can't be used with return probe."); | 220 | semantic_error("Offset/Line/Lazy pattern can't be used with " |
| 221 | "return probe."); | ||
| 213 | 222 | ||
| 214 | pr_debug("symbol:%s file:%s line:%d offset:%d, return:%d\n", | 223 | pr_debug("symbol:%s file:%s line:%d offset:%d return:%d lazy:%s\n", |
| 215 | pp->function, pp->file, pp->line, pp->offset, pp->retprobe); | 224 | pp->function, pp->file, pp->line, pp->offset, pp->retprobe, |
| 225 | pp->lazy_line); | ||
| 216 | } | 226 | } |
| 217 | 227 | ||
| 218 | /* Parse perf-probe event definition */ | 228 | /* Parse perf-probe event definition */ |
| @@ -232,7 +242,7 @@ void parse_perf_probe_event(const char *str, struct probe_point *pp, | |||
| 232 | 242 | ||
| 233 | /* Parse probe point */ | 243 | /* Parse probe point */ |
| 234 | parse_perf_probe_probepoint(argv[0], pp); | 244 | parse_perf_probe_probepoint(argv[0], pp); |
| 235 | if (pp->file || pp->line) | 245 | if (pp->file || pp->line || pp->lazy_line) |
| 236 | *need_dwarf = true; | 246 | *need_dwarf = true; |
| 237 | 247 | ||
| 238 | /* Copy arguments and ensure return probe has no C argument */ | 248 | /* Copy arguments and ensure return probe has no C argument */ |
| @@ -458,6 +468,8 @@ static void clear_probe_point(struct probe_point *pp) | |||
| 458 | free(pp->function); | 468 | free(pp->function); |
| 459 | if (pp->file) | 469 | if (pp->file) |
| 460 | free(pp->file); | 470 | free(pp->file); |
| 471 | if (pp->lazy_line) | ||
| 472 | free(pp->lazy_line); | ||
| 461 | for (i = 0; i < pp->nr_args; i++) | 473 | for (i = 0; i < pp->nr_args; i++) |
| 462 | free(pp->args[i]); | 474 | free(pp->args[i]); |
| 463 | if (pp->args) | 475 | if (pp->args) |
| @@ -496,8 +508,8 @@ void show_perf_probe_events(void) | |||
| 496 | struct str_node *ent; | 508 | struct str_node *ent; |
| 497 | 509 | ||
| 498 | setup_pager(); | 510 | setup_pager(); |
| 499 | |||
| 500 | memset(&pp, 0, sizeof(pp)); | 511 | memset(&pp, 0, sizeof(pp)); |
| 512 | |||
| 501 | fd = open_kprobe_events(O_RDONLY, 0); | 513 | fd = open_kprobe_events(O_RDONLY, 0); |
| 502 | rawlist = get_trace_kprobe_event_rawlist(fd); | 514 | rawlist = get_trace_kprobe_event_rawlist(fd); |
| 503 | close(fd); | 515 | close(fd); |
| @@ -719,6 +731,7 @@ void del_trace_kprobe_events(struct strlist *dellist) | |||
| 719 | } | 731 | } |
| 720 | 732 | ||
| 721 | #define LINEBUF_SIZE 256 | 733 | #define LINEBUF_SIZE 256 |
| 734 | #define NR_ADDITIONAL_LINES 2 | ||
| 722 | 735 | ||
| 723 | static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num) | 736 | static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num) |
| 724 | { | 737 | { |
| @@ -779,5 +792,11 @@ void show_line_range(struct line_range *lr) | |||
| 779 | show_one_line(fp, (l++) - lr->offset, false, false); | 792 | show_one_line(fp, (l++) - lr->offset, false, false); |
| 780 | show_one_line(fp, (l++) - lr->offset, false, true); | 793 | show_one_line(fp, (l++) - lr->offset, false, true); |
| 781 | } | 794 | } |
| 795 | |||
| 796 | if (lr->end == INT_MAX) | ||
| 797 | lr->end = l + NR_ADDITIONAL_LINES; | ||
| 798 | while (l < lr->end && !feof(fp)) | ||
| 799 | show_one_line(fp, (l++) - lr->offset, false, false); | ||
| 800 | |||
| 782 | fclose(fp); | 801 | fclose(fp); |
| 783 | } | 802 | } |
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 1b2124d12f68..c171a243d05b 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
| @@ -32,21 +32,13 @@ | |||
| 32 | #include <stdarg.h> | 32 | #include <stdarg.h> |
| 33 | #include <ctype.h> | 33 | #include <ctype.h> |
| 34 | 34 | ||
| 35 | #include "string.h" | ||
| 35 | #include "event.h" | 36 | #include "event.h" |
| 36 | #include "debug.h" | 37 | #include "debug.h" |
| 37 | #include "util.h" | 38 | #include "util.h" |
| 38 | #include "probe-finder.h" | 39 | #include "probe-finder.h" |
| 39 | 40 | ||
| 40 | 41 | ||
| 41 | /* Dwarf_Die Linkage to parent Die */ | ||
| 42 | struct die_link { | ||
| 43 | struct die_link *parent; /* Parent die */ | ||
| 44 | Dwarf_Die die; /* Current die */ | ||
| 45 | }; | ||
| 46 | |||
| 47 | static Dwarf_Debug __dw_debug; | ||
| 48 | static Dwarf_Error __dw_error; | ||
| 49 | |||
| 50 | /* | 42 | /* |
| 51 | * Generic dwarf analysis helpers | 43 | * Generic dwarf analysis helpers |
| 52 | */ | 44 | */ |
| @@ -113,281 +105,190 @@ static int strtailcmp(const char *s1, const char *s2) | |||
| 113 | return 0; | 105 | return 0; |
| 114 | } | 106 | } |
| 115 | 107 | ||
| 116 | /* Find the fileno of the target file. */ | 108 | /* Line number list operations */ |
| 117 | static Dwarf_Unsigned cu_find_fileno(Dwarf_Die cu_die, const char *fname) | ||
| 118 | { | ||
| 119 | Dwarf_Signed cnt, i; | ||
| 120 | Dwarf_Unsigned found = 0; | ||
| 121 | char **srcs; | ||
| 122 | int ret; | ||
| 123 | 109 | ||
| 124 | if (!fname) | 110 | /* Add a line to line number list */ |
| 125 | return 0; | 111 | static void line_list__add_line(struct list_head *head, unsigned int line) |
| 112 | { | ||
| 113 | struct line_node *ln; | ||
| 114 | struct list_head *p; | ||
| 126 | 115 | ||
| 127 | ret = dwarf_srcfiles(cu_die, &srcs, &cnt, &__dw_error); | 116 | /* Reverse search, because new line will be the last one */ |
| 128 | if (ret == DW_DLV_OK) { | 117 | list_for_each_entry_reverse(ln, head, list) { |
| 129 | for (i = 0; i < cnt && !found; i++) { | 118 | if (ln->line < line) { |
| 130 | if (strtailcmp(srcs[i], fname) == 0) | 119 | p = &ln->list; |
| 131 | found = i + 1; | 120 | goto found; |
| 132 | dwarf_dealloc(__dw_debug, srcs[i], DW_DLA_STRING); | 121 | } else if (ln->line == line) /* Already exist */ |
| 133 | } | 122 | return ; |
| 134 | for (; i < cnt; i++) | ||
| 135 | dwarf_dealloc(__dw_debug, srcs[i], DW_DLA_STRING); | ||
| 136 | dwarf_dealloc(__dw_debug, srcs, DW_DLA_LIST); | ||
| 137 | } | 123 | } |
| 138 | if (found) | 124 | /* List is empty, or the smallest entry */ |
| 139 | pr_debug("found fno: %d\n", (int)found); | 125 | p = head; |
| 140 | return found; | 126 | found: |
| 127 | pr_debug("line list: add a line %u\n", line); | ||
| 128 | ln = zalloc(sizeof(struct line_node)); | ||
| 129 | DIE_IF(ln == NULL); | ||
| 130 | ln->line = line; | ||
| 131 | INIT_LIST_HEAD(&ln->list); | ||
| 132 | list_add(&ln->list, p); | ||
| 141 | } | 133 | } |
| 142 | 134 | ||
| 143 | static int cu_get_filename(Dwarf_Die cu_die, Dwarf_Unsigned fno, char **buf) | 135 | /* Check if the line in line number list */ |
| 136 | static int line_list__has_line(struct list_head *head, unsigned int line) | ||
| 144 | { | 137 | { |
| 145 | Dwarf_Signed cnt, i; | 138 | struct line_node *ln; |
| 146 | char **srcs; | 139 | |
| 147 | int ret = 0; | 140 | /* Reverse search, because new line will be the last one */ |
| 148 | 141 | list_for_each_entry(ln, head, list) | |
| 149 | if (!buf || !fno) | 142 | if (ln->line == line) |
| 150 | return -EINVAL; | 143 | return 1; |
| 151 | 144 | ||
| 152 | ret = dwarf_srcfiles(cu_die, &srcs, &cnt, &__dw_error); | 145 | return 0; |
| 153 | if (ret == DW_DLV_OK) { | ||
| 154 | if ((Dwarf_Unsigned)cnt > fno - 1) { | ||
| 155 | *buf = strdup(srcs[fno - 1]); | ||
| 156 | ret = 0; | ||
| 157 | pr_debug("found filename: %s\n", *buf); | ||
| 158 | } else | ||
| 159 | ret = -ENOENT; | ||
| 160 | for (i = 0; i < cnt; i++) | ||
| 161 | dwarf_dealloc(__dw_debug, srcs[i], DW_DLA_STRING); | ||
| 162 | dwarf_dealloc(__dw_debug, srcs, DW_DLA_LIST); | ||
| 163 | } else | ||
| 164 | ret = -EINVAL; | ||
| 165 | return ret; | ||
| 166 | } | 146 | } |
| 167 | 147 | ||
| 168 | /* Compare diename and tname */ | 148 | /* Init line number list */ |
| 169 | static int die_compare_name(Dwarf_Die dw_die, const char *tname) | 149 | static void line_list__init(struct list_head *head) |
| 170 | { | 150 | { |
| 171 | char *name; | 151 | INIT_LIST_HEAD(head); |
| 172 | int ret; | ||
| 173 | ret = dwarf_diename(dw_die, &name, &__dw_error); | ||
| 174 | DIE_IF(ret == DW_DLV_ERROR); | ||
| 175 | if (ret == DW_DLV_OK) { | ||
| 176 | ret = strcmp(tname, name); | ||
| 177 | dwarf_dealloc(__dw_debug, name, DW_DLA_STRING); | ||
| 178 | } else | ||
| 179 | ret = -1; | ||
| 180 | return ret; | ||
| 181 | } | 152 | } |
| 182 | 153 | ||
| 183 | /* Check the address is in the subprogram(function). */ | 154 | /* Free line number list */ |
| 184 | static int die_within_subprogram(Dwarf_Die sp_die, Dwarf_Addr addr, | 155 | static void line_list__free(struct list_head *head) |
| 185 | Dwarf_Signed *offs) | ||
| 186 | { | 156 | { |
| 187 | Dwarf_Addr lopc, hipc; | 157 | struct line_node *ln; |
| 188 | int ret; | 158 | while (!list_empty(head)) { |
| 189 | 159 | ln = list_first_entry(head, struct line_node, list); | |
| 190 | /* TODO: check ranges */ | 160 | list_del(&ln->list); |
| 191 | ret = dwarf_lowpc(sp_die, &lopc, &__dw_error); | 161 | free(ln); |
| 192 | DIE_IF(ret == DW_DLV_ERROR); | 162 | } |
| 193 | if (ret == DW_DLV_NO_ENTRY) | ||
| 194 | return 0; | ||
| 195 | ret = dwarf_highpc(sp_die, &hipc, &__dw_error); | ||
| 196 | DIE_IF(ret != DW_DLV_OK); | ||
| 197 | if (lopc <= addr && addr < hipc) { | ||
| 198 | *offs = addr - lopc; | ||
| 199 | return 1; | ||
| 200 | } else | ||
| 201 | return 0; | ||
| 202 | } | 163 | } |
| 203 | 164 | ||
| 204 | /* Check the die is inlined function */ | 165 | /* Dwarf wrappers */ |
| 205 | static Dwarf_Bool die_inlined_subprogram(Dwarf_Die dw_die) | 166 | |
| 167 | /* Find the realpath of the target file. */ | ||
| 168 | static const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname) | ||
| 206 | { | 169 | { |
| 207 | /* TODO: check strictly */ | 170 | Dwarf_Files *files; |
| 208 | Dwarf_Bool inl; | 171 | size_t nfiles, i; |
| 172 | const char *src = NULL; | ||
| 209 | int ret; | 173 | int ret; |
| 210 | 174 | ||
| 211 | ret = dwarf_hasattr(dw_die, DW_AT_inline, &inl, &__dw_error); | 175 | if (!fname) |
| 212 | DIE_IF(ret == DW_DLV_ERROR); | 176 | return NULL; |
| 213 | return inl; | ||
| 214 | } | ||
| 215 | 177 | ||
| 216 | /* Get the offset of abstruct_origin */ | 178 | ret = dwarf_getsrcfiles(cu_die, &files, &nfiles); |
| 217 | static Dwarf_Off die_get_abstract_origin(Dwarf_Die dw_die) | 179 | if (ret != 0) |
| 218 | { | 180 | return NULL; |
| 219 | Dwarf_Attribute attr; | ||
| 220 | Dwarf_Off cu_offs; | ||
| 221 | int ret; | ||
| 222 | 181 | ||
| 223 | ret = dwarf_attr(dw_die, DW_AT_abstract_origin, &attr, &__dw_error); | 182 | for (i = 0; i < nfiles; i++) { |
| 224 | DIE_IF(ret != DW_DLV_OK); | 183 | src = dwarf_filesrc(files, i, NULL, NULL); |
| 225 | ret = dwarf_formref(attr, &cu_offs, &__dw_error); | 184 | if (strtailcmp(src, fname) == 0) |
| 226 | DIE_IF(ret != DW_DLV_OK); | 185 | break; |
| 227 | dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR); | 186 | } |
| 228 | return cu_offs; | 187 | return src; |
| 229 | } | 188 | } |
| 230 | 189 | ||
| 231 | /* Get entry pc(or low pc, 1st entry of ranges) of the die */ | 190 | struct __addr_die_search_param { |
| 232 | static Dwarf_Addr die_get_entrypc(Dwarf_Die dw_die) | 191 | Dwarf_Addr addr; |
| 192 | Dwarf_Die *die_mem; | ||
| 193 | }; | ||
| 194 | |||
| 195 | static int __die_search_func_cb(Dwarf_Die *fn_die, void *data) | ||
| 233 | { | 196 | { |
| 234 | Dwarf_Attribute attr; | 197 | struct __addr_die_search_param *ad = data; |
| 235 | Dwarf_Addr addr; | ||
| 236 | Dwarf_Off offs; | ||
| 237 | Dwarf_Ranges *ranges; | ||
| 238 | Dwarf_Signed cnt; | ||
| 239 | int ret; | ||
| 240 | 198 | ||
| 241 | /* Try to get entry pc */ | 199 | if (dwarf_tag(fn_die) == DW_TAG_subprogram && |
| 242 | ret = dwarf_attr(dw_die, DW_AT_entry_pc, &attr, &__dw_error); | 200 | dwarf_haspc(fn_die, ad->addr)) { |
| 243 | DIE_IF(ret == DW_DLV_ERROR); | 201 | memcpy(ad->die_mem, fn_die, sizeof(Dwarf_Die)); |
| 244 | if (ret == DW_DLV_OK) { | 202 | return DWARF_CB_ABORT; |
| 245 | ret = dwarf_formaddr(attr, &addr, &__dw_error); | ||
| 246 | DIE_IF(ret != DW_DLV_OK); | ||
| 247 | dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR); | ||
| 248 | return addr; | ||
| 249 | } | 203 | } |
| 204 | return DWARF_CB_OK; | ||
| 205 | } | ||
| 250 | 206 | ||
| 251 | /* Try to get low pc */ | 207 | /* Search a real subprogram including this line, */ |
| 252 | ret = dwarf_lowpc(dw_die, &addr, &__dw_error); | 208 | static Dwarf_Die *die_get_real_subprogram(Dwarf_Die *cu_die, Dwarf_Addr addr, |
| 253 | DIE_IF(ret == DW_DLV_ERROR); | 209 | Dwarf_Die *die_mem) |
| 254 | if (ret == DW_DLV_OK) | 210 | { |
| 255 | return addr; | 211 | struct __addr_die_search_param ad; |
| 256 | 212 | ad.addr = addr; | |
| 257 | /* Try to get ranges */ | 213 | ad.die_mem = die_mem; |
| 258 | ret = dwarf_attr(dw_die, DW_AT_ranges, &attr, &__dw_error); | 214 | /* dwarf_getscopes can't find subprogram. */ |
| 259 | DIE_IF(ret != DW_DLV_OK); | 215 | if (!dwarf_getfuncs(cu_die, __die_search_func_cb, &ad, 0)) |
| 260 | ret = dwarf_formref(attr, &offs, &__dw_error); | 216 | return NULL; |
| 261 | DIE_IF(ret != DW_DLV_OK); | 217 | else |
| 262 | ret = dwarf_get_ranges(__dw_debug, offs, &ranges, &cnt, NULL, | 218 | return die_mem; |
| 263 | &__dw_error); | ||
| 264 | DIE_IF(ret != DW_DLV_OK); | ||
| 265 | addr = ranges[0].dwr_addr1; | ||
| 266 | dwarf_ranges_dealloc(__dw_debug, ranges, cnt); | ||
| 267 | return addr; | ||
| 268 | } | 219 | } |
| 269 | 220 | ||
| 270 | /* | 221 | /* Similar to dwarf_getfuncs, but returns inlined_subroutine if exists. */ |
| 271 | * Search a Die from Die tree. | 222 | static Dwarf_Die *die_get_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, |
| 272 | * Note: cur_link->die should be deallocated in this function. | 223 | Dwarf_Die *die_mem) |
| 273 | */ | ||
| 274 | static int __search_die_tree(struct die_link *cur_link, | ||
| 275 | int (*die_cb)(struct die_link *, void *), | ||
| 276 | void *data) | ||
| 277 | { | 224 | { |
| 278 | Dwarf_Die new_die; | 225 | Dwarf_Die child_die; |
| 279 | struct die_link new_link; | ||
| 280 | int ret; | 226 | int ret; |
| 281 | 227 | ||
| 282 | if (!die_cb) | 228 | ret = dwarf_child(sp_die, die_mem); |
| 283 | return 0; | 229 | if (ret != 0) |
| 284 | 230 | return NULL; | |
| 285 | /* Check current die */ | ||
| 286 | while (!(ret = die_cb(cur_link, data))) { | ||
| 287 | /* Check child die */ | ||
| 288 | ret = dwarf_child(cur_link->die, &new_die, &__dw_error); | ||
| 289 | DIE_IF(ret == DW_DLV_ERROR); | ||
| 290 | if (ret == DW_DLV_OK) { | ||
| 291 | new_link.parent = cur_link; | ||
| 292 | new_link.die = new_die; | ||
| 293 | ret = __search_die_tree(&new_link, die_cb, data); | ||
| 294 | if (ret) | ||
| 295 | break; | ||
| 296 | } | ||
| 297 | 231 | ||
| 298 | /* Move to next sibling */ | 232 | do { |
| 299 | ret = dwarf_siblingof(__dw_debug, cur_link->die, &new_die, | 233 | if (dwarf_tag(die_mem) == DW_TAG_inlined_subroutine && |
| 300 | &__dw_error); | 234 | dwarf_haspc(die_mem, addr)) |
| 301 | DIE_IF(ret == DW_DLV_ERROR); | 235 | return die_mem; |
| 302 | dwarf_dealloc(__dw_debug, cur_link->die, DW_DLA_DIE); | ||
| 303 | cur_link->die = new_die; | ||
| 304 | if (ret == DW_DLV_NO_ENTRY) | ||
| 305 | return 0; | ||
| 306 | } | ||
| 307 | dwarf_dealloc(__dw_debug, cur_link->die, DW_DLA_DIE); | ||
| 308 | return ret; | ||
| 309 | } | ||
| 310 | 236 | ||
| 311 | /* Search a die in its children's die tree */ | 237 | if (die_get_inlinefunc(die_mem, addr, &child_die)) { |
| 312 | static int search_die_from_children(Dwarf_Die parent_die, | 238 | memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); |
| 313 | int (*die_cb)(struct die_link *, void *), | 239 | return die_mem; |
| 314 | void *data) | 240 | } |
| 315 | { | 241 | } while (dwarf_siblingof(die_mem, die_mem) == 0); |
| 316 | struct die_link new_link; | ||
| 317 | int ret; | ||
| 318 | 242 | ||
| 319 | new_link.parent = NULL; | 243 | return NULL; |
| 320 | ret = dwarf_child(parent_die, &new_link.die, &__dw_error); | ||
| 321 | DIE_IF(ret == DW_DLV_ERROR); | ||
| 322 | if (ret == DW_DLV_OK) | ||
| 323 | return __search_die_tree(&new_link, die_cb, data); | ||
| 324 | else | ||
| 325 | return 0; | ||
| 326 | } | 244 | } |
| 327 | 245 | ||
| 328 | /* Find a locdesc corresponding to the address */ | 246 | /* Compare diename and tname */ |
| 329 | static int attr_get_locdesc(Dwarf_Attribute attr, Dwarf_Locdesc *desc, | 247 | static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) |
| 330 | Dwarf_Addr addr) | ||
| 331 | { | 248 | { |
| 332 | Dwarf_Signed lcnt; | 249 | const char *name; |
| 333 | Dwarf_Locdesc **llbuf; | 250 | name = dwarf_diename(dw_die); |
| 334 | int ret, i; | 251 | DIE_IF(name == NULL); |
| 335 | 252 | return strcmp(tname, name); | |
| 336 | ret = dwarf_loclist_n(attr, &llbuf, &lcnt, &__dw_error); | ||
| 337 | DIE_IF(ret != DW_DLV_OK); | ||
| 338 | ret = DW_DLV_NO_ENTRY; | ||
| 339 | for (i = 0; i < lcnt; ++i) { | ||
| 340 | if (llbuf[i]->ld_lopc <= addr && | ||
| 341 | llbuf[i]->ld_hipc > addr) { | ||
| 342 | memcpy(desc, llbuf[i], sizeof(Dwarf_Locdesc)); | ||
| 343 | desc->ld_s = | ||
| 344 | malloc(sizeof(Dwarf_Loc) * llbuf[i]->ld_cents); | ||
| 345 | DIE_IF(desc->ld_s == NULL); | ||
| 346 | memcpy(desc->ld_s, llbuf[i]->ld_s, | ||
| 347 | sizeof(Dwarf_Loc) * llbuf[i]->ld_cents); | ||
| 348 | ret = DW_DLV_OK; | ||
| 349 | break; | ||
| 350 | } | ||
| 351 | dwarf_dealloc(__dw_debug, llbuf[i]->ld_s, DW_DLA_LOC_BLOCK); | ||
| 352 | dwarf_dealloc(__dw_debug, llbuf[i], DW_DLA_LOCDESC); | ||
| 353 | } | ||
| 354 | /* Releasing loop */ | ||
| 355 | for (; i < lcnt; ++i) { | ||
| 356 | dwarf_dealloc(__dw_debug, llbuf[i]->ld_s, DW_DLA_LOC_BLOCK); | ||
| 357 | dwarf_dealloc(__dw_debug, llbuf[i], DW_DLA_LOCDESC); | ||
| 358 | } | ||
| 359 | dwarf_dealloc(__dw_debug, llbuf, DW_DLA_LIST); | ||
| 360 | return ret; | ||
| 361 | } | 253 | } |
| 362 | 254 | ||
| 363 | /* Get decl_file attribute value (file number) */ | 255 | /* Get entry pc(or low pc, 1st entry of ranges) of the die */ |
| 364 | static Dwarf_Unsigned die_get_decl_file(Dwarf_Die sp_die) | 256 | static Dwarf_Addr die_get_entrypc(Dwarf_Die *dw_die) |
| 365 | { | 257 | { |
| 366 | Dwarf_Attribute attr; | 258 | Dwarf_Addr epc; |
| 367 | Dwarf_Unsigned fno; | ||
| 368 | int ret; | 259 | int ret; |
| 369 | 260 | ||
| 370 | ret = dwarf_attr(sp_die, DW_AT_decl_file, &attr, &__dw_error); | 261 | ret = dwarf_entrypc(dw_die, &epc); |
| 371 | DIE_IF(ret != DW_DLV_OK); | 262 | DIE_IF(ret == -1); |
| 372 | dwarf_formudata(attr, &fno, &__dw_error); | 263 | return epc; |
| 373 | DIE_IF(ret != DW_DLV_OK); | ||
| 374 | dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR); | ||
| 375 | return fno; | ||
| 376 | } | 264 | } |
| 377 | 265 | ||
| 378 | /* Get decl_line attribute value (line number) */ | 266 | /* Get a variable die */ |
| 379 | static Dwarf_Unsigned die_get_decl_line(Dwarf_Die sp_die) | 267 | static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name, |
| 268 | Dwarf_Die *die_mem) | ||
| 380 | { | 269 | { |
| 381 | Dwarf_Attribute attr; | 270 | Dwarf_Die child_die; |
| 382 | Dwarf_Unsigned lno; | 271 | int tag; |
| 383 | int ret; | 272 | int ret; |
| 384 | 273 | ||
| 385 | ret = dwarf_attr(sp_die, DW_AT_decl_line, &attr, &__dw_error); | 274 | ret = dwarf_child(sp_die, die_mem); |
| 386 | DIE_IF(ret != DW_DLV_OK); | 275 | if (ret != 0) |
| 387 | dwarf_formudata(attr, &lno, &__dw_error); | 276 | return NULL; |
| 388 | DIE_IF(ret != DW_DLV_OK); | 277 | |
| 389 | dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR); | 278 | do { |
| 390 | return lno; | 279 | tag = dwarf_tag(die_mem); |
| 280 | if ((tag == DW_TAG_formal_parameter || | ||
| 281 | tag == DW_TAG_variable) && | ||
| 282 | (die_compare_name(die_mem, name) == 0)) | ||
| 283 | return die_mem; | ||
| 284 | |||
| 285 | if (die_find_variable(die_mem, name, &child_die)) { | ||
| 286 | memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); | ||
| 287 | return die_mem; | ||
| 288 | } | ||
| 289 | } while (dwarf_siblingof(die_mem, die_mem) == 0); | ||
| 290 | |||
| 291 | return NULL; | ||
| 391 | } | 292 | } |
| 392 | 293 | ||
| 393 | /* | 294 | /* |
| @@ -395,47 +296,45 @@ static Dwarf_Unsigned die_get_decl_line(Dwarf_Die sp_die) | |||
| 395 | */ | 296 | */ |
| 396 | 297 | ||
| 397 | /* Show a location */ | 298 | /* Show a location */ |
| 398 | static void show_location(Dwarf_Loc *loc, struct probe_finder *pf) | 299 | static void show_location(Dwarf_Op *op, struct probe_finder *pf) |
| 399 | { | 300 | { |
| 400 | Dwarf_Small op; | 301 | unsigned int regn; |
| 401 | Dwarf_Unsigned regn; | 302 | Dwarf_Word offs = 0; |
| 402 | Dwarf_Signed offs; | ||
| 403 | int deref = 0, ret; | 303 | int deref = 0, ret; |
| 404 | const char *regs; | 304 | const char *regs; |
| 405 | 305 | ||
| 406 | op = loc->lr_atom; | 306 | /* TODO: support CFA */ |
| 407 | |||
| 408 | /* If this is based on frame buffer, set the offset */ | 307 | /* If this is based on frame buffer, set the offset */ |
| 409 | if (op == DW_OP_fbreg) { | 308 | if (op->atom == DW_OP_fbreg) { |
| 309 | if (pf->fb_ops == NULL) | ||
| 310 | die("The attribute of frame base is not supported.\n"); | ||
| 410 | deref = 1; | 311 | deref = 1; |
| 411 | offs = (Dwarf_Signed)loc->lr_number; | 312 | offs = op->number; |
| 412 | op = pf->fbloc.ld_s[0].lr_atom; | 313 | op = &pf->fb_ops[0]; |
| 413 | loc = &pf->fbloc.ld_s[0]; | 314 | } |
| 414 | } else | ||
| 415 | offs = 0; | ||
| 416 | 315 | ||
| 417 | if (op >= DW_OP_breg0 && op <= DW_OP_breg31) { | 316 | if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) { |
| 418 | regn = op - DW_OP_breg0; | 317 | regn = op->atom - DW_OP_breg0; |
| 419 | offs += (Dwarf_Signed)loc->lr_number; | 318 | offs += op->number; |
| 420 | deref = 1; | 319 | deref = 1; |
| 421 | } else if (op >= DW_OP_reg0 && op <= DW_OP_reg31) { | 320 | } else if (op->atom >= DW_OP_reg0 && op->atom <= DW_OP_reg31) { |
| 422 | regn = op - DW_OP_reg0; | 321 | regn = op->atom - DW_OP_reg0; |
| 423 | } else if (op == DW_OP_bregx) { | 322 | } else if (op->atom == DW_OP_bregx) { |
| 424 | regn = loc->lr_number; | 323 | regn = op->number; |
| 425 | offs += (Dwarf_Signed)loc->lr_number2; | 324 | offs += op->number2; |
| 426 | deref = 1; | 325 | deref = 1; |
| 427 | } else if (op == DW_OP_regx) { | 326 | } else if (op->atom == DW_OP_regx) { |
| 428 | regn = loc->lr_number; | 327 | regn = op->number; |
| 429 | } else | 328 | } else |
| 430 | die("Dwarf_OP %d is not supported.", op); | 329 | die("DW_OP %d is not supported.", op->atom); |
| 431 | 330 | ||
| 432 | regs = get_arch_regstr(regn); | 331 | regs = get_arch_regstr(regn); |
| 433 | if (!regs) | 332 | if (!regs) |
| 434 | die("%lld exceeds max register number.", regn); | 333 | die("%u exceeds max register number.", regn); |
| 435 | 334 | ||
| 436 | if (deref) | 335 | if (deref) |
| 437 | ret = snprintf(pf->buf, pf->len, | 336 | ret = snprintf(pf->buf, pf->len, " %s=%+jd(%s)", |
| 438 | " %s=%+lld(%s)", pf->var, offs, regs); | 337 | pf->var, (intmax_t)offs, regs); |
| 439 | else | 338 | else |
| 440 | ret = snprintf(pf->buf, pf->len, " %s=%s", pf->var, regs); | 339 | ret = snprintf(pf->buf, pf->len, " %s=%s", pf->var, regs); |
| 441 | DIE_IF(ret < 0); | 340 | DIE_IF(ret < 0); |
| @@ -443,52 +342,36 @@ static void show_location(Dwarf_Loc *loc, struct probe_finder *pf) | |||
| 443 | } | 342 | } |
| 444 | 343 | ||
| 445 | /* Show a variables in kprobe event format */ | 344 | /* Show a variables in kprobe event format */ |
| 446 | static void show_variable(Dwarf_Die vr_die, struct probe_finder *pf) | 345 | static void show_variable(Dwarf_Die *vr_die, struct probe_finder *pf) |
| 447 | { | 346 | { |
| 448 | Dwarf_Attribute attr; | 347 | Dwarf_Attribute attr; |
| 449 | Dwarf_Locdesc ld; | 348 | Dwarf_Op *expr; |
| 349 | size_t nexpr; | ||
| 450 | int ret; | 350 | int ret; |
| 451 | 351 | ||
| 452 | ret = dwarf_attr(vr_die, DW_AT_location, &attr, &__dw_error); | 352 | if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL) |
| 453 | if (ret != DW_DLV_OK) | ||
| 454 | goto error; | 353 | goto error; |
| 455 | ret = attr_get_locdesc(attr, &ld, (pf->addr - pf->cu_base)); | 354 | /* TODO: handle more than 1 exprs */ |
| 456 | if (ret != DW_DLV_OK) | 355 | ret = dwarf_getlocation_addr(&attr, pf->addr, &expr, &nexpr, 1); |
| 356 | if (ret <= 0 || nexpr == 0) | ||
| 457 | goto error; | 357 | goto error; |
| 458 | /* TODO? */ | 358 | |
| 459 | DIE_IF(ld.ld_cents != 1); | 359 | show_location(expr, pf); |
| 460 | show_location(&ld.ld_s[0], pf); | 360 | /* *expr will be cached in libdw. Don't free it. */ |
| 461 | free(ld.ld_s); | ||
| 462 | dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR); | ||
| 463 | return ; | 361 | return ; |
| 464 | error: | 362 | error: |
| 363 | /* TODO: Support const_value */ | ||
| 465 | die("Failed to find the location of %s at this address.\n" | 364 | die("Failed to find the location of %s at this address.\n" |
| 466 | " Perhaps, it has been optimized out.", pf->var); | 365 | " Perhaps, it has been optimized out.", pf->var); |
| 467 | } | 366 | } |
| 468 | 367 | ||
| 469 | static int variable_callback(struct die_link *dlink, void *data) | ||
| 470 | { | ||
| 471 | struct probe_finder *pf = (struct probe_finder *)data; | ||
| 472 | Dwarf_Half tag; | ||
| 473 | int ret; | ||
| 474 | |||
| 475 | ret = dwarf_tag(dlink->die, &tag, &__dw_error); | ||
| 476 | DIE_IF(ret == DW_DLV_ERROR); | ||
| 477 | if ((tag == DW_TAG_formal_parameter || | ||
| 478 | tag == DW_TAG_variable) && | ||
| 479 | (die_compare_name(dlink->die, pf->var) == 0)) { | ||
| 480 | show_variable(dlink->die, pf); | ||
| 481 | return 1; | ||
| 482 | } | ||
| 483 | /* TODO: Support struct members and arrays */ | ||
| 484 | return 0; | ||
| 485 | } | ||
| 486 | |||
| 487 | /* Find a variable in a subprogram die */ | 368 | /* Find a variable in a subprogram die */ |
| 488 | static void find_variable(Dwarf_Die sp_die, struct probe_finder *pf) | 369 | static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) |
| 489 | { | 370 | { |
| 490 | int ret; | 371 | int ret; |
| 372 | Dwarf_Die vr_die; | ||
| 491 | 373 | ||
| 374 | /* TODO: Support struct members and arrays */ | ||
| 492 | if (!is_c_varname(pf->var)) { | 375 | if (!is_c_varname(pf->var)) { |
| 493 | /* Output raw parameters */ | 376 | /* Output raw parameters */ |
| 494 | ret = snprintf(pf->buf, pf->len, " %s", pf->var); | 377 | ret = snprintf(pf->buf, pf->len, " %s", pf->var); |
| @@ -499,58 +382,51 @@ static void find_variable(Dwarf_Die sp_die, struct probe_finder *pf) | |||
| 499 | 382 | ||
| 500 | pr_debug("Searching '%s' variable in context.\n", pf->var); | 383 | pr_debug("Searching '%s' variable in context.\n", pf->var); |
| 501 | /* Search child die for local variables and parameters. */ | 384 | /* Search child die for local variables and parameters. */ |
| 502 | ret = search_die_from_children(sp_die, variable_callback, pf); | 385 | if (!die_find_variable(sp_die, pf->var, &vr_die)) |
| 503 | if (!ret) | ||
| 504 | die("Failed to find '%s' in this function.", pf->var); | 386 | die("Failed to find '%s' in this function.", pf->var); |
| 505 | } | ||
| 506 | |||
| 507 | /* Get a frame base on the address */ | ||
| 508 | static void get_current_frame_base(Dwarf_Die sp_die, struct probe_finder *pf) | ||
| 509 | { | ||
| 510 | Dwarf_Attribute attr; | ||
| 511 | int ret; | ||
| 512 | |||
| 513 | ret = dwarf_attr(sp_die, DW_AT_frame_base, &attr, &__dw_error); | ||
| 514 | DIE_IF(ret != DW_DLV_OK); | ||
| 515 | ret = attr_get_locdesc(attr, &pf->fbloc, (pf->addr - pf->cu_base)); | ||
| 516 | DIE_IF(ret != DW_DLV_OK); | ||
| 517 | dwarf_dealloc(__dw_debug, attr, DW_DLA_ATTR); | ||
| 518 | } | ||
| 519 | 387 | ||
| 520 | static void free_current_frame_base(struct probe_finder *pf) | 388 | show_variable(&vr_die, pf); |
| 521 | { | ||
| 522 | free(pf->fbloc.ld_s); | ||
| 523 | memset(&pf->fbloc, 0, sizeof(Dwarf_Locdesc)); | ||
| 524 | } | 389 | } |
| 525 | 390 | ||
| 526 | /* Show a probe point to output buffer */ | 391 | /* Show a probe point to output buffer */ |
| 527 | static void show_probepoint(Dwarf_Die sp_die, Dwarf_Signed offs, | 392 | static void show_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) |
| 528 | struct probe_finder *pf) | ||
| 529 | { | 393 | { |
| 530 | struct probe_point *pp = pf->pp; | 394 | struct probe_point *pp = pf->pp; |
| 531 | char *name; | 395 | Dwarf_Addr eaddr; |
| 396 | Dwarf_Die die_mem; | ||
| 397 | const char *name; | ||
| 532 | char tmp[MAX_PROBE_BUFFER]; | 398 | char tmp[MAX_PROBE_BUFFER]; |
| 533 | int ret, i, len; | 399 | int ret, i, len; |
| 400 | Dwarf_Attribute fb_attr; | ||
| 401 | size_t nops; | ||
| 402 | |||
| 403 | /* If no real subprogram, find a real one */ | ||
| 404 | if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) { | ||
| 405 | sp_die = die_get_real_subprogram(&pf->cu_die, | ||
| 406 | pf->addr, &die_mem); | ||
| 407 | if (!sp_die) | ||
| 408 | die("Probe point is not found in subprograms."); | ||
| 409 | } | ||
| 534 | 410 | ||
| 535 | /* Output name of probe point */ | 411 | /* Output name of probe point */ |
| 536 | ret = dwarf_diename(sp_die, &name, &__dw_error); | 412 | name = dwarf_diename(sp_die); |
| 537 | DIE_IF(ret == DW_DLV_ERROR); | 413 | if (name) { |
| 538 | if (ret == DW_DLV_OK) { | 414 | dwarf_entrypc(sp_die, &eaddr); |
| 539 | ret = snprintf(tmp, MAX_PROBE_BUFFER, "%s+%u", name, | 415 | ret = snprintf(tmp, MAX_PROBE_BUFFER, "%s+%lu", name, |
| 540 | (unsigned int)offs); | 416 | (unsigned long)(pf->addr - eaddr)); |
| 541 | /* Copy the function name if possible */ | 417 | /* Copy the function name if possible */ |
| 542 | if (!pp->function) { | 418 | if (!pp->function) { |
| 543 | pp->function = strdup(name); | 419 | pp->function = strdup(name); |
| 544 | pp->offset = offs; | 420 | pp->offset = (size_t)(pf->addr - eaddr); |
| 545 | } | 421 | } |
| 546 | dwarf_dealloc(__dw_debug, name, DW_DLA_STRING); | ||
| 547 | } else { | 422 | } else { |
| 548 | /* This function has no name. */ | 423 | /* This function has no name. */ |
| 549 | ret = snprintf(tmp, MAX_PROBE_BUFFER, "0x%llx", pf->addr); | 424 | ret = snprintf(tmp, MAX_PROBE_BUFFER, "0x%jx", |
| 425 | (uintmax_t)pf->addr); | ||
| 550 | if (!pp->function) { | 426 | if (!pp->function) { |
| 551 | /* TODO: Use _stext */ | 427 | /* TODO: Use _stext */ |
| 552 | pp->function = strdup(""); | 428 | pp->function = strdup(""); |
| 553 | pp->offset = (int)pf->addr; | 429 | pp->offset = (size_t)pf->addr; |
| 554 | } | 430 | } |
| 555 | } | 431 | } |
| 556 | DIE_IF(ret < 0); | 432 | DIE_IF(ret < 0); |
| @@ -558,8 +434,14 @@ static void show_probepoint(Dwarf_Die sp_die, Dwarf_Signed offs, | |||
| 558 | len = ret; | 434 | len = ret; |
| 559 | pr_debug("Probe point found: %s\n", tmp); | 435 | pr_debug("Probe point found: %s\n", tmp); |
| 560 | 436 | ||
| 437 | /* Get the frame base attribute/ops */ | ||
| 438 | dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr); | ||
| 439 | ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1); | ||
| 440 | if (ret <= 0 || nops == 0) | ||
| 441 | pf->fb_ops = NULL; | ||
| 442 | |||
| 561 | /* Find each argument */ | 443 | /* Find each argument */ |
| 562 | get_current_frame_base(sp_die, pf); | 444 | /* TODO: use dwarf_cfi_addrframe */ |
| 563 | for (i = 0; i < pp->nr_args; i++) { | 445 | for (i = 0; i < pp->nr_args; i++) { |
| 564 | pf->var = pp->args[i]; | 446 | pf->var = pp->args[i]; |
| 565 | pf->buf = &tmp[len]; | 447 | pf->buf = &tmp[len]; |
| @@ -567,289 +449,325 @@ static void show_probepoint(Dwarf_Die sp_die, Dwarf_Signed offs, | |||
| 567 | find_variable(sp_die, pf); | 449 | find_variable(sp_die, pf); |
| 568 | len += strlen(pf->buf); | 450 | len += strlen(pf->buf); |
| 569 | } | 451 | } |
| 570 | free_current_frame_base(pf); | 452 | |
| 453 | /* *pf->fb_ops will be cached in libdw. Don't free it. */ | ||
| 454 | pf->fb_ops = NULL; | ||
| 455 | |||
| 456 | if (pp->found == MAX_PROBES) | ||
| 457 | die("Too many( > %d) probe point found.\n", MAX_PROBES); | ||
| 571 | 458 | ||
| 572 | pp->probes[pp->found] = strdup(tmp); | 459 | pp->probes[pp->found] = strdup(tmp); |
| 573 | pp->found++; | 460 | pp->found++; |
| 574 | } | 461 | } |
| 575 | 462 | ||
| 576 | static int probeaddr_callback(struct die_link *dlink, void *data) | 463 | /* Find probe point from its line number */ |
| 464 | static void find_probe_point_by_line(struct probe_finder *pf) | ||
| 577 | { | 465 | { |
| 578 | struct probe_finder *pf = (struct probe_finder *)data; | 466 | Dwarf_Lines *lines; |
| 579 | Dwarf_Half tag; | 467 | Dwarf_Line *line; |
| 580 | Dwarf_Signed offs; | 468 | size_t nlines, i; |
| 469 | Dwarf_Addr addr; | ||
| 470 | int lineno; | ||
| 581 | int ret; | 471 | int ret; |
| 582 | 472 | ||
| 583 | ret = dwarf_tag(dlink->die, &tag, &__dw_error); | 473 | ret = dwarf_getsrclines(&pf->cu_die, &lines, &nlines); |
| 584 | DIE_IF(ret == DW_DLV_ERROR); | 474 | DIE_IF(ret != 0); |
| 585 | /* Check the address is in this subprogram */ | 475 | |
| 586 | if (tag == DW_TAG_subprogram && | 476 | for (i = 0; i < nlines; i++) { |
| 587 | die_within_subprogram(dlink->die, pf->addr, &offs)) { | 477 | line = dwarf_onesrcline(lines, i); |
| 588 | show_probepoint(dlink->die, offs, pf); | 478 | dwarf_lineno(line, &lineno); |
| 589 | return 1; | 479 | if (lineno != pf->lno) |
| 480 | continue; | ||
| 481 | |||
| 482 | /* TODO: Get fileno from line, but how? */ | ||
| 483 | if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) | ||
| 484 | continue; | ||
| 485 | |||
| 486 | ret = dwarf_lineaddr(line, &addr); | ||
| 487 | DIE_IF(ret != 0); | ||
| 488 | pr_debug("Probe line found: line[%d]:%d addr:0x%jx\n", | ||
| 489 | (int)i, lineno, (uintmax_t)addr); | ||
| 490 | pf->addr = addr; | ||
| 491 | |||
| 492 | show_probe_point(NULL, pf); | ||
| 493 | /* Continuing, because target line might be inlined. */ | ||
| 590 | } | 494 | } |
| 591 | return 0; | ||
| 592 | } | 495 | } |
| 593 | 496 | ||
| 594 | /* Find probe point from its line number */ | 497 | /* Find lines which match lazy pattern */ |
| 595 | static void find_probe_point_by_line(struct probe_finder *pf) | 498 | static int find_lazy_match_lines(struct list_head *head, |
| 499 | const char *fname, const char *pat) | ||
| 500 | { | ||
| 501 | char *fbuf, *p1, *p2; | ||
| 502 | int fd, line, nlines = 0; | ||
| 503 | struct stat st; | ||
| 504 | |||
| 505 | fd = open(fname, O_RDONLY); | ||
| 506 | if (fd < 0) | ||
| 507 | die("failed to open %s", fname); | ||
| 508 | DIE_IF(fstat(fd, &st) < 0); | ||
| 509 | fbuf = malloc(st.st_size + 2); | ||
| 510 | DIE_IF(fbuf == NULL); | ||
| 511 | DIE_IF(read(fd, fbuf, st.st_size) < 0); | ||
| 512 | close(fd); | ||
| 513 | fbuf[st.st_size] = '\n'; /* Dummy line */ | ||
| 514 | fbuf[st.st_size + 1] = '\0'; | ||
| 515 | p1 = fbuf; | ||
| 516 | line = 1; | ||
| 517 | while ((p2 = strchr(p1, '\n')) != NULL) { | ||
| 518 | *p2 = '\0'; | ||
| 519 | if (strlazymatch(p1, pat)) { | ||
| 520 | line_list__add_line(head, line); | ||
| 521 | nlines++; | ||
| 522 | } | ||
| 523 | line++; | ||
| 524 | p1 = p2 + 1; | ||
| 525 | } | ||
| 526 | free(fbuf); | ||
| 527 | return nlines; | ||
| 528 | } | ||
| 529 | |||
| 530 | /* Find probe points from lazy pattern */ | ||
| 531 | static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) | ||
| 596 | { | 532 | { |
| 597 | Dwarf_Signed cnt, i, clm; | 533 | Dwarf_Lines *lines; |
| 598 | Dwarf_Line *lines; | 534 | Dwarf_Line *line; |
| 599 | Dwarf_Unsigned lineno = 0; | 535 | size_t nlines, i; |
| 600 | Dwarf_Addr addr; | 536 | Dwarf_Addr addr; |
| 601 | Dwarf_Unsigned fno; | 537 | Dwarf_Die die_mem; |
| 538 | int lineno; | ||
| 602 | int ret; | 539 | int ret; |
| 603 | 540 | ||
| 604 | ret = dwarf_srclines(pf->cu_die, &lines, &cnt, &__dw_error); | 541 | if (list_empty(&pf->lcache)) { |
| 605 | DIE_IF(ret != DW_DLV_OK); | 542 | /* Matching lazy line pattern */ |
| 543 | ret = find_lazy_match_lines(&pf->lcache, pf->fname, | ||
| 544 | pf->pp->lazy_line); | ||
| 545 | if (ret <= 0) | ||
| 546 | die("No matched lines found in %s.", pf->fname); | ||
| 547 | } | ||
| 606 | 548 | ||
| 607 | for (i = 0; i < cnt; i++) { | 549 | ret = dwarf_getsrclines(&pf->cu_die, &lines, &nlines); |
| 608 | ret = dwarf_line_srcfileno(lines[i], &fno, &__dw_error); | 550 | DIE_IF(ret != 0); |
| 609 | DIE_IF(ret != DW_DLV_OK); | 551 | for (i = 0; i < nlines; i++) { |
| 610 | if (fno != pf->fno) | 552 | line = dwarf_onesrcline(lines, i); |
| 553 | |||
| 554 | dwarf_lineno(line, &lineno); | ||
| 555 | if (!line_list__has_line(&pf->lcache, lineno)) | ||
| 611 | continue; | 556 | continue; |
| 612 | 557 | ||
| 613 | ret = dwarf_lineno(lines[i], &lineno, &__dw_error); | 558 | /* TODO: Get fileno from line, but how? */ |
| 614 | DIE_IF(ret != DW_DLV_OK); | 559 | if (strtailcmp(dwarf_linesrc(line, NULL, NULL), pf->fname) != 0) |
| 615 | if (lineno != pf->lno) | ||
| 616 | continue; | 560 | continue; |
| 617 | 561 | ||
| 618 | ret = dwarf_lineoff(lines[i], &clm, &__dw_error); | 562 | ret = dwarf_lineaddr(line, &addr); |
| 619 | DIE_IF(ret != DW_DLV_OK); | 563 | DIE_IF(ret != 0); |
| 564 | if (sp_die) { | ||
| 565 | /* Address filtering 1: does sp_die include addr? */ | ||
| 566 | if (!dwarf_haspc(sp_die, addr)) | ||
| 567 | continue; | ||
| 568 | /* Address filtering 2: No child include addr? */ | ||
| 569 | if (die_get_inlinefunc(sp_die, addr, &die_mem)) | ||
| 570 | continue; | ||
| 571 | } | ||
| 620 | 572 | ||
| 621 | ret = dwarf_lineaddr(lines[i], &addr, &__dw_error); | 573 | pr_debug("Probe line found: line[%d]:%d addr:0x%llx\n", |
| 622 | DIE_IF(ret != DW_DLV_OK); | 574 | (int)i, lineno, (unsigned long long)addr); |
| 623 | pr_debug("Probe line found: line[%d]:%u,%d addr:0x%llx\n", | ||
| 624 | (int)i, (unsigned)lineno, (int)clm, addr); | ||
| 625 | pf->addr = addr; | 575 | pf->addr = addr; |
| 626 | /* Search a real subprogram including this line, */ | 576 | |
| 627 | ret = search_die_from_children(pf->cu_die, | 577 | show_probe_point(sp_die, pf); |
| 628 | probeaddr_callback, pf); | ||
| 629 | if (ret == 0) | ||
| 630 | die("Probe point is not found in subprograms."); | ||
| 631 | /* Continuing, because target line might be inlined. */ | 578 | /* Continuing, because target line might be inlined. */ |
| 632 | } | 579 | } |
| 633 | dwarf_srclines_dealloc(__dw_debug, lines, cnt); | 580 | /* TODO: deallocate lines, but how? */ |
| 581 | } | ||
| 582 | |||
| 583 | static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) | ||
| 584 | { | ||
| 585 | struct probe_finder *pf = (struct probe_finder *)data; | ||
| 586 | struct probe_point *pp = pf->pp; | ||
| 587 | |||
| 588 | if (pp->lazy_line) | ||
| 589 | find_probe_point_lazy(in_die, pf); | ||
| 590 | else { | ||
| 591 | /* Get probe address */ | ||
| 592 | pf->addr = die_get_entrypc(in_die); | ||
| 593 | pf->addr += pp->offset; | ||
| 594 | pr_debug("found inline addr: 0x%jx\n", | ||
| 595 | (uintmax_t)pf->addr); | ||
| 596 | |||
| 597 | show_probe_point(in_die, pf); | ||
| 598 | } | ||
| 599 | |||
| 600 | return DWARF_CB_OK; | ||
| 634 | } | 601 | } |
| 635 | 602 | ||
| 636 | /* Search function from function name */ | 603 | /* Search function from function name */ |
| 637 | static int probefunc_callback(struct die_link *dlink, void *data) | 604 | static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) |
| 638 | { | 605 | { |
| 639 | struct probe_finder *pf = (struct probe_finder *)data; | 606 | struct probe_finder *pf = (struct probe_finder *)data; |
| 640 | struct probe_point *pp = pf->pp; | 607 | struct probe_point *pp = pf->pp; |
| 641 | struct die_link *lk; | ||
| 642 | Dwarf_Signed offs; | ||
| 643 | Dwarf_Half tag; | ||
| 644 | int ret; | ||
| 645 | 608 | ||
| 646 | ret = dwarf_tag(dlink->die, &tag, &__dw_error); | 609 | /* Check tag and diename */ |
| 647 | DIE_IF(ret == DW_DLV_ERROR); | 610 | if (dwarf_tag(sp_die) != DW_TAG_subprogram || |
| 648 | if (tag == DW_TAG_subprogram) { | 611 | die_compare_name(sp_die, pp->function) != 0) |
| 649 | if (die_compare_name(dlink->die, pp->function) == 0) { | 612 | return 0; |
| 650 | if (pp->line) { /* Function relative line */ | 613 | |
| 651 | pf->fno = die_get_decl_file(dlink->die); | 614 | pf->fname = dwarf_decl_file(sp_die); |
| 652 | pf->lno = die_get_decl_line(dlink->die) | 615 | if (pp->line) { /* Function relative line */ |
| 653 | + pp->line; | 616 | dwarf_decl_line(sp_die, &pf->lno); |
| 654 | find_probe_point_by_line(pf); | 617 | pf->lno += pp->line; |
| 655 | return 1; | 618 | find_probe_point_by_line(pf); |
| 656 | } | 619 | } else if (!dwarf_func_inline(sp_die)) { |
| 657 | if (die_inlined_subprogram(dlink->die)) { | 620 | /* Real function */ |
| 658 | /* Inlined function, save it. */ | 621 | if (pp->lazy_line) |
| 659 | ret = dwarf_die_CU_offset(dlink->die, | 622 | find_probe_point_lazy(sp_die, pf); |
| 660 | &pf->inl_offs, | 623 | else { |
| 661 | &__dw_error); | 624 | pf->addr = die_get_entrypc(sp_die); |
| 662 | DIE_IF(ret != DW_DLV_OK); | ||
| 663 | pr_debug("inline definition offset %lld\n", | ||
| 664 | pf->inl_offs); | ||
| 665 | return 0; /* Continue to search */ | ||
| 666 | } | ||
| 667 | /* Get probe address */ | ||
| 668 | pf->addr = die_get_entrypc(dlink->die); | ||
| 669 | pf->addr += pp->offset; | 625 | pf->addr += pp->offset; |
| 670 | /* TODO: Check the address in this function */ | 626 | /* TODO: Check the address in this function */ |
| 671 | show_probepoint(dlink->die, pp->offset, pf); | 627 | show_probe_point(sp_die, pf); |
| 672 | return 1; /* Exit; no same symbol in this CU. */ | ||
| 673 | } | 628 | } |
| 674 | } else if (tag == DW_TAG_inlined_subroutine && pf->inl_offs) { | 629 | } else |
| 675 | if (die_get_abstract_origin(dlink->die) == pf->inl_offs) { | 630 | /* Inlined function: search instances */ |
| 676 | /* Get probe address */ | 631 | dwarf_func_inline_instances(sp_die, probe_point_inline_cb, pf); |
| 677 | pf->addr = die_get_entrypc(dlink->die); | 632 | |
| 678 | pf->addr += pp->offset; | 633 | return 1; /* Exit; no same symbol in this CU. */ |
| 679 | pr_debug("found inline addr: 0x%llx\n", pf->addr); | ||
| 680 | /* Inlined function. Get a real subprogram */ | ||
| 681 | for (lk = dlink->parent; lk != NULL; lk = lk->parent) { | ||
| 682 | tag = 0; | ||
| 683 | dwarf_tag(lk->die, &tag, &__dw_error); | ||
| 684 | DIE_IF(ret == DW_DLV_ERROR); | ||
| 685 | if (tag == DW_TAG_subprogram && | ||
| 686 | !die_inlined_subprogram(lk->die)) | ||
| 687 | goto found; | ||
| 688 | } | ||
| 689 | die("Failed to find real subprogram."); | ||
| 690 | found: | ||
| 691 | /* Get offset from subprogram */ | ||
| 692 | ret = die_within_subprogram(lk->die, pf->addr, &offs); | ||
| 693 | DIE_IF(!ret); | ||
| 694 | show_probepoint(lk->die, offs, pf); | ||
| 695 | /* Continue to search */ | ||
| 696 | } | ||
| 697 | } | ||
| 698 | return 0; | ||
| 699 | } | 634 | } |
| 700 | 635 | ||
| 701 | static void find_probe_point_by_func(struct probe_finder *pf) | 636 | static void find_probe_point_by_func(struct probe_finder *pf) |
| 702 | { | 637 | { |
| 703 | search_die_from_children(pf->cu_die, probefunc_callback, pf); | 638 | dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, pf, 0); |
| 704 | } | 639 | } |
| 705 | 640 | ||
| 706 | /* Find a probe point */ | 641 | /* Find a probe point */ |
| 707 | int find_probepoint(int fd, struct probe_point *pp) | 642 | int find_probe_point(int fd, struct probe_point *pp) |
| 708 | { | 643 | { |
| 709 | Dwarf_Half addr_size = 0; | ||
| 710 | Dwarf_Unsigned next_cuh = 0; | ||
| 711 | int cu_number = 0, ret; | ||
| 712 | struct probe_finder pf = {.pp = pp}; | 644 | struct probe_finder pf = {.pp = pp}; |
| 645 | Dwarf_Off off, noff; | ||
| 646 | size_t cuhl; | ||
| 647 | Dwarf_Die *diep; | ||
| 648 | Dwarf *dbg; | ||
| 713 | 649 | ||
| 714 | ret = dwarf_init(fd, DW_DLC_READ, 0, 0, &__dw_debug, &__dw_error); | 650 | dbg = dwarf_begin(fd, DWARF_C_READ); |
| 715 | if (ret != DW_DLV_OK) | 651 | if (!dbg) |
| 716 | return -ENOENT; | 652 | return -ENOENT; |
| 717 | 653 | ||
| 718 | pp->found = 0; | 654 | pp->found = 0; |
| 719 | while (++cu_number) { | 655 | off = 0; |
| 720 | /* Search CU (Compilation Unit) */ | 656 | line_list__init(&pf.lcache); |
| 721 | ret = dwarf_next_cu_header(__dw_debug, NULL, NULL, NULL, | 657 | /* Loop on CUs (Compilation Unit) */ |
| 722 | &addr_size, &next_cuh, &__dw_error); | 658 | while (!dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL)) { |
| 723 | DIE_IF(ret == DW_DLV_ERROR); | ||
| 724 | if (ret == DW_DLV_NO_ENTRY) | ||
| 725 | break; | ||
| 726 | |||
| 727 | /* Get the DIE(Debugging Information Entry) of this CU */ | 659 | /* Get the DIE(Debugging Information Entry) of this CU */ |
| 728 | ret = dwarf_siblingof(__dw_debug, 0, &pf.cu_die, &__dw_error); | 660 | diep = dwarf_offdie(dbg, off + cuhl, &pf.cu_die); |
| 729 | DIE_IF(ret != DW_DLV_OK); | 661 | if (!diep) |
| 662 | continue; | ||
| 730 | 663 | ||
| 731 | /* Check if target file is included. */ | 664 | /* Check if target file is included. */ |
| 732 | if (pp->file) | 665 | if (pp->file) |
| 733 | pf.fno = cu_find_fileno(pf.cu_die, pp->file); | 666 | pf.fname = cu_find_realpath(&pf.cu_die, pp->file); |
| 734 | 667 | else | |
| 735 | if (!pp->file || pf.fno) { | 668 | pf.fname = NULL; |
| 736 | /* Save CU base address (for frame_base) */ | 669 | |
| 737 | ret = dwarf_lowpc(pf.cu_die, &pf.cu_base, &__dw_error); | 670 | if (!pp->file || pf.fname) { |
| 738 | DIE_IF(ret == DW_DLV_ERROR); | ||
| 739 | if (ret == DW_DLV_NO_ENTRY) | ||
| 740 | pf.cu_base = 0; | ||
| 741 | if (pp->function) | 671 | if (pp->function) |
| 742 | find_probe_point_by_func(&pf); | 672 | find_probe_point_by_func(&pf); |
| 673 | else if (pp->lazy_line) | ||
| 674 | find_probe_point_lazy(NULL, &pf); | ||
| 743 | else { | 675 | else { |
| 744 | pf.lno = pp->line; | 676 | pf.lno = pp->line; |
| 745 | find_probe_point_by_line(&pf); | 677 | find_probe_point_by_line(&pf); |
| 746 | } | 678 | } |
| 747 | } | 679 | } |
| 748 | dwarf_dealloc(__dw_debug, pf.cu_die, DW_DLA_DIE); | 680 | off = noff; |
| 749 | } | 681 | } |
| 750 | ret = dwarf_finish(__dw_debug, &__dw_error); | 682 | line_list__free(&pf.lcache); |
| 751 | DIE_IF(ret != DW_DLV_OK); | 683 | dwarf_end(dbg); |
| 752 | 684 | ||
| 753 | return pp->found; | 685 | return pp->found; |
| 754 | } | 686 | } |
| 755 | 687 | ||
| 756 | |||
| 757 | static void line_range_add_line(struct line_range *lr, unsigned int line) | ||
| 758 | { | ||
| 759 | struct line_node *ln; | ||
| 760 | struct list_head *p; | ||
| 761 | |||
| 762 | /* Reverse search, because new line will be the last one */ | ||
| 763 | list_for_each_entry_reverse(ln, &lr->line_list, list) { | ||
| 764 | if (ln->line < line) { | ||
| 765 | p = &ln->list; | ||
| 766 | goto found; | ||
| 767 | } else if (ln->line == line) /* Already exist */ | ||
| 768 | return ; | ||
| 769 | } | ||
| 770 | /* List is empty, or the smallest entry */ | ||
| 771 | p = &lr->line_list; | ||
| 772 | found: | ||
| 773 | pr_debug("Debug: add a line %u\n", line); | ||
| 774 | ln = zalloc(sizeof(struct line_node)); | ||
| 775 | DIE_IF(ln == NULL); | ||
| 776 | ln->line = line; | ||
| 777 | INIT_LIST_HEAD(&ln->list); | ||
| 778 | list_add(&ln->list, p); | ||
| 779 | } | ||
| 780 | |||
| 781 | /* Find line range from its line number */ | 688 | /* Find line range from its line number */ |
| 782 | static void find_line_range_by_line(struct line_finder *lf) | 689 | static void find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) |
| 783 | { | 690 | { |
| 784 | Dwarf_Signed cnt, i; | 691 | Dwarf_Lines *lines; |
| 785 | Dwarf_Line *lines; | 692 | Dwarf_Line *line; |
| 786 | Dwarf_Unsigned lineno = 0; | 693 | size_t nlines, i; |
| 787 | Dwarf_Unsigned fno; | ||
| 788 | Dwarf_Addr addr; | 694 | Dwarf_Addr addr; |
| 695 | int lineno; | ||
| 789 | int ret; | 696 | int ret; |
| 697 | const char *src; | ||
| 698 | Dwarf_Die die_mem; | ||
| 790 | 699 | ||
| 791 | ret = dwarf_srclines(lf->cu_die, &lines, &cnt, &__dw_error); | 700 | line_list__init(&lf->lr->line_list); |
| 792 | DIE_IF(ret != DW_DLV_OK); | 701 | ret = dwarf_getsrclines(&lf->cu_die, &lines, &nlines); |
| 702 | DIE_IF(ret != 0); | ||
| 793 | 703 | ||
| 794 | for (i = 0; i < cnt; i++) { | 704 | for (i = 0; i < nlines; i++) { |
| 795 | ret = dwarf_line_srcfileno(lines[i], &fno, &__dw_error); | 705 | line = dwarf_onesrcline(lines, i); |
| 796 | DIE_IF(ret != DW_DLV_OK); | 706 | ret = dwarf_lineno(line, &lineno); |
| 797 | if (fno != lf->fno) | 707 | DIE_IF(ret != 0); |
| 798 | continue; | ||
| 799 | |||
| 800 | ret = dwarf_lineno(lines[i], &lineno, &__dw_error); | ||
| 801 | DIE_IF(ret != DW_DLV_OK); | ||
| 802 | if (lf->lno_s > lineno || lf->lno_e < lineno) | 708 | if (lf->lno_s > lineno || lf->lno_e < lineno) |
| 803 | continue; | 709 | continue; |
| 804 | 710 | ||
| 805 | /* Filter line in the function address range */ | 711 | if (sp_die) { |
| 806 | if (lf->addr_s && lf->addr_e) { | 712 | /* Address filtering 1: does sp_die include addr? */ |
| 807 | ret = dwarf_lineaddr(lines[i], &addr, &__dw_error); | 713 | ret = dwarf_lineaddr(line, &addr); |
| 808 | DIE_IF(ret != DW_DLV_OK); | 714 | DIE_IF(ret != 0); |
| 809 | if (lf->addr_s > addr || lf->addr_e <= addr) | 715 | if (!dwarf_haspc(sp_die, addr)) |
| 716 | continue; | ||
| 717 | |||
| 718 | /* Address filtering 2: No child include addr? */ | ||
| 719 | if (die_get_inlinefunc(sp_die, addr, &die_mem)) | ||
| 810 | continue; | 720 | continue; |
| 811 | } | 721 | } |
| 812 | line_range_add_line(lf->lr, (unsigned int)lineno); | 722 | |
| 723 | /* TODO: Get fileno from line, but how? */ | ||
| 724 | src = dwarf_linesrc(line, NULL, NULL); | ||
| 725 | if (strtailcmp(src, lf->fname) != 0) | ||
| 726 | continue; | ||
| 727 | |||
| 728 | /* Copy real path */ | ||
| 729 | if (!lf->lr->path) | ||
| 730 | lf->lr->path = strdup(src); | ||
| 731 | line_list__add_line(&lf->lr->line_list, (unsigned int)lineno); | ||
| 813 | } | 732 | } |
| 814 | dwarf_srclines_dealloc(__dw_debug, lines, cnt); | 733 | /* Update status */ |
| 815 | if (!list_empty(&lf->lr->line_list)) | 734 | if (!list_empty(&lf->lr->line_list)) |
| 816 | lf->found = 1; | 735 | lf->found = 1; |
| 736 | else { | ||
| 737 | free(lf->lr->path); | ||
| 738 | lf->lr->path = NULL; | ||
| 739 | } | ||
| 740 | } | ||
| 741 | |||
| 742 | static int line_range_inline_cb(Dwarf_Die *in_die, void *data) | ||
| 743 | { | ||
| 744 | find_line_range_by_line(in_die, (struct line_finder *)data); | ||
| 745 | return DWARF_CB_ABORT; /* No need to find other instances */ | ||
| 817 | } | 746 | } |
| 818 | 747 | ||
| 819 | /* Search function from function name */ | 748 | /* Search function from function name */ |
| 820 | static int linefunc_callback(struct die_link *dlink, void *data) | 749 | static int line_range_search_cb(Dwarf_Die *sp_die, void *data) |
| 821 | { | 750 | { |
| 822 | struct line_finder *lf = (struct line_finder *)data; | 751 | struct line_finder *lf = (struct line_finder *)data; |
| 823 | struct line_range *lr = lf->lr; | 752 | struct line_range *lr = lf->lr; |
| 824 | Dwarf_Half tag; | ||
| 825 | int ret; | ||
| 826 | 753 | ||
| 827 | ret = dwarf_tag(dlink->die, &tag, &__dw_error); | 754 | if (dwarf_tag(sp_die) == DW_TAG_subprogram && |
| 828 | DIE_IF(ret == DW_DLV_ERROR); | 755 | die_compare_name(sp_die, lr->function) == 0) { |
| 829 | if (tag == DW_TAG_subprogram && | 756 | lf->fname = dwarf_decl_file(sp_die); |
| 830 | die_compare_name(dlink->die, lr->function) == 0) { | 757 | dwarf_decl_line(sp_die, &lr->offset); |
| 831 | /* Get the address range of this function */ | 758 | pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset); |
| 832 | ret = dwarf_highpc(dlink->die, &lf->addr_e, &__dw_error); | ||
| 833 | if (ret == DW_DLV_OK) | ||
| 834 | ret = dwarf_lowpc(dlink->die, &lf->addr_s, &__dw_error); | ||
| 835 | DIE_IF(ret == DW_DLV_ERROR); | ||
| 836 | if (ret == DW_DLV_NO_ENTRY) { | ||
| 837 | lf->addr_s = 0; | ||
| 838 | lf->addr_e = 0; | ||
| 839 | } | ||
| 840 | |||
| 841 | lf->fno = die_get_decl_file(dlink->die); | ||
| 842 | lr->offset = die_get_decl_line(dlink->die);; | ||
| 843 | lf->lno_s = lr->offset + lr->start; | 759 | lf->lno_s = lr->offset + lr->start; |
| 844 | if (!lr->end) | 760 | if (!lr->end) |
| 845 | lf->lno_e = (Dwarf_Unsigned)-1; | 761 | lf->lno_e = INT_MAX; |
| 846 | else | 762 | else |
| 847 | lf->lno_e = lr->offset + lr->end; | 763 | lf->lno_e = lr->offset + lr->end; |
| 848 | lr->start = lf->lno_s; | 764 | lr->start = lf->lno_s; |
| 849 | lr->end = lf->lno_e; | 765 | lr->end = lf->lno_e; |
| 850 | find_line_range_by_line(lf); | 766 | if (dwarf_func_inline(sp_die)) |
| 851 | /* If we find a target function, this should be end. */ | 767 | dwarf_func_inline_instances(sp_die, |
| 852 | lf->found = 1; | 768 | line_range_inline_cb, lf); |
| 769 | else | ||
| 770 | find_line_range_by_line(sp_die, lf); | ||
| 853 | return 1; | 771 | return 1; |
| 854 | } | 772 | } |
| 855 | return 0; | 773 | return 0; |
| @@ -857,55 +775,55 @@ static int linefunc_callback(struct die_link *dlink, void *data) | |||
| 857 | 775 | ||
| 858 | static void find_line_range_by_func(struct line_finder *lf) | 776 | static void find_line_range_by_func(struct line_finder *lf) |
| 859 | { | 777 | { |
| 860 | search_die_from_children(lf->cu_die, linefunc_callback, lf); | 778 | dwarf_getfuncs(&lf->cu_die, line_range_search_cb, lf, 0); |
| 861 | } | 779 | } |
| 862 | 780 | ||
| 863 | int find_line_range(int fd, struct line_range *lr) | 781 | int find_line_range(int fd, struct line_range *lr) |
| 864 | { | 782 | { |
| 865 | Dwarf_Half addr_size = 0; | 783 | struct line_finder lf = {.lr = lr, .found = 0}; |
| 866 | Dwarf_Unsigned next_cuh = 0; | ||
| 867 | int ret; | 784 | int ret; |
| 868 | struct line_finder lf = {.lr = lr}; | 785 | Dwarf_Off off = 0, noff; |
| 786 | size_t cuhl; | ||
| 787 | Dwarf_Die *diep; | ||
| 788 | Dwarf *dbg; | ||
| 869 | 789 | ||
| 870 | ret = dwarf_init(fd, DW_DLC_READ, 0, 0, &__dw_debug, &__dw_error); | 790 | dbg = dwarf_begin(fd, DWARF_C_READ); |
| 871 | if (ret != DW_DLV_OK) | 791 | if (!dbg) |
| 872 | return -ENOENT; | 792 | return -ENOENT; |
| 873 | 793 | ||
| 794 | /* Loop on CUs (Compilation Unit) */ | ||
| 874 | while (!lf.found) { | 795 | while (!lf.found) { |
| 875 | /* Search CU (Compilation Unit) */ | 796 | ret = dwarf_nextcu(dbg, off, &noff, &cuhl, NULL, NULL, NULL); |
| 876 | ret = dwarf_next_cu_header(__dw_debug, NULL, NULL, NULL, | 797 | if (ret != 0) |
| 877 | &addr_size, &next_cuh, &__dw_error); | ||
| 878 | DIE_IF(ret == DW_DLV_ERROR); | ||
| 879 | if (ret == DW_DLV_NO_ENTRY) | ||
| 880 | break; | 798 | break; |
| 881 | 799 | ||
| 882 | /* Get the DIE(Debugging Information Entry) of this CU */ | 800 | /* Get the DIE(Debugging Information Entry) of this CU */ |
| 883 | ret = dwarf_siblingof(__dw_debug, 0, &lf.cu_die, &__dw_error); | 801 | diep = dwarf_offdie(dbg, off + cuhl, &lf.cu_die); |
| 884 | DIE_IF(ret != DW_DLV_OK); | 802 | if (!diep) |
| 803 | continue; | ||
| 885 | 804 | ||
| 886 | /* Check if target file is included. */ | 805 | /* Check if target file is included. */ |
| 887 | if (lr->file) | 806 | if (lr->file) |
| 888 | lf.fno = cu_find_fileno(lf.cu_die, lr->file); | 807 | lf.fname = cu_find_realpath(&lf.cu_die, lr->file); |
| 808 | else | ||
| 809 | lf.fname = 0; | ||
| 889 | 810 | ||
| 890 | if (!lr->file || lf.fno) { | 811 | if (!lr->file || lf.fname) { |
| 891 | if (lr->function) | 812 | if (lr->function) |
| 892 | find_line_range_by_func(&lf); | 813 | find_line_range_by_func(&lf); |
| 893 | else { | 814 | else { |
| 894 | lf.lno_s = lr->start; | 815 | lf.lno_s = lr->start; |
| 895 | if (!lr->end) | 816 | if (!lr->end) |
| 896 | lf.lno_e = (Dwarf_Unsigned)-1; | 817 | lf.lno_e = INT_MAX; |
| 897 | else | 818 | else |
| 898 | lf.lno_e = lr->end; | 819 | lf.lno_e = lr->end; |
| 899 | find_line_range_by_line(&lf); | 820 | find_line_range_by_line(NULL, &lf); |
| 900 | } | 821 | } |
| 901 | /* Get the real file path */ | ||
| 902 | if (lf.found) | ||
| 903 | cu_get_filename(lf.cu_die, lf.fno, &lr->path); | ||
| 904 | } | 822 | } |
| 905 | dwarf_dealloc(__dw_debug, lf.cu_die, DW_DLA_DIE); | 823 | off = noff; |
| 906 | } | 824 | } |
| 907 | ret = dwarf_finish(__dw_debug, &__dw_error); | 825 | pr_debug("path: %lx\n", (unsigned long)lr->path); |
| 908 | DIE_IF(ret != DW_DLV_OK); | 826 | dwarf_end(dbg); |
| 909 | return lf.found; | 827 | return lf.found; |
| 910 | } | 828 | } |
| 911 | 829 | ||
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index 972b386116f1..21f7354397b4 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | #ifndef _PROBE_FINDER_H | 1 | #ifndef _PROBE_FINDER_H |
| 2 | #define _PROBE_FINDER_H | 2 | #define _PROBE_FINDER_H |
| 3 | 3 | ||
| 4 | #include <stdbool.h> | ||
| 4 | #include "util.h" | 5 | #include "util.h" |
| 5 | 6 | ||
| 6 | #define MAX_PATH_LEN 256 | 7 | #define MAX_PATH_LEN 256 |
| @@ -20,6 +21,7 @@ struct probe_point { | |||
| 20 | /* Inputs */ | 21 | /* Inputs */ |
| 21 | char *file; /* File name */ | 22 | char *file; /* File name */ |
| 22 | int line; /* Line number */ | 23 | int line; /* Line number */ |
| 24 | char *lazy_line; /* Lazy line pattern */ | ||
| 23 | 25 | ||
| 24 | char *function; /* Function name */ | 26 | char *function; /* Function name */ |
| 25 | int offset; /* Offset bytes */ | 27 | int offset; /* Offset bytes */ |
| @@ -46,53 +48,45 @@ struct line_range { | |||
| 46 | char *function; /* Function name */ | 48 | char *function; /* Function name */ |
| 47 | unsigned int start; /* Start line number */ | 49 | unsigned int start; /* Start line number */ |
| 48 | unsigned int end; /* End line number */ | 50 | unsigned int end; /* End line number */ |
| 49 | unsigned int offset; /* Start line offset */ | 51 | int offset; /* Start line offset */ |
| 50 | char *path; /* Real path name */ | 52 | char *path; /* Real path name */ |
| 51 | struct list_head line_list; /* Visible lines */ | 53 | struct list_head line_list; /* Visible lines */ |
| 52 | }; | 54 | }; |
| 53 | 55 | ||
| 54 | #ifndef NO_LIBDWARF | 56 | #ifndef NO_DWARF_SUPPORT |
| 55 | extern int find_probepoint(int fd, struct probe_point *pp); | 57 | extern int find_probe_point(int fd, struct probe_point *pp); |
| 56 | extern int find_line_range(int fd, struct line_range *lr); | 58 | extern int find_line_range(int fd, struct line_range *lr); |
| 57 | 59 | ||
| 58 | /* Workaround for undefined _MIPS_SZLONG bug in libdwarf.h: */ | ||
| 59 | #ifndef _MIPS_SZLONG | ||
| 60 | # define _MIPS_SZLONG 0 | ||
| 61 | #endif | ||
| 62 | |||
| 63 | #include <dwarf.h> | 60 | #include <dwarf.h> |
| 64 | #include <libdwarf.h> | 61 | #include <libdw.h> |
| 65 | 62 | ||
| 66 | struct probe_finder { | 63 | struct probe_finder { |
| 67 | struct probe_point *pp; /* Target probe point */ | 64 | struct probe_point *pp; /* Target probe point */ |
| 68 | 65 | ||
| 69 | /* For function searching */ | 66 | /* For function searching */ |
| 70 | Dwarf_Addr addr; /* Address */ | 67 | Dwarf_Addr addr; /* Address */ |
| 71 | Dwarf_Unsigned fno; /* File number */ | 68 | const char *fname; /* File name */ |
| 72 | Dwarf_Unsigned lno; /* Line number */ | 69 | int lno; /* Line number */ |
| 73 | Dwarf_Off inl_offs; /* Inline offset */ | 70 | Dwarf_Die cu_die; /* Current CU */ |
| 74 | Dwarf_Die cu_die; /* Current CU */ | ||
| 75 | 71 | ||
| 76 | /* For variable searching */ | 72 | /* For variable searching */ |
| 77 | Dwarf_Addr cu_base; /* Current CU base address */ | 73 | Dwarf_Op *fb_ops; /* Frame base attribute */ |
| 78 | Dwarf_Locdesc fbloc; /* Location of Current Frame Base */ | 74 | const char *var; /* Current variable name */ |
| 79 | const char *var; /* Current variable name */ | 75 | char *buf; /* Current output buffer */ |
| 80 | char *buf; /* Current output buffer */ | 76 | int len; /* Length of output buffer */ |
| 81 | int len; /* Length of output buffer */ | 77 | struct list_head lcache; /* Line cache for lazy match */ |
| 82 | }; | 78 | }; |
| 83 | 79 | ||
| 84 | struct line_finder { | 80 | struct line_finder { |
| 85 | struct line_range *lr; /* Target line range */ | 81 | struct line_range *lr; /* Target line range */ |
| 86 | 82 | ||
| 87 | Dwarf_Unsigned fno; /* File number */ | 83 | const char *fname; /* File name */ |
| 88 | Dwarf_Unsigned lno_s; /* Start line number */ | 84 | int lno_s; /* Start line number */ |
| 89 | Dwarf_Unsigned lno_e; /* End line number */ | 85 | int lno_e; /* End line number */ |
| 90 | Dwarf_Addr addr_s; /* Start address */ | 86 | Dwarf_Die cu_die; /* Current CU */ |
| 91 | Dwarf_Addr addr_e; /* End address */ | ||
| 92 | Dwarf_Die cu_die; /* Current CU */ | ||
| 93 | int found; | 87 | int found; |
| 94 | }; | 88 | }; |
| 95 | 89 | ||
| 96 | #endif /* NO_LIBDWARF */ | 90 | #endif /* NO_DWARF_SUPPORT */ |
| 97 | 91 | ||
| 98 | #endif /*_PROBE_FINDER_H */ | 92 | #endif /*_PROBE_FINDER_H */ |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 0de7258e70a5..eed1cb889008 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
| @@ -70,6 +70,7 @@ struct perf_session *perf_session__new(const char *filename, int mode, bool forc | |||
| 70 | 70 | ||
| 71 | memcpy(self->filename, filename, len); | 71 | memcpy(self->filename, filename, len); |
| 72 | self->threads = RB_ROOT; | 72 | self->threads = RB_ROOT; |
| 73 | self->stats_by_id = RB_ROOT; | ||
| 73 | self->last_match = NULL; | 74 | self->last_match = NULL; |
| 74 | self->mmap_window = 32; | 75 | self->mmap_window = 32; |
| 75 | self->cwd = NULL; | 76 | self->cwd = NULL; |
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 31950fcd8a4d..5c33417eebb3 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
| @@ -20,6 +20,7 @@ struct perf_session { | |||
| 20 | struct thread *last_match; | 20 | struct thread *last_match; |
| 21 | struct map *vmlinux_maps[MAP__NR_TYPES]; | 21 | struct map *vmlinux_maps[MAP__NR_TYPES]; |
| 22 | struct events_stats events_stats; | 22 | struct events_stats events_stats; |
| 23 | struct rb_root stats_by_id; | ||
| 23 | unsigned long event_total[PERF_RECORD_MAX]; | 24 | unsigned long event_total[PERF_RECORD_MAX]; |
| 24 | unsigned long unknown_events; | 25 | unsigned long unknown_events; |
| 25 | struct rb_root hists; | 26 | struct rb_root hists; |
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index c397d4f6f748..a175949ed216 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c | |||
| @@ -265,21 +265,21 @@ error: | |||
| 265 | return false; | 265 | return false; |
| 266 | } | 266 | } |
| 267 | 267 | ||
| 268 | /** | 268 | /* Glob/lazy pattern matching */ |
| 269 | * strglobmatch - glob expression pattern matching | 269 | static bool __match_glob(const char *str, const char *pat, bool ignore_space) |
| 270 | * @str: the target string to match | ||
| 271 | * @pat: the pattern string to match | ||
| 272 | * | ||
| 273 | * This returns true if the @str matches @pat. @pat can includes wildcards | ||
| 274 | * ('*','?') and character classes ([CHARS], complementation and ranges are | ||
| 275 | * also supported). Also, this supports escape character ('\') to use special | ||
| 276 | * characters as normal character. | ||
| 277 | * | ||
| 278 | * Note: if @pat syntax is broken, this always returns false. | ||
| 279 | */ | ||
| 280 | bool strglobmatch(const char *str, const char *pat) | ||
| 281 | { | 270 | { |
| 282 | while (*str && *pat && *pat != '*') { | 271 | while (*str && *pat && *pat != '*') { |
| 272 | if (ignore_space) { | ||
| 273 | /* Ignore spaces for lazy matching */ | ||
| 274 | if (isspace(*str)) { | ||
| 275 | str++; | ||
| 276 | continue; | ||
| 277 | } | ||
| 278 | if (isspace(*pat)) { | ||
| 279 | pat++; | ||
| 280 | continue; | ||
| 281 | } | ||
| 282 | } | ||
| 283 | if (*pat == '?') { /* Matches any single character */ | 283 | if (*pat == '?') { /* Matches any single character */ |
| 284 | str++; | 284 | str++; |
| 285 | pat++; | 285 | pat++; |
| @@ -308,3 +308,32 @@ bool strglobmatch(const char *str, const char *pat) | |||
| 308 | return !*str && !*pat; | 308 | return !*str && !*pat; |
| 309 | } | 309 | } |
| 310 | 310 | ||
| 311 | /** | ||
| 312 | * strglobmatch - glob expression pattern matching | ||
| 313 | * @str: the target string to match | ||
| 314 | * @pat: the pattern string to match | ||
| 315 | * | ||
| 316 | * This returns true if the @str matches @pat. @pat can includes wildcards | ||
| 317 | * ('*','?') and character classes ([CHARS], complementation and ranges are | ||
| 318 | * also supported). Also, this supports escape character ('\') to use special | ||
| 319 | * characters as normal character. | ||
| 320 | * | ||
| 321 | * Note: if @pat syntax is broken, this always returns false. | ||
| 322 | */ | ||
| 323 | bool strglobmatch(const char *str, const char *pat) | ||
| 324 | { | ||
| 325 | return __match_glob(str, pat, false); | ||
| 326 | } | ||
| 327 | |||
| 328 | /** | ||
| 329 | * strlazymatch - matching pattern strings lazily with glob pattern | ||
| 330 | * @str: the target string to match | ||
| 331 | * @pat: the pattern string to match | ||
| 332 | * | ||
| 333 | * This is similar to strglobmatch, except this ignores spaces in | ||
| 334 | * the target string. | ||
| 335 | */ | ||
| 336 | bool strlazymatch(const char *str, const char *pat) | ||
| 337 | { | ||
| 338 | return __match_glob(str, pat, true); | ||
| 339 | } | ||
diff --git a/tools/perf/util/string.h b/tools/perf/util/string.h index 02ede58c54b4..542e44de3719 100644 --- a/tools/perf/util/string.h +++ b/tools/perf/util/string.h | |||
| @@ -10,6 +10,7 @@ s64 perf_atoll(const char *str); | |||
| 10 | char **argv_split(const char *str, int *argcp); | 10 | char **argv_split(const char *str, int *argcp); |
| 11 | void argv_free(char **argv); | 11 | void argv_free(char **argv); |
| 12 | bool strglobmatch(const char *str, const char *pat); | 12 | bool strglobmatch(const char *str, const char *pat); |
| 13 | bool strlazymatch(const char *str, const char *pat); | ||
| 13 | 14 | ||
| 14 | #define _STR(x) #x | 15 | #define _STR(x) #x |
| 15 | #define STR(x) _STR(x) | 16 | #define STR(x) _STR(x) |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 323c0aea0a91..c458c4a371d1 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
| @@ -163,9 +163,17 @@ void dso__set_long_name(struct dso *self, char *name) | |||
| 163 | self->long_name_len = strlen(name); | 163 | self->long_name_len = strlen(name); |
| 164 | } | 164 | } |
| 165 | 165 | ||
| 166 | static void dso__set_short_name(struct dso *self, const char *name) | ||
| 167 | { | ||
| 168 | if (name == NULL) | ||
| 169 | return; | ||
| 170 | self->short_name = name; | ||
| 171 | self->short_name_len = strlen(name); | ||
| 172 | } | ||
| 173 | |||
| 166 | static void dso__set_basename(struct dso *self) | 174 | static void dso__set_basename(struct dso *self) |
| 167 | { | 175 | { |
| 168 | self->short_name = basename(self->long_name); | 176 | dso__set_short_name(self, basename(self->long_name)); |
| 169 | } | 177 | } |
| 170 | 178 | ||
| 171 | struct dso *dso__new(const char *name) | 179 | struct dso *dso__new(const char *name) |
| @@ -176,7 +184,7 @@ struct dso *dso__new(const char *name) | |||
| 176 | int i; | 184 | int i; |
| 177 | strcpy(self->name, name); | 185 | strcpy(self->name, name); |
| 178 | dso__set_long_name(self, self->name); | 186 | dso__set_long_name(self, self->name); |
| 179 | self->short_name = self->name; | 187 | dso__set_short_name(self, self->name); |
| 180 | for (i = 0; i < MAP__NR_TYPES; ++i) | 188 | for (i = 0; i < MAP__NR_TYPES; ++i) |
| 181 | self->symbols[i] = self->symbol_names[i] = RB_ROOT; | 189 | self->symbols[i] = self->symbol_names[i] = RB_ROOT; |
| 182 | self->slen_calculated = 0; | 190 | self->slen_calculated = 0; |
| @@ -897,7 +905,6 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, | |||
| 897 | struct kmap *kmap = self->kernel ? map__kmap(map) : NULL; | 905 | struct kmap *kmap = self->kernel ? map__kmap(map) : NULL; |
| 898 | struct map *curr_map = map; | 906 | struct map *curr_map = map; |
| 899 | struct dso *curr_dso = self; | 907 | struct dso *curr_dso = self; |
| 900 | size_t dso_name_len = strlen(self->short_name); | ||
| 901 | Elf_Data *symstrs, *secstrs; | 908 | Elf_Data *symstrs, *secstrs; |
| 902 | uint32_t nr_syms; | 909 | uint32_t nr_syms; |
| 903 | int err = -1; | 910 | int err = -1; |
| @@ -987,7 +994,8 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, | |||
| 987 | char dso_name[PATH_MAX]; | 994 | char dso_name[PATH_MAX]; |
| 988 | 995 | ||
| 989 | if (strcmp(section_name, | 996 | if (strcmp(section_name, |
| 990 | curr_dso->short_name + dso_name_len) == 0) | 997 | (curr_dso->short_name + |
| 998 | self->short_name_len)) == 0) | ||
| 991 | goto new_symbol; | 999 | goto new_symbol; |
| 992 | 1000 | ||
| 993 | if (strcmp(section_name, ".text") == 0) { | 1001 | if (strcmp(section_name, ".text") == 0) { |
| @@ -1782,7 +1790,7 @@ struct dso *dso__new_kernel(const char *name) | |||
| 1782 | struct dso *self = dso__new(name ?: "[kernel.kallsyms]"); | 1790 | struct dso *self = dso__new(name ?: "[kernel.kallsyms]"); |
| 1783 | 1791 | ||
| 1784 | if (self != NULL) { | 1792 | if (self != NULL) { |
| 1785 | self->short_name = "[kernel]"; | 1793 | dso__set_short_name(self, "[kernel]"); |
| 1786 | self->kernel = 1; | 1794 | self->kernel = 1; |
| 1787 | } | 1795 | } |
| 1788 | 1796 | ||
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 280dadd32a08..f30a37428919 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
| @@ -110,9 +110,10 @@ struct dso { | |||
| 110 | u8 sorted_by_name; | 110 | u8 sorted_by_name; |
| 111 | u8 loaded; | 111 | u8 loaded; |
| 112 | u8 build_id[BUILD_ID_SIZE]; | 112 | u8 build_id[BUILD_ID_SIZE]; |
| 113 | u16 long_name_len; | ||
| 114 | const char *short_name; | 113 | const char *short_name; |
| 115 | char *long_name; | 114 | char *long_name; |
| 115 | u16 long_name_len; | ||
| 116 | u16 short_name_len; | ||
| 116 | char name[0]; | 117 | char name[0]; |
| 117 | }; | 118 | }; |
| 118 | 119 | ||
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 21b92162282b..fa968312ee7d 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
| @@ -79,8 +79,8 @@ int thread__comm_len(struct thread *self) | |||
| 79 | return self->comm_len; | 79 | return self->comm_len; |
| 80 | } | 80 | } |
| 81 | 81 | ||
| 82 | static size_t __map_groups__fprintf_maps(struct map_groups *self, | 82 | size_t __map_groups__fprintf_maps(struct map_groups *self, |
| 83 | enum map_type type, FILE *fp) | 83 | enum map_type type, FILE *fp) |
| 84 | { | 84 | { |
| 85 | size_t printed = fprintf(fp, "%s:\n", map_type__name[type]); | 85 | size_t printed = fprintf(fp, "%s:\n", map_type__name[type]); |
| 86 | struct rb_node *nd; | 86 | struct rb_node *nd; |
| @@ -89,7 +89,7 @@ static size_t __map_groups__fprintf_maps(struct map_groups *self, | |||
| 89 | struct map *pos = rb_entry(nd, struct map, rb_node); | 89 | struct map *pos = rb_entry(nd, struct map, rb_node); |
| 90 | printed += fprintf(fp, "Map:"); | 90 | printed += fprintf(fp, "Map:"); |
| 91 | printed += map__fprintf(pos, fp); | 91 | printed += map__fprintf(pos, fp); |
| 92 | if (verbose > 1) { | 92 | if (verbose > 2) { |
| 93 | printed += dso__fprintf(pos->dso, type, fp); | 93 | printed += dso__fprintf(pos->dso, type, fp); |
| 94 | printed += fprintf(fp, "--\n"); | 94 | printed += fprintf(fp, "--\n"); |
| 95 | } | 95 | } |
| @@ -183,8 +183,8 @@ struct thread *perf_session__findnew(struct perf_session *self, pid_t pid) | |||
| 183 | return th; | 183 | return th; |
| 184 | } | 184 | } |
| 185 | 185 | ||
| 186 | static void map_groups__remove_overlappings(struct map_groups *self, | 186 | static int map_groups__fixup_overlappings(struct map_groups *self, |
| 187 | struct map *map) | 187 | struct map *map) |
| 188 | { | 188 | { |
| 189 | struct rb_root *root = &self->maps[map->type]; | 189 | struct rb_root *root = &self->maps[map->type]; |
| 190 | struct rb_node *next = rb_first(root); | 190 | struct rb_node *next = rb_first(root); |
| @@ -209,7 +209,36 @@ static void map_groups__remove_overlappings(struct map_groups *self, | |||
| 209 | * list. | 209 | * list. |
| 210 | */ | 210 | */ |
| 211 | list_add_tail(&pos->node, &self->removed_maps[map->type]); | 211 | list_add_tail(&pos->node, &self->removed_maps[map->type]); |
| 212 | /* | ||
| 213 | * Now check if we need to create new maps for areas not | ||
| 214 | * overlapped by the new map: | ||
| 215 | */ | ||
| 216 | if (map->start > pos->start) { | ||
| 217 | struct map *before = map__clone(pos); | ||
| 218 | |||
| 219 | if (before == NULL) | ||
| 220 | return -ENOMEM; | ||
| 221 | |||
| 222 | before->end = map->start - 1; | ||
| 223 | map_groups__insert(self, before); | ||
| 224 | if (verbose >= 2) | ||
| 225 | map__fprintf(before, stderr); | ||
| 226 | } | ||
| 227 | |||
| 228 | if (map->end < pos->end) { | ||
| 229 | struct map *after = map__clone(pos); | ||
| 230 | |||
| 231 | if (after == NULL) | ||
| 232 | return -ENOMEM; | ||
| 233 | |||
| 234 | after->start = map->end + 1; | ||
| 235 | map_groups__insert(self, after); | ||
| 236 | if (verbose >= 2) | ||
| 237 | map__fprintf(after, stderr); | ||
| 238 | } | ||
| 212 | } | 239 | } |
| 240 | |||
| 241 | return 0; | ||
| 213 | } | 242 | } |
| 214 | 243 | ||
| 215 | void maps__insert(struct rb_root *maps, struct map *map) | 244 | void maps__insert(struct rb_root *maps, struct map *map) |
| @@ -254,7 +283,7 @@ struct map *maps__find(struct rb_root *maps, u64 ip) | |||
| 254 | 283 | ||
| 255 | void thread__insert_map(struct thread *self, struct map *map) | 284 | void thread__insert_map(struct thread *self, struct map *map) |
| 256 | { | 285 | { |
| 257 | map_groups__remove_overlappings(&self->mg, map); | 286 | map_groups__fixup_overlappings(&self->mg, map); |
| 258 | map_groups__insert(&self->mg, map); | 287 | map_groups__insert(&self->mg, map); |
| 259 | } | 288 | } |
| 260 | 289 | ||
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 0a28f39de545..dcf70303e58e 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
| @@ -10,6 +10,9 @@ struct map_groups { | |||
| 10 | struct list_head removed_maps[MAP__NR_TYPES]; | 10 | struct list_head removed_maps[MAP__NR_TYPES]; |
| 11 | }; | 11 | }; |
| 12 | 12 | ||
| 13 | size_t __map_groups__fprintf_maps(struct map_groups *self, | ||
| 14 | enum map_type type, FILE *fp); | ||
| 15 | |||
| 13 | struct thread { | 16 | struct thread { |
| 14 | struct rb_node rb_node; | 17 | struct rb_node rb_node; |
| 15 | struct map_groups mg; | 18 | struct map_groups mg; |
