perf probe: Support DW_OP_plus_uconst in DW_AT_data_member_location
authorMasami Hiramatsu <mhiramat@redhat.com>
Wed, 14 Apr 2010 20:44:00 +0000 (17:44 -0300)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Wed, 14 Apr 2010 20:44:00 +0000 (17:44 -0300)
DW_OP_plus_uconst can be used for DW_AT_data_member_location.
This patch adds DW_OP_plus_uconst support when getting
structure member offset.

Commiter note:

Fixed up the size_t format specifier in one case:

cc1: warnings being treated as errors
util/probe-finder.c: In function ‘die_get_data_member_location’:
util/probe-finder.c:270: error: format ‘%d’ expects type ‘int’, but argument 4 has type ‘size_t’
make: *** [/home/acme/git/build/perf/util/probe-finder.o] Error 1

LKML-Reference: <20100414223958.14630.5230.stgit@localhost6.localdomain6>
Signed-off-by: Masami Hiramatsu <mhiramat@redhat.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/util/probe-finder.c

index b4c93659929ae39ac24683a35a5120e95d1361df..03b469197a03e55a08f671ebb1a4b149eefa6a26 100644 (file)
@@ -249,6 +249,33 @@ static int die_get_byte_size(Dwarf_Die *tp_die)
        return (int)ret;
 }
 
+/* Get data_member_location offset */
+static int die_get_data_member_location(Dwarf_Die *mb_die, Dwarf_Word *offs)
+{
+       Dwarf_Attribute attr;
+       Dwarf_Op *expr;
+       size_t nexpr;
+       int ret;
+
+       if (dwarf_attr(mb_die, DW_AT_data_member_location, &attr) == NULL)
+               return -ENOENT;
+
+       if (dwarf_formudata(&attr, offs) != 0) {
+               /* DW_AT_data_member_location should be DW_OP_plus_uconst */
+               ret = dwarf_getlocation(&attr, &expr, &nexpr);
+               if (ret < 0 || nexpr == 0)
+                       return -ENOENT;
+
+               if (expr[0].atom != DW_OP_plus_uconst || nexpr != 1) {
+                       pr_debug("Unable to get offset:Unexpected OP %x (%zd)\n",
+                                expr[0].atom, nexpr);
+                       return -ENOTSUP;
+               }
+               *offs = (Dwarf_Word)expr[0].number;
+       }
+       return 0;
+}
+
 /* Return values for die_find callbacks */
 enum {
        DIE_FIND_CB_FOUND = 0,          /* End of Search */
@@ -482,9 +509,9 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
                                    Dwarf_Die *die_mem)
 {
        struct kprobe_trace_arg_ref *ref = *ref_ptr;
-       Dwarf_Attribute attr;
        Dwarf_Die type;
        Dwarf_Word offs;
+       int ret;
 
        pr_debug("converting %s in %s\n", field->name, varname);
        if (die_get_real_type(vr_die, &type) == NULL) {
@@ -542,17 +569,17 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
        }
 
        /* Get the offset of the field */
-       if (dwarf_attr(die_mem, DW_AT_data_member_location, &attr) == NULL ||
-           dwarf_formudata(&attr, &offs) != 0) {
+       ret = die_get_data_member_location(die_mem, &offs);
+       if (ret < 0) {
                pr_warning("Failed to get the offset of %s.\n", field->name);
-               return -ENOENT;
+               return ret;
        }
        ref->offset += (long)offs;
 
        /* Converting next field */
        if (field->next)
                return convert_variable_fields(die_mem, field->name,
-                                              field->next, &ref, die_mem);
+                                       field->next, &ref, die_mem);
        else
                return 0;
 }