aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/util
diff options
context:
space:
mode:
authorMasami Hiramatsu <mhiramat@redhat.com>2010-03-16 18:06:26 -0400
committerIngo Molnar <mingo@elte.hu>2010-03-17 07:11:15 -0400
commit7df2f32956cf0f1a45df38cd0e0fe0c3467580e8 (patch)
tree1cccbf0d4239ebdd9c0f67762b5a2f0facbe53dc /tools/perf/util
parentfb1587d869a399554220e166d4b90b581a8ade01 (diff)
perf probe: Add data structure member access support
Support accessing members in the data structures. With this, perf-probe accepts data-structure members(IOW, it now accepts dot '.' and arrow '->' operators) as probe arguemnts. e.g. ./perf probe --add 'schedule:44 rq->curr' ./perf probe --add 'vfs_read file->f_op->read file->f_path.dentry' Note that '>' can be interpreted as redirection in command-line. Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com> Cc: systemtap <systemtap@sources.redhat.com> Cc: DLE <dle-develop@lists.sourceforge.net> Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Paul Mackerras <paulus@samba.org> Cc: Mike Galbraith <efault@gmx.de> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> LKML-Reference: <20100316220626.32050.57552.stgit@localhost6.localdomain6> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'tools/perf/util')
-rw-r--r--tools/perf/util/probe-event.c90
-rw-r--r--tools/perf/util/probe-event.h12
-rw-r--r--tools/perf/util/probe-finder.c105
3 files changed, 201 insertions, 6 deletions
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 4e3c1aea7892..64dea6c3d58a 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -262,6 +262,49 @@ static void parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
262 pp->lazy_line); 262 pp->lazy_line);
263} 263}
264 264
265/* Parse perf-probe event argument */
266static void parse_perf_probe_arg(const char *str, struct perf_probe_arg *arg)
267{
268 const char *tmp;
269 struct perf_probe_arg_field **fieldp;
270
271 pr_debug("parsing arg: %s into ", str);
272
273 tmp = strpbrk(str, "-.");
274 if (!is_c_varname(str) || !tmp) {
275 /* A variable, register, symbol or special value */
276 arg->name = xstrdup(str);
277 pr_debug("%s\n", arg->name);
278 return;
279 }
280
281 /* Structure fields */
282 arg->name = xstrndup(str, tmp - str);
283 pr_debug("%s, ", arg->name);
284 fieldp = &arg->field;
285
286 do {
287 *fieldp = xzalloc(sizeof(struct perf_probe_arg_field));
288 if (*tmp == '.') {
289 str = tmp + 1;
290 (*fieldp)->ref = false;
291 } else if (tmp[1] == '>') {
292 str = tmp + 2;
293 (*fieldp)->ref = true;
294 } else
295 semantic_error("Argument parse error: %s", str);
296
297 tmp = strpbrk(str, "-.");
298 if (tmp) {
299 (*fieldp)->name = xstrndup(str, tmp - str);
300 pr_debug("%s(%d), ", (*fieldp)->name, (*fieldp)->ref);
301 fieldp = &(*fieldp)->next;
302 }
303 } while (tmp);
304 (*fieldp)->name = xstrdup(str);
305 pr_debug("%s(%d)\n", (*fieldp)->name, (*fieldp)->ref);
306}
307
265/* Parse perf-probe event command */ 308/* Parse perf-probe event command */
266void parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev) 309void parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev)
267{ 310{
@@ -281,7 +324,7 @@ void parse_perf_probe_command(const char *cmd, struct perf_probe_event *pev)
281 pev->nargs = argc - 1; 324 pev->nargs = argc - 1;
282 pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs); 325 pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs);
283 for (i = 0; i < pev->nargs; i++) { 326 for (i = 0; i < pev->nargs; i++) {
284 pev->args[i].name = xstrdup(argv[i + 1]); 327 parse_perf_probe_arg(argv[i + 1], &pev->args[i]);
285 if (is_c_varname(pev->args[i].name) && pev->point.retprobe) 328 if (is_c_varname(pev->args[i].name) && pev->point.retprobe)
286 semantic_error("You can't specify local variable for" 329 semantic_error("You can't specify local variable for"
287 " kretprobe"); 330 " kretprobe");
@@ -353,6 +396,33 @@ void parse_kprobe_trace_command(const char *cmd, struct kprobe_trace_event *tev)
353 argv_free(argv); 396 argv_free(argv);
354} 397}
355 398
399/* Compose only probe arg */
400int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf, size_t len)
401{
402 struct perf_probe_arg_field *field = pa->field;
403 int ret;
404 char *tmp = buf;
405
406 ret = e_snprintf(tmp, len, "%s", pa->name);
407 if (ret <= 0)
408 goto error;
409 tmp += ret;
410 len -= ret;
411
412 while (field) {
413 ret = e_snprintf(tmp, len, "%s%s", field->ref ? "->" : ".",
414 field->name);
415 if (ret <= 0)
416 goto error;
417 tmp += ret;
418 len -= ret;
419 field = field->next;
420 }
421 return tmp - buf;
422error:
423 die("Failed to synthesize perf probe argument: %s", strerror(-ret));
424}
425
356/* Compose only probe point (not argument) */ 426/* Compose only probe point (not argument) */
357static char *synthesize_perf_probe_point(struct perf_probe_point *pp) 427static char *synthesize_perf_probe_point(struct perf_probe_point *pp)
358{ 428{
@@ -563,6 +633,7 @@ void convert_to_perf_probe_event(struct kprobe_trace_event *tev,
563void clear_perf_probe_event(struct perf_probe_event *pev) 633void clear_perf_probe_event(struct perf_probe_event *pev)
564{ 634{
565 struct perf_probe_point *pp = &pev->point; 635 struct perf_probe_point *pp = &pev->point;
636 struct perf_probe_arg_field *field, *next;
566 int i; 637 int i;
567 638
568 if (pev->event) 639 if (pev->event)
@@ -575,9 +646,18 @@ void clear_perf_probe_event(struct perf_probe_event *pev)
575 free(pp->function); 646 free(pp->function);
576 if (pp->lazy_line) 647 if (pp->lazy_line)
577 free(pp->lazy_line); 648 free(pp->lazy_line);
578 for (i = 0; i < pev->nargs; i++) 649 for (i = 0; i < pev->nargs; i++) {
579 if (pev->args[i].name) 650 if (pev->args[i].name)
580 free(pev->args[i].name); 651 free(pev->args[i].name);
652 field = pev->args[i].field;
653 while (field) {
654 next = field->next;
655 if (field->name)
656 free(field->name);
657 free(field);
658 field = next;
659 }
660 }
581 if (pev->args) 661 if (pev->args)
582 free(pev->args); 662 free(pev->args);
583 memset(pev, 0, sizeof(*pev)); 663 memset(pev, 0, sizeof(*pev));
@@ -682,8 +762,10 @@ static void show_perf_probe_event(struct perf_probe_event *pev)
682 762
683 if (pev->nargs > 0) { 763 if (pev->nargs > 0) {
684 printf(" with"); 764 printf(" with");
685 for (i = 0; i < pev->nargs; i++) 765 for (i = 0; i < pev->nargs; i++) {
686 printf(" %s", pev->args[i].name); 766 synthesize_perf_probe_arg(&pev->args[i], buf, 128);
767 printf(" %s", buf);
768 }
687 } 769 }
688 printf(")\n"); 770 printf(")\n");
689 free(place); 771 free(place);
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 2a2f0a26dc67..cd308b0a4d96 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -45,9 +45,17 @@ struct perf_probe_point {
45 bool retprobe; /* Return probe flag */ 45 bool retprobe; /* Return probe flag */
46}; 46};
47 47
48/* Perf probe probing argument field chain */
49struct 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
48/* Perf probe probing argument */ 55/* Perf probe probing argument */
49struct perf_probe_arg { 56struct perf_probe_arg {
50 char *name; /* Argument name */ 57 char *name; /* Argument name */
58 struct perf_probe_arg_field *field; /* Structure fields */
51}; 59};
52 60
53/* Perf probe probing event (point + arg) */ 61/* Perf probe probing event (point + arg) */
@@ -86,6 +94,8 @@ extern void parse_kprobe_trace_command(const char *cmd,
86/* Events to command string */ 94/* Events to command string */
87extern char *synthesize_perf_probe_command(struct perf_probe_event *pev); 95extern char *synthesize_perf_probe_command(struct perf_probe_event *pev);
88extern char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev); 96extern char *synthesize_kprobe_trace_command(struct kprobe_trace_event *tev);
97extern int synthesize_perf_probe_arg(struct perf_probe_arg *pa, char *buf,
98 size_t len);
89 99
90/* Check the perf_probe_event needs debuginfo */ 100/* Check the perf_probe_event needs debuginfo */
91extern bool perf_probe_event_need_dwarf(struct perf_probe_event *pev); 101extern bool perf_probe_event_need_dwarf(struct perf_probe_event *pev);
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index e02b60770485..db52ec2e84de 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -206,6 +206,28 @@ static Dwarf_Addr die_get_entrypc(Dwarf_Die *dw_die)
206 return epc; 206 return epc;
207} 207}
208 208
209/* Get type die, but skip qualifiers and typedef */
210static Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem)
211{
212 Dwarf_Attribute attr;
213 int tag;
214
215 do {
216 if (dwarf_attr(vr_die, DW_AT_type, &attr) == NULL ||
217 dwarf_formref_die(&attr, die_mem) == NULL)
218 return NULL;
219
220 tag = dwarf_tag(die_mem);
221 vr_die = die_mem;
222 } while (tag == DW_TAG_const_type ||
223 tag == DW_TAG_restrict_type ||
224 tag == DW_TAG_volatile_type ||
225 tag == DW_TAG_shared_type ||
226 tag == DW_TAG_typedef);
227
228 return die_mem;
229}
230
209/* Return values for die_find callbacks */ 231/* Return values for die_find callbacks */
210enum { 232enum {
211 DIE_FIND_CB_FOUND = 0, /* End of Search */ 233 DIE_FIND_CB_FOUND = 0, /* End of Search */
@@ -314,6 +336,25 @@ static Dwarf_Die *die_find_variable(Dwarf_Die *sp_die, const char *name,
314 die_mem); 336 die_mem);
315} 337}
316 338
339static int __die_find_member_cb(Dwarf_Die *die_mem, void *data)
340{
341 const char *name = data;
342
343 if ((dwarf_tag(die_mem) == DW_TAG_member) &&
344 (die_compare_name(die_mem, name) == 0))
345 return DIE_FIND_CB_FOUND;
346
347 return DIE_FIND_CB_SIBLING;
348}
349
350/* Find a member called 'name' */
351static Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name,
352 Dwarf_Die *die_mem)
353{
354 return die_find_child(st_die, __die_find_member_cb, (void *)name,
355 die_mem);
356}
357
317/* 358/*
318 * Probe finder related functions 359 * Probe finder related functions
319 */ 360 */
@@ -363,6 +404,62 @@ static void convert_location(Dwarf_Op *op, struct probe_finder *pf)
363 } 404 }
364} 405}
365 406
407static void convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
408 struct perf_probe_arg_field *field,
409 struct kprobe_trace_arg_ref **ref_ptr)
410{
411 struct kprobe_trace_arg_ref *ref = *ref_ptr;
412 Dwarf_Attribute attr;
413 Dwarf_Die member;
414 Dwarf_Die type;
415 Dwarf_Word offs;
416
417 pr_debug("converting %s in %s\n", field->name, varname);
418 if (die_get_real_type(vr_die, &type) == NULL)
419 die("Failed to get a type information of %s.", varname);
420
421 /* Check the pointer and dereference */
422 if (dwarf_tag(&type) == DW_TAG_pointer_type) {
423 if (!field->ref)
424 die("Semantic error: %s must be referred by '->'",
425 field->name);
426 /* Get the type pointed by this pointer */
427 if (die_get_real_type(&type, &type) == NULL)
428 die("Failed to get a type information of %s.", varname);
429
430 ref = xzalloc(sizeof(struct kprobe_trace_arg_ref));
431 if (*ref_ptr)
432 (*ref_ptr)->next = ref;
433 else
434 *ref_ptr = ref;
435 } else {
436 if (field->ref)
437 die("Semantic error: %s must be referred by '.'",
438 field->name);
439 if (!ref)
440 die("Structure on a register is not supported yet.");
441 }
442
443 /* Verify it is a data structure */
444 if (dwarf_tag(&type) != DW_TAG_structure_type)
445 die("%s is not a data structure.", varname);
446
447 if (die_find_member(&type, field->name, &member) == NULL)
448 die("%s(tyep:%s) has no member %s.", varname,
449 dwarf_diename(&type), field->name);
450
451 /* Get the offset of the field */
452 if (dwarf_attr(&member, DW_AT_data_member_location, &attr) == NULL ||
453 dwarf_formudata(&attr, &offs) != 0)
454 die("Failed to get the offset of %s.", field->name);
455 ref->offset += (long)offs;
456
457 /* Converting next field */
458 if (field->next)
459 convert_variable_fields(&member, field->name, field->next,
460 &ref);
461}
462
366/* Show a variables in kprobe event format */ 463/* Show a variables in kprobe event format */
367static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf) 464static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
368{ 465{
@@ -379,6 +476,10 @@ static void convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
379 goto error; 476 goto error;
380 477
381 convert_location(expr, pf); 478 convert_location(expr, pf);
479
480 if (pf->pvar->field)
481 convert_variable_fields(vr_die, pf->pvar->name,
482 pf->pvar->field, &pf->tvar->ref);
382 /* *expr will be cached in libdw. Don't free it. */ 483 /* *expr will be cached in libdw. Don't free it. */
383 return ; 484 return ;
384error: 485error:
@@ -391,13 +492,15 @@ error:
391static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf) 492static void find_variable(Dwarf_Die *sp_die, struct probe_finder *pf)
392{ 493{
393 Dwarf_Die vr_die; 494 Dwarf_Die vr_die;
495 char buf[128];
394 496
395 /* TODO: Support struct members and arrays */ 497 /* TODO: Support struct members and arrays */
396 if (!is_c_varname(pf->pvar->name)) { 498 if (!is_c_varname(pf->pvar->name)) {
397 /* Copy raw parameters */ 499 /* Copy raw parameters */
398 pf->tvar->value = xstrdup(pf->pvar->name); 500 pf->tvar->value = xstrdup(pf->pvar->name);
399 } else { 501 } else {
400 pf->tvar->name = xstrdup(pf->pvar->name); 502 synthesize_perf_probe_arg(pf->pvar, buf, 128);
503 pf->tvar->name = xstrdup(buf);
401 pr_debug("Searching '%s' variable in context.\n", 504 pr_debug("Searching '%s' variable in context.\n",
402 pf->pvar->name); 505 pf->pvar->name);
403 /* Search child die for local variables and parameters. */ 506 /* Search child die for local variables and parameters. */