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 00000000000..4e01490e51e --- /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 00000000000..86c78bb3309 --- /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 50a7132887f..a33b94952e3 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 e8daf5ca6fd..2be33c7dbf0 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 e5f99b24048..16f360cce5b 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 8f056884969..7c004b6ef24 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 1b2124d12f6..c171a243d05 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 972b386116f..21f7354397b 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 0de7258e70a..eed1cb88900 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 31950fcd8a4..5c33417eebb 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 c397d4f6f74..a175949ed21 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 02ede58c54b..542e44de371 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 323c0aea0a9..c458c4a371d 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 280dadd32a0..f30a3742891 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 21b92162282..fa968312ee7 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 0a28f39de54..dcf70303e58 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; |