Commit 3d918a12a1b3088ac16ff37fa52760639d6e2403

Authored by Masami Hiramatsu
Committed by Arnaldo Carvalho de Melo
1 parent 7969ec7728

perf probe: Find fentry mcount fuzzed parameter location

At this point, --fentry (mcount function entry) option for gcc fuzzes
the debuginfo variable locations by skipping the mcount instruction
offset (on x86, this is a 5 byte call instruction).

This makes variable searching fail at the entry of functions which
are mcount'ed.

e.g.)
Available variables at vfs_read
        @<vfs_read+0>
                (No matched variables)

This patch adds additional location search at the function entry point
to solve this issue, which tries to find the earliest address for the
variable location.

Note that this only works with function parameters (formal parameters)
because any local variables should not exist on the function entry
address (those are not initialized yet).

With this patch, perf probe shows correct parameters if possible;
 # perf probe --vars vfs_read
 Available variables at vfs_read
         @<vfs_read+0>
                 char*   buf
                 loff_t* pos
                 size_t  count
                 struct file*    file

Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/20131011071025.15557.13275.stgit@udc4-manage.rcp.hitachi.co.jp
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

Showing 1 changed file with 31 additions and 8 deletions Side-by-side Diff

tools/perf/util/probe-finder.c
... ... @@ -273,12 +273,15 @@
273 273 /*
274 274 * Convert a location into trace_arg.
275 275 * If tvar == NULL, this just checks variable can be converted.
  276 + * If fentry == true and vr_die is a parameter, do huristic search
  277 + * for the location fuzzed by function entry mcount.
276 278 */
277 279 static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr,
278   - Dwarf_Op *fb_ops,
  280 + Dwarf_Op *fb_ops, Dwarf_Die *sp_die,
279 281 struct probe_trace_arg *tvar)
280 282 {
281 283 Dwarf_Attribute attr;
  284 + Dwarf_Addr tmp = 0;
282 285 Dwarf_Op *op;
283 286 size_t nops;
284 287 unsigned int regn;
285 288  
... ... @@ -291,12 +294,29 @@
291 294 goto static_var;
292 295  
293 296 /* TODO: handle more than 1 exprs */
294   - if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL ||
295   - dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0 ||
296   - nops == 0) {
297   - /* TODO: Support const_value */
  297 + if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL)
  298 + return -EINVAL; /* Broken DIE ? */
  299 + if (dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0) {
  300 + ret = dwarf_entrypc(sp_die, &tmp);
  301 + if (ret || addr != tmp ||
  302 + dwarf_tag(vr_die) != DW_TAG_formal_parameter ||
  303 + dwarf_highpc(sp_die, &tmp))
  304 + return -ENOENT;
  305 + /*
  306 + * This is fuzzed by fentry mcount. We try to find the
  307 + * parameter location at the earliest address.
  308 + */
  309 + for (addr += 1; addr <= tmp; addr++) {
  310 + if (dwarf_getlocation_addr(&attr, addr, &op,
  311 + &nops, 1) > 0)
  312 + goto found;
  313 + }
298 314 return -ENOENT;
299 315 }
  316 +found:
  317 + if (nops == 0)
  318 + /* TODO: Support const_value */
  319 + return -ENOENT;
300 320  
301 321 if (op->atom == DW_OP_addr) {
302 322 static_var:
... ... @@ -600,7 +620,7 @@
600 620 dwarf_diename(vr_die));
601 621  
602 622 ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops,
603   - pf->tvar);
  623 + &pf->sp_die, pf->tvar);
604 624 if (ret == -ENOENT)
605 625 pr_err("Failed to find the location of %s at this address.\n"
606 626 " Perhaps, it has been optimized out.\n", pf->pvar->var);
607 627  
... ... @@ -1148,13 +1168,15 @@
1148 1168 static int copy_variables_cb(Dwarf_Die *die_mem, void *data)
1149 1169 {
1150 1170 struct local_vars_finder *vf = data;
  1171 + struct probe_finder *pf = vf->pf;
1151 1172 int tag;
1152 1173  
1153 1174 tag = dwarf_tag(die_mem);
1154 1175 if (tag == DW_TAG_formal_parameter ||
1155 1176 tag == DW_TAG_variable) {
1156 1177 if (convert_variable_location(die_mem, vf->pf->addr,
1157   - vf->pf->fb_ops, NULL) == 0) {
  1178 + vf->pf->fb_ops, &pf->sp_die,
  1179 + NULL) == 0) {
1158 1180 vf->args[vf->nargs].var = (char *)dwarf_diename(die_mem);
1159 1181 if (vf->args[vf->nargs].var == NULL) {
1160 1182 vf->ret = -ENOMEM;
... ... @@ -1302,7 +1324,8 @@
1302 1324 if (tag == DW_TAG_formal_parameter ||
1303 1325 tag == DW_TAG_variable) {
1304 1326 ret = convert_variable_location(die_mem, af->pf.addr,
1305   - af->pf.fb_ops, NULL);
  1327 + af->pf.fb_ops, &af->pf.sp_die,
  1328 + NULL);
1306 1329 if (ret == 0) {
1307 1330 ret = die_get_varname(die_mem, buf, MAX_VAR_LEN);
1308 1331 pr_debug2("Add new var: %s\n", buf);