diff options
Diffstat (limited to 'tools/perf/util')
31 files changed, 2225 insertions, 810 deletions
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h index 918eb376abe3..4b9aab7f0405 100644 --- a/tools/perf/util/cache.h +++ b/tools/perf/util/cache.h | |||
@@ -1,6 +1,7 @@ | |||
1 | #ifndef __PERF_CACHE_H | 1 | #ifndef __PERF_CACHE_H |
2 | #define __PERF_CACHE_H | 2 | #define __PERF_CACHE_H |
3 | 3 | ||
4 | #include <stdbool.h> | ||
4 | #include "util.h" | 5 | #include "util.h" |
5 | #include "strbuf.h" | 6 | #include "strbuf.h" |
6 | #include "../perf.h" | 7 | #include "../perf.h" |
@@ -69,6 +70,19 @@ extern const char *pager_program; | |||
69 | extern int pager_in_use(void); | 70 | extern int pager_in_use(void); |
70 | extern int pager_use_color; | 71 | extern int pager_use_color; |
71 | 72 | ||
73 | extern bool use_browser; | ||
74 | |||
75 | #ifdef NO_NEWT_SUPPORT | ||
76 | static inline void setup_browser(void) | ||
77 | { | ||
78 | setup_pager(); | ||
79 | } | ||
80 | static inline void exit_browser(bool wait_for_ok __used) {} | ||
81 | #else | ||
82 | void setup_browser(void); | ||
83 | void exit_browser(bool wait_for_ok); | ||
84 | #endif | ||
85 | |||
72 | extern const char *editor_program; | 86 | extern const char *editor_program; |
73 | extern const char *excludes_file; | 87 | extern const char *excludes_file; |
74 | 88 | ||
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index b3b71258272a..db628af6d20d 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (C) 2009, Frederic Weisbecker <fweisbec@gmail.com> | 2 | * Copyright (C) 2009-2010, Frederic Weisbecker <fweisbec@gmail.com> |
3 | * | 3 | * |
4 | * Handle the callchains from the stream in an ad-hoc radix tree and then | 4 | * Handle the callchains from the stream in an ad-hoc radix tree and then |
5 | * sort them in an rbtree. | 5 | * sort them in an rbtree. |
@@ -183,12 +183,23 @@ create_child(struct callchain_node *parent, bool inherit_children) | |||
183 | return new; | 183 | return new; |
184 | } | 184 | } |
185 | 185 | ||
186 | |||
187 | struct resolved_ip { | ||
188 | u64 ip; | ||
189 | struct map_symbol ms; | ||
190 | }; | ||
191 | |||
192 | struct resolved_chain { | ||
193 | u64 nr; | ||
194 | struct resolved_ip ips[0]; | ||
195 | }; | ||
196 | |||
197 | |||
186 | /* | 198 | /* |
187 | * Fill the node with callchain values | 199 | * Fill the node with callchain values |
188 | */ | 200 | */ |
189 | static void | 201 | static void |
190 | fill_node(struct callchain_node *node, struct ip_callchain *chain, | 202 | fill_node(struct callchain_node *node, struct resolved_chain *chain, int start) |
191 | int start, struct symbol **syms) | ||
192 | { | 203 | { |
193 | unsigned int i; | 204 | unsigned int i; |
194 | 205 | ||
@@ -200,8 +211,8 @@ fill_node(struct callchain_node *node, struct ip_callchain *chain, | |||
200 | perror("not enough memory for the code path tree"); | 211 | perror("not enough memory for the code path tree"); |
201 | return; | 212 | return; |
202 | } | 213 | } |
203 | call->ip = chain->ips[i]; | 214 | call->ip = chain->ips[i].ip; |
204 | call->sym = syms[i]; | 215 | call->ms = chain->ips[i].ms; |
205 | list_add_tail(&call->list, &node->val); | 216 | list_add_tail(&call->list, &node->val); |
206 | } | 217 | } |
207 | node->val_nr = chain->nr - start; | 218 | node->val_nr = chain->nr - start; |
@@ -210,13 +221,13 @@ fill_node(struct callchain_node *node, struct ip_callchain *chain, | |||
210 | } | 221 | } |
211 | 222 | ||
212 | static void | 223 | static void |
213 | add_child(struct callchain_node *parent, struct ip_callchain *chain, | 224 | add_child(struct callchain_node *parent, struct resolved_chain *chain, |
214 | int start, struct symbol **syms) | 225 | int start) |
215 | { | 226 | { |
216 | struct callchain_node *new; | 227 | struct callchain_node *new; |
217 | 228 | ||
218 | new = create_child(parent, false); | 229 | new = create_child(parent, false); |
219 | fill_node(new, chain, start, syms); | 230 | fill_node(new, chain, start); |
220 | 231 | ||
221 | new->children_hit = 0; | 232 | new->children_hit = 0; |
222 | new->hit = 1; | 233 | new->hit = 1; |
@@ -228,9 +239,8 @@ add_child(struct callchain_node *parent, struct ip_callchain *chain, | |||
228 | * Then create another child to host the given callchain of new branch | 239 | * Then create another child to host the given callchain of new branch |
229 | */ | 240 | */ |
230 | static void | 241 | static void |
231 | split_add_child(struct callchain_node *parent, struct ip_callchain *chain, | 242 | split_add_child(struct callchain_node *parent, struct resolved_chain *chain, |
232 | struct callchain_list *to_split, int idx_parents, int idx_local, | 243 | struct callchain_list *to_split, int idx_parents, int idx_local) |
233 | struct symbol **syms) | ||
234 | { | 244 | { |
235 | struct callchain_node *new; | 245 | struct callchain_node *new; |
236 | struct list_head *old_tail; | 246 | struct list_head *old_tail; |
@@ -257,7 +267,7 @@ split_add_child(struct callchain_node *parent, struct ip_callchain *chain, | |||
257 | /* create a new child for the new branch if any */ | 267 | /* create a new child for the new branch if any */ |
258 | if (idx_total < chain->nr) { | 268 | if (idx_total < chain->nr) { |
259 | parent->hit = 0; | 269 | parent->hit = 0; |
260 | add_child(parent, chain, idx_total, syms); | 270 | add_child(parent, chain, idx_total); |
261 | parent->children_hit++; | 271 | parent->children_hit++; |
262 | } else { | 272 | } else { |
263 | parent->hit = 1; | 273 | parent->hit = 1; |
@@ -265,32 +275,33 @@ split_add_child(struct callchain_node *parent, struct ip_callchain *chain, | |||
265 | } | 275 | } |
266 | 276 | ||
267 | static int | 277 | static int |
268 | __append_chain(struct callchain_node *root, struct ip_callchain *chain, | 278 | __append_chain(struct callchain_node *root, struct resolved_chain *chain, |
269 | unsigned int start, struct symbol **syms); | 279 | unsigned int start); |
270 | 280 | ||
271 | static void | 281 | static void |
272 | __append_chain_children(struct callchain_node *root, struct ip_callchain *chain, | 282 | __append_chain_children(struct callchain_node *root, |
273 | struct symbol **syms, unsigned int start) | 283 | struct resolved_chain *chain, |
284 | unsigned int start) | ||
274 | { | 285 | { |
275 | struct callchain_node *rnode; | 286 | struct callchain_node *rnode; |
276 | 287 | ||
277 | /* lookup in childrens */ | 288 | /* lookup in childrens */ |
278 | chain_for_each_child(rnode, root) { | 289 | chain_for_each_child(rnode, root) { |
279 | unsigned int ret = __append_chain(rnode, chain, start, syms); | 290 | unsigned int ret = __append_chain(rnode, chain, start); |
280 | 291 | ||
281 | if (!ret) | 292 | if (!ret) |
282 | goto inc_children_hit; | 293 | goto inc_children_hit; |
283 | } | 294 | } |
284 | /* nothing in children, add to the current node */ | 295 | /* nothing in children, add to the current node */ |
285 | add_child(root, chain, start, syms); | 296 | add_child(root, chain, start); |
286 | 297 | ||
287 | inc_children_hit: | 298 | inc_children_hit: |
288 | root->children_hit++; | 299 | root->children_hit++; |
289 | } | 300 | } |
290 | 301 | ||
291 | static int | 302 | static int |
292 | __append_chain(struct callchain_node *root, struct ip_callchain *chain, | 303 | __append_chain(struct callchain_node *root, struct resolved_chain *chain, |
293 | unsigned int start, struct symbol **syms) | 304 | unsigned int start) |
294 | { | 305 | { |
295 | struct callchain_list *cnode; | 306 | struct callchain_list *cnode; |
296 | unsigned int i = start; | 307 | unsigned int i = start; |
@@ -302,13 +313,19 @@ __append_chain(struct callchain_node *root, struct ip_callchain *chain, | |||
302 | * anywhere inside a function. | 313 | * anywhere inside a function. |
303 | */ | 314 | */ |
304 | list_for_each_entry(cnode, &root->val, list) { | 315 | list_for_each_entry(cnode, &root->val, list) { |
316 | struct symbol *sym; | ||
317 | |||
305 | if (i == chain->nr) | 318 | if (i == chain->nr) |
306 | break; | 319 | break; |
307 | if (cnode->sym && syms[i]) { | 320 | |
308 | if (cnode->sym->start != syms[i]->start) | 321 | sym = chain->ips[i].ms.sym; |
322 | |||
323 | if (cnode->ms.sym && sym) { | ||
324 | if (cnode->ms.sym->start != sym->start) | ||
309 | break; | 325 | break; |
310 | } else if (cnode->ip != chain->ips[i]) | 326 | } else if (cnode->ip != chain->ips[i].ip) |
311 | break; | 327 | break; |
328 | |||
312 | if (!found) | 329 | if (!found) |
313 | found = true; | 330 | found = true; |
314 | i++; | 331 | i++; |
@@ -320,7 +337,7 @@ __append_chain(struct callchain_node *root, struct ip_callchain *chain, | |||
320 | 337 | ||
321 | /* we match only a part of the node. Split it and add the new chain */ | 338 | /* we match only a part of the node. Split it and add the new chain */ |
322 | if (i - start < root->val_nr) { | 339 | if (i - start < root->val_nr) { |
323 | split_add_child(root, chain, cnode, start, i - start, syms); | 340 | split_add_child(root, chain, cnode, start, i - start); |
324 | return 0; | 341 | return 0; |
325 | } | 342 | } |
326 | 343 | ||
@@ -331,15 +348,50 @@ __append_chain(struct callchain_node *root, struct ip_callchain *chain, | |||
331 | } | 348 | } |
332 | 349 | ||
333 | /* We match the node and still have a part remaining */ | 350 | /* We match the node and still have a part remaining */ |
334 | __append_chain_children(root, chain, syms, i); | 351 | __append_chain_children(root, chain, i); |
335 | 352 | ||
336 | return 0; | 353 | return 0; |
337 | } | 354 | } |
338 | 355 | ||
339 | void append_chain(struct callchain_node *root, struct ip_callchain *chain, | 356 | static void filter_context(struct ip_callchain *old, struct resolved_chain *new, |
340 | struct symbol **syms) | 357 | struct map_symbol *syms) |
358 | { | ||
359 | int i, j = 0; | ||
360 | |||
361 | for (i = 0; i < (int)old->nr; i++) { | ||
362 | if (old->ips[i] >= PERF_CONTEXT_MAX) | ||
363 | continue; | ||
364 | |||
365 | new->ips[j].ip = old->ips[i]; | ||
366 | new->ips[j].ms = syms[i]; | ||
367 | j++; | ||
368 | } | ||
369 | |||
370 | new->nr = j; | ||
371 | } | ||
372 | |||
373 | |||
374 | int append_chain(struct callchain_node *root, struct ip_callchain *chain, | ||
375 | struct map_symbol *syms) | ||
341 | { | 376 | { |
377 | struct resolved_chain *filtered; | ||
378 | |||
342 | if (!chain->nr) | 379 | if (!chain->nr) |
343 | return; | 380 | return 0; |
344 | __append_chain_children(root, chain, syms, 0); | 381 | |
382 | filtered = malloc(sizeof(*filtered) + | ||
383 | chain->nr * sizeof(struct resolved_ip)); | ||
384 | if (!filtered) | ||
385 | return -ENOMEM; | ||
386 | |||
387 | filter_context(chain, filtered, syms); | ||
388 | |||
389 | if (!filtered->nr) | ||
390 | goto end; | ||
391 | |||
392 | __append_chain_children(root, filtered, 0); | ||
393 | end: | ||
394 | free(filtered); | ||
395 | |||
396 | return 0; | ||
345 | } | 397 | } |
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index ad4626de4c2b..8a7e8bbd0fda 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
@@ -39,7 +39,7 @@ struct callchain_param { | |||
39 | 39 | ||
40 | struct callchain_list { | 40 | struct callchain_list { |
41 | u64 ip; | 41 | u64 ip; |
42 | struct symbol *sym; | 42 | struct map_symbol ms; |
43 | struct list_head list; | 43 | struct list_head list; |
44 | }; | 44 | }; |
45 | 45 | ||
@@ -56,6 +56,6 @@ static inline u64 cumul_hits(struct callchain_node *node) | |||
56 | } | 56 | } |
57 | 57 | ||
58 | int register_callchain_param(struct callchain_param *param); | 58 | int register_callchain_param(struct callchain_param *param); |
59 | void append_chain(struct callchain_node *root, struct ip_callchain *chain, | 59 | int append_chain(struct callchain_node *root, struct ip_callchain *chain, |
60 | struct symbol **syms); | 60 | struct map_symbol *syms); |
61 | #endif /* __PERF_CALLCHAIN_H */ | 61 | #endif /* __PERF_CALLCHAIN_H */ |
diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c index e88bca55a599..9da01914e0af 100644 --- a/tools/perf/util/color.c +++ b/tools/perf/util/color.c | |||
@@ -203,7 +203,10 @@ int color_fprintf(FILE *fp, const char *color, const char *fmt, ...) | |||
203 | int r; | 203 | int r; |
204 | 204 | ||
205 | va_start(args, fmt); | 205 | va_start(args, fmt); |
206 | r = color_vfprintf(fp, color, fmt, args); | 206 | if (use_browser) |
207 | r = vfprintf(fp, fmt, args); | ||
208 | else | ||
209 | r = color_vfprintf(fp, color, fmt, args); | ||
207 | va_end(args); | 210 | va_end(args); |
208 | return r; | 211 | return r; |
209 | } | 212 | } |
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index 0905600c3851..033d66db863a 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c | |||
@@ -6,6 +6,7 @@ | |||
6 | #include <stdarg.h> | 6 | #include <stdarg.h> |
7 | #include <stdio.h> | 7 | #include <stdio.h> |
8 | 8 | ||
9 | #include "cache.h" | ||
9 | #include "color.h" | 10 | #include "color.h" |
10 | #include "event.h" | 11 | #include "event.h" |
11 | #include "debug.h" | 12 | #include "debug.h" |
@@ -21,7 +22,10 @@ int eprintf(int level, const char *fmt, ...) | |||
21 | 22 | ||
22 | if (verbose >= level) { | 23 | if (verbose >= level) { |
23 | va_start(args, fmt); | 24 | va_start(args, fmt); |
24 | ret = vfprintf(stderr, fmt, args); | 25 | if (use_browser) |
26 | ret = browser__show_help(fmt, args); | ||
27 | else | ||
28 | ret = vfprintf(stderr, fmt, args); | ||
25 | va_end(args); | 29 | va_end(args); |
26 | } | 30 | } |
27 | 31 | ||
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index c6c24c522dea..0172edf3f153 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h | |||
@@ -7,9 +7,16 @@ | |||
7 | extern int verbose; | 7 | extern int verbose; |
8 | extern int dump_trace; | 8 | extern int dump_trace; |
9 | 9 | ||
10 | int eprintf(int level, | ||
11 | const char *fmt, ...) __attribute__((format(printf, 2, 3))); | ||
12 | int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); | 10 | int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); |
13 | void trace_event(event_t *event); | 11 | void trace_event(event_t *event); |
14 | 12 | ||
13 | #ifdef NO_NEWT_SUPPORT | ||
14 | static inline int browser__show_help(const char *format __used, va_list ap __used) | ||
15 | { | ||
16 | return 0; | ||
17 | } | ||
18 | #else | ||
19 | int browser__show_help(const char *format, va_list ap); | ||
20 | #endif | ||
21 | |||
15 | #endif /* __PERF_DEBUG_H */ | 22 | #endif /* __PERF_DEBUG_H */ |
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 705ec63548b4..052eaeccc202 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -393,7 +393,8 @@ int event__process_mmap(event_t *self, struct perf_session *session) | |||
393 | } | 393 | } |
394 | 394 | ||
395 | thread = perf_session__findnew(session, self->mmap.pid); | 395 | thread = perf_session__findnew(session, self->mmap.pid); |
396 | map = map__new(&self->mmap, MAP__FUNCTION, | 396 | map = map__new(self->mmap.start, self->mmap.len, self->mmap.pgoff, |
397 | self->mmap.pid, self->mmap.filename, MAP__FUNCTION, | ||
397 | session->cwd, session->cwdlen); | 398 | session->cwd, session->cwdlen); |
398 | 399 | ||
399 | if (thread == NULL || map == NULL) | 400 | if (thread == NULL || map == NULL) |
@@ -513,24 +514,32 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session, | |||
513 | 514 | ||
514 | dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); | 515 | dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); |
515 | 516 | ||
516 | thread__find_addr_location(thread, session, cpumode, MAP__FUNCTION, | 517 | thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION, |
517 | self->ip.ip, al, filter); | 518 | self->ip.ip, al); |
518 | dump_printf(" ...... dso: %s\n", | 519 | dump_printf(" ...... dso: %s\n", |
519 | al->map ? al->map->dso->long_name : | 520 | al->map ? al->map->dso->long_name : |
520 | al->level == 'H' ? "[hypervisor]" : "<not found>"); | 521 | al->level == 'H' ? "[hypervisor]" : "<not found>"); |
521 | /* | 522 | al->sym = NULL; |
522 | * We have to do this here as we may have a dso with no symbol hit that | 523 | |
523 | * has a name longer than the ones with symbols sampled. | 524 | if (al->map) { |
524 | */ | 525 | if (symbol_conf.dso_list && |
525 | if (al->map && !sort_dso.elide && !al->map->dso->slen_calculated) | 526 | (!al->map || !al->map->dso || |
526 | dso__calc_col_width(al->map->dso); | 527 | !(strlist__has_entry(symbol_conf.dso_list, |
527 | 528 | al->map->dso->short_name) || | |
528 | if (symbol_conf.dso_list && | 529 | (al->map->dso->short_name != al->map->dso->long_name && |
529 | (!al->map || !al->map->dso || | 530 | strlist__has_entry(symbol_conf.dso_list, |
530 | !(strlist__has_entry(symbol_conf.dso_list, al->map->dso->short_name) || | 531 | al->map->dso->long_name))))) |
531 | (al->map->dso->short_name != al->map->dso->long_name && | 532 | goto out_filtered; |
532 | strlist__has_entry(symbol_conf.dso_list, al->map->dso->long_name))))) | 533 | /* |
533 | goto out_filtered; | 534 | * We have to do this here as we may have a dso with no symbol |
535 | * hit that has a name longer than the ones with symbols | ||
536 | * sampled. | ||
537 | */ | ||
538 | if (!sort_dso.elide && !al->map->dso->slen_calculated) | ||
539 | dso__calc_col_width(al->map->dso); | ||
540 | |||
541 | al->sym = map__find_symbol(al->map, al->addr, filter); | ||
542 | } | ||
534 | 543 | ||
535 | if (symbol_conf.sym_list && al->sym && | 544 | if (symbol_conf.sym_list && al->sym && |
536 | !strlist__has_entry(symbol_conf.sym_list, al->sym->name)) | 545 | !strlist__has_entry(symbol_conf.sym_list, al->sym->name)) |
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 2be33c7dbf03..09e09e78cb62 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c | |||
@@ -22,8 +22,10 @@ struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, | |||
22 | struct hist_entry *he; | 22 | struct hist_entry *he; |
23 | struct hist_entry entry = { | 23 | struct hist_entry entry = { |
24 | .thread = al->thread, | 24 | .thread = al->thread, |
25 | .map = al->map, | 25 | .ms = { |
26 | .sym = al->sym, | 26 | .map = al->map, |
27 | .sym = al->sym, | ||
28 | }, | ||
27 | .ip = al->addr, | 29 | .ip = al->addr, |
28 | .level = al->level, | 30 | .level = al->level, |
29 | .count = count, | 31 | .count = count, |
@@ -258,8 +260,8 @@ static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, | |||
258 | } else | 260 | } else |
259 | ret += fprintf(fp, "%s", " "); | 261 | ret += fprintf(fp, "%s", " "); |
260 | } | 262 | } |
261 | if (chain->sym) | 263 | if (chain->ms.sym) |
262 | ret += fprintf(fp, "%s\n", chain->sym->name); | 264 | ret += fprintf(fp, "%s\n", chain->ms.sym->name); |
263 | else | 265 | else |
264 | ret += fprintf(fp, "%p\n", (void *)(long)chain->ip); | 266 | ret += fprintf(fp, "%p\n", (void *)(long)chain->ip); |
265 | 267 | ||
@@ -278,7 +280,7 @@ static void init_rem_hits(void) | |||
278 | } | 280 | } |
279 | 281 | ||
280 | strcpy(rem_sq_bracket->name, "[...]"); | 282 | strcpy(rem_sq_bracket->name, "[...]"); |
281 | rem_hits.sym = rem_sq_bracket; | 283 | rem_hits.ms.sym = rem_sq_bracket; |
282 | } | 284 | } |
283 | 285 | ||
284 | static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | 286 | static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, |
@@ -328,8 +330,6 @@ static size_t __callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
328 | left_margin); | 330 | left_margin); |
329 | i = 0; | 331 | i = 0; |
330 | list_for_each_entry(chain, &child->val, list) { | 332 | list_for_each_entry(chain, &child->val, list) { |
331 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
332 | continue; | ||
333 | ret += ipchain__fprintf_graph(fp, chain, depth, | 333 | ret += ipchain__fprintf_graph(fp, chain, depth, |
334 | new_depth_mask, i++, | 334 | new_depth_mask, i++, |
335 | new_total, | 335 | new_total, |
@@ -368,9 +368,6 @@ static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
368 | int ret = 0; | 368 | int ret = 0; |
369 | 369 | ||
370 | list_for_each_entry(chain, &self->val, list) { | 370 | list_for_each_entry(chain, &self->val, list) { |
371 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
372 | continue; | ||
373 | |||
374 | if (!i++ && sort__first_dimension == SORT_SYM) | 371 | if (!i++ && sort__first_dimension == SORT_SYM) |
375 | continue; | 372 | continue; |
376 | 373 | ||
@@ -385,8 +382,8 @@ static size_t callchain__fprintf_graph(FILE *fp, struct callchain_node *self, | |||
385 | } else | 382 | } else |
386 | ret += callchain__fprintf_left_margin(fp, left_margin); | 383 | ret += callchain__fprintf_left_margin(fp, left_margin); |
387 | 384 | ||
388 | if (chain->sym) | 385 | if (chain->ms.sym) |
389 | ret += fprintf(fp, " %s\n", chain->sym->name); | 386 | ret += fprintf(fp, " %s\n", chain->ms.sym->name); |
390 | else | 387 | else |
391 | ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); | 388 | ret += fprintf(fp, " %p\n", (void *)(long)chain->ip); |
392 | } | 389 | } |
@@ -411,8 +408,8 @@ static size_t callchain__fprintf_flat(FILE *fp, struct callchain_node *self, | |||
411 | list_for_each_entry(chain, &self->val, list) { | 408 | list_for_each_entry(chain, &self->val, list) { |
412 | if (chain->ip >= PERF_CONTEXT_MAX) | 409 | if (chain->ip >= PERF_CONTEXT_MAX) |
413 | continue; | 410 | continue; |
414 | if (chain->sym) | 411 | if (chain->ms.sym) |
415 | ret += fprintf(fp, " %s\n", chain->sym->name); | 412 | ret += fprintf(fp, " %s\n", chain->ms.sym->name); |
416 | else | 413 | else |
417 | ret += fprintf(fp, " %p\n", | 414 | ret += fprintf(fp, " %p\n", |
418 | (void *)(long)chain->ip); | 415 | (void *)(long)chain->ip); |
@@ -455,11 +452,11 @@ static size_t hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self, | |||
455 | return ret; | 452 | return ret; |
456 | } | 453 | } |
457 | 454 | ||
458 | static size_t hist_entry__fprintf(struct hist_entry *self, | 455 | size_t hist_entry__fprintf(struct hist_entry *self, |
459 | struct perf_session *pair_session, | 456 | struct perf_session *pair_session, |
460 | bool show_displacement, | 457 | bool show_displacement, |
461 | long displacement, FILE *fp, | 458 | long displacement, FILE *fp, |
462 | u64 session_total) | 459 | u64 session_total) |
463 | { | 460 | { |
464 | struct sort_entry *se; | 461 | struct sort_entry *se; |
465 | u64 count, total; | 462 | u64 count, total; |
@@ -485,9 +482,9 @@ static size_t hist_entry__fprintf(struct hist_entry *self, | |||
485 | 482 | ||
486 | if (symbol_conf.show_nr_samples) { | 483 | if (symbol_conf.show_nr_samples) { |
487 | if (sep) | 484 | if (sep) |
488 | fprintf(fp, "%c%lld", *sep, count); | 485 | ret += fprintf(fp, "%c%lld", *sep, count); |
489 | else | 486 | else |
490 | fprintf(fp, "%11lld", count); | 487 | ret += fprintf(fp, "%11lld", count); |
491 | } | 488 | } |
492 | 489 | ||
493 | if (pair_session) { | 490 | if (pair_session) { |
@@ -518,9 +515,9 @@ static size_t hist_entry__fprintf(struct hist_entry *self, | |||
518 | snprintf(bf, sizeof(bf), " "); | 515 | snprintf(bf, sizeof(bf), " "); |
519 | 516 | ||
520 | if (sep) | 517 | if (sep) |
521 | fprintf(fp, "%c%s", *sep, bf); | 518 | ret += fprintf(fp, "%c%s", *sep, bf); |
522 | else | 519 | else |
523 | fprintf(fp, "%6.6s", bf); | 520 | ret += fprintf(fp, "%6.6s", bf); |
524 | } | 521 | } |
525 | } | 522 | } |
526 | 523 | ||
@@ -528,27 +525,27 @@ static size_t hist_entry__fprintf(struct hist_entry *self, | |||
528 | if (se->elide) | 525 | if (se->elide) |
529 | continue; | 526 | continue; |
530 | 527 | ||
531 | fprintf(fp, "%s", sep ?: " "); | 528 | ret += fprintf(fp, "%s", sep ?: " "); |
532 | ret += se->print(fp, self, se->width ? *se->width : 0); | 529 | ret += se->print(fp, self, se->width ? *se->width : 0); |
533 | } | 530 | } |
534 | 531 | ||
535 | ret += fprintf(fp, "\n"); | 532 | return ret + fprintf(fp, "\n"); |
536 | 533 | } | |
537 | if (symbol_conf.use_callchain) { | ||
538 | int left_margin = 0; | ||
539 | 534 | ||
540 | if (sort__first_dimension == SORT_COMM) { | 535 | static size_t hist_entry__fprintf_callchain(struct hist_entry *self, FILE *fp, |
541 | se = list_first_entry(&hist_entry__sort_list, typeof(*se), | 536 | u64 session_total) |
542 | list); | 537 | { |
543 | left_margin = se->width ? *se->width : 0; | 538 | int left_margin = 0; |
544 | left_margin -= thread__comm_len(self->thread); | ||
545 | } | ||
546 | 539 | ||
547 | hist_entry_callchain__fprintf(fp, self, session_total, | 540 | if (sort__first_dimension == SORT_COMM) { |
548 | left_margin); | 541 | struct sort_entry *se = list_first_entry(&hist_entry__sort_list, |
542 | typeof(*se), list); | ||
543 | left_margin = se->width ? *se->width : 0; | ||
544 | left_margin -= thread__comm_len(self->thread); | ||
549 | } | 545 | } |
550 | 546 | ||
551 | return ret; | 547 | return hist_entry_callchain__fprintf(fp, self, session_total, |
548 | left_margin); | ||
552 | } | 549 | } |
553 | 550 | ||
554 | size_t perf_session__fprintf_hists(struct rb_root *hists, | 551 | size_t perf_session__fprintf_hists(struct rb_root *hists, |
@@ -655,7 +652,11 @@ print_entries: | |||
655 | } | 652 | } |
656 | ret += hist_entry__fprintf(h, pair, show_displacement, | 653 | ret += hist_entry__fprintf(h, pair, show_displacement, |
657 | displacement, fp, session_total); | 654 | displacement, fp, session_total); |
658 | if (h->map == NULL && verbose > 1) { | 655 | |
656 | if (symbol_conf.use_callchain) | ||
657 | ret += hist_entry__fprintf_callchain(h, fp, session_total); | ||
658 | |||
659 | if (h->ms.map == NULL && verbose > 1) { | ||
659 | __map_groups__fprintf_maps(&h->thread->mg, | 660 | __map_groups__fprintf_maps(&h->thread->mg, |
660 | MAP__FUNCTION, fp); | 661 | MAP__FUNCTION, fp); |
661 | fprintf(fp, "%.10s end\n", graph_dotted_line); | 662 | fprintf(fp, "%.10s end\n", graph_dotted_line); |
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 16f360cce5bf..fe366ce5db45 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h | |||
@@ -18,6 +18,11 @@ struct hist_entry *__perf_session__add_hist_entry(struct rb_root *hists, | |||
18 | u64 count, bool *hit); | 18 | u64 count, bool *hit); |
19 | extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); | 19 | extern int64_t hist_entry__cmp(struct hist_entry *, struct hist_entry *); |
20 | 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 *); |
21 | size_t hist_entry__fprintf(struct hist_entry *self, | ||
22 | struct perf_session *pair_session, | ||
23 | bool show_displacement, | ||
24 | long displacement, FILE *fp, | ||
25 | u64 session_total); | ||
21 | void hist_entry__free(struct hist_entry *); | 26 | void hist_entry__free(struct hist_entry *); |
22 | 27 | ||
23 | void perf_session__output_resort(struct rb_root *hists, u64 total_samples); | 28 | void perf_session__output_resort(struct rb_root *hists, u64 total_samples); |
diff --git a/tools/perf/util/include/linux/compiler.h b/tools/perf/util/include/linux/compiler.h index dfb0713ed47f..791f9dd27ebf 100644 --- a/tools/perf/util/include/linux/compiler.h +++ b/tools/perf/util/include/linux/compiler.h | |||
@@ -7,4 +7,6 @@ | |||
7 | #define __user | 7 | #define __user |
8 | #define __attribute_const__ | 8 | #define __attribute_const__ |
9 | 9 | ||
10 | #define __used __attribute__((__unused__)) | ||
11 | |||
10 | #endif | 12 | #endif |
diff --git a/tools/perf/util/include/linux/kernel.h b/tools/perf/util/include/linux/kernel.h index f2611655ab51..388ab1bfd114 100644 --- a/tools/perf/util/include/linux/kernel.h +++ b/tools/perf/util/include/linux/kernel.h | |||
@@ -85,16 +85,19 @@ simple_strtoul(const char *nptr, char **endptr, int base) | |||
85 | return strtoul(nptr, endptr, base); | 85 | return strtoul(nptr, endptr, base); |
86 | } | 86 | } |
87 | 87 | ||
88 | int eprintf(int level, | ||
89 | const char *fmt, ...) __attribute__((format(printf, 2, 3))); | ||
90 | |||
88 | #ifndef pr_fmt | 91 | #ifndef pr_fmt |
89 | #define pr_fmt(fmt) fmt | 92 | #define pr_fmt(fmt) fmt |
90 | #endif | 93 | #endif |
91 | 94 | ||
92 | #define pr_err(fmt, ...) \ | 95 | #define pr_err(fmt, ...) \ |
93 | do { fprintf(stderr, pr_fmt(fmt), ##__VA_ARGS__); } while (0) | 96 | eprintf(0, pr_fmt(fmt), ##__VA_ARGS__) |
94 | #define pr_warning(fmt, ...) \ | 97 | #define pr_warning(fmt, ...) \ |
95 | do { fprintf(stderr, pr_fmt(fmt), ##__VA_ARGS__); } while (0) | 98 | eprintf(0, pr_fmt(fmt), ##__VA_ARGS__) |
96 | #define pr_info(fmt, ...) \ | 99 | #define pr_info(fmt, ...) \ |
97 | do { fprintf(stderr, pr_fmt(fmt), ##__VA_ARGS__); } while (0) | 100 | eprintf(0, pr_fmt(fmt), ##__VA_ARGS__) |
98 | #define pr_debug(fmt, ...) \ | 101 | #define pr_debug(fmt, ...) \ |
99 | eprintf(1, pr_fmt(fmt), ##__VA_ARGS__) | 102 | eprintf(1, pr_fmt(fmt), ##__VA_ARGS__) |
100 | #define pr_debugN(n, fmt, ...) \ | 103 | #define pr_debugN(n, fmt, ...) \ |
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index e509cd59c67d..9f2963f9ee9a 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c | |||
@@ -1,9 +1,9 @@ | |||
1 | #include "event.h" | ||
2 | #include "symbol.h" | 1 | #include "symbol.h" |
2 | #include <limits.h> | ||
3 | #include <stdlib.h> | 3 | #include <stdlib.h> |
4 | #include <string.h> | 4 | #include <string.h> |
5 | #include <stdio.h> | 5 | #include <stdio.h> |
6 | #include "debug.h" | 6 | #include "map.h" |
7 | 7 | ||
8 | const char *map_type__name[MAP__NR_TYPES] = { | 8 | const char *map_type__name[MAP__NR_TYPES] = { |
9 | [MAP__FUNCTION] = "Functions", | 9 | [MAP__FUNCTION] = "Functions", |
@@ -38,13 +38,12 @@ void map__init(struct map *self, enum map_type type, | |||
38 | RB_CLEAR_NODE(&self->rb_node); | 38 | RB_CLEAR_NODE(&self->rb_node); |
39 | } | 39 | } |
40 | 40 | ||
41 | struct map *map__new(struct mmap_event *event, enum map_type type, | 41 | struct map *map__new(u64 start, u64 len, u64 pgoff, u32 pid, char *filename, |
42 | char *cwd, int cwdlen) | 42 | enum map_type type, char *cwd, int cwdlen) |
43 | { | 43 | { |
44 | struct map *self = malloc(sizeof(*self)); | 44 | struct map *self = malloc(sizeof(*self)); |
45 | 45 | ||
46 | if (self != NULL) { | 46 | if (self != NULL) { |
47 | const char *filename = event->filename; | ||
48 | char newfilename[PATH_MAX]; | 47 | char newfilename[PATH_MAX]; |
49 | struct dso *dso; | 48 | struct dso *dso; |
50 | int anon; | 49 | int anon; |
@@ -62,7 +61,7 @@ struct map *map__new(struct mmap_event *event, enum map_type type, | |||
62 | anon = is_anon_memory(filename); | 61 | anon = is_anon_memory(filename); |
63 | 62 | ||
64 | if (anon) { | 63 | if (anon) { |
65 | snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", event->pid); | 64 | snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid); |
66 | filename = newfilename; | 65 | filename = newfilename; |
67 | } | 66 | } |
68 | 67 | ||
@@ -70,8 +69,7 @@ struct map *map__new(struct mmap_event *event, enum map_type type, | |||
70 | if (dso == NULL) | 69 | if (dso == NULL) |
71 | goto out_delete; | 70 | goto out_delete; |
72 | 71 | ||
73 | map__init(self, type, event->start, event->start + event->len, | 72 | map__init(self, type, start, start + len, pgoff, dso); |
74 | event->pgoff, dso); | ||
75 | 73 | ||
76 | if (anon) { | 74 | if (anon) { |
77 | set_identity: | 75 | set_identity: |
@@ -235,3 +233,84 @@ u64 map__objdump_2ip(struct map *map, u64 addr) | |||
235 | map->unmap_ip(map, addr); /* RIP -> IP */ | 233 | map->unmap_ip(map, addr); /* RIP -> IP */ |
236 | return ip; | 234 | return ip; |
237 | } | 235 | } |
236 | |||
237 | struct symbol *map_groups__find_symbol(struct map_groups *self, | ||
238 | enum map_type type, u64 addr, | ||
239 | symbol_filter_t filter) | ||
240 | { | ||
241 | struct map *map = map_groups__find(self, type, addr); | ||
242 | |||
243 | if (map != NULL) | ||
244 | return map__find_symbol(map, map->map_ip(map, addr), filter); | ||
245 | |||
246 | return NULL; | ||
247 | } | ||
248 | |||
249 | static u64 map__reloc_map_ip(struct map *map, u64 ip) | ||
250 | { | ||
251 | return ip + (s64)map->pgoff; | ||
252 | } | ||
253 | |||
254 | static u64 map__reloc_unmap_ip(struct map *map, u64 ip) | ||
255 | { | ||
256 | return ip - (s64)map->pgoff; | ||
257 | } | ||
258 | |||
259 | void map__reloc_vmlinux(struct map *self) | ||
260 | { | ||
261 | struct kmap *kmap = map__kmap(self); | ||
262 | s64 reloc; | ||
263 | |||
264 | if (!kmap->ref_reloc_sym || !kmap->ref_reloc_sym->unrelocated_addr) | ||
265 | return; | ||
266 | |||
267 | reloc = (kmap->ref_reloc_sym->unrelocated_addr - | ||
268 | kmap->ref_reloc_sym->addr); | ||
269 | |||
270 | if (!reloc) | ||
271 | return; | ||
272 | |||
273 | self->map_ip = map__reloc_map_ip; | ||
274 | self->unmap_ip = map__reloc_unmap_ip; | ||
275 | self->pgoff = reloc; | ||
276 | } | ||
277 | |||
278 | void maps__insert(struct rb_root *maps, struct map *map) | ||
279 | { | ||
280 | struct rb_node **p = &maps->rb_node; | ||
281 | struct rb_node *parent = NULL; | ||
282 | const u64 ip = map->start; | ||
283 | struct map *m; | ||
284 | |||
285 | while (*p != NULL) { | ||
286 | parent = *p; | ||
287 | m = rb_entry(parent, struct map, rb_node); | ||
288 | if (ip < m->start) | ||
289 | p = &(*p)->rb_left; | ||
290 | else | ||
291 | p = &(*p)->rb_right; | ||
292 | } | ||
293 | |||
294 | rb_link_node(&map->rb_node, parent, p); | ||
295 | rb_insert_color(&map->rb_node, maps); | ||
296 | } | ||
297 | |||
298 | struct map *maps__find(struct rb_root *maps, u64 ip) | ||
299 | { | ||
300 | struct rb_node **p = &maps->rb_node; | ||
301 | struct rb_node *parent = NULL; | ||
302 | struct map *m; | ||
303 | |||
304 | while (*p != NULL) { | ||
305 | parent = *p; | ||
306 | m = rb_entry(parent, struct map, rb_node); | ||
307 | if (ip < m->start) | ||
308 | p = &(*p)->rb_left; | ||
309 | else if (ip > m->end) | ||
310 | p = &(*p)->rb_right; | ||
311 | else | ||
312 | return m; | ||
313 | } | ||
314 | |||
315 | return NULL; | ||
316 | } | ||
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index b756368076c6..6a703fa74707 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h | |||
@@ -4,7 +4,8 @@ | |||
4 | #include <linux/compiler.h> | 4 | #include <linux/compiler.h> |
5 | #include <linux/list.h> | 5 | #include <linux/list.h> |
6 | #include <linux/rbtree.h> | 6 | #include <linux/rbtree.h> |
7 | #include <linux/types.h> | 7 | #include <stdio.h> |
8 | #include "types.h" | ||
8 | 9 | ||
9 | enum map_type { | 10 | enum map_type { |
10 | MAP__FUNCTION = 0, | 11 | MAP__FUNCTION = 0, |
@@ -68,14 +69,13 @@ u64 map__rip_2objdump(struct map *map, u64 rip); | |||
68 | u64 map__objdump_2ip(struct map *map, u64 addr); | 69 | u64 map__objdump_2ip(struct map *map, u64 addr); |
69 | 70 | ||
70 | struct symbol; | 71 | struct symbol; |
71 | struct mmap_event; | ||
72 | 72 | ||
73 | typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym); | 73 | typedef int (*symbol_filter_t)(struct map *map, struct symbol *sym); |
74 | 74 | ||
75 | void map__init(struct map *self, enum map_type type, | 75 | void map__init(struct map *self, enum map_type type, |
76 | u64 start, u64 end, u64 pgoff, struct dso *dso); | 76 | u64 start, u64 end, u64 pgoff, struct dso *dso); |
77 | struct map *map__new(struct mmap_event *event, enum map_type, | 77 | struct map *map__new(u64 start, u64 len, u64 pgoff, u32 pid, char *filename, |
78 | char *cwd, int cwdlen); | 78 | enum map_type type, char *cwd, int cwdlen); |
79 | void map__delete(struct map *self); | 79 | void map__delete(struct map *self); |
80 | struct map *map__clone(struct map *self); | 80 | struct map *map__clone(struct map *self); |
81 | int map__overlap(struct map *l, struct map *r); | 81 | int map__overlap(struct map *l, struct map *r); |
@@ -91,4 +91,48 @@ void map__fixup_end(struct map *self); | |||
91 | 91 | ||
92 | void map__reloc_vmlinux(struct map *self); | 92 | void map__reloc_vmlinux(struct map *self); |
93 | 93 | ||
94 | struct map_groups { | ||
95 | struct rb_root maps[MAP__NR_TYPES]; | ||
96 | struct list_head removed_maps[MAP__NR_TYPES]; | ||
97 | }; | ||
98 | |||
99 | size_t __map_groups__fprintf_maps(struct map_groups *self, | ||
100 | enum map_type type, FILE *fp); | ||
101 | void maps__insert(struct rb_root *maps, struct map *map); | ||
102 | struct map *maps__find(struct rb_root *maps, u64 addr); | ||
103 | void map_groups__init(struct map_groups *self); | ||
104 | size_t map_groups__fprintf_maps(struct map_groups *self, FILE *fp); | ||
105 | |||
106 | static inline void map_groups__insert(struct map_groups *self, struct map *map) | ||
107 | { | ||
108 | maps__insert(&self->maps[map->type], map); | ||
109 | } | ||
110 | |||
111 | static inline struct map *map_groups__find(struct map_groups *self, | ||
112 | enum map_type type, u64 addr) | ||
113 | { | ||
114 | return maps__find(&self->maps[type], addr); | ||
115 | } | ||
116 | |||
117 | struct symbol *map_groups__find_symbol(struct map_groups *self, | ||
118 | enum map_type type, u64 addr, | ||
119 | symbol_filter_t filter); | ||
120 | |||
121 | static inline struct symbol *map_groups__find_function(struct map_groups *self, | ||
122 | u64 addr, | ||
123 | symbol_filter_t filter) | ||
124 | { | ||
125 | return map_groups__find_symbol(self, MAP__FUNCTION, addr, filter); | ||
126 | } | ||
127 | |||
128 | struct map *map_groups__find_by_name(struct map_groups *self, | ||
129 | enum map_type type, const char *name); | ||
130 | int __map_groups__create_kernel_maps(struct map_groups *self, | ||
131 | struct map *vmlinux_maps[MAP__NR_TYPES], | ||
132 | struct dso *kernel); | ||
133 | int map_groups__create_kernel_maps(struct map_groups *self, | ||
134 | struct map *vmlinux_maps[MAP__NR_TYPES]); | ||
135 | struct map *map_groups__new_module(struct map_groups *self, u64 start, | ||
136 | const char *filename); | ||
137 | |||
94 | #endif /* __PERF_MAP_H */ | 138 | #endif /* __PERF_MAP_H */ |
diff --git a/tools/perf/util/newt.c b/tools/perf/util/newt.c new file mode 100644 index 000000000000..e99bcc8d1939 --- /dev/null +++ b/tools/perf/util/newt.c | |||
@@ -0,0 +1,526 @@ | |||
1 | #define _GNU_SOURCE | ||
2 | #include <stdio.h> | ||
3 | #undef _GNU_SOURCE | ||
4 | |||
5 | #include <stdlib.h> | ||
6 | #include <newt.h> | ||
7 | #include <sys/ttydefaults.h> | ||
8 | |||
9 | #include "cache.h" | ||
10 | #include "hist.h" | ||
11 | #include "session.h" | ||
12 | #include "sort.h" | ||
13 | #include "symbol.h" | ||
14 | |||
15 | static void newt_form__set_exit_keys(newtComponent self) | ||
16 | { | ||
17 | newtFormAddHotKey(self, NEWT_KEY_ESCAPE); | ||
18 | newtFormAddHotKey(self, 'Q'); | ||
19 | newtFormAddHotKey(self, 'q'); | ||
20 | newtFormAddHotKey(self, CTRL('c')); | ||
21 | } | ||
22 | |||
23 | static newtComponent newt_form__new(void) | ||
24 | { | ||
25 | newtComponent self = newtForm(NULL, NULL, 0); | ||
26 | if (self) | ||
27 | newt_form__set_exit_keys(self); | ||
28 | return self; | ||
29 | } | ||
30 | |||
31 | static int popup_menu(int argc, const char *argv[]) | ||
32 | { | ||
33 | struct newtExitStruct es; | ||
34 | int i, rc = -1, max_len = 5; | ||
35 | newtComponent listbox, form = newt_form__new(); | ||
36 | |||
37 | if (form == NULL) | ||
38 | return -1; | ||
39 | |||
40 | listbox = newtListbox(0, 0, argc, NEWT_FLAG_RETURNEXIT); | ||
41 | if (listbox == NULL) | ||
42 | goto out_destroy_form; | ||
43 | |||
44 | newtFormAddComponents(form, listbox, NULL); | ||
45 | |||
46 | for (i = 0; i < argc; ++i) { | ||
47 | int len = strlen(argv[i]); | ||
48 | if (len > max_len) | ||
49 | max_len = len; | ||
50 | if (newtListboxAddEntry(listbox, argv[i], (void *)(long)i)) | ||
51 | goto out_destroy_form; | ||
52 | } | ||
53 | |||
54 | newtCenteredWindow(max_len, argc, NULL); | ||
55 | newtFormRun(form, &es); | ||
56 | rc = newtListboxGetCurrent(listbox) - NULL; | ||
57 | if (es.reason == NEWT_EXIT_HOTKEY) | ||
58 | rc = -1; | ||
59 | newtPopWindow(); | ||
60 | out_destroy_form: | ||
61 | newtFormDestroy(form); | ||
62 | return rc; | ||
63 | } | ||
64 | |||
65 | static bool dialog_yesno(const char *msg) | ||
66 | { | ||
67 | /* newtWinChoice should really be accepting const char pointers... */ | ||
68 | char yes[] = "Yes", no[] = "No"; | ||
69 | return newtWinChoice(NULL, no, yes, (char *)msg) == 2; | ||
70 | } | ||
71 | |||
72 | /* | ||
73 | * When debugging newt problems it was useful to be able to "unroll" | ||
74 | * the calls to newtCheckBoxTreeAdd{Array,Item}, so that we can generate | ||
75 | * a source file with the sequence of calls to these methods, to then | ||
76 | * tweak the arrays to get the intended results, so I'm keeping this code | ||
77 | * here, may be useful again in the future. | ||
78 | */ | ||
79 | #undef NEWT_DEBUG | ||
80 | |||
81 | static void newt_checkbox_tree__add(newtComponent tree, const char *str, | ||
82 | void *priv, int *indexes) | ||
83 | { | ||
84 | #ifdef NEWT_DEBUG | ||
85 | /* Print the newtCheckboxTreeAddArray to tinker with its index arrays */ | ||
86 | int i = 0, len = 40 - strlen(str); | ||
87 | |||
88 | fprintf(stderr, | ||
89 | "\tnewtCheckboxTreeAddItem(tree, %*.*s\"%s\", (void *)%p, 0, ", | ||
90 | len, len, " ", str, priv); | ||
91 | while (indexes[i] != NEWT_ARG_LAST) { | ||
92 | if (indexes[i] != NEWT_ARG_APPEND) | ||
93 | fprintf(stderr, " %d,", indexes[i]); | ||
94 | else | ||
95 | fprintf(stderr, " %s,", "NEWT_ARG_APPEND"); | ||
96 | ++i; | ||
97 | } | ||
98 | fprintf(stderr, " %s", " NEWT_ARG_LAST);\n"); | ||
99 | fflush(stderr); | ||
100 | #endif | ||
101 | newtCheckboxTreeAddArray(tree, str, priv, 0, indexes); | ||
102 | } | ||
103 | |||
104 | static char *callchain_list__sym_name(struct callchain_list *self, | ||
105 | char *bf, size_t bfsize) | ||
106 | { | ||
107 | if (self->ms.sym) | ||
108 | return self->ms.sym->name; | ||
109 | |||
110 | snprintf(bf, bfsize, "%#Lx", self->ip); | ||
111 | return bf; | ||
112 | } | ||
113 | |||
114 | static void __callchain__append_graph_browser(struct callchain_node *self, | ||
115 | newtComponent tree, u64 total, | ||
116 | int *indexes, int depth) | ||
117 | { | ||
118 | struct rb_node *node; | ||
119 | u64 new_total, remaining; | ||
120 | int idx = 0; | ||
121 | |||
122 | if (callchain_param.mode == CHAIN_GRAPH_REL) | ||
123 | new_total = self->children_hit; | ||
124 | else | ||
125 | new_total = total; | ||
126 | |||
127 | remaining = new_total; | ||
128 | node = rb_first(&self->rb_root); | ||
129 | while (node) { | ||
130 | struct callchain_node *child = rb_entry(node, struct callchain_node, rb_node); | ||
131 | struct rb_node *next = rb_next(node); | ||
132 | u64 cumul = cumul_hits(child); | ||
133 | struct callchain_list *chain; | ||
134 | int first = true, printed = 0; | ||
135 | int chain_idx = -1; | ||
136 | remaining -= cumul; | ||
137 | |||
138 | indexes[depth] = NEWT_ARG_APPEND; | ||
139 | indexes[depth + 1] = NEWT_ARG_LAST; | ||
140 | |||
141 | list_for_each_entry(chain, &child->val, list) { | ||
142 | char ipstr[BITS_PER_LONG / 4 + 1], | ||
143 | *alloc_str = NULL; | ||
144 | const char *str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); | ||
145 | |||
146 | if (first) { | ||
147 | double percent = cumul * 100.0 / new_total; | ||
148 | |||
149 | first = false; | ||
150 | if (asprintf(&alloc_str, "%2.2f%% %s", percent, str) < 0) | ||
151 | str = "Not enough memory!"; | ||
152 | else | ||
153 | str = alloc_str; | ||
154 | } else { | ||
155 | indexes[depth] = idx; | ||
156 | indexes[depth + 1] = NEWT_ARG_APPEND; | ||
157 | indexes[depth + 2] = NEWT_ARG_LAST; | ||
158 | ++chain_idx; | ||
159 | } | ||
160 | newt_checkbox_tree__add(tree, str, &chain->ms, indexes); | ||
161 | free(alloc_str); | ||
162 | ++printed; | ||
163 | } | ||
164 | |||
165 | indexes[depth] = idx; | ||
166 | if (chain_idx != -1) | ||
167 | indexes[depth + 1] = chain_idx; | ||
168 | if (printed != 0) | ||
169 | ++idx; | ||
170 | __callchain__append_graph_browser(child, tree, new_total, indexes, | ||
171 | depth + (chain_idx != -1 ? 2 : 1)); | ||
172 | node = next; | ||
173 | } | ||
174 | } | ||
175 | |||
176 | static void callchain__append_graph_browser(struct callchain_node *self, | ||
177 | newtComponent tree, u64 total, | ||
178 | int *indexes, int parent_idx) | ||
179 | { | ||
180 | struct callchain_list *chain; | ||
181 | int i = 0; | ||
182 | |||
183 | indexes[1] = NEWT_ARG_APPEND; | ||
184 | indexes[2] = NEWT_ARG_LAST; | ||
185 | |||
186 | list_for_each_entry(chain, &self->val, list) { | ||
187 | char ipstr[BITS_PER_LONG / 4 + 1], *str; | ||
188 | |||
189 | if (chain->ip >= PERF_CONTEXT_MAX) | ||
190 | continue; | ||
191 | |||
192 | if (!i++ && sort__first_dimension == SORT_SYM) | ||
193 | continue; | ||
194 | |||
195 | str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr)); | ||
196 | newt_checkbox_tree__add(tree, str, &chain->ms, indexes); | ||
197 | } | ||
198 | |||
199 | indexes[1] = parent_idx; | ||
200 | indexes[2] = NEWT_ARG_APPEND; | ||
201 | indexes[3] = NEWT_ARG_LAST; | ||
202 | __callchain__append_graph_browser(self, tree, total, indexes, 2); | ||
203 | } | ||
204 | |||
205 | static void hist_entry__append_callchain_browser(struct hist_entry *self, | ||
206 | newtComponent tree, u64 total, int parent_idx) | ||
207 | { | ||
208 | struct rb_node *rb_node; | ||
209 | int indexes[1024] = { [0] = parent_idx, }; | ||
210 | int idx = 0; | ||
211 | struct callchain_node *chain; | ||
212 | |||
213 | rb_node = rb_first(&self->sorted_chain); | ||
214 | while (rb_node) { | ||
215 | chain = rb_entry(rb_node, struct callchain_node, rb_node); | ||
216 | switch (callchain_param.mode) { | ||
217 | case CHAIN_FLAT: | ||
218 | break; | ||
219 | case CHAIN_GRAPH_ABS: /* falldown */ | ||
220 | case CHAIN_GRAPH_REL: | ||
221 | callchain__append_graph_browser(chain, tree, total, indexes, idx++); | ||
222 | break; | ||
223 | case CHAIN_NONE: | ||
224 | default: | ||
225 | break; | ||
226 | } | ||
227 | rb_node = rb_next(rb_node); | ||
228 | } | ||
229 | } | ||
230 | |||
231 | /* | ||
232 | * FIXME: get lib/string.c linked with perf somehow | ||
233 | */ | ||
234 | static char *skip_spaces(const char *str) | ||
235 | { | ||
236 | while (isspace(*str)) | ||
237 | ++str; | ||
238 | return (char *)str; | ||
239 | } | ||
240 | |||
241 | static char *strim(char *s) | ||
242 | { | ||
243 | size_t size; | ||
244 | char *end; | ||
245 | |||
246 | s = skip_spaces(s); | ||
247 | size = strlen(s); | ||
248 | if (!size) | ||
249 | return s; | ||
250 | |||
251 | end = s + size - 1; | ||
252 | while (end >= s && isspace(*end)) | ||
253 | end--; | ||
254 | *(end + 1) = '\0'; | ||
255 | |||
256 | return s; | ||
257 | } | ||
258 | |||
259 | static size_t hist_entry__append_browser(struct hist_entry *self, | ||
260 | newtComponent tree, u64 total) | ||
261 | { | ||
262 | char bf[1024], *s; | ||
263 | FILE *fp; | ||
264 | |||
265 | if (symbol_conf.exclude_other && !self->parent) | ||
266 | return 0; | ||
267 | |||
268 | fp = fmemopen(bf, sizeof(bf), "w"); | ||
269 | if (fp == NULL) | ||
270 | return 0; | ||
271 | |||
272 | hist_entry__fprintf(self, NULL, false, 0, fp, total); | ||
273 | fclose(fp); | ||
274 | |||
275 | /* | ||
276 | * FIXME: We shouldn't need to trim, as the printing routines shouldn't | ||
277 | * add spaces it in the first place, the stdio output routines should | ||
278 | * call a __snprintf method instead of the current __print (that | ||
279 | * actually is a __fprintf) one, but get the raw string and _then_ add | ||
280 | * the newline, as this is a detail of stdio printing, not needed in | ||
281 | * other UIs, e.g. newt. | ||
282 | */ | ||
283 | s = strim(bf); | ||
284 | |||
285 | if (symbol_conf.use_callchain) { | ||
286 | int indexes[2]; | ||
287 | |||
288 | indexes[0] = NEWT_ARG_APPEND; | ||
289 | indexes[1] = NEWT_ARG_LAST; | ||
290 | newt_checkbox_tree__add(tree, s, &self->ms, indexes); | ||
291 | } else | ||
292 | newtListboxAppendEntry(tree, s, &self->ms); | ||
293 | |||
294 | return strlen(s); | ||
295 | } | ||
296 | |||
297 | static void map_symbol__annotate_browser(const struct map_symbol *self) | ||
298 | { | ||
299 | FILE *fp; | ||
300 | int cols, rows; | ||
301 | newtComponent form, tree; | ||
302 | struct newtExitStruct es; | ||
303 | char *str; | ||
304 | size_t line_len, max_line_len = 0; | ||
305 | size_t max_usable_width; | ||
306 | char *line = NULL; | ||
307 | |||
308 | if (self->sym == NULL) | ||
309 | return; | ||
310 | |||
311 | if (asprintf(&str, "perf annotate -d \"%s\" %s 2>&1 | expand", | ||
312 | self->map->dso->name, self->sym->name) < 0) | ||
313 | return; | ||
314 | |||
315 | fp = popen(str, "r"); | ||
316 | if (fp == NULL) | ||
317 | goto out_free_str; | ||
318 | |||
319 | newtPushHelpLine("Press ESC to exit"); | ||
320 | newtGetScreenSize(&cols, &rows); | ||
321 | tree = newtListbox(0, 0, rows - 5, NEWT_FLAG_SCROLL); | ||
322 | |||
323 | while (!feof(fp)) { | ||
324 | if (getline(&line, &line_len, fp) < 0 || !line_len) | ||
325 | break; | ||
326 | while (line_len != 0 && isspace(line[line_len - 1])) | ||
327 | line[--line_len] = '\0'; | ||
328 | |||
329 | if (line_len > max_line_len) | ||
330 | max_line_len = line_len; | ||
331 | newtListboxAppendEntry(tree, line, NULL); | ||
332 | } | ||
333 | fclose(fp); | ||
334 | free(line); | ||
335 | |||
336 | max_usable_width = cols - 22; | ||
337 | if (max_line_len > max_usable_width) | ||
338 | max_line_len = max_usable_width; | ||
339 | |||
340 | newtListboxSetWidth(tree, max_line_len); | ||
341 | |||
342 | newtCenteredWindow(max_line_len + 2, rows - 5, self->sym->name); | ||
343 | form = newt_form__new(); | ||
344 | newtFormAddComponents(form, tree, NULL); | ||
345 | |||
346 | newtFormRun(form, &es); | ||
347 | newtFormDestroy(form); | ||
348 | newtPopWindow(); | ||
349 | newtPopHelpLine(); | ||
350 | out_free_str: | ||
351 | free(str); | ||
352 | } | ||
353 | |||
354 | static const void *newt__symbol_tree_get_current(newtComponent self) | ||
355 | { | ||
356 | if (symbol_conf.use_callchain) | ||
357 | return newtCheckboxTreeGetCurrent(self); | ||
358 | return newtListboxGetCurrent(self); | ||
359 | } | ||
360 | |||
361 | static void perf_session__selection(newtComponent self, void *data) | ||
362 | { | ||
363 | const struct map_symbol **symbol_ptr = data; | ||
364 | *symbol_ptr = newt__symbol_tree_get_current(self); | ||
365 | } | ||
366 | |||
367 | void perf_session__browse_hists(struct rb_root *hists, u64 session_total, | ||
368 | const char *helpline) | ||
369 | { | ||
370 | struct sort_entry *se; | ||
371 | struct rb_node *nd; | ||
372 | char seq[] = "."; | ||
373 | unsigned int width; | ||
374 | char *col_width = symbol_conf.col_width_list_str; | ||
375 | int rows, cols, idx; | ||
376 | int max_len = 0; | ||
377 | char str[1024]; | ||
378 | newtComponent form, tree; | ||
379 | struct newtExitStruct es; | ||
380 | const struct map_symbol *selection; | ||
381 | |||
382 | snprintf(str, sizeof(str), "Samples: %Ld", session_total); | ||
383 | newtDrawRootText(0, 0, str); | ||
384 | newtPushHelpLine(helpline); | ||
385 | |||
386 | newtGetScreenSize(&cols, &rows); | ||
387 | |||
388 | if (symbol_conf.use_callchain) | ||
389 | tree = newtCheckboxTreeMulti(0, 0, rows - 5, seq, | ||
390 | NEWT_FLAG_SCROLL); | ||
391 | else | ||
392 | tree = newtListbox(0, 0, rows - 5, (NEWT_FLAG_SCROLL | | ||
393 | NEWT_FLAG_RETURNEXIT)); | ||
394 | |||
395 | newtComponentAddCallback(tree, perf_session__selection, &selection); | ||
396 | |||
397 | list_for_each_entry(se, &hist_entry__sort_list, list) { | ||
398 | if (se->elide) | ||
399 | continue; | ||
400 | width = strlen(se->header); | ||
401 | if (se->width) { | ||
402 | if (symbol_conf.col_width_list_str) { | ||
403 | if (col_width) { | ||
404 | *se->width = atoi(col_width); | ||
405 | col_width = strchr(col_width, ','); | ||
406 | if (col_width) | ||
407 | ++col_width; | ||
408 | } | ||
409 | } | ||
410 | *se->width = max(*se->width, width); | ||
411 | } | ||
412 | } | ||
413 | |||
414 | idx = 0; | ||
415 | for (nd = rb_first(hists); nd; nd = rb_next(nd)) { | ||
416 | struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node); | ||
417 | int len = hist_entry__append_browser(h, tree, session_total); | ||
418 | if (len > max_len) | ||
419 | max_len = len; | ||
420 | if (symbol_conf.use_callchain) | ||
421 | hist_entry__append_callchain_browser(h, tree, session_total, idx++); | ||
422 | } | ||
423 | |||
424 | if (max_len > cols) | ||
425 | max_len = cols - 3; | ||
426 | |||
427 | if (!symbol_conf.use_callchain) | ||
428 | newtListboxSetWidth(tree, max_len); | ||
429 | |||
430 | newtCenteredWindow(max_len + (symbol_conf.use_callchain ? 5 : 0), | ||
431 | rows - 5, "Report"); | ||
432 | form = newt_form__new(); | ||
433 | newtFormAddHotKey(form, 'A'); | ||
434 | newtFormAddHotKey(form, 'a'); | ||
435 | newtFormAddHotKey(form, NEWT_KEY_RIGHT); | ||
436 | newtFormAddComponents(form, tree, NULL); | ||
437 | selection = newt__symbol_tree_get_current(tree); | ||
438 | |||
439 | while (1) { | ||
440 | char annotate[512]; | ||
441 | const char *options[2]; | ||
442 | int nr_options = 0, choice = 0; | ||
443 | |||
444 | newtFormRun(form, &es); | ||
445 | if (es.reason == NEWT_EXIT_HOTKEY) { | ||
446 | if (toupper(es.u.key) == 'A') | ||
447 | goto do_annotate; | ||
448 | if (es.u.key == NEWT_KEY_ESCAPE || | ||
449 | toupper(es.u.key) == 'Q' || | ||
450 | es.u.key == CTRL('c')) { | ||
451 | if (dialog_yesno("Do you really want to exit?")) | ||
452 | break; | ||
453 | else | ||
454 | continue; | ||
455 | } | ||
456 | } | ||
457 | |||
458 | if (selection->sym != NULL) { | ||
459 | snprintf(annotate, sizeof(annotate), | ||
460 | "Annotate %s", selection->sym->name); | ||
461 | options[nr_options++] = annotate; | ||
462 | } | ||
463 | |||
464 | options[nr_options++] = "Exit"; | ||
465 | choice = popup_menu(nr_options, options); | ||
466 | if (choice == nr_options - 1) | ||
467 | break; | ||
468 | do_annotate: | ||
469 | if (selection->sym != NULL && choice >= 0) { | ||
470 | if (selection->map->dso->origin == DSO__ORIG_KERNEL) { | ||
471 | newtPopHelpLine(); | ||
472 | newtPushHelpLine("No vmlinux file found, can't " | ||
473 | "annotate with just a " | ||
474 | "kallsyms file"); | ||
475 | continue; | ||
476 | } | ||
477 | map_symbol__annotate_browser(selection); | ||
478 | } | ||
479 | } | ||
480 | |||
481 | newtFormDestroy(form); | ||
482 | newtPopWindow(); | ||
483 | } | ||
484 | |||
485 | static char browser__last_msg[1024]; | ||
486 | |||
487 | int browser__show_help(const char *format, va_list ap) | ||
488 | { | ||
489 | int ret; | ||
490 | static int backlog; | ||
491 | |||
492 | ret = vsnprintf(browser__last_msg + backlog, | ||
493 | sizeof(browser__last_msg) - backlog, format, ap); | ||
494 | backlog += ret; | ||
495 | |||
496 | if (browser__last_msg[backlog - 1] == '\n') { | ||
497 | newtPopHelpLine(); | ||
498 | newtPushHelpLine(browser__last_msg); | ||
499 | newtRefresh(); | ||
500 | backlog = 0; | ||
501 | } | ||
502 | |||
503 | return ret; | ||
504 | } | ||
505 | |||
506 | void setup_browser(void) | ||
507 | { | ||
508 | if (!isatty(1)) | ||
509 | return; | ||
510 | |||
511 | use_browser = true; | ||
512 | newtInit(); | ||
513 | newtCls(); | ||
514 | newtPushHelpLine(" "); | ||
515 | } | ||
516 | |||
517 | void exit_browser(bool wait_for_ok) | ||
518 | { | ||
519 | if (use_browser) { | ||
520 | if (wait_for_ok) { | ||
521 | char title[] = "Fatal Error", ok[] = "Ok"; | ||
522 | newtWinMessage(title, ok, browser__last_msg); | ||
523 | } | ||
524 | newtFinished(); | ||
525 | } | ||
526 | } | ||
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 05d0c5c2030c..435781e0c205 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -5,6 +5,7 @@ | |||
5 | #include "parse-events.h" | 5 | #include "parse-events.h" |
6 | #include "exec_cmd.h" | 6 | #include "exec_cmd.h" |
7 | #include "string.h" | 7 | #include "string.h" |
8 | #include "symbol.h" | ||
8 | #include "cache.h" | 9 | #include "cache.h" |
9 | #include "header.h" | 10 | #include "header.h" |
10 | #include "debugfs.h" | 11 | #include "debugfs.h" |
@@ -656,6 +657,10 @@ parse_raw_event(const char **strp, struct perf_event_attr *attr) | |||
656 | return EVT_FAILED; | 657 | return EVT_FAILED; |
657 | n = hex2u64(str + 1, &config); | 658 | n = hex2u64(str + 1, &config); |
658 | if (n > 0) { | 659 | if (n > 0) { |
660 | if (str[n+1] == 'p') { | ||
661 | attr->precise = 1; | ||
662 | n++; | ||
663 | } | ||
659 | *strp = str + n + 1; | 664 | *strp = str + n + 1; |
660 | attr->type = PERF_TYPE_RAW; | 665 | attr->type = PERF_TYPE_RAW; |
661 | attr->config = config; | 666 | attr->config = config; |
diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index efebd5b476b3..79dfa0c34b3c 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c | |||
@@ -500,6 +500,7 @@ int usage_with_options_internal(const char * const *usagestr, | |||
500 | void usage_with_options(const char * const *usagestr, | 500 | void usage_with_options(const char * const *usagestr, |
501 | const struct option *opts) | 501 | const struct option *opts) |
502 | { | 502 | { |
503 | exit_browser(false); | ||
503 | usage_with_options_internal(usagestr, opts, 0); | 504 | usage_with_options_internal(usagestr, opts, 0); |
504 | exit(129); | 505 | exit(129); |
505 | } | 506 | } |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 7c004b6ef24f..3fc0be741b8e 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -33,19 +33,26 @@ | |||
33 | #include <limits.h> | 33 | #include <limits.h> |
34 | 34 | ||
35 | #undef _GNU_SOURCE | 35 | #undef _GNU_SOURCE |
36 | #include "util.h" | ||
36 | #include "event.h" | 37 | #include "event.h" |
37 | #include "string.h" | 38 | #include "string.h" |
38 | #include "strlist.h" | 39 | #include "strlist.h" |
39 | #include "debug.h" | 40 | #include "debug.h" |
40 | #include "cache.h" | 41 | #include "cache.h" |
41 | #include "color.h" | 42 | #include "color.h" |
42 | #include "parse-events.h" /* For debugfs_path */ | 43 | #include "symbol.h" |
44 | #include "thread.h" | ||
45 | #include "trace-event.h" /* For __unused */ | ||
46 | #include "parse-events.h" /* For debugfs_path */ | ||
43 | #include "probe-event.h" | 47 | #include "probe-event.h" |
48 | #include "probe-finder.h" | ||
44 | 49 | ||
45 | #define MAX_CMDLEN 256 | 50 | #define MAX_CMDLEN 256 |
46 | #define MAX_PROBE_ARGS 128 | 51 | #define MAX_PROBE_ARGS 128 |
47 | #define PERFPROBE_GROUP "probe" | 52 | #define PERFPROBE_GROUP "probe" |
48 | 53 | ||
54 | bool probe_event_dry_run; /* Dry run flag */ | ||
55 | |||
49 | #define semantic_error(msg ...) die("Semantic error :" msg) | 56 | #define semantic_error(msg ...) die("Semantic error :" msg) |
50 | 57 | ||
51 | /* If there is no space to write, returns -E2BIG. */ | 58 | /* If there is no space to write, returns -E2BIG. */ |
@@ -64,6 +71,208 @@ static int e_snprintf(char *str, size_t size, const char *format, ...) | |||
64 | return ret; | 71 | return ret; |
65 | } | 72 | } |
66 | 73 | ||
74 | static char *synthesize_perf_probe_point(struct perf_probe_point *pp); | ||
75 | static struct map_groups kmap_groups; | ||
76 | static struct map *kmaps[MAP__NR_TYPES]; | ||
77 | |||
78 | /* Initialize symbol maps and path of vmlinux */ | ||
79 | static void init_vmlinux(void) | ||
80 | { | ||
81 | symbol_conf.sort_by_name = true; | ||
82 | if (symbol_conf.vmlinux_name == NULL) | ||
83 | symbol_conf.try_vmlinux_path = true; | ||
84 | else | ||
85 | pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name); | ||
86 | if (symbol__init() < 0) | ||
87 | die("Failed to init symbol map."); | ||
88 | |||
89 | map_groups__init(&kmap_groups); | ||
90 | if (map_groups__create_kernel_maps(&kmap_groups, kmaps) < 0) | ||
91 | die("Failed to create kernel maps."); | ||
92 | } | ||
93 | |||
94 | #ifdef DWARF_SUPPORT | ||
95 | static int open_vmlinux(void) | ||
96 | { | ||
97 | if (map__load(kmaps[MAP__FUNCTION], NULL) < 0) { | ||
98 | pr_debug("Failed to load kernel map.\n"); | ||
99 | return -EINVAL; | ||
100 | } | ||
101 | pr_debug("Try to open %s\n", kmaps[MAP__FUNCTION]->dso->long_name); | ||
102 | return open(kmaps[MAP__FUNCTION]->dso->long_name, O_RDONLY); | ||
103 | } | ||
104 | |||
105 | static void convert_to_perf_probe_point(struct kprobe_trace_point *tp, | ||
106 | struct perf_probe_point *pp) | ||
107 | { | ||
108 | struct symbol *sym; | ||
109 | int fd, ret = 0; | ||
110 | |||
111 | sym = map__find_symbol_by_name(kmaps[MAP__FUNCTION], | ||
112 | tp->symbol, NULL); | ||
113 | if (sym) { | ||
114 | fd = open_vmlinux(); | ||
115 | ret = find_perf_probe_point(fd, sym->start + tp->offset, pp); | ||
116 | close(fd); | ||
117 | } | ||
118 | if (ret <= 0) { | ||
119 | pp->function = xstrdup(tp->symbol); | ||
120 | pp->offset = tp->offset; | ||
121 | } | ||
122 | pp->retprobe = tp->retprobe; | ||
123 | } | ||
124 | |||
125 | /* Try to find perf_probe_event with debuginfo */ | ||
126 | static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, | ||
127 | struct kprobe_trace_event **tevs) | ||
128 | { | ||
129 | bool need_dwarf = perf_probe_event_need_dwarf(pev); | ||
130 | int fd, ntevs; | ||
131 | |||
132 | fd = open_vmlinux(); | ||
133 | if (fd < 0) { | ||
134 | if (need_dwarf) | ||
135 | die("Could not open debuginfo file."); | ||
136 | |||
137 | pr_debug("Could not open vmlinux. Try to use symbols.\n"); | ||
138 | return 0; | ||
139 | } | ||
140 | |||
141 | /* Searching trace events corresponding to probe event */ | ||
142 | ntevs = find_kprobe_trace_events(fd, pev, tevs); | ||
143 | close(fd); | ||
144 | |||
145 | if (ntevs > 0) /* Succeeded to find trace events */ | ||
146 | return ntevs; | ||
147 | |||
148 | if (ntevs == 0) /* No error but failed to find probe point. */ | ||
149 | die("Probe point '%s' not found. - probe not added.", | ||
150 | synthesize_perf_probe_point(&pev->point)); | ||
151 | |||
152 | /* Error path */ | ||
153 | if (need_dwarf) { | ||
154 | if (ntevs == -ENOENT) | ||
155 | pr_warning("No dwarf info found in the vmlinux - " | ||
156 | "please rebuild with CONFIG_DEBUG_INFO=y.\n"); | ||
157 | die("Could not analyze debuginfo."); | ||
158 | } | ||
159 | pr_debug("An error occurred in debuginfo analysis." | ||
160 | " Try to use symbols.\n"); | ||
161 | return 0; | ||
162 | |||
163 | } | ||
164 | |||
165 | #define LINEBUF_SIZE 256 | ||
166 | #define NR_ADDITIONAL_LINES 2 | ||
167 | |||
168 | static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num) | ||
169 | { | ||
170 | char buf[LINEBUF_SIZE]; | ||
171 | const char *color = PERF_COLOR_BLUE; | ||
172 | |||
173 | if (fgets(buf, LINEBUF_SIZE, fp) == NULL) | ||
174 | goto error; | ||
175 | if (!skip) { | ||
176 | if (show_num) | ||
177 | fprintf(stdout, "%7u %s", l, buf); | ||
178 | else | ||
179 | color_fprintf(stdout, color, " %s", buf); | ||
180 | } | ||
181 | |||
182 | while (strlen(buf) == LINEBUF_SIZE - 1 && | ||
183 | buf[LINEBUF_SIZE - 2] != '\n') { | ||
184 | if (fgets(buf, LINEBUF_SIZE, fp) == NULL) | ||
185 | goto error; | ||
186 | if (!skip) { | ||
187 | if (show_num) | ||
188 | fprintf(stdout, "%s", buf); | ||
189 | else | ||
190 | color_fprintf(stdout, color, "%s", buf); | ||
191 | } | ||
192 | } | ||
193 | return; | ||
194 | error: | ||
195 | if (feof(fp)) | ||
196 | die("Source file is shorter than expected."); | ||
197 | else | ||
198 | die("File read error: %s", strerror(errno)); | ||
199 | } | ||
200 | |||
201 | /* | ||
202 | * Show line-range always requires debuginfo to find source file and | ||
203 | * line number. | ||
204 | */ | ||
205 | void show_line_range(struct line_range *lr) | ||
206 | { | ||
207 | unsigned int l = 1; | ||
208 | struct line_node *ln; | ||
209 | FILE *fp; | ||
210 | int fd, ret; | ||
211 | |||
212 | /* Search a line range */ | ||
213 | init_vmlinux(); | ||
214 | fd = open_vmlinux(); | ||
215 | if (fd < 0) | ||
216 | die("Could not open debuginfo file."); | ||
217 | ret = find_line_range(fd, lr); | ||
218 | if (ret <= 0) | ||
219 | die("Source line is not found.\n"); | ||
220 | close(fd); | ||
221 | |||
222 | setup_pager(); | ||
223 | |||
224 | if (lr->function) | ||
225 | fprintf(stdout, "<%s:%d>\n", lr->function, | ||
226 | lr->start - lr->offset); | ||
227 | else | ||
228 | fprintf(stdout, "<%s:%d>\n", lr->file, lr->start); | ||
229 | |||
230 | fp = fopen(lr->path, "r"); | ||
231 | if (fp == NULL) | ||
232 | die("Failed to open %s: %s", lr->path, strerror(errno)); | ||
233 | /* Skip to starting line number */ | ||
234 | while (l < lr->start) | ||
235 | show_one_line(fp, l++, true, false); | ||
236 | |||
237 | list_for_each_entry(ln, &lr->line_list, list) { | ||
238 | while (ln->line > l) | ||
239 | show_one_line(fp, (l++) - lr->offset, false, false); | ||
240 | show_one_line(fp, (l++) - lr->offset, false, true); | ||
241 | } | ||
242 | |||
243 | if (lr->end == INT_MAX) | ||
244 | lr->end = l + NR_ADDITIONAL_LINES; | ||
245 | while (l < lr->end && !feof(fp)) | ||
246 | show_one_line(fp, (l++) - lr->offset, false, false); | ||
247 | |||
248 | fclose(fp); | ||
249 | } | ||
250 | |||
251 | #else /* !DWARF_SUPPORT */ | ||
252 | |||
253 | static void convert_to_perf_probe_point(struct kprobe_trace_point *tp, | ||
254 | struct perf_probe_point *pp) | ||
255 | { | ||
256 | pp->function = xstrdup(tp->symbol); | ||
257 | pp->offset = tp->offset; | ||
258 | pp->retprobe = tp->retprobe; | ||
259 | } | ||
260 | |||
261 | static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev, | ||
262 | struct kprobe_trace_event **tevs __unused) | ||
263 | { | ||
264 | if (perf_probe_event_need_dwarf(pev)) | ||
265 | die("Debuginfo-analysis is not supported"); | ||
266 | return 0; | ||
267 | } | ||
268 | |||
269 | void show_line_range(struct line_range *lr __unused) | ||
270 | { | ||
271 | die("Debuginfo-analysis is not supported"); | ||
272 | } | ||
273 | |||
274 | #endif | ||
275 | |||
67 | void parse_line_range_desc(const char *arg, struct line_range *lr) | 276 | void parse_line_range_desc(const char *arg, struct line_range *lr) |
68 | { | 277 | { |
69 | const char *ptr; | 278 | const char *ptr; |
@@ -90,9 +299,9 @@ void parse_line_range_desc(const char *arg, struct line_range *lr) | |||
90 | if (*tmp != '\0') | 299 | if (*tmp != '\0') |
91 | semantic_error("Tailing with invalid character '%d'.", | 300 | semantic_error("Tailing with invalid character '%d'.", |
92 | *tmp); | 301 | *tmp); |
93 | tmp = strndup(arg, (ptr - arg)); | 302 | tmp = xstrndup(arg, (ptr - arg)); |
94 | } else | 303 | } else |
95 | tmp = strdup(arg); | 304 | tmp = xstrdup(arg); |
96 | 305 | ||
97 | if (strchr(tmp, '.')) | 306 | if (strchr(tmp, '.')) |
98 | lr->file = tmp; | 307 | lr->file = tmp; |
@@ -113,8 +322,9 @@ static bool check_event_name(const char *name) | |||
113 | } | 322 | } |
114 | 323 | ||
115 | /* Parse probepoint definition. */ | 324 | /* Parse probepoint definition. */ |
116 | static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) | 325 | static void parse_perf_probe_point(char *arg, struct perf_probe_event *pev) |
117 | { | 326 | { |
327 | struct perf_probe_point *pp = &pev->point; | ||
118 | char *ptr, *tmp; | 328 | char *ptr, *tmp; |
119 | char c, nc = 0; | 329 | char c, nc = 0; |
120 | /* | 330 | /* |
@@ -135,7 +345,8 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) | |||
135 | if (!check_event_name(arg)) | 345 | if (!check_event_name(arg)) |
136 | semantic_error("%s is bad for event name -it must " | 346 | semantic_error("%s is bad for event name -it must " |
137 | "follow C symbol-naming rule.", arg); | 347 | "follow C symbol-naming rule.", arg); |
138 | pp->event = strdup(arg); | 348 | pev->event = xstrdup(arg); |
349 | pev->group = NULL; | ||
139 | arg = tmp; | 350 | arg = tmp; |
140 | } | 351 | } |
141 | 352 | ||
@@ -147,17 +358,16 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) | |||
147 | 358 | ||
148 | /* Check arg is function or file and copy it */ | 359 | /* Check arg is function or file and copy it */ |
149 | if (strchr(arg, '.')) /* File */ | 360 | if (strchr(arg, '.')) /* File */ |
150 | pp->file = strdup(arg); | 361 | pp->file = xstrdup(arg); |
151 | else /* Function */ | 362 | else /* Function */ |
152 | pp->function = strdup(arg); | 363 | pp->function = xstrdup(arg); |
153 | DIE_IF(pp->file == NULL && pp->function == NULL); | ||
154 | 364 | ||
155 | /* Parse other options */ | 365 | /* Parse other options */ |
156 | while (ptr) { | 366 | while (ptr) { |
157 | arg = ptr; | 367 | arg = ptr; |
158 | c = nc; | 368 | c = nc; |
159 | if (c == ';') { /* Lazy pattern must be the last part */ | 369 | if (c == ';') { /* Lazy pattern must be the last part */ |
160 | pp->lazy_line = strdup(arg); | 370 | pp->lazy_line = xstrdup(arg); |
161 | break; | 371 | break; |
162 | } | 372 | } |
163 | ptr = strpbrk(arg, ";:+@%"); | 373 | ptr = strpbrk(arg, ";:+@%"); |
@@ -181,8 +391,7 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) | |||
181 | case '@': /* File name */ | 391 | case '@': /* File name */ |
182 | if (pp->file) | 392 | if (pp->file) |
183 | semantic_error("SRC@SRC is not allowed."); | 393 | semantic_error("SRC@SRC is not allowed."); |
184 | pp->file = strdup(arg); | 394 | pp->file = xstrdup(arg); |
185 | DIE_IF(pp->file == NULL); | ||
186 | break; | 395 | break; |
187 | case '%': /* Probe places */ | 396 | case '%': /* Probe places */ |
188 | if (strcmp(arg, "return") == 0) { | 397 | if (strcmp(arg, "return") == 0) { |
@@ -220,59 +429,108 @@ static void parse_perf_probe_probepoint(char *arg, struct probe_point *pp) | |||
220 | semantic_error("Offset/Line/Lazy pattern can't be used with " | 429 | semantic_error("Offset/Line/Lazy pattern can't be used with " |
221 | "return probe."); | 430 | "return probe."); |
222 | 431 | ||
223 | pr_debug("symbol:%s file:%s line:%d offset:%d return:%d lazy:%s\n", | 432 | pr_debug("symbol:%s file:%s line:%d offset:%lu return:%d lazy:%s\n", |
224 | pp->function, pp->file, pp->line, pp->offset, pp->retprobe, | 433 | pp->function, pp->file, pp->line, pp->offset, pp->retprobe, |
225 | pp->lazy_line); | 434 | pp->lazy_line); |
226 | } | 435 | } |
227 | 436 | ||
228 | /* Parse perf-probe event definition */ | 437 | /* Parse perf-probe event argument */ |
229 | void parse_perf_probe_event(const char *str, struct probe_point *pp, | 438 | static void parse_perf_probe_arg(const char *str, struct perf_probe_arg *arg) |
230 | bool *need_dwarf) | 439 | { |
440 | const char *tmp; | ||
441 | struct perf_probe_arg_field **fieldp; | ||
442 | |||
443 | pr_debug("parsing arg: %s into ", str); | ||
444 | |||
445 | tmp = strpbrk(str, "-."); | ||
446 | if (!is_c_varname(str) || !tmp) { | ||
447 | /* A variable, register, symbol or special value */ | ||
448 | arg->name = xstrdup(str); | ||
449 | pr_debug("%s\n", arg->name); | ||
450 | return; | ||
451 | } | ||
452 | |||
453 | /* Structure fields */ | ||
454 | arg->name = xstrndup(str, tmp - str); | ||
455 | pr_debug("%s, ", arg->name); | ||
456 | fieldp = &arg->field; | ||
457 | |||
458 | do { | ||
459 | *fieldp = xzalloc(sizeof(struct perf_probe_arg_field)); | ||
460 | if (*tmp == '.') { | ||
461 | str = tmp + 1; | ||
462 | (*fieldp)->ref = false; | ||
463 | } else if (tmp[1] == '>') { | ||
464 | str = tmp + 2; | ||
465 | (*fieldp)->ref = true; | ||
466 | } else | ||
467 | semantic_error("Argument parse error: %s", str); | ||
468 | |||
469 | tmp = strpbrk(str, "-."); | ||
470 | if (tmp) { | ||
471 | (*fieldp)->name = xstrndup(str, tmp - str); | ||
472 | pr_debug("%s(%d), ", (*fieldp)->name, (*fieldp)->ref); | ||
473 | fieldp = &(*fieldp)->next; | ||
474 | } | ||
475 | } while (tmp); | ||
476 | (*fieldp)->name = xstrdup(str); | ||
477 | pr_debug("%s(%d)\n", (*fieldp)->name, (*fieldp)->ref); | ||
478 | } | ||
479 | |||
480 | /* Parse perf-probe event command */ | ||
481 | void parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev) | ||
231 | { | 482 | { |
232 | char **argv; | 483 | char **argv; |
233 | int argc, i; | 484 | int argc, i; |
234 | 485 | ||
235 | *need_dwarf = false; | 486 | argv = argv_split(cmd, &argc); |
236 | |||
237 | argv = argv_split(str, &argc); | ||
238 | if (!argv) | 487 | if (!argv) |
239 | die("argv_split failed."); | 488 | die("argv_split failed."); |
240 | if (argc > MAX_PROBE_ARGS + 1) | 489 | if (argc > MAX_PROBE_ARGS + 1) |
241 | semantic_error("Too many arguments"); | 490 | semantic_error("Too many arguments"); |
242 | 491 | ||
243 | /* Parse probe point */ | 492 | /* Parse probe point */ |
244 | parse_perf_probe_probepoint(argv[0], pp); | 493 | parse_perf_probe_point(argv[0], pev); |
245 | if (pp->file || pp->line || pp->lazy_line) | ||
246 | *need_dwarf = true; | ||
247 | 494 | ||
248 | /* Copy arguments and ensure return probe has no C argument */ | 495 | /* Copy arguments and ensure return probe has no C argument */ |
249 | pp->nr_args = argc - 1; | 496 | pev->nargs = argc - 1; |
250 | pp->args = zalloc(sizeof(char *) * pp->nr_args); | 497 | pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs); |
251 | for (i = 0; i < pp->nr_args; i++) { | 498 | for (i = 0; i < pev->nargs; i++) { |
252 | pp->args[i] = strdup(argv[i + 1]); | 499 | parse_perf_probe_arg(argv[i + 1], &pev->args[i]); |
253 | if (!pp->args[i]) | 500 | if (is_c_varname(pev->args[i].name) && pev->point.retprobe) |
254 | die("Failed to copy argument."); | 501 | semantic_error("You can't specify local variable for" |
255 | if (is_c_varname(pp->args[i])) { | 502 | " kretprobe"); |
256 | if (pp->retprobe) | ||
257 | semantic_error("You can't specify local" | ||
258 | " variable for kretprobe"); | ||
259 | *need_dwarf = true; | ||
260 | } | ||
261 | } | 503 | } |
262 | 504 | ||
263 | argv_free(argv); | 505 | argv_free(argv); |
264 | } | 506 | } |
265 | 507 | ||
508 | /* Return true if this perf_probe_event requires debuginfo */ | ||
509 | bool perf_probe_event_need_dwarf(struct perf_probe_event *pev) | ||
510 | { | ||
511 | int i; | ||
512 | |||
513 | if (pev->point.file || pev->point.line || pev->point.lazy_line) | ||
514 | return true; | ||
515 | |||
516 | for (i = 0; i < pev->nargs; i++) | ||
517 | if (is_c_varname(pev->args[i].name)) | ||
518 | return true; | ||
519 | |||
520 | return false; | ||
521 | } | ||
522 | |||
266 | /* Parse kprobe_events event into struct probe_point */ | 523 | /* Parse kprobe_events event into struct probe_point */ |
267 | void parse_trace_kprobe_event(const char *str, struct probe_point *pp) | 524 | void parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev) |
268 | { | 525 | { |
526 | struct kprobe_trace_point *tp = &tev->point; | ||
269 | char pr; | 527 | char pr; |
270 | char *p; | 528 | char *p; |
271 | int ret, i, argc; | 529 | int ret, i, argc; |
272 | char **argv; | 530 | char **argv; |
273 | 531 | ||
274 | pr_debug("Parsing kprobe_events: %s\n", str); | 532 | pr_debug("Parsing kprobe_events: %s\n", cmd); |
275 | argv = argv_split(str, &argc); | 533 | argv = argv_split(cmd, &argc); |
276 | if (!argv) | 534 | if (!argv) |
277 | die("argv_split failed."); | 535 | die("argv_split failed."); |
278 | if (argc < 2) | 536 | if (argc < 2) |
@@ -280,132 +538,314 @@ void parse_trace_kprobe_event(const char *str, struct probe_point *pp) | |||
280 | 538 | ||
281 | /* Scan event and group name. */ | 539 | /* Scan event and group name. */ |
282 | ret = sscanf(argv[0], "%c:%a[^/ \t]/%a[^ \t]", | 540 | ret = sscanf(argv[0], "%c:%a[^/ \t]/%a[^ \t]", |
283 | &pr, (float *)(void *)&pp->group, | 541 | &pr, (float *)(void *)&tev->group, |
284 | (float *)(void *)&pp->event); | 542 | (float *)(void *)&tev->event); |
285 | if (ret != 3) | 543 | if (ret != 3) |
286 | semantic_error("Failed to parse event name: %s", argv[0]); | 544 | semantic_error("Failed to parse event name: %s", argv[0]); |
287 | pr_debug("Group:%s Event:%s probe:%c\n", pp->group, pp->event, pr); | 545 | pr_debug("Group:%s Event:%s probe:%c\n", tev->group, tev->event, pr); |
288 | 546 | ||
289 | pp->retprobe = (pr == 'r'); | 547 | tp->retprobe = (pr == 'r'); |
290 | 548 | ||
291 | /* Scan function name and offset */ | 549 | /* Scan function name and offset */ |
292 | ret = sscanf(argv[1], "%a[^+]+%d", (float *)(void *)&pp->function, | 550 | ret = sscanf(argv[1], "%a[^+]+%lu", (float *)(void *)&tp->symbol, |
293 | &pp->offset); | 551 | &tp->offset); |
294 | if (ret == 1) | 552 | if (ret == 1) |
295 | pp->offset = 0; | 553 | tp->offset = 0; |
296 | |||
297 | /* kprobe_events doesn't have this information */ | ||
298 | pp->line = 0; | ||
299 | pp->file = NULL; | ||
300 | 554 | ||
301 | pp->nr_args = argc - 2; | 555 | tev->nargs = argc - 2; |
302 | pp->args = zalloc(sizeof(char *) * pp->nr_args); | 556 | tev->args = xzalloc(sizeof(struct kprobe_trace_arg) * tev->nargs); |
303 | for (i = 0; i < pp->nr_args; i++) { | 557 | for (i = 0; i < tev->nargs; i++) { |
304 | p = strchr(argv[i + 2], '='); | 558 | p = strchr(argv[i + 2], '='); |
305 | if (p) /* We don't need which register is assigned. */ | 559 | if (p) /* We don't need which register is assigned. */ |
306 | *p = '\0'; | 560 | *p++ = '\0'; |
307 | pp->args[i] = strdup(argv[i + 2]); | 561 | else |
308 | if (!pp->args[i]) | 562 | p = argv[i + 2]; |
309 | die("Failed to copy argument."); | 563 | tev->args[i].name = xstrdup(argv[i + 2]); |
564 | /* TODO: parse regs and offset */ | ||
565 | tev->args[i].value = xstrdup(p); | ||
310 | } | 566 | } |
311 | 567 | ||
312 | argv_free(argv); | 568 | argv_free(argv); |
313 | } | 569 | } |
314 | 570 | ||
315 | /* Synthesize only probe point (not argument) */ | 571 | /* Compose only probe arg */ |
316 | int synthesize_perf_probe_point(struct probe_point *pp) | 572 | int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len) |
317 | { | 573 | { |
318 | char *buf; | 574 | struct perf_probe_arg_field *field = pa->field; |
319 | char offs[64] = "", line[64] = ""; | ||
320 | int ret; | 575 | int ret; |
576 | char *tmp = buf; | ||
321 | 577 | ||
322 | pp->probes[0] = buf = zalloc(MAX_CMDLEN); | 578 | ret = e_snprintf(tmp, len, "%s", pa->name); |
323 | pp->found = 1; | 579 | if (ret <= 0) |
324 | if (!buf) | 580 | goto error; |
325 | die("Failed to allocate memory by zalloc."); | 581 | tmp += ret; |
582 | len -= ret; | ||
583 | |||
584 | while (field) { | ||
585 | ret = e_snprintf(tmp, len, "%s%s", field->ref ? "->" : ".", | ||
586 | field->name); | ||
587 | if (ret <= 0) | ||
588 | goto error; | ||
589 | tmp += ret; | ||
590 | len -= ret; | ||
591 | field = field->next; | ||
592 | } | ||
593 | return tmp - buf; | ||
594 | error: | ||
595 | die("Failed to synthesize perf probe argument: %s", strerror(-ret)); | ||
596 | } | ||
597 | |||
598 | /* Compose only probe point (not argument) */ | ||
599 | static char *synthesize_perf_probe_point(struct perf_probe_point *pp) | ||
600 | { | ||
601 | char *buf, *tmp; | ||
602 | char offs[32] = "", line[32] = "", file[32] = ""; | ||
603 | int ret, len; | ||
604 | |||
605 | buf = xzalloc(MAX_CMDLEN); | ||
326 | if (pp->offset) { | 606 | if (pp->offset) { |
327 | ret = e_snprintf(offs, 64, "+%d", pp->offset); | 607 | ret = e_snprintf(offs, 32, "+%lu", pp->offset); |
328 | if (ret <= 0) | 608 | if (ret <= 0) |
329 | goto error; | 609 | goto error; |
330 | } | 610 | } |
331 | if (pp->line) { | 611 | if (pp->line) { |
332 | ret = e_snprintf(line, 64, ":%d", pp->line); | 612 | ret = e_snprintf(line, 32, ":%d", pp->line); |
613 | if (ret <= 0) | ||
614 | goto error; | ||
615 | } | ||
616 | if (pp->file) { | ||
617 | len = strlen(pp->file) - 32; | ||
618 | if (len < 0) | ||
619 | len = 0; | ||
620 | tmp = strchr(pp->file + len, '/'); | ||
621 | if (!tmp) | ||
622 | tmp = pp->file + len - 1; | ||
623 | ret = e_snprintf(file, 32, "@%s", tmp + 1); | ||
333 | if (ret <= 0) | 624 | if (ret <= 0) |
334 | goto error; | 625 | goto error; |
335 | } | 626 | } |
336 | 627 | ||
337 | if (pp->function) | 628 | if (pp->function) |
338 | ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s", pp->function, | 629 | ret = e_snprintf(buf, MAX_CMDLEN, "%s%s%s%s%s", pp->function, |
339 | offs, pp->retprobe ? "%return" : "", line); | 630 | offs, pp->retprobe ? "%return" : "", line, |
631 | file); | ||
340 | else | 632 | else |
341 | ret = e_snprintf(buf, MAX_CMDLEN, "%s%s", pp->file, line); | 633 | ret = e_snprintf(buf, MAX_CMDLEN, "%s%s", file, line); |
342 | if (ret <= 0) { | 634 | if (ret <= 0) |
635 | goto error; | ||
636 | |||
637 | return buf; | ||
343 | error: | 638 | error: |
344 | free(pp->probes[0]); | 639 | die("Failed to synthesize perf probe point: %s", strerror(-ret)); |
345 | pp->probes[0] = NULL; | ||
346 | pp->found = 0; | ||
347 | } | ||
348 | return ret; | ||
349 | } | 640 | } |
350 | 641 | ||
351 | int synthesize_perf_probe_event(struct probe_point *pp) | 642 | #if 0 |
643 | char *synthesize_perf_probe_command(struct perf_probe_event *pev) | ||
352 | { | 644 | { |
353 | char *buf; | 645 | char *buf; |
354 | int i, len, ret; | 646 | int i, len, ret; |
355 | 647 | ||
356 | len = synthesize_perf_probe_point(pp); | 648 | buf = synthesize_perf_probe_point(&pev->point); |
357 | if (len < 0) | 649 | if (!buf) |
358 | return 0; | 650 | return NULL; |
359 | 651 | ||
360 | buf = pp->probes[0]; | 652 | len = strlen(buf); |
361 | for (i = 0; i < pp->nr_args; i++) { | 653 | for (i = 0; i < pev->nargs; i++) { |
362 | ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s", | 654 | ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s", |
363 | pp->args[i]); | 655 | pev->args[i].name); |
364 | if (ret <= 0) | 656 | if (ret <= 0) { |
365 | goto error; | 657 | free(buf); |
658 | return NULL; | ||
659 | } | ||
366 | len += ret; | 660 | len += ret; |
367 | } | 661 | } |
368 | pp->found = 1; | ||
369 | 662 | ||
370 | return pp->found; | 663 | return buf; |
371 | error: | 664 | } |
372 | free(pp->probes[0]); | 665 | #endif |
373 | pp->probes[0] = NULL; | ||
374 | 666 | ||
375 | return ret; | 667 | static int __synthesize_kprobe_trace_arg_ref(struct kprobe_trace_arg_ref *ref, |
668 | char **buf, size_t *buflen, | ||
669 | int depth) | ||
670 | { | ||
671 | int ret; | ||
672 | if (ref->next) { | ||
673 | depth = __synthesize_kprobe_trace_arg_ref(ref->next, buf, | ||
674 | buflen, depth + 1); | ||
675 | if (depth < 0) | ||
676 | goto out; | ||
677 | } | ||
678 | |||
679 | ret = e_snprintf(*buf, *buflen, "%+ld(", ref->offset); | ||
680 | if (ret < 0) | ||
681 | depth = ret; | ||
682 | else { | ||
683 | *buf += ret; | ||
684 | *buflen -= ret; | ||
685 | } | ||
686 | out: | ||
687 | return depth; | ||
688 | |||
689 | } | ||
690 | |||
691 | static int synthesize_kprobe_trace_arg(struct kprobe_trace_arg *arg, | ||
692 | char *buf, size_t buflen) | ||
693 | { | ||
694 | int ret, depth = 0; | ||
695 | char *tmp = buf; | ||
696 | |||
697 | /* Argument name or separator */ | ||
698 | if (arg->name) | ||
699 | ret = e_snprintf(buf, buflen, " %s=", arg->name); | ||
700 | else | ||
701 | ret = e_snprintf(buf, buflen, " "); | ||
702 | if (ret < 0) | ||
703 | return ret; | ||
704 | buf += ret; | ||
705 | buflen -= ret; | ||
706 | |||
707 | /* Dereferencing arguments */ | ||
708 | if (arg->ref) { | ||
709 | depth = __synthesize_kprobe_trace_arg_ref(arg->ref, &buf, | ||
710 | &buflen, 1); | ||
711 | if (depth < 0) | ||
712 | return depth; | ||
713 | } | ||
714 | |||
715 | /* Print argument value */ | ||
716 | ret = e_snprintf(buf, buflen, "%s", arg->value); | ||
717 | if (ret < 0) | ||
718 | return ret; | ||
719 | buf += ret; | ||
720 | buflen -= ret; | ||
721 | |||
722 | /* Closing */ | ||
723 | while (depth--) { | ||
724 | ret = e_snprintf(buf, buflen, ")"); | ||
725 | if (ret < 0) | ||
726 | return ret; | ||
727 | buf += ret; | ||
728 | buflen -= ret; | ||
729 | } | ||
730 | |||
731 | return buf - tmp; | ||
376 | } | 732 | } |
377 | 733 | ||
378 | int synthesize_trace_kprobe_event(struct probe_point *pp) | 734 | char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev) |
379 | { | 735 | { |
736 | struct kprobe_trace_point *tp = &tev->point; | ||
380 | char *buf; | 737 | char *buf; |
381 | int i, len, ret; | 738 | int i, len, ret; |
382 | 739 | ||
383 | pp->probes[0] = buf = zalloc(MAX_CMDLEN); | 740 | buf = xzalloc(MAX_CMDLEN); |
384 | if (!buf) | 741 | len = e_snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s+%lu", |
385 | die("Failed to allocate memory by zalloc."); | 742 | tp->retprobe ? 'r' : 'p', |
386 | ret = e_snprintf(buf, MAX_CMDLEN, "%s+%d", pp->function, pp->offset); | 743 | tev->group, tev->event, |
387 | if (ret <= 0) | 744 | tp->symbol, tp->offset); |
745 | if (len <= 0) | ||
388 | goto error; | 746 | goto error; |
389 | len = ret; | ||
390 | 747 | ||
391 | for (i = 0; i < pp->nr_args; i++) { | 748 | for (i = 0; i < tev->nargs; i++) { |
392 | ret = e_snprintf(&buf[len], MAX_CMDLEN - len, " %s", | 749 | ret = synthesize_kprobe_trace_arg(&tev->args[i], buf + len, |
393 | pp->args[i]); | 750 | MAX_CMDLEN - len); |
394 | if (ret <= 0) | 751 | if (ret <= 0) |
395 | goto error; | 752 | goto error; |
396 | len += ret; | 753 | len += ret; |
397 | } | 754 | } |
398 | pp->found = 1; | ||
399 | 755 | ||
400 | return pp->found; | 756 | return buf; |
401 | error: | 757 | error: |
402 | free(pp->probes[0]); | 758 | free(buf); |
403 | pp->probes[0] = NULL; | 759 | return NULL; |
760 | } | ||
404 | 761 | ||
405 | return ret; | 762 | void convert_to_perf_probe_event(struct kprobe_trace_event *tev, |
763 | struct perf_probe_event *pev) | ||
764 | { | ||
765 | char buf[64]; | ||
766 | int i; | ||
767 | |||
768 | /* Convert event/group name */ | ||
769 | pev->event = xstrdup(tev->event); | ||
770 | pev->group = xstrdup(tev->group); | ||
771 | |||
772 | /* Convert trace_point to probe_point */ | ||
773 | convert_to_perf_probe_point(&tev->point, &pev->point); | ||
774 | |||
775 | /* Convert trace_arg to probe_arg */ | ||
776 | pev->nargs = tev->nargs; | ||
777 | pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs); | ||
778 | for (i = 0; i < tev->nargs; i++) | ||
779 | if (tev->args[i].name) | ||
780 | pev->args[i].name = xstrdup(tev->args[i].name); | ||
781 | else { | ||
782 | synthesize_kprobe_trace_arg(&tev->args[i], buf, 64); | ||
783 | pev->args[i].name = xstrdup(buf); | ||
784 | } | ||
785 | } | ||
786 | |||
787 | void clear_perf_probe_event(struct perf_probe_event *pev) | ||
788 | { | ||
789 | struct perf_probe_point *pp = &pev->point; | ||
790 | struct perf_probe_arg_field *field, *next; | ||
791 | int i; | ||
792 | |||
793 | if (pev->event) | ||
794 | free(pev->event); | ||
795 | if (pev->group) | ||
796 | free(pev->group); | ||
797 | if (pp->file) | ||
798 | free(pp->file); | ||
799 | if (pp->function) | ||
800 | free(pp->function); | ||
801 | if (pp->lazy_line) | ||
802 | free(pp->lazy_line); | ||
803 | for (i = 0; i < pev->nargs; i++) { | ||
804 | if (pev->args[i].name) | ||
805 | free(pev->args[i].name); | ||
806 | field = pev->args[i].field; | ||
807 | while (field) { | ||
808 | next = field->next; | ||
809 | if (field->name) | ||
810 | free(field->name); | ||
811 | free(field); | ||
812 | field = next; | ||
813 | } | ||
814 | } | ||
815 | if (pev->args) | ||
816 | free(pev->args); | ||
817 | memset(pev, 0, sizeof(*pev)); | ||
406 | } | 818 | } |
407 | 819 | ||
408 | static int open_kprobe_events(int flags, int mode) | 820 | void clear_kprobe_trace_event(struct kprobe_trace_event *tev) |
821 | { | ||
822 | struct kprobe_trace_arg_ref *ref, *next; | ||
823 | int i; | ||
824 | |||
825 | if (tev->event) | ||
826 | free(tev->event); | ||
827 | if (tev->group) | ||
828 | free(tev->group); | ||
829 | if (tev->point.symbol) | ||
830 | free(tev->point.symbol); | ||
831 | for (i = 0; i < tev->nargs; i++) { | ||
832 | if (tev->args[i].name) | ||
833 | free(tev->args[i].name); | ||
834 | if (tev->args[i].value) | ||
835 | free(tev->args[i].value); | ||
836 | ref = tev->args[i].ref; | ||
837 | while (ref) { | ||
838 | next = ref->next; | ||
839 | free(ref); | ||
840 | ref = next; | ||
841 | } | ||
842 | } | ||
843 | if (tev->args) | ||
844 | free(tev->args); | ||
845 | memset(tev, 0, sizeof(*tev)); | ||
846 | } | ||
847 | |||
848 | static int open_kprobe_events(bool readwrite) | ||
409 | { | 849 | { |
410 | char buf[PATH_MAX]; | 850 | char buf[PATH_MAX]; |
411 | int ret; | 851 | int ret; |
@@ -414,7 +854,11 @@ static int open_kprobe_events(int flags, int mode) | |||
414 | if (ret < 0) | 854 | if (ret < 0) |
415 | die("Failed to make kprobe_events path."); | 855 | die("Failed to make kprobe_events path."); |
416 | 856 | ||
417 | ret = open(buf, flags, mode); | 857 | if (readwrite && !probe_event_dry_run) |
858 | ret = open(buf, O_RDWR, O_APPEND); | ||
859 | else | ||
860 | ret = open(buf, O_RDONLY, 0); | ||
861 | |||
418 | if (ret < 0) { | 862 | if (ret < 0) { |
419 | if (errno == ENOENT) | 863 | if (errno == ENOENT) |
420 | die("kprobe_events file does not exist -" | 864 | die("kprobe_events file does not exist -" |
@@ -427,7 +871,7 @@ static int open_kprobe_events(int flags, int mode) | |||
427 | } | 871 | } |
428 | 872 | ||
429 | /* Get raw string list of current kprobe_events */ | 873 | /* Get raw string list of current kprobe_events */ |
430 | static struct strlist *get_trace_kprobe_event_rawlist(int fd) | 874 | static struct strlist *get_kprobe_trace_command_rawlist(int fd) |
431 | { | 875 | { |
432 | int ret, idx; | 876 | int ret, idx; |
433 | FILE *fp; | 877 | FILE *fp; |
@@ -455,99 +899,85 @@ static struct strlist *get_trace_kprobe_event_rawlist(int fd) | |||
455 | return sl; | 899 | return sl; |
456 | } | 900 | } |
457 | 901 | ||
458 | /* Free and zero clear probe_point */ | ||
459 | static void clear_probe_point(struct probe_point *pp) | ||
460 | { | ||
461 | int i; | ||
462 | |||
463 | if (pp->event) | ||
464 | free(pp->event); | ||
465 | if (pp->group) | ||
466 | free(pp->group); | ||
467 | if (pp->function) | ||
468 | free(pp->function); | ||
469 | if (pp->file) | ||
470 | free(pp->file); | ||
471 | if (pp->lazy_line) | ||
472 | free(pp->lazy_line); | ||
473 | for (i = 0; i < pp->nr_args; i++) | ||
474 | free(pp->args[i]); | ||
475 | if (pp->args) | ||
476 | free(pp->args); | ||
477 | for (i = 0; i < pp->found; i++) | ||
478 | free(pp->probes[i]); | ||
479 | memset(pp, 0, sizeof(*pp)); | ||
480 | } | ||
481 | |||
482 | /* Show an event */ | 902 | /* Show an event */ |
483 | static void show_perf_probe_event(const char *event, const char *place, | 903 | static void show_perf_probe_event(struct perf_probe_event *pev) |
484 | struct probe_point *pp) | ||
485 | { | 904 | { |
486 | int i, ret; | 905 | int i, ret; |
487 | char buf[128]; | 906 | char buf[128]; |
907 | char *place; | ||
488 | 908 | ||
489 | ret = e_snprintf(buf, 128, "%s:%s", pp->group, event); | 909 | /* Synthesize only event probe point */ |
910 | place = synthesize_perf_probe_point(&pev->point); | ||
911 | |||
912 | ret = e_snprintf(buf, 128, "%s:%s", pev->group, pev->event); | ||
490 | if (ret < 0) | 913 | if (ret < 0) |
491 | die("Failed to copy event: %s", strerror(-ret)); | 914 | die("Failed to copy event: %s", strerror(-ret)); |
492 | printf(" %-40s (on %s", buf, place); | 915 | printf(" %-20s (on %s", buf, place); |
493 | 916 | ||
494 | if (pp->nr_args > 0) { | 917 | if (pev->nargs > 0) { |
495 | printf(" with"); | 918 | printf(" with"); |
496 | for (i = 0; i < pp->nr_args; i++) | 919 | for (i = 0; i < pev->nargs; i++) { |
497 | printf(" %s", pp->args[i]); | 920 | synthesize_perf_probe_arg(&pev->args[i], buf, 128); |
921 | printf(" %s", buf); | ||
922 | } | ||
498 | } | 923 | } |
499 | printf(")\n"); | 924 | printf(")\n"); |
925 | free(place); | ||
500 | } | 926 | } |
501 | 927 | ||
502 | /* List up current perf-probe events */ | 928 | /* List up current perf-probe events */ |
503 | void show_perf_probe_events(void) | 929 | void show_perf_probe_events(void) |
504 | { | 930 | { |
505 | int fd; | 931 | int fd; |
506 | struct probe_point pp; | 932 | struct kprobe_trace_event tev; |
933 | struct perf_probe_event pev; | ||
507 | struct strlist *rawlist; | 934 | struct strlist *rawlist; |
508 | struct str_node *ent; | 935 | struct str_node *ent; |
509 | 936 | ||
510 | setup_pager(); | 937 | setup_pager(); |
511 | memset(&pp, 0, sizeof(pp)); | 938 | init_vmlinux(); |
939 | |||
940 | memset(&tev, 0, sizeof(tev)); | ||
941 | memset(&pev, 0, sizeof(pev)); | ||
512 | 942 | ||
513 | fd = open_kprobe_events(O_RDONLY, 0); | 943 | fd = open_kprobe_events(false); |
514 | rawlist = get_trace_kprobe_event_rawlist(fd); | 944 | rawlist = get_kprobe_trace_command_rawlist(fd); |
515 | close(fd); | 945 | close(fd); |
516 | 946 | ||
517 | strlist__for_each(ent, rawlist) { | 947 | strlist__for_each(ent, rawlist) { |
518 | parse_trace_kprobe_event(ent->s, &pp); | 948 | parse_kprobe_trace_command(ent->s, &tev); |
519 | /* Synthesize only event probe point */ | 949 | convert_to_perf_probe_event(&tev, &pev); |
520 | synthesize_perf_probe_point(&pp); | ||
521 | /* Show an event */ | 950 | /* Show an event */ |
522 | show_perf_probe_event(pp.event, pp.probes[0], &pp); | 951 | show_perf_probe_event(&pev); |
523 | clear_probe_point(&pp); | 952 | clear_perf_probe_event(&pev); |
953 | clear_kprobe_trace_event(&tev); | ||
524 | } | 954 | } |
525 | 955 | ||
526 | strlist__delete(rawlist); | 956 | strlist__delete(rawlist); |
527 | } | 957 | } |
528 | 958 | ||
529 | /* Get current perf-probe event names */ | 959 | /* Get current perf-probe event names */ |
530 | static struct strlist *get_perf_event_names(int fd, bool include_group) | 960 | static struct strlist *get_kprobe_trace_event_names(int fd, bool include_group) |
531 | { | 961 | { |
532 | char buf[128]; | 962 | char buf[128]; |
533 | struct strlist *sl, *rawlist; | 963 | struct strlist *sl, *rawlist; |
534 | struct str_node *ent; | 964 | struct str_node *ent; |
535 | struct probe_point pp; | 965 | struct kprobe_trace_event tev; |
536 | 966 | ||
537 | memset(&pp, 0, sizeof(pp)); | 967 | memset(&tev, 0, sizeof(tev)); |
538 | rawlist = get_trace_kprobe_event_rawlist(fd); | ||
539 | 968 | ||
969 | rawlist = get_kprobe_trace_command_rawlist(fd); | ||
540 | sl = strlist__new(true, NULL); | 970 | sl = strlist__new(true, NULL); |
541 | strlist__for_each(ent, rawlist) { | 971 | strlist__for_each(ent, rawlist) { |
542 | parse_trace_kprobe_event(ent->s, &pp); | 972 | parse_kprobe_trace_command(ent->s, &tev); |
543 | if (include_group) { | 973 | if (include_group) { |
544 | if (e_snprintf(buf, 128, "%s:%s", pp.group, | 974 | if (e_snprintf(buf, 128, "%s:%s", tev.group, |
545 | pp.event) < 0) | 975 | tev.event) < 0) |
546 | die("Failed to copy group:event name."); | 976 | die("Failed to copy group:event name."); |
547 | strlist__add(sl, buf); | 977 | strlist__add(sl, buf); |
548 | } else | 978 | } else |
549 | strlist__add(sl, pp.event); | 979 | strlist__add(sl, tev.event); |
550 | clear_probe_point(&pp); | 980 | clear_kprobe_trace_event(&tev); |
551 | } | 981 | } |
552 | 982 | ||
553 | strlist__delete(rawlist); | 983 | strlist__delete(rawlist); |
@@ -555,14 +985,18 @@ static struct strlist *get_perf_event_names(int fd, bool include_group) | |||
555 | return sl; | 985 | return sl; |
556 | } | 986 | } |
557 | 987 | ||
558 | static void write_trace_kprobe_event(int fd, const char *buf) | 988 | static void write_kprobe_trace_event(int fd, struct kprobe_trace_event *tev) |
559 | { | 989 | { |
560 | int ret; | 990 | int ret; |
991 | char *buf = synthesize_kprobe_trace_command(tev); | ||
561 | 992 | ||
562 | pr_debug("Writing event: %s\n", buf); | 993 | pr_debug("Writing event: %s\n", buf); |
563 | ret = write(fd, buf, strlen(buf)); | 994 | if (!probe_event_dry_run) { |
564 | if (ret <= 0) | 995 | ret = write(fd, buf, strlen(buf)); |
565 | die("Failed to write event: %s", strerror(errno)); | 996 | if (ret <= 0) |
997 | die("Failed to write event: %s", strerror(errno)); | ||
998 | } | ||
999 | free(buf); | ||
566 | } | 1000 | } |
567 | 1001 | ||
568 | static void get_new_event_name(char *buf, size_t len, const char *base, | 1002 | static void get_new_event_name(char *buf, size_t len, const char *base, |
@@ -595,65 +1029,145 @@ static void get_new_event_name(char *buf, size_t len, const char *base, | |||
595 | die("Too many events are on the same function."); | 1029 | die("Too many events are on the same function."); |
596 | } | 1030 | } |
597 | 1031 | ||
598 | void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, | 1032 | static void __add_kprobe_trace_events(struct perf_probe_event *pev, |
599 | bool force_add) | 1033 | struct kprobe_trace_event *tevs, |
1034 | int ntevs, bool allow_suffix) | ||
600 | { | 1035 | { |
601 | int i, j, fd; | 1036 | int i, fd; |
602 | struct probe_point *pp; | 1037 | struct kprobe_trace_event *tev = NULL; |
603 | char buf[MAX_CMDLEN]; | 1038 | char buf[64]; |
604 | char event[64]; | 1039 | const char *event, *group; |
605 | struct strlist *namelist; | 1040 | struct strlist *namelist; |
606 | bool allow_suffix; | ||
607 | 1041 | ||
608 | fd = open_kprobe_events(O_RDWR, O_APPEND); | 1042 | fd = open_kprobe_events(true); |
609 | /* Get current event names */ | 1043 | /* Get current event names */ |
610 | namelist = get_perf_event_names(fd, false); | 1044 | namelist = get_kprobe_trace_event_names(fd, false); |
611 | 1045 | ||
612 | for (j = 0; j < nr_probes; j++) { | 1046 | printf("Add new event%s\n", (ntevs > 1) ? "s:" : ":"); |
613 | pp = probes + j; | 1047 | for (i = 0; i < ntevs; i++) { |
614 | if (!pp->event) | 1048 | tev = &tevs[i]; |
615 | pp->event = strdup(pp->function); | 1049 | if (pev->event) |
616 | if (!pp->group) | 1050 | event = pev->event; |
617 | pp->group = strdup(PERFPROBE_GROUP); | 1051 | else |
618 | DIE_IF(!pp->event || !pp->group); | 1052 | if (pev->point.function) |
619 | /* If force_add is true, suffix search is allowed */ | 1053 | event = pev->point.function; |
620 | allow_suffix = force_add; | 1054 | else |
621 | for (i = 0; i < pp->found; i++) { | 1055 | event = tev->point.symbol; |
622 | /* Get an unused new event name */ | 1056 | if (pev->group) |
623 | get_new_event_name(event, 64, pp->event, namelist, | 1057 | group = pev->group; |
624 | allow_suffix); | 1058 | else |
625 | snprintf(buf, MAX_CMDLEN, "%c:%s/%s %s\n", | 1059 | group = PERFPROBE_GROUP; |
626 | pp->retprobe ? 'r' : 'p', | 1060 | |
627 | pp->group, event, | 1061 | /* Get an unused new event name */ |
628 | pp->probes[i]); | 1062 | get_new_event_name(buf, 64, event, namelist, allow_suffix); |
629 | write_trace_kprobe_event(fd, buf); | 1063 | event = buf; |
630 | printf("Added new event:\n"); | 1064 | |
631 | /* Get the first parameter (probe-point) */ | 1065 | tev->event = xstrdup(event); |
632 | sscanf(pp->probes[i], "%s", buf); | 1066 | tev->group = xstrdup(group); |
633 | show_perf_probe_event(event, buf, pp); | 1067 | write_kprobe_trace_event(fd, tev); |
634 | /* Add added event name to namelist */ | 1068 | /* Add added event name to namelist */ |
635 | strlist__add(namelist, event); | 1069 | strlist__add(namelist, event); |
636 | /* | 1070 | |
637 | * Probes after the first probe which comes from same | 1071 | /* Trick here - save current event/group */ |
638 | * user input are always allowed to add suffix, because | 1072 | event = pev->event; |
639 | * there might be several addresses corresponding to | 1073 | group = pev->group; |
640 | * one code line. | 1074 | pev->event = tev->event; |
641 | */ | 1075 | pev->group = tev->group; |
642 | allow_suffix = true; | 1076 | show_perf_probe_event(pev); |
643 | } | 1077 | /* Trick here - restore current event/group */ |
1078 | pev->event = (char *)event; | ||
1079 | pev->group = (char *)group; | ||
1080 | |||
1081 | /* | ||
1082 | * Probes after the first probe which comes from same | ||
1083 | * user input are always allowed to add suffix, because | ||
1084 | * there might be several addresses corresponding to | ||
1085 | * one code line. | ||
1086 | */ | ||
1087 | allow_suffix = true; | ||
644 | } | 1088 | } |
645 | /* Show how to use the event. */ | 1089 | /* Show how to use the event. */ |
646 | printf("\nYou can now use it on all perf tools, such as:\n\n"); | 1090 | printf("\nYou can now use it on all perf tools, such as:\n\n"); |
647 | printf("\tperf record -e %s:%s -a sleep 1\n\n", PERFPROBE_GROUP, event); | 1091 | printf("\tperf record -e %s:%s -a sleep 1\n\n", tev->group, tev->event); |
648 | 1092 | ||
649 | strlist__delete(namelist); | 1093 | strlist__delete(namelist); |
650 | close(fd); | 1094 | close(fd); |
651 | } | 1095 | } |
652 | 1096 | ||
1097 | static int convert_to_kprobe_trace_events(struct perf_probe_event *pev, | ||
1098 | struct kprobe_trace_event **tevs) | ||
1099 | { | ||
1100 | struct symbol *sym; | ||
1101 | int ntevs = 0, i; | ||
1102 | struct kprobe_trace_event *tev; | ||
1103 | |||
1104 | /* Convert perf_probe_event with debuginfo */ | ||
1105 | ntevs = try_to_find_kprobe_trace_events(pev, tevs); | ||
1106 | if (ntevs > 0) | ||
1107 | return ntevs; | ||
1108 | |||
1109 | /* Allocate trace event buffer */ | ||
1110 | ntevs = 1; | ||
1111 | tev = *tevs = xzalloc(sizeof(struct kprobe_trace_event)); | ||
1112 | |||
1113 | /* Copy parameters */ | ||
1114 | tev->point.symbol = xstrdup(pev->point.function); | ||
1115 | tev->point.offset = pev->point.offset; | ||
1116 | tev->nargs = pev->nargs; | ||
1117 | if (tev->nargs) { | ||
1118 | tev->args = xzalloc(sizeof(struct kprobe_trace_arg) | ||
1119 | * tev->nargs); | ||
1120 | for (i = 0; i < tev->nargs; i++) | ||
1121 | tev->args[i].value = xstrdup(pev->args[i].name); | ||
1122 | } | ||
1123 | |||
1124 | /* Currently just checking function name from symbol map */ | ||
1125 | sym = map__find_symbol_by_name(kmaps[MAP__FUNCTION], | ||
1126 | tev->point.symbol, NULL); | ||
1127 | if (!sym) | ||
1128 | die("Kernel symbol \'%s\' not found - probe not added.", | ||
1129 | tev->point.symbol); | ||
1130 | |||
1131 | return ntevs; | ||
1132 | } | ||
1133 | |||
1134 | struct __event_package { | ||
1135 | struct perf_probe_event *pev; | ||
1136 | struct kprobe_trace_event *tevs; | ||
1137 | int ntevs; | ||
1138 | }; | ||
1139 | |||
1140 | void add_perf_probe_events(struct perf_probe_event *pevs, int npevs, | ||
1141 | bool force_add) | ||
1142 | { | ||
1143 | int i; | ||
1144 | struct __event_package *pkgs; | ||
1145 | |||
1146 | pkgs = xzalloc(sizeof(struct __event_package) * npevs); | ||
1147 | |||
1148 | /* Init vmlinux path */ | ||
1149 | init_vmlinux(); | ||
1150 | |||
1151 | /* Loop 1: convert all events */ | ||
1152 | for (i = 0; i < npevs; i++) { | ||
1153 | pkgs[i].pev = &pevs[i]; | ||
1154 | /* Convert with or without debuginfo */ | ||
1155 | pkgs[i].ntevs = convert_to_kprobe_trace_events(pkgs[i].pev, | ||
1156 | &pkgs[i].tevs); | ||
1157 | } | ||
1158 | |||
1159 | /* Loop 2: add all events */ | ||
1160 | for (i = 0; i < npevs; i++) | ||
1161 | __add_kprobe_trace_events(pkgs[i].pev, pkgs[i].tevs, | ||
1162 | pkgs[i].ntevs, force_add); | ||
1163 | /* TODO: cleanup all trace events? */ | ||
1164 | } | ||
1165 | |||
653 | static void __del_trace_kprobe_event(int fd, struct str_node *ent) | 1166 | static void __del_trace_kprobe_event(int fd, struct str_node *ent) |
654 | { | 1167 | { |
655 | char *p; | 1168 | char *p; |
656 | char buf[128]; | 1169 | char buf[128]; |
1170 | int ret; | ||
657 | 1171 | ||
658 | /* Convert from perf-probe event to trace-kprobe event */ | 1172 | /* Convert from perf-probe event to trace-kprobe event */ |
659 | if (e_snprintf(buf, 128, "-:%s", ent->s) < 0) | 1173 | if (e_snprintf(buf, 128, "-:%s", ent->s) < 0) |
@@ -663,7 +1177,10 @@ static void __del_trace_kprobe_event(int fd, struct str_node *ent) | |||
663 | die("Internal error: %s should have ':' but not.", ent->s); | 1177 | die("Internal error: %s should have ':' but not.", ent->s); |
664 | *p = '/'; | 1178 | *p = '/'; |
665 | 1179 | ||
666 | write_trace_kprobe_event(fd, buf); | 1180 | pr_debug("Writing event: %s\n", buf); |
1181 | ret = write(fd, buf, strlen(buf)); | ||
1182 | if (ret <= 0) | ||
1183 | die("Failed to write event: %s", strerror(errno)); | ||
667 | printf("Remove event: %s\n", ent->s); | 1184 | printf("Remove event: %s\n", ent->s); |
668 | } | 1185 | } |
669 | 1186 | ||
@@ -696,7 +1213,7 @@ static void del_trace_kprobe_event(int fd, const char *group, | |||
696 | pr_info("Info: event \"%s\" does not exist, could not remove it.\n", buf); | 1213 | pr_info("Info: event \"%s\" does not exist, could not remove it.\n", buf); |
697 | } | 1214 | } |
698 | 1215 | ||
699 | void del_trace_kprobe_events(struct strlist *dellist) | 1216 | void del_perf_probe_events(struct strlist *dellist) |
700 | { | 1217 | { |
701 | int fd; | 1218 | int fd; |
702 | const char *group, *event; | 1219 | const char *group, *event; |
@@ -704,14 +1221,12 @@ void del_trace_kprobe_events(struct strlist *dellist) | |||
704 | struct str_node *ent; | 1221 | struct str_node *ent; |
705 | struct strlist *namelist; | 1222 | struct strlist *namelist; |
706 | 1223 | ||
707 | fd = open_kprobe_events(O_RDWR, O_APPEND); | 1224 | fd = open_kprobe_events(true); |
708 | /* Get current event names */ | 1225 | /* Get current event names */ |
709 | namelist = get_perf_event_names(fd, true); | 1226 | namelist = get_kprobe_trace_event_names(fd, true); |
710 | 1227 | ||
711 | strlist__for_each(ent, dellist) { | 1228 | strlist__for_each(ent, dellist) { |
712 | str = strdup(ent->s); | 1229 | str = xstrdup(ent->s); |
713 | if (!str) | ||
714 | die("Failed to copy event."); | ||
715 | pr_debug("Parsing: %s\n", str); | 1230 | pr_debug("Parsing: %s\n", str); |
716 | p = strchr(str, ':'); | 1231 | p = strchr(str, ':'); |
717 | if (p) { | 1232 | if (p) { |
@@ -730,73 +1245,3 @@ void del_trace_kprobe_events(struct strlist *dellist) | |||
730 | close(fd); | 1245 | close(fd); |
731 | } | 1246 | } |
732 | 1247 | ||
733 | #define LINEBUF_SIZE 256 | ||
734 | #define NR_ADDITIONAL_LINES 2 | ||
735 | |||
736 | static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num) | ||
737 | { | ||
738 | char buf[LINEBUF_SIZE]; | ||
739 | const char *color = PERF_COLOR_BLUE; | ||
740 | |||
741 | if (fgets(buf, LINEBUF_SIZE, fp) == NULL) | ||
742 | goto error; | ||
743 | if (!skip) { | ||
744 | if (show_num) | ||
745 | fprintf(stdout, "%7u %s", l, buf); | ||
746 | else | ||
747 | color_fprintf(stdout, color, " %s", buf); | ||
748 | } | ||
749 | |||
750 | while (strlen(buf) == LINEBUF_SIZE - 1 && | ||
751 | buf[LINEBUF_SIZE - 2] != '\n') { | ||
752 | if (fgets(buf, LINEBUF_SIZE, fp) == NULL) | ||
753 | goto error; | ||
754 | if (!skip) { | ||
755 | if (show_num) | ||
756 | fprintf(stdout, "%s", buf); | ||
757 | else | ||
758 | color_fprintf(stdout, color, "%s", buf); | ||
759 | } | ||
760 | } | ||
761 | return; | ||
762 | error: | ||
763 | if (feof(fp)) | ||
764 | die("Source file is shorter than expected."); | ||
765 | else | ||
766 | die("File read error: %s", strerror(errno)); | ||
767 | } | ||
768 | |||
769 | void show_line_range(struct line_range *lr) | ||
770 | { | ||
771 | unsigned int l = 1; | ||
772 | struct line_node *ln; | ||
773 | FILE *fp; | ||
774 | |||
775 | setup_pager(); | ||
776 | |||
777 | if (lr->function) | ||
778 | fprintf(stdout, "<%s:%d>\n", lr->function, | ||
779 | lr->start - lr->offset); | ||
780 | else | ||
781 | fprintf(stdout, "<%s:%d>\n", lr->file, lr->start); | ||
782 | |||
783 | fp = fopen(lr->path, "r"); | ||
784 | if (fp == NULL) | ||
785 | die("Failed to open %s: %s", lr->path, strerror(errno)); | ||
786 | /* Skip to starting line number */ | ||
787 | while (l < lr->start) | ||
788 | show_one_line(fp, l++, true, false); | ||
789 | |||
790 | list_for_each_entry(ln, &lr->line_list, list) { | ||
791 | while (ln->line > l) | ||
792 | show_one_line(fp, (l++) - lr->offset, false, false); | ||
793 | show_one_line(fp, (l++) - lr->offset, false, true); | ||
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 | |||
801 | fclose(fp); | ||
802 | } | ||
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 711287d4baea..cd308b0a4d96 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h | |||
@@ -2,22 +2,123 @@ | |||
2 | #define _PROBE_EVENT_H | 2 | #define _PROBE_EVENT_H |
3 | 3 | ||
4 | #include <stdbool.h> | 4 | #include <stdbool.h> |
5 | #include "probe-finder.h" | ||
6 | #include "strlist.h" | 5 | #include "strlist.h" |
7 | 6 | ||
8 | extern void parse_line_range_desc(const char *arg, struct line_range *lr); | 7 | extern bool probe_event_dry_run; |
9 | extern void parse_perf_probe_event(const char *str, struct probe_point *pp, | 8 | |
10 | bool *need_dwarf); | 9 | /* kprobe-tracer tracing point */ |
11 | extern int synthesize_perf_probe_point(struct probe_point *pp); | 10 | struct kprobe_trace_point { |
12 | extern int synthesize_perf_probe_event(struct probe_point *pp); | 11 | char *symbol; /* Base symbol */ |
13 | extern void parse_trace_kprobe_event(const char *str, struct probe_point *pp); | 12 | unsigned long offset; /* Offset from symbol */ |
14 | extern int synthesize_trace_kprobe_event(struct probe_point *pp); | 13 | bool retprobe; /* Return probe flag */ |
15 | extern void add_trace_kprobe_events(struct probe_point *probes, int nr_probes, | 14 | }; |
16 | bool force_add); | 15 | |
17 | extern void del_trace_kprobe_events(struct strlist *dellist); | 16 | /* kprobe-tracer tracing argument referencing offset */ |
17 | struct kprobe_trace_arg_ref { | ||
18 | struct kprobe_trace_arg_ref *next; /* Next reference */ | ||
19 | long offset; /* Offset value */ | ||
20 | }; | ||
21 | |||
22 | /* kprobe-tracer tracing argument */ | ||
23 | struct kprobe_trace_arg { | ||
24 | char *name; /* Argument name */ | ||
25 | char *value; /* Base value */ | ||
26 | struct kprobe_trace_arg_ref *ref; /* Referencing offset */ | ||
27 | }; | ||
28 | |||
29 | /* kprobe-tracer tracing event (point + arg) */ | ||
30 | struct kprobe_trace_event { | ||
31 | char *event; /* Event name */ | ||
32 | char *group; /* Group name */ | ||
33 | struct kprobe_trace_point point; /* Trace point */ | ||
34 | int nargs; /* Number of args */ | ||
35 | struct kprobe_trace_arg *args; /* Arguments */ | ||
36 | }; | ||
37 | |||
38 | /* Perf probe probing point */ | ||
39 | struct perf_probe_point { | ||
40 | char *file; /* File path */ | ||
41 | char *function; /* Function name */ | ||
42 | int line; /* Line number */ | ||
43 | char *lazy_line; /* Lazy matching pattern */ | ||
44 | unsigned long offset; /* Offset from function entry */ | ||
45 | bool retprobe; /* Return probe flag */ | ||
46 | }; | ||
47 | |||
48 | /* Perf probe probing argument field chain */ | ||
49 | struct perf_probe_arg_field { | ||
50 | struct perf_probe_arg_field *next; /* Next field */ | ||
51 | char *name; /* Name of the field */ | ||
52 | bool ref; /* Referencing flag */ | ||
53 | }; | ||
54 | |||
55 | /* Perf probe probing argument */ | ||
56 | struct perf_probe_arg { | ||
57 | char *name; /* Argument name */ | ||
58 | struct perf_probe_arg_field *field; /* Structure fields */ | ||
59 | }; | ||
60 | |||
61 | /* Perf probe probing event (point + arg) */ | ||
62 | struct perf_probe_event { | ||
63 | char *event; /* Event name */ | ||
64 | char *group; /* Group name */ | ||
65 | struct perf_probe_point point; /* Probe point */ | ||
66 | int nargs; /* Number of arguments */ | ||
67 | struct perf_probe_arg *args; /* Arguments */ | ||
68 | }; | ||
69 | |||
70 | |||
71 | /* Line number container */ | ||
72 | struct line_node { | ||
73 | struct list_head list; | ||
74 | unsigned int line; | ||
75 | }; | ||
76 | |||
77 | /* Line range */ | ||
78 | struct line_range { | ||
79 | char *file; /* File name */ | ||
80 | char *function; /* Function name */ | ||
81 | unsigned int start; /* Start line number */ | ||
82 | unsigned int end; /* End line number */ | ||
83 | int offset; /* Start line offset */ | ||
84 | char *path; /* Real path name */ | ||
85 | struct list_head line_list; /* Visible lines */ | ||
86 | }; | ||
87 | |||
88 | /* Command string to events */ | ||
89 | extern void parse_perf_probe_command(const char *cmd, | ||
90 | struct perf_probe_event *pev); | ||
91 | extern void parse_kprobe_trace_command(const char *cmd, | ||
92 | struct kprobe_trace_event *tev); | ||
93 | |||
94 | /* Events to command string */ | ||
95 | extern char *synthesize_perf_probe_command(struct perf_probe_event *pev); | ||
96 | extern char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev); | ||
97 | extern int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, | ||
98 | size_t len); | ||
99 | |||
100 | /* Check the perf_probe_event needs debuginfo */ | ||
101 | extern bool perf_probe_event_need_dwarf(struct perf_probe_event *pev); | ||
102 | |||
103 | /* Convert from kprobe_trace_event to perf_probe_event */ | ||
104 | extern void convert_to_perf_probe_event(struct kprobe_trace_event *tev, | ||
105 | struct perf_probe_event *pev); | ||
106 | |||
107 | /* Release event contents */ | ||
108 | extern void clear_perf_probe_event(struct perf_probe_event *pev); | ||
109 | extern void clear_kprobe_trace_event(struct kprobe_trace_event *tev); | ||
110 | |||
111 | /* Command string to line-range */ | ||
112 | extern void parse_line_range_desc(const char *cmd, struct line_range *lr); | ||
113 | |||
114 | |||
115 | extern void add_perf_probe_events(struct perf_probe_event *pevs, int ntevs, | ||
116 | bool force_add); | ||
117 | extern void del_perf_probe_events(struct strlist *dellist); | ||
18 | extern void show_perf_probe_events(void); | 118 | extern void show_perf_probe_events(void); |
19 | extern void show_line_range(struct line_range *lr); | 119 | extern void show_line_range(struct line_range *lr); |
20 | 120 | ||
121 | |||
21 | /* Maximum index number of event-name postfix */ | 122 | /* Maximum index number of event-name postfix */ |
22 | #define MAX_EVENT_INDEX 1024 | 123 | #define MAX_EVENT_INDEX 1024 |
23 | 124 | ||
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index c171a243d05b..a8513772df08 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c | |||
@@ -125,8 +125,7 @@ static void line_list__add_line(struct list_head *head, unsigned int line) | |||
125 | p = head; | 125 | p = head; |
126 | found: | 126 | found: |
127 | pr_debug("line list: add a line %u\n", line); | 127 | pr_debug("line list: add a line %u\n", line); |
128 | ln = zalloc(sizeof(struct line_node)); | 128 | ln = xzalloc(sizeof(struct line_node)); |
129 | DIE_IF(ln == NULL); | ||
130 | ln->line = line; | 129 | ln->line = line; |
131 | INIT_LIST_HEAD(&ln->list); | 130 | INIT_LIST_HEAD(&ln->list); |
132 | list_add(&ln->list, p); | 131 | list_add(&ln->list, p); |
@@ -184,9 +183,89 @@ static const char *cu_find_realpath(Dwarf_Die *cu_die, const char *fname) | |||
184 | if (strtailcmp(src, fname) == 0) | 183 | if (strtailcmp(src, fname) == 0) |
185 | break; | 184 | break; |
186 | } | 185 | } |
186 | if (i == nfiles) | ||
187 | return NULL; | ||
187 | return src; | 188 | return src; |
188 | } | 189 | } |
189 | 190 | ||
191 | /* Compare diename and tname */ | ||
192 | static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) | ||
193 | { | ||
194 | const char *name; | ||
195 | name = dwarf_diename(dw_die); | ||
196 | DIE_IF(name == NULL); | ||
197 | return strcmp(tname, name); | ||
198 | } | ||
199 | |||
200 | /* Get entry pc(or low pc, 1st entry of ranges) of the die */ | ||
201 | static Dwarf_Addr die_get_entrypc(Dwarf_Die *dw_die) | ||
202 | { | ||
203 | Dwarf_Addr epc; | ||
204 | int ret; | ||
205 | |||
206 | ret = dwarf_entrypc(dw_die, &epc); | ||
207 | DIE_IF(ret == -1); | ||
208 | return epc; | ||
209 | } | ||
210 | |||
211 | /* Get type die, but skip qualifiers and typedef */ | ||
212 | static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) | ||
213 | { | ||
214 | Dwarf_Attribute attr; | ||
215 | int tag; | ||
216 | |||
217 | do { | ||
218 | if (dwarf_attr(vr_die, DW_AT_type, &attr) == NULL || | ||
219 | dwarf_formref_die(&attr, die_mem) == NULL) | ||
220 | return NULL; | ||
221 | |||
222 | tag = dwarf_tag(die_mem); | ||
223 | vr_die = die_mem; | ||
224 | } while (tag == DW_TAG_const_type || | ||
225 | tag == DW_TAG_restrict_type || | ||
226 | tag == DW_TAG_volatile_type || | ||
227 | tag == DW_TAG_shared_type || | ||
228 | tag == DW_TAG_typedef); | ||
229 | |||
230 | return die_mem; | ||
231 | } | ||
232 | |||
233 | /* Return values for die_find callbacks */ | ||
234 | enum { | ||
235 | DIE_FIND_CB_FOUND = 0, /* End of Search */ | ||
236 | DIE_FIND_CB_CHILD = 1, /* Search only children */ | ||
237 | DIE_FIND_CB_SIBLING = 2, /* Search only siblings */ | ||
238 | DIE_FIND_CB_CONTINUE = 3, /* Search children and siblings */ | ||
239 | }; | ||
240 | |||
241 | /* Search a child die */ | ||
242 | static Dwarf_Die *die_find_child(Dwarf_Die *rt_die, | ||
243 | int (*callback)(Dwarf_Die *, void *), | ||
244 | void *data, Dwarf_Die *die_mem) | ||
245 | { | ||
246 | Dwarf_Die child_die; | ||
247 | int ret; | ||
248 | |||
249 | ret = dwarf_child(rt_die, die_mem); | ||
250 | if (ret != 0) | ||
251 | return NULL; | ||
252 | |||
253 | do { | ||
254 | ret = callback(die_mem, data); | ||
255 | if (ret == DIE_FIND_CB_FOUND) | ||
256 | return die_mem; | ||
257 | |||
258 | if ((ret & DIE_FIND_CB_CHILD) && | ||
259 | die_find_child(die_mem, callback, data, &child_die)) { | ||
260 | memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); | ||
261 | return die_mem; | ||
262 | } | ||
263 | } while ((ret & DIE_FIND_CB_SIBLING) && | ||
264 | dwarf_siblingof(die_mem, die_mem) == 0); | ||
265 | |||
266 | return NULL; | ||
267 | } | ||
268 | |||
190 | struct __addr_die_search_param { | 269 | struct __addr_die_search_param { |
191 | Dwarf_Addr addr; | 270 | Dwarf_Addr addr; |
192 | Dwarf_Die *die_mem; | 271 | Dwarf_Die *die_mem; |
@@ -205,8 +284,8 @@ static int __die_search_func_cb(Dwarf_Die *fn_die, void *data) | |||
205 | } | 284 | } |
206 | 285 | ||
207 | /* Search a real subprogram including this line, */ | 286 | /* Search a real subprogram including this line, */ |
208 | static Dwarf_Die *die_get_real_subprogram(Dwarf_Die *cu_die, Dwarf_Addr addr, | 287 | static Dwarf_Die *die_find_real_subprogram(Dwarf_Die *cu_die, Dwarf_Addr addr, |
209 | Dwarf_Die *die_mem) | 288 | Dwarf_Die *die_mem) |
210 | { | 289 | { |
211 | struct __addr_die_search_param ad; | 290 | struct __addr_die_search_param ad; |
212 | ad.addr = addr; | 291 | ad.addr = addr; |
@@ -218,77 +297,64 @@ static Dwarf_Die *die_get_real_subprogram(Dwarf_Die *cu_die, Dwarf_Addr addr, | |||
218 | return die_mem; | 297 | return die_mem; |
219 | } | 298 | } |
220 | 299 | ||
221 | /* Similar to dwarf_getfuncs, but returns inlined_subroutine if exists. */ | 300 | /* die_find callback for inline function search */ |
222 | static Dwarf_Die *die_get_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, | 301 | static int __die_find_inline_cb(Dwarf_Die *die_mem, void *data) |
223 | Dwarf_Die *die_mem) | ||
224 | { | 302 | { |
225 | Dwarf_Die child_die; | 303 | Dwarf_Addr *addr = data; |
226 | int ret; | ||
227 | |||
228 | ret = dwarf_child(sp_die, die_mem); | ||
229 | if (ret != 0) | ||
230 | return NULL; | ||
231 | 304 | ||
232 | do { | 305 | if (dwarf_tag(die_mem) == DW_TAG_inlined_subroutine && |
233 | if (dwarf_tag(die_mem) == DW_TAG_inlined_subroutine && | 306 | dwarf_haspc(die_mem, *addr)) |
234 | dwarf_haspc(die_mem, addr)) | 307 | return DIE_FIND_CB_FOUND; |
235 | return die_mem; | ||
236 | 308 | ||
237 | if (die_get_inlinefunc(die_mem, addr, &child_die)) { | 309 | return DIE_FIND_CB_CONTINUE; |
238 | memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); | ||
239 | return die_mem; | ||
240 | } | ||
241 | } while (dwarf_siblingof(die_mem, die_mem) == 0); | ||
242 | |||
243 | return NULL; | ||
244 | } | 310 | } |
245 | 311 | ||
246 | /* Compare diename and tname */ | 312 | /* Similar to dwarf_getfuncs, but returns inlined_subroutine if exists. */ |
247 | static bool die_compare_name(Dwarf_Die *dw_die, const char *tname) | 313 | static Dwarf_Die *die_find_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr, |
314 | Dwarf_Die *die_mem) | ||
248 | { | 315 | { |
249 | const char *name; | 316 | return die_find_child(sp_die, __die_find_inline_cb, &addr, die_mem); |
250 | name = dwarf_diename(dw_die); | ||
251 | DIE_IF(name == NULL); | ||
252 | return strcmp(tname, name); | ||
253 | } | 317 | } |
254 | 318 | ||
255 | /* Get entry pc(or low pc, 1st entry of ranges) of the die */ | 319 | static int __die_find_variable_cb(Dwarf_Die *die_mem, void *data) |
256 | static Dwarf_Addr die_get_entrypc(Dwarf_Die *dw_die) | ||
257 | { | 320 | { |
258 | Dwarf_Addr epc; | 321 | const char *name = data; |
259 | int ret; | 322 | int tag; |
260 | 323 | ||
261 | ret = dwarf_entrypc(dw_die, &epc); | 324 | tag = dwarf_tag(die_mem); |
262 | DIE_IF(ret == -1); | 325 | if ((tag == DW_TAG_formal_parameter || |
263 | return epc; | 326 | tag == DW_TAG_variable) && |
327 | (die_compare_name(die_mem, name) == 0)) | ||
328 | return DIE_FIND_CB_FOUND; | ||
329 | |||
330 | return DIE_FIND_CB_CONTINUE; | ||
264 | } | 331 | } |
265 | 332 | ||
266 | /* Get a variable die */ | 333 | /* Find a variable called 'name' */ |
267 | static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name, | 334 | static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name, |
268 | Dwarf_Die *die_mem) | 335 | Dwarf_Die *die_mem) |
269 | { | 336 | { |
270 | Dwarf_Die child_die; | 337 | return die_find_child(sp_die, __die_find_variable_cb, (void *)name, |
271 | int tag; | 338 | die_mem); |
272 | int ret; | 339 | } |
273 | 340 | ||
274 | ret = dwarf_child(sp_die, die_mem); | 341 | static int __die_find_member_cb(Dwarf_Die *die_mem, void *data) |
275 | if (ret != 0) | 342 | { |
276 | return NULL; | 343 | const char *name = data; |
277 | 344 | ||
278 | do { | 345 | if ((dwarf_tag(die_mem) == DW_TAG_member) && |
279 | tag = dwarf_tag(die_mem); | 346 | (die_compare_name(die_mem, name) == 0)) |
280 | if ((tag == DW_TAG_formal_parameter || | 347 | return DIE_FIND_CB_FOUND; |
281 | tag == DW_TAG_variable) && | ||
282 | (die_compare_name(die_mem, name) == 0)) | ||
283 | return die_mem; | ||
284 | 348 | ||
285 | if (die_find_variable(die_mem, name, &child_die)) { | 349 | return DIE_FIND_CB_SIBLING; |
286 | memcpy(die_mem, &child_die, sizeof(Dwarf_Die)); | 350 | } |
287 | return die_mem; | ||
288 | } | ||
289 | } while (dwarf_siblingof(die_mem, die_mem) == 0); | ||
290 | 351 | ||
291 | return NULL; | 352 | /* Find a member called 'name' */ |
353 | static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name, | ||
354 | Dwarf_Die *die_mem) | ||
355 | { | ||
356 | return die_find_child(st_die, __die_find_member_cb, (void *)name, | ||
357 | die_mem); | ||
292 | } | 358 | } |
293 | 359 | ||
294 | /* | 360 | /* |
@@ -296,19 +362,20 @@ static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name, | |||
296 | */ | 362 | */ |
297 | 363 | ||
298 | /* Show a location */ | 364 | /* Show a location */ |
299 | static void show_location(Dwarf_Op *op, struct probe_finder *pf) | 365 | static void convert_location(Dwarf_Op *op, struct probe_finder *pf) |
300 | { | 366 | { |
301 | unsigned int regn; | 367 | unsigned int regn; |
302 | Dwarf_Word offs = 0; | 368 | Dwarf_Word offs = 0; |
303 | int deref = 0, ret; | 369 | bool ref = false; |
304 | const char *regs; | 370 | const char *regs; |
371 | struct kprobe_trace_arg *tvar = pf->tvar; | ||
305 | 372 | ||
306 | /* TODO: support CFA */ | 373 | /* TODO: support CFA */ |
307 | /* If this is based on frame buffer, set the offset */ | 374 | /* If this is based on frame buffer, set the offset */ |
308 | if (op->atom == DW_OP_fbreg) { | 375 | if (op->atom == DW_OP_fbreg) { |
309 | if (pf->fb_ops == NULL) | 376 | if (pf->fb_ops == NULL) |
310 | die("The attribute of frame base is not supported.\n"); | 377 | die("The attribute of frame base is not supported.\n"); |
311 | deref = 1; | 378 | ref = true; |
312 | offs = op->number; | 379 | offs = op->number; |
313 | op = &pf->fb_ops[0]; | 380 | op = &pf->fb_ops[0]; |
314 | } | 381 | } |
@@ -316,13 +383,13 @@ static void show_location(Dwarf_Op *op, struct probe_finder *pf) | |||
316 | if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) { | 383 | if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) { |
317 | regn = op->atom - DW_OP_breg0; | 384 | regn = op->atom - DW_OP_breg0; |
318 | offs += op->number; | 385 | offs += op->number; |
319 | deref = 1; | 386 | ref = true; |
320 | } else if (op->atom >= DW_OP_reg0 && op->atom <= DW_OP_reg31) { | 387 | } else if (op->atom >= DW_OP_reg0 && op->atom <= DW_OP_reg31) { |
321 | regn = op->atom - DW_OP_reg0; | 388 | regn = op->atom - DW_OP_reg0; |
322 | } else if (op->atom == DW_OP_bregx) { | 389 | } else if (op->atom == DW_OP_bregx) { |
323 | regn = op->number; | 390 | regn = op->number; |
324 | offs += op->number2; | 391 | offs += op->number2; |
325 | deref = 1; | 392 | ref = true; |
326 | } else if (op->atom == DW_OP_regx) { | 393 | } else if (op->atom == DW_OP_regx) { |
327 | regn = op->number; | 394 | regn = op->number; |
328 | } else | 395 | } else |
@@ -332,17 +399,75 @@ static void show_location(Dwarf_Op *op, struct probe_finder *pf) | |||
332 | if (!regs) | 399 | if (!regs) |
333 | die("%u exceeds max register number.", regn); | 400 | die("%u exceeds max register number.", regn); |
334 | 401 | ||
335 | if (deref) | 402 | tvar->value = xstrdup(regs); |
336 | ret = snprintf(pf->buf, pf->len, " %s=%+jd(%s)", | 403 | if (ref) { |
337 | pf->var, (intmax_t)offs, regs); | 404 | tvar->ref = xzalloc(sizeof(struct kprobe_trace_arg_ref)); |
338 | else | 405 | tvar->ref->offset = (long)offs; |
339 | ret = snprintf(pf->buf, pf->len, " %s=%s", pf->var, regs); | 406 | } |
340 | DIE_IF(ret < 0); | 407 | } |
341 | DIE_IF(ret >= pf->len); | 408 | |
409 | static void convert_variable_fields(Dwarf_Die *vr_die, const char *varname, | ||
410 | struct perf_probe_arg_field *field, | ||
411 | struct kprobe_trace_arg_ref **ref_ptr) | ||
412 | { | ||
413 | struct kprobe_trace_arg_ref *ref = *ref_ptr; | ||
414 | Dwarf_Attribute attr; | ||
415 | Dwarf_Die member; | ||
416 | Dwarf_Die type; | ||
417 | Dwarf_Word offs; | ||
418 | |||
419 | pr_debug("converting %s in %s\n", field->name, varname); | ||
420 | if (die_get_real_type(vr_die, &type) == NULL) | ||
421 | die("Failed to get a type information of %s.", varname); | ||
422 | |||
423 | /* Check the pointer and dereference */ | ||
424 | if (dwarf_tag(&type) == DW_TAG_pointer_type) { | ||
425 | if (!field->ref) | ||
426 | die("Semantic error: %s must be referred by '->'", | ||
427 | field->name); | ||
428 | /* Get the type pointed by this pointer */ | ||
429 | if (die_get_real_type(&type, &type) == NULL) | ||
430 | die("Failed to get a type information of %s.", varname); | ||
431 | |||
432 | /* Verify it is a data structure */ | ||
433 | if (dwarf_tag(&type) != DW_TAG_structure_type) | ||
434 | die("%s is not a data structure.", varname); | ||
435 | |||
436 | ref = xzalloc(sizeof(struct kprobe_trace_arg_ref)); | ||
437 | if (*ref_ptr) | ||
438 | (*ref_ptr)->next = ref; | ||
439 | else | ||
440 | *ref_ptr = ref; | ||
441 | } else { | ||
442 | /* Verify it is a data structure */ | ||
443 | if (dwarf_tag(&type) != DW_TAG_structure_type) | ||
444 | die("%s is not a data structure.", varname); | ||
445 | |||
446 | if (field->ref) | ||
447 | die("Semantic error: %s must be referred by '.'", | ||
448 | field->name); | ||
449 | if (!ref) | ||
450 | die("Structure on a register is not supported yet."); | ||
451 | } | ||
452 | |||
453 | if (die_find_member(&type, field->name, &member) == NULL) | ||
454 | die("%s(tyep:%s) has no member %s.", varname, | ||
455 | dwarf_diename(&type), field->name); | ||
456 | |||
457 | /* Get the offset of the field */ | ||
458 | if (dwarf_attr(&member, DW_AT_data_member_location, &attr) == NULL || | ||
459 | dwarf_formudata(&attr, &offs) != 0) | ||
460 | die("Failed to get the offset of %s.", field->name); | ||
461 | ref->offset += (long)offs; | ||
462 | |||
463 | /* Converting next field */ | ||
464 | if (field->next) | ||
465 | convert_variable_fields(&member, field->name, field->next, | ||
466 | &ref); | ||
342 | } | 467 | } |
343 | 468 | ||
344 | /* Show a variables in kprobe event format */ | 469 | /* Show a variables in kprobe event format */ |
345 | static void show_variable(Dwarf_Die *vr_die, struct probe_finder *pf) | 470 | static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) |
346 | { | 471 | { |
347 | Dwarf_Attribute attr; | 472 | Dwarf_Attribute attr; |
348 | Dwarf_Op *expr; | 473 | Dwarf_Op *expr; |
@@ -356,83 +481,77 @@ static void show_variable(Dwarf_Die *vr_die, struct probe_finder *pf) | |||
356 | if (ret <= 0 || nexpr == 0) | 481 | if (ret <= 0 || nexpr == 0) |
357 | goto error; | 482 | goto error; |
358 | 483 | ||
359 | show_location(expr, pf); | 484 | convert_location(expr, pf); |
485 | |||
486 | if (pf->pvar->field) | ||
487 | convert_variable_fields(vr_die, pf->pvar->name, | ||
488 | pf->pvar->field, &pf->tvar->ref); | ||
360 | /* *expr will be cached in libdw. Don't free it. */ | 489 | /* *expr will be cached in libdw. Don't free it. */ |
361 | return ; | 490 | return ; |
362 | error: | 491 | error: |
363 | /* TODO: Support const_value */ | 492 | /* TODO: Support const_value */ |
364 | die("Failed to find the location of %s at this address.\n" | 493 | die("Failed to find the location of %s at this address.\n" |
365 | " Perhaps, it has been optimized out.", pf->var); | 494 | " Perhaps, it has been optimized out.", pf->pvar->name); |
366 | } | 495 | } |
367 | 496 | ||
368 | /* Find a variable in a subprogram die */ | 497 | /* Find a variable in a subprogram die */ |
369 | static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) | 498 | static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) |
370 | { | 499 | { |
371 | int ret; | ||
372 | Dwarf_Die vr_die; | 500 | Dwarf_Die vr_die; |
501 | char buf[128]; | ||
373 | 502 | ||
374 | /* TODO: Support struct members and arrays */ | 503 | /* TODO: Support struct members and arrays */ |
375 | if (!is_c_varname(pf->var)) { | 504 | if (!is_c_varname(pf->pvar->name)) { |
376 | /* Output raw parameters */ | 505 | /* Copy raw parameters */ |
377 | ret = snprintf(pf->buf, pf->len, " %s", pf->var); | 506 | pf->tvar->value = xstrdup(pf->pvar->name); |
378 | DIE_IF(ret < 0); | 507 | } else { |
379 | DIE_IF(ret >= pf->len); | 508 | synthesize_perf_probe_arg(pf->pvar, buf, 128); |
380 | return ; | 509 | pf->tvar->name = xstrdup(buf); |
510 | pr_debug("Searching '%s' variable in context.\n", | ||
511 | pf->pvar->name); | ||
512 | /* Search child die for local variables and parameters. */ | ||
513 | if (!die_find_variable(sp_die, pf->pvar->name, &vr_die)) | ||
514 | die("Failed to find '%s' in this function.", | ||
515 | pf->pvar->name); | ||
516 | convert_variable(&vr_die, pf); | ||
381 | } | 517 | } |
382 | |||
383 | pr_debug("Searching '%s' variable in context.\n", pf->var); | ||
384 | /* Search child die for local variables and parameters. */ | ||
385 | if (!die_find_variable(sp_die, pf->var, &vr_die)) | ||
386 | die("Failed to find '%s' in this function.", pf->var); | ||
387 | |||
388 | show_variable(&vr_die, pf); | ||
389 | } | 518 | } |
390 | 519 | ||
391 | /* Show a probe point to output buffer */ | 520 | /* Show a probe point to output buffer */ |
392 | static void show_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) | 521 | static void convert_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) |
393 | { | 522 | { |
394 | struct probe_point *pp = pf->pp; | 523 | struct kprobe_trace_event *tev; |
395 | Dwarf_Addr eaddr; | 524 | Dwarf_Addr eaddr; |
396 | Dwarf_Die die_mem; | 525 | Dwarf_Die die_mem; |
397 | const char *name; | 526 | const char *name; |
398 | char tmp[MAX_PROBE_BUFFER]; | 527 | int ret, i; |
399 | int ret, i, len; | ||
400 | Dwarf_Attribute fb_attr; | 528 | Dwarf_Attribute fb_attr; |
401 | size_t nops; | 529 | size_t nops; |
402 | 530 | ||
531 | if (pf->ntevs == MAX_PROBES) | ||
532 | die("Too many( > %d) probe point found.\n", MAX_PROBES); | ||
533 | tev = &pf->tevs[pf->ntevs++]; | ||
534 | |||
403 | /* If no real subprogram, find a real one */ | 535 | /* If no real subprogram, find a real one */ |
404 | if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) { | 536 | if (!sp_die || dwarf_tag(sp_die) != DW_TAG_subprogram) { |
405 | sp_die = die_get_real_subprogram(&pf->cu_die, | 537 | sp_die = die_find_real_subprogram(&pf->cu_die, |
406 | pf->addr, &die_mem); | 538 | pf->addr, &die_mem); |
407 | if (!sp_die) | 539 | if (!sp_die) |
408 | die("Probe point is not found in subprograms."); | 540 | die("Probe point is not found in subprograms."); |
409 | } | 541 | } |
410 | 542 | ||
411 | /* Output name of probe point */ | 543 | /* Copy the name of probe point */ |
412 | name = dwarf_diename(sp_die); | 544 | name = dwarf_diename(sp_die); |
413 | if (name) { | 545 | if (name) { |
414 | dwarf_entrypc(sp_die, &eaddr); | 546 | dwarf_entrypc(sp_die, &eaddr); |
415 | ret = snprintf(tmp, MAX_PROBE_BUFFER, "%s+%lu", name, | 547 | tev->point.symbol = xstrdup(name); |
416 | (unsigned long)(pf->addr - eaddr)); | 548 | tev->point.offset = (unsigned long)(pf->addr - eaddr); |
417 | /* Copy the function name if possible */ | 549 | } else |
418 | if (!pp->function) { | ||
419 | pp->function = strdup(name); | ||
420 | pp->offset = (size_t)(pf->addr - eaddr); | ||
421 | } | ||
422 | } else { | ||
423 | /* This function has no name. */ | 550 | /* This function has no name. */ |
424 | ret = snprintf(tmp, MAX_PROBE_BUFFER, "0x%jx", | 551 | tev->point.offset = (unsigned long)pf->addr; |
425 | (uintmax_t)pf->addr); | 552 | |
426 | if (!pp->function) { | 553 | pr_debug("Probe point found: %s+%lu\n", tev->point.symbol, |
427 | /* TODO: Use _stext */ | 554 | tev->point.offset); |
428 | pp->function = strdup(""); | ||
429 | pp->offset = (size_t)pf->addr; | ||
430 | } | ||
431 | } | ||
432 | DIE_IF(ret < 0); | ||
433 | DIE_IF(ret >= MAX_PROBE_BUFFER); | ||
434 | len = ret; | ||
435 | pr_debug("Probe point found: %s\n", tmp); | ||
436 | 555 | ||
437 | /* Get the frame base attribute/ops */ | 556 | /* Get the frame base attribute/ops */ |
438 | dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr); | 557 | dwarf_attr(sp_die, DW_AT_frame_base, &fb_attr); |
@@ -442,22 +561,16 @@ static void show_probe_point(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
442 | 561 | ||
443 | /* Find each argument */ | 562 | /* Find each argument */ |
444 | /* TODO: use dwarf_cfi_addrframe */ | 563 | /* TODO: use dwarf_cfi_addrframe */ |
445 | for (i = 0; i < pp->nr_args; i++) { | 564 | tev->nargs = pf->pev->nargs; |
446 | pf->var = pp->args[i]; | 565 | tev->args = xzalloc(sizeof(struct kprobe_trace_arg) * tev->nargs); |
447 | pf->buf = &tmp[len]; | 566 | for (i = 0; i < pf->pev->nargs; i++) { |
448 | pf->len = MAX_PROBE_BUFFER - len; | 567 | pf->pvar = &pf->pev->args[i]; |
568 | pf->tvar = &tev->args[i]; | ||
449 | find_variable(sp_die, pf); | 569 | find_variable(sp_die, pf); |
450 | len += strlen(pf->buf); | ||
451 | } | 570 | } |
452 | 571 | ||
453 | /* *pf->fb_ops will be cached in libdw. Don't free it. */ | 572 | /* *pf->fb_ops will be cached in libdw. Don't free it. */ |
454 | pf->fb_ops = NULL; | 573 | pf->fb_ops = NULL; |
455 | |||
456 | if (pp->found == MAX_PROBES) | ||
457 | die("Too many( > %d) probe point found.\n", MAX_PROBES); | ||
458 | |||
459 | pp->probes[pp->found] = strdup(tmp); | ||
460 | pp->found++; | ||
461 | } | 574 | } |
462 | 575 | ||
463 | /* Find probe point from its line number */ | 576 | /* Find probe point from its line number */ |
@@ -489,7 +602,7 @@ static void find_probe_point_by_line(struct probe_finder *pf) | |||
489 | (int)i, lineno, (uintmax_t)addr); | 602 | (int)i, lineno, (uintmax_t)addr); |
490 | pf->addr = addr; | 603 | pf->addr = addr; |
491 | 604 | ||
492 | show_probe_point(NULL, pf); | 605 | convert_probe_point(NULL, pf); |
493 | /* Continuing, because target line might be inlined. */ | 606 | /* Continuing, because target line might be inlined. */ |
494 | } | 607 | } |
495 | } | 608 | } |
@@ -506,8 +619,7 @@ static int find_lazy_match_lines(struct list_head *head, | |||
506 | if (fd < 0) | 619 | if (fd < 0) |
507 | die("failed to open %s", fname); | 620 | die("failed to open %s", fname); |
508 | DIE_IF(fstat(fd, &st) < 0); | 621 | DIE_IF(fstat(fd, &st) < 0); |
509 | fbuf = malloc(st.st_size + 2); | 622 | fbuf = xmalloc(st.st_size + 2); |
510 | DIE_IF(fbuf == NULL); | ||
511 | DIE_IF(read(fd, fbuf, st.st_size) < 0); | 623 | DIE_IF(read(fd, fbuf, st.st_size) < 0); |
512 | close(fd); | 624 | close(fd); |
513 | fbuf[st.st_size] = '\n'; /* Dummy line */ | 625 | fbuf[st.st_size] = '\n'; /* Dummy line */ |
@@ -541,7 +653,7 @@ static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
541 | if (list_empty(&pf->lcache)) { | 653 | if (list_empty(&pf->lcache)) { |
542 | /* Matching lazy line pattern */ | 654 | /* Matching lazy line pattern */ |
543 | ret = find_lazy_match_lines(&pf->lcache, pf->fname, | 655 | ret = find_lazy_match_lines(&pf->lcache, pf->fname, |
544 | pf->pp->lazy_line); | 656 | pf->pev->point.lazy_line); |
545 | if (ret <= 0) | 657 | if (ret <= 0) |
546 | die("No matched lines found in %s.", pf->fname); | 658 | die("No matched lines found in %s.", pf->fname); |
547 | } | 659 | } |
@@ -566,7 +678,7 @@ static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
566 | if (!dwarf_haspc(sp_die, addr)) | 678 | if (!dwarf_haspc(sp_die, addr)) |
567 | continue; | 679 | continue; |
568 | /* Address filtering 2: No child include addr? */ | 680 | /* Address filtering 2: No child include addr? */ |
569 | if (die_get_inlinefunc(sp_die, addr, &die_mem)) | 681 | if (die_find_inlinefunc(sp_die, addr, &die_mem)) |
570 | continue; | 682 | continue; |
571 | } | 683 | } |
572 | 684 | ||
@@ -574,7 +686,7 @@ static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
574 | (int)i, lineno, (unsigned long long)addr); | 686 | (int)i, lineno, (unsigned long long)addr); |
575 | pf->addr = addr; | 687 | pf->addr = addr; |
576 | 688 | ||
577 | show_probe_point(sp_die, pf); | 689 | convert_probe_point(sp_die, pf); |
578 | /* Continuing, because target line might be inlined. */ | 690 | /* Continuing, because target line might be inlined. */ |
579 | } | 691 | } |
580 | /* TODO: deallocate lines, but how? */ | 692 | /* TODO: deallocate lines, but how? */ |
@@ -583,7 +695,7 @@ static void find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf) | |||
583 | static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) | 695 | static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) |
584 | { | 696 | { |
585 | struct probe_finder *pf = (struct probe_finder *)data; | 697 | struct probe_finder *pf = (struct probe_finder *)data; |
586 | struct probe_point *pp = pf->pp; | 698 | struct perf_probe_point *pp = &pf->pev->point; |
587 | 699 | ||
588 | if (pp->lazy_line) | 700 | if (pp->lazy_line) |
589 | find_probe_point_lazy(in_die, pf); | 701 | find_probe_point_lazy(in_die, pf); |
@@ -594,7 +706,7 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) | |||
594 | pr_debug("found inline addr: 0x%jx\n", | 706 | pr_debug("found inline addr: 0x%jx\n", |
595 | (uintmax_t)pf->addr); | 707 | (uintmax_t)pf->addr); |
596 | 708 | ||
597 | show_probe_point(in_die, pf); | 709 | convert_probe_point(in_die, pf); |
598 | } | 710 | } |
599 | 711 | ||
600 | return DWARF_CB_OK; | 712 | return DWARF_CB_OK; |
@@ -604,7 +716,7 @@ static int probe_point_inline_cb(Dwarf_Die *in_die, void *data) | |||
604 | static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) | 716 | static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) |
605 | { | 717 | { |
606 | struct probe_finder *pf = (struct probe_finder *)data; | 718 | struct probe_finder *pf = (struct probe_finder *)data; |
607 | struct probe_point *pp = pf->pp; | 719 | struct perf_probe_point *pp = &pf->pev->point; |
608 | 720 | ||
609 | /* Check tag and diename */ | 721 | /* Check tag and diename */ |
610 | if (dwarf_tag(sp_die) != DW_TAG_subprogram || | 722 | if (dwarf_tag(sp_die) != DW_TAG_subprogram || |
@@ -624,7 +736,7 @@ static int probe_point_search_cb(Dwarf_Die *sp_die, void *data) | |||
624 | pf->addr = die_get_entrypc(sp_die); | 736 | pf->addr = die_get_entrypc(sp_die); |
625 | pf->addr += pp->offset; | 737 | pf->addr += pp->offset; |
626 | /* TODO: Check the address in this function */ | 738 | /* TODO: Check the address in this function */ |
627 | show_probe_point(sp_die, pf); | 739 | convert_probe_point(sp_die, pf); |
628 | } | 740 | } |
629 | } else | 741 | } else |
630 | /* Inlined function: search instances */ | 742 | /* Inlined function: search instances */ |
@@ -638,20 +750,25 @@ static void find_probe_point_by_func(struct probe_finder *pf) | |||
638 | dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, pf, 0); | 750 | dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, pf, 0); |
639 | } | 751 | } |
640 | 752 | ||
641 | /* Find a probe point */ | 753 | /* Find kprobe_trace_events specified by perf_probe_event from debuginfo */ |
642 | int find_probe_point(int fd, struct probe_point *pp) | 754 | int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, |
755 | struct kprobe_trace_event **tevs) | ||
643 | { | 756 | { |
644 | struct probe_finder pf = {.pp = pp}; | 757 | struct probe_finder pf = {.pev = pev}; |
758 | struct perf_probe_point *pp = &pev->point; | ||
645 | Dwarf_Off off, noff; | 759 | Dwarf_Off off, noff; |
646 | size_t cuhl; | 760 | size_t cuhl; |
647 | Dwarf_Die *diep; | 761 | Dwarf_Die *diep; |
648 | Dwarf *dbg; | 762 | Dwarf *dbg; |
649 | 763 | ||
764 | pf.tevs = xzalloc(sizeof(struct kprobe_trace_event) * MAX_PROBES); | ||
765 | *tevs = pf.tevs; | ||
766 | pf.ntevs = 0; | ||
767 | |||
650 | dbg = dwarf_begin(fd, DWARF_C_READ); | 768 | dbg = dwarf_begin(fd, DWARF_C_READ); |
651 | if (!dbg) | 769 | if (!dbg) |
652 | return -ENOENT; | 770 | return -ENOENT; |
653 | 771 | ||
654 | pp->found = 0; | ||
655 | off = 0; | 772 | off = 0; |
656 | line_list__init(&pf.lcache); | 773 | line_list__init(&pf.lcache); |
657 | /* Loop on CUs (Compilation Unit) */ | 774 | /* Loop on CUs (Compilation Unit) */ |
@@ -682,9 +799,81 @@ int find_probe_point(int fd, struct probe_point *pp) | |||
682 | line_list__free(&pf.lcache); | 799 | line_list__free(&pf.lcache); |
683 | dwarf_end(dbg); | 800 | dwarf_end(dbg); |
684 | 801 | ||
685 | return pp->found; | 802 | return pf.ntevs; |
803 | } | ||
804 | |||
805 | /* Reverse search */ | ||
806 | int find_perf_probe_point(int fd, unsigned long addr, | ||
807 | struct perf_probe_point *ppt) | ||
808 | { | ||
809 | Dwarf_Die cudie, spdie, indie; | ||
810 | Dwarf *dbg; | ||
811 | Dwarf_Line *line; | ||
812 | Dwarf_Addr laddr, eaddr; | ||
813 | const char *tmp; | ||
814 | int lineno, ret = 0; | ||
815 | |||
816 | dbg = dwarf_begin(fd, DWARF_C_READ); | ||
817 | if (!dbg) | ||
818 | return -ENOENT; | ||
819 | |||
820 | /* Find cu die */ | ||
821 | if (!dwarf_addrdie(dbg, (Dwarf_Addr)addr, &cudie)) { | ||
822 | ret = -EINVAL; | ||
823 | goto end; | ||
824 | } | ||
825 | |||
826 | /* Find a corresponding line */ | ||
827 | line = dwarf_getsrc_die(&cudie, (Dwarf_Addr)addr); | ||
828 | if (line) { | ||
829 | dwarf_lineaddr(line, &laddr); | ||
830 | if ((Dwarf_Addr)addr == laddr) { | ||
831 | dwarf_lineno(line, &lineno); | ||
832 | ppt->line = lineno; | ||
833 | |||
834 | tmp = dwarf_linesrc(line, NULL, NULL); | ||
835 | DIE_IF(!tmp); | ||
836 | ppt->file = xstrdup(tmp); | ||
837 | ret = 1; | ||
838 | } | ||
839 | } | ||
840 | |||
841 | /* Find a corresponding function */ | ||
842 | if (die_find_real_subprogram(&cudie, (Dwarf_Addr)addr, &spdie)) { | ||
843 | tmp = dwarf_diename(&spdie); | ||
844 | if (!tmp) | ||
845 | goto end; | ||
846 | |||
847 | dwarf_entrypc(&spdie, &eaddr); | ||
848 | if (!lineno) { | ||
849 | /* We don't have a line number, let's use offset */ | ||
850 | ppt->function = xstrdup(tmp); | ||
851 | ppt->offset = addr - (unsigned long)eaddr; | ||
852 | ret = 1; | ||
853 | goto end; | ||
854 | } | ||
855 | if (die_find_inlinefunc(&spdie, (Dwarf_Addr)addr, &indie)) { | ||
856 | /* addr in an inline function */ | ||
857 | tmp = dwarf_diename(&indie); | ||
858 | if (!tmp) | ||
859 | goto end; | ||
860 | dwarf_decl_line(&indie, &lineno); | ||
861 | } else { | ||
862 | if (eaddr == addr) /* No offset: function entry */ | ||
863 | lineno = ppt->line; | ||
864 | else | ||
865 | dwarf_decl_line(&spdie, &lineno); | ||
866 | } | ||
867 | ppt->function = xstrdup(tmp); | ||
868 | ppt->line -= lineno; /* Make a relative line number */ | ||
869 | } | ||
870 | |||
871 | end: | ||
872 | dwarf_end(dbg); | ||
873 | return ret; | ||
686 | } | 874 | } |
687 | 875 | ||
876 | |||
688 | /* Find line range from its line number */ | 877 | /* Find line range from its line number */ |
689 | static void find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) | 878 | static void find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) |
690 | { | 879 | { |
@@ -716,7 +905,7 @@ static void find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) | |||
716 | continue; | 905 | continue; |
717 | 906 | ||
718 | /* Address filtering 2: No child include addr? */ | 907 | /* Address filtering 2: No child include addr? */ |
719 | if (die_get_inlinefunc(sp_die, addr, &die_mem)) | 908 | if (die_find_inlinefunc(sp_die, addr, &die_mem)) |
720 | continue; | 909 | continue; |
721 | } | 910 | } |
722 | 911 | ||
@@ -727,7 +916,7 @@ static void find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) | |||
727 | 916 | ||
728 | /* Copy real path */ | 917 | /* Copy real path */ |
729 | if (!lf->lr->path) | 918 | if (!lf->lr->path) |
730 | lf->lr->path = strdup(src); | 919 | lf->lr->path = xstrdup(src); |
731 | line_list__add_line(&lf->lr->line_list, (unsigned int)lineno); | 920 | line_list__add_line(&lf->lr->line_list, (unsigned int)lineno); |
732 | } | 921 | } |
733 | /* Update status */ | 922 | /* Update status */ |
diff --git a/tools/perf/util/probe-finder.h b/tools/perf/util/probe-finder.h index 21f7354397b4..3564f22954f1 100644 --- a/tools/perf/util/probe-finder.h +++ b/tools/perf/util/probe-finder.h | |||
@@ -3,6 +3,7 @@ | |||
3 | 3 | ||
4 | #include <stdbool.h> | 4 | #include <stdbool.h> |
5 | #include "util.h" | 5 | #include "util.h" |
6 | #include "probe-event.h" | ||
6 | 7 | ||
7 | #define MAX_PATH_LEN 256 | 8 | #define MAX_PATH_LEN 256 |
8 | #define MAX_PROBE_BUFFER 1024 | 9 | #define MAX_PROBE_BUFFER 1024 |
@@ -14,67 +15,36 @@ static inline int is_c_varname(const char *name) | |||
14 | return isalpha(name[0]) || name[0] == '_'; | 15 | return isalpha(name[0]) || name[0] == '_'; |
15 | } | 16 | } |
16 | 17 | ||
17 | struct probe_point { | 18 | #ifdef DWARF_SUPPORT |
18 | char *event; /* Event name */ | 19 | /* Find kprobe_trace_events specified by perf_probe_event from debuginfo */ |
19 | char *group; /* Event group */ | 20 | extern int find_kprobe_trace_events(int fd, struct perf_probe_event *pev, |
21 | struct kprobe_trace_event **tevs); | ||
20 | 22 | ||
21 | /* Inputs */ | 23 | /* Find a perf_probe_point from debuginfo */ |
22 | char *file; /* File name */ | 24 | extern int find_perf_probe_point(int fd, unsigned long addr, |
23 | int line; /* Line number */ | 25 | struct perf_probe_point *ppt); |
24 | char *lazy_line; /* Lazy line pattern */ | ||
25 | 26 | ||
26 | char *function; /* Function name */ | ||
27 | int offset; /* Offset bytes */ | ||
28 | |||
29 | int nr_args; /* Number of arguments */ | ||
30 | char **args; /* Arguments */ | ||
31 | |||
32 | int retprobe; /* Return probe */ | ||
33 | |||
34 | /* Output */ | ||
35 | int found; /* Number of found probe points */ | ||
36 | char *probes[MAX_PROBES]; /* Output buffers (will be allocated)*/ | ||
37 | }; | ||
38 | |||
39 | /* Line number container */ | ||
40 | struct line_node { | ||
41 | struct list_head list; | ||
42 | unsigned int line; | ||
43 | }; | ||
44 | |||
45 | /* Line range */ | ||
46 | struct line_range { | ||
47 | char *file; /* File name */ | ||
48 | char *function; /* Function name */ | ||
49 | unsigned int start; /* Start line number */ | ||
50 | unsigned int end; /* End line number */ | ||
51 | int offset; /* Start line offset */ | ||
52 | char *path; /* Real path name */ | ||
53 | struct list_head line_list; /* Visible lines */ | ||
54 | }; | ||
55 | |||
56 | #ifndef NO_DWARF_SUPPORT | ||
57 | extern int find_probe_point(int fd, struct probe_point *pp); | ||
58 | extern int find_line_range(int fd, struct line_range *lr); | 27 | extern int find_line_range(int fd, struct line_range *lr); |
59 | 28 | ||
60 | #include <dwarf.h> | 29 | #include <dwarf.h> |
61 | #include <libdw.h> | 30 | #include <libdw.h> |
62 | 31 | ||
63 | struct probe_finder { | 32 | struct probe_finder { |
64 | struct probe_point *pp; /* Target probe point */ | 33 | struct perf_probe_event *pev; /* Target probe event */ |
34 | int ntevs; /* number of trace events */ | ||
35 | struct kprobe_trace_event *tevs; /* Result trace events */ | ||
65 | 36 | ||
66 | /* For function searching */ | 37 | /* For function searching */ |
67 | Dwarf_Addr addr; /* Address */ | 38 | Dwarf_Addr addr; /* Address */ |
68 | const char *fname; /* File name */ | 39 | const char *fname; /* Real file name */ |
69 | int lno; /* Line number */ | 40 | int lno; /* Line number */ |
70 | Dwarf_Die cu_die; /* Current CU */ | 41 | Dwarf_Die cu_die; /* Current CU */ |
42 | struct list_head lcache; /* Line cache for lazy match */ | ||
71 | 43 | ||
72 | /* For variable searching */ | 44 | /* For variable searching */ |
73 | Dwarf_Op *fb_ops; /* Frame base attribute */ | 45 | Dwarf_Op *fb_ops; /* Frame base attribute */ |
74 | const char *var; /* Current variable name */ | 46 | struct perf_probe_arg *pvar; /* Current target variable */ |
75 | char *buf; /* Current output buffer */ | 47 | struct kprobe_trace_arg *tvar; /* Current result variable */ |
76 | int len; /* Length of output buffer */ | ||
77 | struct list_head lcache; /* Line cache for lazy match */ | ||
78 | }; | 48 | }; |
79 | 49 | ||
80 | struct line_finder { | 50 | struct line_finder { |
@@ -87,6 +57,6 @@ struct line_finder { | |||
87 | int found; | 57 | int found; |
88 | }; | 58 | }; |
89 | 59 | ||
90 | #endif /* NO_DWARF_SUPPORT */ | 60 | #endif /* DWARF_SUPPORT */ |
91 | 61 | ||
92 | #endif /*_PROBE_FINDER_H */ | 62 | #endif /*_PROBE_FINDER_H */ |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index eed1cb889008..76b4ac689df9 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -117,13 +117,13 @@ static bool symbol__match_parent_regex(struct symbol *sym) | |||
117 | return 0; | 117 | return 0; |
118 | } | 118 | } |
119 | 119 | ||
120 | struct symbol **perf_session__resolve_callchain(struct perf_session *self, | 120 | struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, |
121 | struct thread *thread, | 121 | struct thread *thread, |
122 | struct ip_callchain *chain, | 122 | struct ip_callchain *chain, |
123 | struct symbol **parent) | 123 | struct symbol **parent) |
124 | { | 124 | { |
125 | u8 cpumode = PERF_RECORD_MISC_USER; | 125 | u8 cpumode = PERF_RECORD_MISC_USER; |
126 | struct symbol **syms = NULL; | 126 | struct map_symbol *syms = NULL; |
127 | unsigned int i; | 127 | unsigned int i; |
128 | 128 | ||
129 | if (symbol_conf.use_callchain) { | 129 | if (symbol_conf.use_callchain) { |
@@ -160,7 +160,8 @@ struct symbol **perf_session__resolve_callchain(struct perf_session *self, | |||
160 | *parent = al.sym; | 160 | *parent = al.sym; |
161 | if (!symbol_conf.use_callchain) | 161 | if (!symbol_conf.use_callchain) |
162 | break; | 162 | break; |
163 | syms[i] = al.sym; | 163 | syms[i].map = al.map; |
164 | syms[i].sym = al.sym; | ||
164 | } | 165 | } |
165 | } | 166 | } |
166 | 167 | ||
@@ -543,32 +544,3 @@ int perf_session__set_kallsyms_ref_reloc_sym(struct perf_session *self, | |||
543 | 544 | ||
544 | return 0; | 545 | return 0; |
545 | } | 546 | } |
546 | |||
547 | static u64 map__reloc_map_ip(struct map *map, u64 ip) | ||
548 | { | ||
549 | return ip + (s64)map->pgoff; | ||
550 | } | ||
551 | |||
552 | static u64 map__reloc_unmap_ip(struct map *map, u64 ip) | ||
553 | { | ||
554 | return ip - (s64)map->pgoff; | ||
555 | } | ||
556 | |||
557 | void map__reloc_vmlinux(struct map *self) | ||
558 | { | ||
559 | struct kmap *kmap = map__kmap(self); | ||
560 | s64 reloc; | ||
561 | |||
562 | if (!kmap->ref_reloc_sym || !kmap->ref_reloc_sym->unrelocated_addr) | ||
563 | return; | ||
564 | |||
565 | reloc = (kmap->ref_reloc_sym->unrelocated_addr - | ||
566 | kmap->ref_reloc_sym->addr); | ||
567 | |||
568 | if (!reloc) | ||
569 | return; | ||
570 | |||
571 | self->map_ip = map__reloc_map_ip; | ||
572 | self->unmap_ip = map__reloc_unmap_ip; | ||
573 | self->pgoff = reloc; | ||
574 | } | ||
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 5c33417eebb3..631f8157fc17 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
@@ -57,10 +57,10 @@ int __perf_session__process_events(struct perf_session *self, | |||
57 | int perf_session__process_events(struct perf_session *self, | 57 | int perf_session__process_events(struct perf_session *self, |
58 | struct perf_event_ops *event_ops); | 58 | struct perf_event_ops *event_ops); |
59 | 59 | ||
60 | struct symbol **perf_session__resolve_callchain(struct perf_session *self, | 60 | struct map_symbol *perf_session__resolve_callchain(struct perf_session *self, |
61 | struct thread *thread, | 61 | struct thread *thread, |
62 | struct ip_callchain *chain, | 62 | struct ip_callchain *chain, |
63 | struct symbol **parent); | 63 | struct symbol **parent); |
64 | 64 | ||
65 | bool perf_session__has_traces(struct perf_session *self, const char *msg); | 65 | bool perf_session__has_traces(struct perf_session *self, const char *msg); |
66 | 66 | ||
@@ -86,4 +86,13 @@ static inline struct map * | |||
86 | { | 86 | { |
87 | return map_groups__new_module(&self->kmaps, start, filename); | 87 | return map_groups__new_module(&self->kmaps, start, filename); |
88 | } | 88 | } |
89 | |||
90 | #ifdef NO_NEWT_SUPPORT | ||
91 | static inline void perf_session__browse_hists(struct rb_root *hists __used, | ||
92 | u64 session_total __used, | ||
93 | const char *helpline __used) {} | ||
94 | #else | ||
95 | void perf_session__browse_hists(struct rb_root *hists, u64 session_total, | ||
96 | const char *helpline); | ||
97 | #endif | ||
89 | #endif /* __PERF_SESSION_H */ | 98 | #endif /* __PERF_SESSION_H */ |
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index cb0f327de9e8..9b80c13cae46 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
@@ -131,8 +131,8 @@ sort__comm_print(FILE *fp, struct hist_entry *self, unsigned int width) | |||
131 | int64_t | 131 | int64_t |
132 | sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) | 132 | sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) |
133 | { | 133 | { |
134 | struct dso *dso_l = left->map ? left->map->dso : NULL; | 134 | struct dso *dso_l = left->ms.map ? left->ms.map->dso : NULL; |
135 | struct dso *dso_r = right->map ? right->map->dso : NULL; | 135 | struct dso *dso_r = right->ms.map ? right->ms.map->dso : NULL; |
136 | const char *dso_name_l, *dso_name_r; | 136 | const char *dso_name_l, *dso_name_r; |
137 | 137 | ||
138 | if (!dso_l || !dso_r) | 138 | if (!dso_l || !dso_r) |
@@ -152,9 +152,9 @@ sort__dso_cmp(struct hist_entry *left, struct hist_entry *right) | |||
152 | size_t | 152 | size_t |
153 | sort__dso_print(FILE *fp, struct hist_entry *self, unsigned int width) | 153 | sort__dso_print(FILE *fp, struct hist_entry *self, unsigned int width) |
154 | { | 154 | { |
155 | if (self->map && self->map->dso) { | 155 | if (self->ms.map && self->ms.map->dso) { |
156 | const char *dso_name = !verbose ? self->map->dso->short_name : | 156 | const char *dso_name = !verbose ? self->ms.map->dso->short_name : |
157 | self->map->dso->long_name; | 157 | self->ms.map->dso->long_name; |
158 | return repsep_fprintf(fp, "%-*s", width, dso_name); | 158 | return repsep_fprintf(fp, "%-*s", width, dso_name); |
159 | } | 159 | } |
160 | 160 | ||
@@ -168,11 +168,11 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | |||
168 | { | 168 | { |
169 | u64 ip_l, ip_r; | 169 | u64 ip_l, ip_r; |
170 | 170 | ||
171 | if (left->sym == right->sym) | 171 | if (left->ms.sym == right->ms.sym) |
172 | return 0; | 172 | return 0; |
173 | 173 | ||
174 | ip_l = left->sym ? left->sym->start : left->ip; | 174 | ip_l = left->ms.sym ? left->ms.sym->start : left->ip; |
175 | ip_r = right->sym ? right->sym->start : right->ip; | 175 | ip_r = right->ms.sym ? right->ms.sym->start : right->ip; |
176 | 176 | ||
177 | return (int64_t)(ip_r - ip_l); | 177 | return (int64_t)(ip_r - ip_l); |
178 | } | 178 | } |
@@ -184,13 +184,13 @@ sort__sym_print(FILE *fp, struct hist_entry *self, unsigned int width __used) | |||
184 | size_t ret = 0; | 184 | size_t ret = 0; |
185 | 185 | ||
186 | if (verbose) { | 186 | if (verbose) { |
187 | char o = self->map ? dso__symtab_origin(self->map->dso) : '!'; | 187 | char o = self->ms.map ? dso__symtab_origin(self->ms.map->dso) : '!'; |
188 | ret += repsep_fprintf(fp, "%#018llx %c ", (u64)self->ip, o); | 188 | ret += repsep_fprintf(fp, "%#018llx %c ", (u64)self->ip, o); |
189 | } | 189 | } |
190 | 190 | ||
191 | ret += repsep_fprintf(fp, "[%c] ", self->level); | 191 | ret += repsep_fprintf(fp, "[%c] ", self->level); |
192 | if (self->sym) | 192 | if (self->ms.sym) |
193 | ret += repsep_fprintf(fp, "%s", self->sym->name); | 193 | ret += repsep_fprintf(fp, "%s", self->ms.sym->name); |
194 | else | 194 | else |
195 | ret += repsep_fprintf(fp, "%#016llx", (u64)self->ip); | 195 | ret += repsep_fprintf(fp, "%#016llx", (u64)self->ip); |
196 | 196 | ||
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 753f9ea99fb0..598568696f97 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h | |||
@@ -45,8 +45,7 @@ struct hist_entry { | |||
45 | struct rb_node rb_node; | 45 | struct rb_node rb_node; |
46 | u64 count; | 46 | u64 count; |
47 | struct thread *thread; | 47 | struct thread *thread; |
48 | struct map *map; | 48 | struct map_symbol ms; |
49 | struct symbol *sym; | ||
50 | u64 ip; | 49 | u64 ip; |
51 | char level; | 50 | char level; |
52 | struct symbol *parent; | 51 | struct symbol *parent; |
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index a175949ed216..d4389242cfd7 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c | |||
@@ -1,49 +1,6 @@ | |||
1 | #include "string.h" | 1 | #include "string.h" |
2 | #include "util.h" | 2 | #include "util.h" |
3 | 3 | ||
4 | static int hex(char ch) | ||
5 | { | ||
6 | if ((ch >= '0') && (ch <= '9')) | ||
7 | return ch - '0'; | ||
8 | if ((ch >= 'a') && (ch <= 'f')) | ||
9 | return ch - 'a' + 10; | ||
10 | if ((ch >= 'A') && (ch <= 'F')) | ||
11 | return ch - 'A' + 10; | ||
12 | return -1; | ||
13 | } | ||
14 | |||
15 | /* | ||
16 | * While we find nice hex chars, build a long_val. | ||
17 | * Return number of chars processed. | ||
18 | */ | ||
19 | int hex2u64(const char *ptr, u64 *long_val) | ||
20 | { | ||
21 | const char *p = ptr; | ||
22 | *long_val = 0; | ||
23 | |||
24 | while (*p) { | ||
25 | const int hex_val = hex(*p); | ||
26 | |||
27 | if (hex_val < 0) | ||
28 | break; | ||
29 | |||
30 | *long_val = (*long_val << 4) | hex_val; | ||
31 | p++; | ||
32 | } | ||
33 | |||
34 | return p - ptr; | ||
35 | } | ||
36 | |||
37 | char *strxfrchar(char *s, char from, char to) | ||
38 | { | ||
39 | char *p = s; | ||
40 | |||
41 | while ((p = strchr(p, from)) != NULL) | ||
42 | *p++ = to; | ||
43 | |||
44 | return s; | ||
45 | } | ||
46 | |||
47 | #define K 1024LL | 4 | #define K 1024LL |
48 | /* | 5 | /* |
49 | * perf_atoll() | 6 | * perf_atoll() |
diff --git a/tools/perf/util/string.h b/tools/perf/util/string.h index 542e44de3719..700582416664 100644 --- a/tools/perf/util/string.h +++ b/tools/perf/util/string.h | |||
@@ -4,8 +4,6 @@ | |||
4 | #include <stdbool.h> | 4 | #include <stdbool.h> |
5 | #include "types.h" | 5 | #include "types.h" |
6 | 6 | ||
7 | int hex2u64(const char *ptr, u64 *val); | ||
8 | char *strxfrchar(char *s, char from, char to); | ||
9 | s64 perf_atoll(const char *str); | 7 | s64 perf_atoll(const char *str); |
10 | char **argv_split(const char *str, int *argcp); | 8 | char **argv_split(const char *str, int *argcp); |
11 | void argv_free(char **argv); | 9 | void argv_free(char **argv); |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index c458c4a371d1..f3d4151e46a1 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -1,13 +1,19 @@ | |||
1 | #include "util.h" | 1 | #define _GNU_SOURCE |
2 | #include "../perf.h" | 2 | #include <ctype.h> |
3 | #include "sort.h" | 3 | #include <dirent.h> |
4 | #include "string.h" | 4 | #include <errno.h> |
5 | #include <libgen.h> | ||
6 | #include <stdlib.h> | ||
7 | #include <stdio.h> | ||
8 | #include <string.h> | ||
9 | #include <sys/types.h> | ||
10 | #include <sys/stat.h> | ||
11 | #include <sys/param.h> | ||
12 | #include <fcntl.h> | ||
13 | #include <unistd.h> | ||
5 | #include "symbol.h" | 14 | #include "symbol.h" |
6 | #include "thread.h" | 15 | #include "strlist.h" |
7 | 16 | ||
8 | #include "debug.h" | ||
9 | |||
10 | #include <asm/bug.h> | ||
11 | #include <libelf.h> | 17 | #include <libelf.h> |
12 | #include <gelf.h> | 18 | #include <gelf.h> |
13 | #include <elf.h> | 19 | #include <elf.h> |
@@ -18,18 +24,6 @@ | |||
18 | #define NT_GNU_BUILD_ID 3 | 24 | #define NT_GNU_BUILD_ID 3 |
19 | #endif | 25 | #endif |
20 | 26 | ||
21 | enum dso_origin { | ||
22 | DSO__ORIG_KERNEL = 0, | ||
23 | DSO__ORIG_JAVA_JIT, | ||
24 | DSO__ORIG_BUILD_ID_CACHE, | ||
25 | DSO__ORIG_FEDORA, | ||
26 | DSO__ORIG_UBUNTU, | ||
27 | DSO__ORIG_BUILDID, | ||
28 | DSO__ORIG_DSO, | ||
29 | DSO__ORIG_KMODULE, | ||
30 | DSO__ORIG_NOT_FOUND, | ||
31 | }; | ||
32 | |||
33 | static void dsos__add(struct list_head *head, struct dso *dso); | 27 | static void dsos__add(struct list_head *head, struct dso *dso); |
34 | static struct map *map__new2(u64 start, struct dso *dso, enum map_type type); | 28 | static struct map *map__new2(u64 start, struct dso *dso, enum map_type type); |
35 | static int dso__load_kernel_sym(struct dso *self, struct map *map, | 29 | static int dso__load_kernel_sym(struct dso *self, struct map *map, |
@@ -126,8 +120,8 @@ static void map_groups__fixup_end(struct map_groups *self) | |||
126 | static struct symbol *symbol__new(u64 start, u64 len, const char *name) | 120 | static struct symbol *symbol__new(u64 start, u64 len, const char *name) |
127 | { | 121 | { |
128 | size_t namelen = strlen(name) + 1; | 122 | size_t namelen = strlen(name) + 1; |
129 | struct symbol *self = zalloc(symbol_conf.priv_size + | 123 | struct symbol *self = calloc(1, (symbol_conf.priv_size + |
130 | sizeof(*self) + namelen); | 124 | sizeof(*self) + namelen)); |
131 | if (self == NULL) | 125 | if (self == NULL) |
132 | return NULL; | 126 | return NULL; |
133 | 127 | ||
@@ -178,7 +172,7 @@ static void dso__set_basename(struct dso *self) | |||
178 | 172 | ||
179 | struct dso *dso__new(const char *name) | 173 | struct dso *dso__new(const char *name) |
180 | { | 174 | { |
181 | struct dso *self = zalloc(sizeof(*self) + strlen(name) + 1); | 175 | struct dso *self = calloc(1, sizeof(*self) + strlen(name) + 1); |
182 | 176 | ||
183 | if (self != NULL) { | 177 | if (self != NULL) { |
184 | int i; | 178 | int i; |
@@ -870,8 +864,8 @@ out_close: | |||
870 | if (err == 0) | 864 | if (err == 0) |
871 | return nr; | 865 | return nr; |
872 | out: | 866 | out: |
873 | pr_warning("%s: problems reading %s PLT info.\n", | 867 | pr_debug("%s: problems reading %s PLT info.\n", |
874 | __func__, self->long_name); | 868 | __func__, self->long_name); |
875 | return 0; | 869 | return 0; |
876 | } | 870 | } |
877 | 871 | ||
@@ -1025,7 +1019,7 @@ static int dso__load_sym(struct dso *self, struct map *map, const char *name, | |||
1025 | } | 1019 | } |
1026 | curr_map->map_ip = identity__map_ip; | 1020 | curr_map->map_ip = identity__map_ip; |
1027 | curr_map->unmap_ip = identity__map_ip; | 1021 | curr_map->unmap_ip = identity__map_ip; |
1028 | curr_dso->origin = DSO__ORIG_KERNEL; | 1022 | curr_dso->origin = self->origin; |
1029 | map_groups__insert(kmap->kmaps, curr_map); | 1023 | map_groups__insert(kmap->kmaps, curr_map); |
1030 | dsos__add(&dsos__kernel, curr_dso); | 1024 | dsos__add(&dsos__kernel, curr_dso); |
1031 | dso__set_loaded(curr_dso, map->type); | 1025 | dso__set_loaded(curr_dso, map->type); |
@@ -1394,13 +1388,13 @@ static int dso__kernel_module_get_build_id(struct dso *self) | |||
1394 | return 0; | 1388 | return 0; |
1395 | } | 1389 | } |
1396 | 1390 | ||
1397 | static int map_groups__set_modules_path_dir(struct map_groups *self, char *dirname) | 1391 | static int map_groups__set_modules_path_dir(struct map_groups *self, char *dir_name) |
1398 | { | 1392 | { |
1399 | struct dirent *dent; | 1393 | struct dirent *dent; |
1400 | DIR *dir = opendir(dirname); | 1394 | DIR *dir = opendir(dir_name); |
1401 | 1395 | ||
1402 | if (!dir) { | 1396 | if (!dir) { |
1403 | pr_debug("%s: cannot open %s dir\n", __func__, dirname); | 1397 | pr_debug("%s: cannot open %s dir\n", __func__, dir_name); |
1404 | return -1; | 1398 | return -1; |
1405 | } | 1399 | } |
1406 | 1400 | ||
@@ -1413,7 +1407,7 @@ static int map_groups__set_modules_path_dir(struct map_groups *self, char *dirna | |||
1413 | continue; | 1407 | continue; |
1414 | 1408 | ||
1415 | snprintf(path, sizeof(path), "%s/%s", | 1409 | snprintf(path, sizeof(path), "%s/%s", |
1416 | dirname, dent->d_name); | 1410 | dir_name, dent->d_name); |
1417 | if (map_groups__set_modules_path_dir(self, path) < 0) | 1411 | if (map_groups__set_modules_path_dir(self, path) < 0) |
1418 | goto failure; | 1412 | goto failure; |
1419 | } else { | 1413 | } else { |
@@ -1433,7 +1427,7 @@ static int map_groups__set_modules_path_dir(struct map_groups *self, char *dirna | |||
1433 | continue; | 1427 | continue; |
1434 | 1428 | ||
1435 | snprintf(path, sizeof(path), "%s/%s", | 1429 | snprintf(path, sizeof(path), "%s/%s", |
1436 | dirname, dent->d_name); | 1430 | dir_name, dent->d_name); |
1437 | 1431 | ||
1438 | long_name = strdup(path); | 1432 | long_name = strdup(path); |
1439 | if (long_name == NULL) | 1433 | if (long_name == NULL) |
@@ -1470,8 +1464,8 @@ static int map_groups__set_modules_path(struct map_groups *self) | |||
1470 | */ | 1464 | */ |
1471 | static struct map *map__new2(u64 start, struct dso *dso, enum map_type type) | 1465 | static struct map *map__new2(u64 start, struct dso *dso, enum map_type type) |
1472 | { | 1466 | { |
1473 | struct map *self = zalloc(sizeof(*self) + | 1467 | struct map *self = calloc(1, (sizeof(*self) + |
1474 | (dso->kernel ? sizeof(struct kmap) : 0)); | 1468 | (dso->kernel ? sizeof(struct kmap) : 0))); |
1475 | if (self != NULL) { | 1469 | if (self != NULL) { |
1476 | /* | 1470 | /* |
1477 | * ->end will be filled after we load all the symbols | 1471 | * ->end will be filled after we load all the symbols |
@@ -1895,6 +1889,17 @@ out_fail: | |||
1895 | return -1; | 1889 | return -1; |
1896 | } | 1890 | } |
1897 | 1891 | ||
1892 | size_t vmlinux_path__fprintf(FILE *fp) | ||
1893 | { | ||
1894 | int i; | ||
1895 | size_t printed = 0; | ||
1896 | |||
1897 | for (i = 0; i < vmlinux_path__nr_entries; ++i) | ||
1898 | printed += fprintf(fp, "[%d] %s\n", i, vmlinux_path[i]); | ||
1899 | |||
1900 | return printed; | ||
1901 | } | ||
1902 | |||
1898 | static int setup_list(struct strlist **list, const char *list_str, | 1903 | static int setup_list(struct strlist **list, const char *list_str, |
1899 | const char *list_name) | 1904 | const char *list_name) |
1900 | { | 1905 | { |
@@ -1964,3 +1969,46 @@ int map_groups__create_kernel_maps(struct map_groups *self, | |||
1964 | map_groups__fixup_end(self); | 1969 | map_groups__fixup_end(self); |
1965 | return 0; | 1970 | return 0; |
1966 | } | 1971 | } |
1972 | |||
1973 | static int hex(char ch) | ||
1974 | { | ||
1975 | if ((ch >= '0') && (ch <= '9')) | ||
1976 | return ch - '0'; | ||
1977 | if ((ch >= 'a') && (ch <= 'f')) | ||
1978 | return ch - 'a' + 10; | ||
1979 | if ((ch >= 'A') && (ch <= 'F')) | ||
1980 | return ch - 'A' + 10; | ||
1981 | return -1; | ||
1982 | } | ||
1983 | |||
1984 | /* | ||
1985 | * While we find nice hex chars, build a long_val. | ||
1986 | * Return number of chars processed. | ||
1987 | */ | ||
1988 | int hex2u64(const char *ptr, u64 *long_val) | ||
1989 | { | ||
1990 | const char *p = ptr; | ||
1991 | *long_val = 0; | ||
1992 | |||
1993 | while (*p) { | ||
1994 | const int hex_val = hex(*p); | ||
1995 | |||
1996 | if (hex_val < 0) | ||
1997 | break; | ||
1998 | |||
1999 | *long_val = (*long_val << 4) | hex_val; | ||
2000 | p++; | ||
2001 | } | ||
2002 | |||
2003 | return p - ptr; | ||
2004 | } | ||
2005 | |||
2006 | char *strxfrchar(char *s, char from, char to) | ||
2007 | { | ||
2008 | char *p = s; | ||
2009 | |||
2010 | while ((p = strchr(p, from)) != NULL) | ||
2011 | *p++ = to; | ||
2012 | |||
2013 | return s; | ||
2014 | } | ||
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index f30a37428919..757fae3f5ee0 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -3,10 +3,11 @@ | |||
3 | 3 | ||
4 | #include <linux/types.h> | 4 | #include <linux/types.h> |
5 | #include <stdbool.h> | 5 | #include <stdbool.h> |
6 | #include "types.h" | 6 | #include <stdint.h> |
7 | #include "map.h" | ||
7 | #include <linux/list.h> | 8 | #include <linux/list.h> |
8 | #include <linux/rbtree.h> | 9 | #include <linux/rbtree.h> |
9 | #include "event.h" | 10 | #include <stdio.h> |
10 | 11 | ||
11 | #define DEBUG_CACHE_DIR ".debug" | 12 | #define DEBUG_CACHE_DIR ".debug" |
12 | 13 | ||
@@ -29,6 +30,9 @@ static inline char *bfd_demangle(void __used *v, const char __used *c, | |||
29 | #endif | 30 | #endif |
30 | #endif | 31 | #endif |
31 | 32 | ||
33 | int hex2u64(const char *ptr, u64 *val); | ||
34 | char *strxfrchar(char *s, char from, char to); | ||
35 | |||
32 | /* | 36 | /* |
33 | * libelf 0.8.x and earlier do not support ELF_C_READ_MMAP; | 37 | * libelf 0.8.x and earlier do not support ELF_C_READ_MMAP; |
34 | * for newer versions we can use mmap to reduce memory usage: | 38 | * for newer versions we can use mmap to reduce memory usage: |
@@ -44,6 +48,8 @@ static inline char *bfd_demangle(void __used *v, const char __used *c, | |||
44 | #define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ | 48 | #define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ |
45 | #endif | 49 | #endif |
46 | 50 | ||
51 | #define BUILD_ID_SIZE 20 | ||
52 | |||
47 | struct symbol { | 53 | struct symbol { |
48 | struct rb_node rb_node; | 54 | struct rb_node rb_node; |
49 | u64 start; | 55 | u64 start; |
@@ -88,6 +94,11 @@ struct ref_reloc_sym { | |||
88 | u64 unrelocated_addr; | 94 | u64 unrelocated_addr; |
89 | }; | 95 | }; |
90 | 96 | ||
97 | struct map_symbol { | ||
98 | struct map *map; | ||
99 | struct symbol *sym; | ||
100 | }; | ||
101 | |||
91 | struct addr_location { | 102 | struct addr_location { |
92 | struct thread *thread; | 103 | struct thread *thread; |
93 | struct map *map; | 104 | struct map *map; |
@@ -106,6 +117,7 @@ struct dso { | |||
106 | u8 has_build_id:1; | 117 | u8 has_build_id:1; |
107 | u8 kernel:1; | 118 | u8 kernel:1; |
108 | u8 hit:1; | 119 | u8 hit:1; |
120 | u8 annotate_warned:1; | ||
109 | unsigned char origin; | 121 | unsigned char origin; |
110 | u8 sorted_by_name; | 122 | u8 sorted_by_name; |
111 | u8 loaded; | 123 | u8 loaded; |
@@ -150,6 +162,19 @@ size_t dsos__fprintf_buildid(FILE *fp, bool with_hits); | |||
150 | 162 | ||
151 | size_t dso__fprintf_buildid(struct dso *self, FILE *fp); | 163 | size_t dso__fprintf_buildid(struct dso *self, FILE *fp); |
152 | size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp); | 164 | size_t dso__fprintf(struct dso *self, enum map_type type, FILE *fp); |
165 | |||
166 | enum dso_origin { | ||
167 | DSO__ORIG_KERNEL = 0, | ||
168 | DSO__ORIG_JAVA_JIT, | ||
169 | DSO__ORIG_BUILD_ID_CACHE, | ||
170 | DSO__ORIG_FEDORA, | ||
171 | DSO__ORIG_UBUNTU, | ||
172 | DSO__ORIG_BUILDID, | ||
173 | DSO__ORIG_DSO, | ||
174 | DSO__ORIG_KMODULE, | ||
175 | DSO__ORIG_NOT_FOUND, | ||
176 | }; | ||
177 | |||
153 | char dso__symtab_origin(const struct dso *self); | 178 | char dso__symtab_origin(const struct dso *self); |
154 | void dso__set_long_name(struct dso *self, char *name); | 179 | void dso__set_long_name(struct dso *self, char *name); |
155 | void dso__set_build_id(struct dso *self, void *build_id); | 180 | void dso__set_build_id(struct dso *self, void *build_id); |
@@ -169,4 +194,6 @@ int kallsyms__parse(const char *filename, void *arg, | |||
169 | int symbol__init(void); | 194 | int symbol__init(void); |
170 | bool symbol_type__is_a(char symbol_type, enum map_type map_type); | 195 | bool symbol_type__is_a(char symbol_type, enum map_type map_type); |
171 | 196 | ||
197 | size_t vmlinux_path__fprintf(FILE *fp); | ||
198 | |||
172 | #endif /* __PERF_SYMBOL */ | 199 | #endif /* __PERF_SYMBOL */ |
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index fa968312ee7d..9bbe27d75306 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
@@ -7,6 +7,37 @@ | |||
7 | #include "util.h" | 7 | #include "util.h" |
8 | #include "debug.h" | 8 | #include "debug.h" |
9 | 9 | ||
10 | int find_all_tid(int pid, pid_t ** all_tid) | ||
11 | { | ||
12 | char name[256]; | ||
13 | int items; | ||
14 | struct dirent **namelist = NULL; | ||
15 | int ret = 0; | ||
16 | int i; | ||
17 | |||
18 | sprintf(name, "/proc/%d/task", pid); | ||
19 | items = scandir(name, &namelist, NULL, NULL); | ||
20 | if (items <= 0) | ||
21 | return -ENOENT; | ||
22 | *all_tid = malloc(sizeof(pid_t) * items); | ||
23 | if (!*all_tid) { | ||
24 | ret = -ENOMEM; | ||
25 | goto failure; | ||
26 | } | ||
27 | |||
28 | for (i = 0; i < items; i++) | ||
29 | (*all_tid)[i] = atoi(namelist[i]->d_name); | ||
30 | |||
31 | ret = items; | ||
32 | |||
33 | failure: | ||
34 | for (i=0; i<items; i++) | ||
35 | free(namelist[i]); | ||
36 | free(namelist); | ||
37 | |||
38 | return ret; | ||
39 | } | ||
40 | |||
10 | void map_groups__init(struct map_groups *self) | 41 | void map_groups__init(struct map_groups *self) |
11 | { | 42 | { |
12 | int i; | 43 | int i; |
@@ -241,46 +272,6 @@ static int map_groups__fixup_overlappings(struct map_groups *self, | |||
241 | return 0; | 272 | return 0; |
242 | } | 273 | } |
243 | 274 | ||
244 | void maps__insert(struct rb_root *maps, struct map *map) | ||
245 | { | ||
246 | struct rb_node **p = &maps->rb_node; | ||
247 | struct rb_node *parent = NULL; | ||
248 | const u64 ip = map->start; | ||
249 | struct map *m; | ||
250 | |||
251 | while (*p != NULL) { | ||
252 | parent = *p; | ||
253 | m = rb_entry(parent, struct map, rb_node); | ||
254 | if (ip < m->start) | ||
255 | p = &(*p)->rb_left; | ||
256 | else | ||
257 | p = &(*p)->rb_right; | ||
258 | } | ||
259 | |||
260 | rb_link_node(&map->rb_node, parent, p); | ||
261 | rb_insert_color(&map->rb_node, maps); | ||
262 | } | ||
263 | |||
264 | struct map *maps__find(struct rb_root *maps, u64 ip) | ||
265 | { | ||
266 | struct rb_node **p = &maps->rb_node; | ||
267 | struct rb_node *parent = NULL; | ||
268 | struct map *m; | ||
269 | |||
270 | while (*p != NULL) { | ||
271 | parent = *p; | ||
272 | m = rb_entry(parent, struct map, rb_node); | ||
273 | if (ip < m->start) | ||
274 | p = &(*p)->rb_left; | ||
275 | else if (ip > m->end) | ||
276 | p = &(*p)->rb_right; | ||
277 | else | ||
278 | return m; | ||
279 | } | ||
280 | |||
281 | return NULL; | ||
282 | } | ||
283 | |||
284 | void thread__insert_map(struct thread *self, struct map *map) | 275 | void thread__insert_map(struct thread *self, struct map *map) |
285 | { | 276 | { |
286 | map_groups__fixup_overlappings(&self->mg, map); | 277 | map_groups__fixup_overlappings(&self->mg, map); |
@@ -336,15 +327,3 @@ size_t perf_session__fprintf(struct perf_session *self, FILE *fp) | |||
336 | 327 | ||
337 | return ret; | 328 | return ret; |
338 | } | 329 | } |
339 | |||
340 | struct symbol *map_groups__find_symbol(struct map_groups *self, | ||
341 | enum map_type type, u64 addr, | ||
342 | symbol_filter_t filter) | ||
343 | { | ||
344 | struct map *map = map_groups__find(self, type, addr); | ||
345 | |||
346 | if (map != NULL) | ||
347 | return map__find_symbol(map, map->map_ip(map, addr), filter); | ||
348 | |||
349 | return NULL; | ||
350 | } | ||
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index dcf70303e58e..9c488fcadec9 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h | |||
@@ -5,14 +5,6 @@ | |||
5 | #include <unistd.h> | 5 | #include <unistd.h> |
6 | #include "symbol.h" | 6 | #include "symbol.h" |
7 | 7 | ||
8 | struct map_groups { | ||
9 | struct rb_root maps[MAP__NR_TYPES]; | ||
10 | struct list_head removed_maps[MAP__NR_TYPES]; | ||
11 | }; | ||
12 | |||
13 | size_t __map_groups__fprintf_maps(struct map_groups *self, | ||
14 | enum map_type type, FILE *fp); | ||
15 | |||
16 | struct thread { | 8 | struct thread { |
17 | struct rb_node rb_node; | 9 | struct rb_node rb_node; |
18 | struct map_groups mg; | 10 | struct map_groups mg; |
@@ -23,29 +15,16 @@ struct thread { | |||
23 | int comm_len; | 15 | int comm_len; |
24 | }; | 16 | }; |
25 | 17 | ||
26 | void map_groups__init(struct map_groups *self); | 18 | struct perf_session; |
19 | |||
20 | int find_all_tid(int pid, pid_t ** all_tid); | ||
27 | int thread__set_comm(struct thread *self, const char *comm); | 21 | int thread__set_comm(struct thread *self, const char *comm); |
28 | int thread__comm_len(struct thread *self); | 22 | int thread__comm_len(struct thread *self); |
29 | struct thread *perf_session__findnew(struct perf_session *self, pid_t pid); | 23 | struct thread *perf_session__findnew(struct perf_session *self, pid_t pid); |
30 | void thread__insert_map(struct thread *self, struct map *map); | 24 | void thread__insert_map(struct thread *self, struct map *map); |
31 | int thread__fork(struct thread *self, struct thread *parent); | 25 | int thread__fork(struct thread *self, struct thread *parent); |
32 | size_t map_groups__fprintf_maps(struct map_groups *self, FILE *fp); | ||
33 | size_t perf_session__fprintf(struct perf_session *self, FILE *fp); | 26 | size_t perf_session__fprintf(struct perf_session *self, FILE *fp); |
34 | 27 | ||
35 | void maps__insert(struct rb_root *maps, struct map *map); | ||
36 | struct map *maps__find(struct rb_root *maps, u64 addr); | ||
37 | |||
38 | static inline void map_groups__insert(struct map_groups *self, struct map *map) | ||
39 | { | ||
40 | maps__insert(&self->maps[map->type], map); | ||
41 | } | ||
42 | |||
43 | static inline struct map *map_groups__find(struct map_groups *self, | ||
44 | enum map_type type, u64 addr) | ||
45 | { | ||
46 | return maps__find(&self->maps[type], addr); | ||
47 | } | ||
48 | |||
49 | static inline struct map *thread__find_map(struct thread *self, | 28 | static inline struct map *thread__find_map(struct thread *self, |
50 | enum map_type type, u64 addr) | 29 | enum map_type type, u64 addr) |
51 | { | 30 | { |
@@ -62,26 +41,4 @@ void thread__find_addr_location(struct thread *self, | |||
62 | enum map_type type, u64 addr, | 41 | enum map_type type, u64 addr, |
63 | struct addr_location *al, | 42 | struct addr_location *al, |
64 | symbol_filter_t filter); | 43 | symbol_filter_t filter); |
65 | struct symbol *map_groups__find_symbol(struct map_groups *self, | ||
66 | enum map_type type, u64 addr, | ||
67 | symbol_filter_t filter); | ||
68 | |||
69 | static inline struct symbol *map_groups__find_function(struct map_groups *self, | ||
70 | u64 addr, | ||
71 | symbol_filter_t filter) | ||
72 | { | ||
73 | return map_groups__find_symbol(self, MAP__FUNCTION, addr, filter); | ||
74 | } | ||
75 | |||
76 | struct map *map_groups__find_by_name(struct map_groups *self, | ||
77 | enum map_type type, const char *name); | ||
78 | |||
79 | int __map_groups__create_kernel_maps(struct map_groups *self, | ||
80 | struct map *vmlinux_maps[MAP__NR_TYPES], | ||
81 | struct dso *kernel); | ||
82 | int map_groups__create_kernel_maps(struct map_groups *self, | ||
83 | struct map *vmlinux_maps[MAP__NR_TYPES]); | ||
84 | |||
85 | struct map *map_groups__new_module(struct map_groups *self, u64 start, | ||
86 | const char *filename); | ||
87 | #endif /* __PERF_THREAD_H */ | 44 | #endif /* __PERF_THREAD_H */ |
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 0f5b2a6f1080..52701087ce04 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
@@ -295,6 +295,13 @@ extern void *xmemdupz(const void *data, size_t len); | |||
295 | extern char *xstrndup(const char *str, size_t len); | 295 | extern char *xstrndup(const char *str, size_t len); |
296 | extern void *xrealloc(void *ptr, size_t size) __attribute__((weak)); | 296 | extern void *xrealloc(void *ptr, size_t size) __attribute__((weak)); |
297 | 297 | ||
298 | static inline void *xzalloc(size_t size) | ||
299 | { | ||
300 | void *buf = xmalloc(size); | ||
301 | |||
302 | return memset(buf, 0, size); | ||
303 | } | ||
304 | |||
298 | static inline void *zalloc(size_t size) | 305 | static inline void *zalloc(size_t size) |
299 | { | 306 | { |
300 | return calloc(1, size); | 307 | return calloc(1, size); |
@@ -309,6 +316,7 @@ static inline int has_extension(const char *filename, const char *ext) | |||
309 | { | 316 | { |
310 | size_t len = strlen(filename); | 317 | size_t len = strlen(filename); |
311 | size_t extlen = strlen(ext); | 318 | size_t extlen = strlen(ext); |
319 | |||
312 | return len > extlen && !memcmp(filename + len - extlen, ext, extlen); | 320 | return len > extlen && !memcmp(filename + len - extlen, ext, extlen); |
313 | } | 321 | } |
314 | 322 | ||
@@ -322,6 +330,7 @@ static inline int has_extension(const char *filename, const char *ext) | |||
322 | #undef isalnum | 330 | #undef isalnum |
323 | #undef tolower | 331 | #undef tolower |
324 | #undef toupper | 332 | #undef toupper |
333 | |||
325 | extern unsigned char sane_ctype[256]; | 334 | extern unsigned char sane_ctype[256]; |
326 | #define GIT_SPACE 0x01 | 335 | #define GIT_SPACE 0x01 |
327 | #define GIT_DIGIT 0x02 | 336 | #define GIT_DIGIT 0x02 |