2019-05-27 13:55:05 +07:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2009-10-09 04:17:38 +07:00
|
|
|
/*
|
|
|
|
* probe-finder.c : C expression to kprobe event converter
|
|
|
|
*
|
|
|
|
* Written by Masami Hiramatsu <mhiramat@redhat.com>
|
|
|
|
*/
|
|
|
|
|
2017-04-18 01:23:08 +07:00
|
|
|
#include <inttypes.h>
|
2009-10-09 04:17:38 +07:00
|
|
|
#include <sys/utsname.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdarg.h>
|
2010-04-20 13:58:32 +07:00
|
|
|
#include <dwarf-regs.h>
|
2009-10-17 07:08:01 +07:00
|
|
|
|
2011-02-04 19:52:11 +07:00
|
|
|
#include <linux/bitops.h>
|
2009-10-17 07:08:10 +07:00
|
|
|
#include "event.h"
|
2014-02-06 12:32:27 +07:00
|
|
|
#include "dso.h"
|
2009-10-17 07:08:10 +07:00
|
|
|
#include "debug.h"
|
2014-02-06 12:32:09 +07:00
|
|
|
#include "intlist.h"
|
2009-10-17 07:08:01 +07:00
|
|
|
#include "util.h"
|
2017-04-18 20:57:25 +07:00
|
|
|
#include "strlist.h"
|
2010-06-15 02:26:30 +07:00
|
|
|
#include "symbol.h"
|
2009-10-09 04:17:38 +07:00
|
|
|
#include "probe-finder.h"
|
2016-08-18 15:58:31 +07:00
|
|
|
#include "probe-file.h"
|
2017-04-18 02:51:59 +07:00
|
|
|
#include "string2.h"
|
2009-10-09 04:17:38 +07:00
|
|
|
|
2010-04-13 00:17:15 +07:00
|
|
|
/* Kprobe tracer basic type is up to u64 */
|
|
|
|
#define MAX_BASIC_TYPE_BITS 64
|
|
|
|
|
2010-10-21 17:13:41 +07:00
|
|
|
/* Dwarf FL wrappers */
|
|
|
|
static char *debuginfo_path; /* Currently dummy */
|
|
|
|
|
|
|
|
static const Dwfl_Callbacks offline_callbacks = {
|
|
|
|
.find_debuginfo = dwfl_standard_find_debuginfo,
|
|
|
|
.debuginfo_path = &debuginfo_path,
|
|
|
|
|
|
|
|
.section_address = dwfl_offline_section_address,
|
|
|
|
|
|
|
|
/* We use this table for core files too. */
|
|
|
|
.find_elf = dwfl_build_id_find_elf,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Get a Dwarf from offline image */
|
2013-11-06 01:32:36 +07:00
|
|
|
static int debuginfo__init_offline_dwarf(struct debuginfo *dbg,
|
2011-06-27 14:27:39 +07:00
|
|
|
const char *path)
|
2010-10-21 17:13:41 +07:00
|
|
|
{
|
2011-06-27 14:27:39 +07:00
|
|
|
int fd;
|
2010-10-21 17:13:41 +07:00
|
|
|
|
2011-06-27 14:27:39 +07:00
|
|
|
fd = open(path, O_RDONLY);
|
|
|
|
if (fd < 0)
|
|
|
|
return fd;
|
2010-10-21 17:13:41 +07:00
|
|
|
|
2013-11-06 01:32:36 +07:00
|
|
|
dbg->dwfl = dwfl_begin(&offline_callbacks);
|
|
|
|
if (!dbg->dwfl)
|
2011-06-27 14:27:39 +07:00
|
|
|
goto error;
|
2010-10-21 17:13:41 +07:00
|
|
|
|
2015-09-30 23:41:28 +07:00
|
|
|
dwfl_report_begin(dbg->dwfl);
|
2013-11-06 01:32:36 +07:00
|
|
|
dbg->mod = dwfl_report_offline(dbg->dwfl, "", "", fd);
|
|
|
|
if (!dbg->mod)
|
2010-10-21 17:13:41 +07:00
|
|
|
goto error;
|
|
|
|
|
2013-11-06 01:32:36 +07:00
|
|
|
dbg->dbg = dwfl_module_getdwarf(dbg->mod, &dbg->bias);
|
|
|
|
if (!dbg->dbg)
|
2011-06-27 14:27:39 +07:00
|
|
|
goto error;
|
|
|
|
|
2015-09-30 23:41:28 +07:00
|
|
|
dwfl_report_end(dbg->dwfl, NULL, NULL);
|
|
|
|
|
2011-06-27 14:27:39 +07:00
|
|
|
return 0;
|
2010-10-21 17:13:41 +07:00
|
|
|
error:
|
2013-11-06 01:32:36 +07:00
|
|
|
if (dbg->dwfl)
|
|
|
|
dwfl_end(dbg->dwfl);
|
2011-06-27 14:27:39 +07:00
|
|
|
else
|
|
|
|
close(fd);
|
2013-11-06 01:32:36 +07:00
|
|
|
memset(dbg, 0, sizeof(*dbg));
|
2011-06-27 14:27:39 +07:00
|
|
|
|
|
|
|
return -ENOENT;
|
2010-10-21 17:13:41 +07:00
|
|
|
}
|
|
|
|
|
2014-02-06 12:32:27 +07:00
|
|
|
static struct debuginfo *__debuginfo__new(const char *path)
|
2011-06-27 14:27:39 +07:00
|
|
|
{
|
2013-11-06 01:32:36 +07:00
|
|
|
struct debuginfo *dbg = zalloc(sizeof(*dbg));
|
|
|
|
if (!dbg)
|
2010-12-17 20:12:18 +07:00
|
|
|
return NULL;
|
|
|
|
|
2013-12-27 03:41:15 +07:00
|
|
|
if (debuginfo__init_offline_dwarf(dbg, path) < 0)
|
|
|
|
zfree(&dbg);
|
2014-02-06 12:32:27 +07:00
|
|
|
if (dbg)
|
|
|
|
pr_debug("Open Debuginfo file: %s\n", path);
|
2013-11-06 01:32:36 +07:00
|
|
|
return dbg;
|
2011-06-27 14:27:39 +07:00
|
|
|
}
|
|
|
|
|
2014-02-06 12:32:27 +07:00
|
|
|
enum dso_binary_type distro_dwarf_types[] = {
|
|
|
|
DSO_BINARY_TYPE__FEDORA_DEBUGINFO,
|
|
|
|
DSO_BINARY_TYPE__UBUNTU_DEBUGINFO,
|
|
|
|
DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO,
|
|
|
|
DSO_BINARY_TYPE__BUILDID_DEBUGINFO,
|
|
|
|
DSO_BINARY_TYPE__NOT_FOUND,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct debuginfo *debuginfo__new(const char *path)
|
|
|
|
{
|
|
|
|
enum dso_binary_type *type;
|
|
|
|
char buf[PATH_MAX], nil = '\0';
|
|
|
|
struct dso *dso;
|
|
|
|
struct debuginfo *dinfo = NULL;
|
|
|
|
|
|
|
|
/* Try to open distro debuginfo files */
|
|
|
|
dso = dso__new(path);
|
|
|
|
if (!dso)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
for (type = distro_dwarf_types;
|
|
|
|
!dinfo && *type != DSO_BINARY_TYPE__NOT_FOUND;
|
|
|
|
type++) {
|
|
|
|
if (dso__read_binary_type_filename(dso, *type, &nil,
|
|
|
|
buf, PATH_MAX) < 0)
|
|
|
|
continue;
|
|
|
|
dinfo = __debuginfo__new(buf);
|
|
|
|
}
|
2015-06-02 21:53:26 +07:00
|
|
|
dso__put(dso);
|
2014-02-06 12:32:27 +07:00
|
|
|
|
|
|
|
out:
|
|
|
|
/* if failed to open all distro debuginfo, open given binary */
|
|
|
|
return dinfo ? : __debuginfo__new(path);
|
|
|
|
}
|
|
|
|
|
2013-11-06 01:32:36 +07:00
|
|
|
void debuginfo__delete(struct debuginfo *dbg)
|
2011-06-27 14:27:39 +07:00
|
|
|
{
|
2013-11-06 01:32:36 +07:00
|
|
|
if (dbg) {
|
|
|
|
if (dbg->dwfl)
|
|
|
|
dwfl_end(dbg->dwfl);
|
|
|
|
free(dbg);
|
2011-06-27 14:27:39 +07:00
|
|
|
}
|
2010-12-17 20:12:18 +07:00
|
|
|
}
|
2010-10-21 17:13:41 +07:00
|
|
|
|
2009-10-09 04:17:38 +07:00
|
|
|
/*
|
|
|
|
* Probe finder related functions
|
|
|
|
*/
|
|
|
|
|
2010-07-29 21:13:51 +07:00
|
|
|
static struct probe_trace_arg_ref *alloc_trace_arg_ref(long offs)
|
2010-05-20 02:57:49 +07:00
|
|
|
{
|
2010-07-29 21:13:51 +07:00
|
|
|
struct probe_trace_arg_ref *ref;
|
|
|
|
ref = zalloc(sizeof(struct probe_trace_arg_ref));
|
2010-05-20 02:57:49 +07:00
|
|
|
if (ref != NULL)
|
|
|
|
ref->offset = offs;
|
|
|
|
return ref;
|
|
|
|
}
|
|
|
|
|
2010-10-21 17:13:23 +07:00
|
|
|
/*
|
|
|
|
* Convert a location into trace_arg.
|
|
|
|
* If tvar == NULL, this just checks variable can be converted.
|
2013-10-11 14:10:26 +07:00
|
|
|
* If fentry == true and vr_die is a parameter, do huristic search
|
|
|
|
* for the location fuzzed by function entry mcount.
|
2010-10-21 17:13:23 +07:00
|
|
|
*/
|
|
|
|
static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr,
|
2013-10-11 14:10:26 +07:00
|
|
|
Dwarf_Op *fb_ops, Dwarf_Die *sp_die,
|
2016-08-25 23:24:57 +07:00
|
|
|
unsigned int machine,
|
2010-10-21 17:13:23 +07:00
|
|
|
struct probe_trace_arg *tvar)
|
2009-10-09 04:17:38 +07:00
|
|
|
{
|
2010-05-20 02:57:49 +07:00
|
|
|
Dwarf_Attribute attr;
|
2013-10-11 14:10:26 +07:00
|
|
|
Dwarf_Addr tmp = 0;
|
2010-05-20 02:57:49 +07:00
|
|
|
Dwarf_Op *op;
|
|
|
|
size_t nops;
|
2010-02-25 20:35:42 +07:00
|
|
|
unsigned int regn;
|
|
|
|
Dwarf_Word offs = 0;
|
2010-03-17 05:06:12 +07:00
|
|
|
bool ref = false;
|
2009-10-09 04:17:38 +07:00
|
|
|
const char *regs;
|
perf probe: Add --range option to show a variable's location range
It is not easy for users to get the accurate byte offset or the line
number where a local variable can be probed.
With '--range' option, local variables in the scope of the probe point
are showed with a byte offset range, and can be added according to this
range information.
For example, there are some variables in the function
generic_perform_write():
<generic_perform_write@mm/filemap.c:0>
0 ssize_t generic_perform_write(struct file *file,
1 struct iov_iter *i, loff_t pos)
2 {
3 struct address_space *mapping = file->f_mapping;
4 const struct address_space_operations *a_ops = mapping->a_ops;
...
42 status = a_ops->write_begin(file, mapping, pos, bytes, flags,
&page, &fsdata);
44 if (unlikely(status < 0))
But we fail when we try to probe the variable 'a_ops' at line 42 or 44.
$ perf probe --add 'generic_perform_write:42 a_ops'
Failed to find the location of a_ops at this address.
Perhaps, it has been optimized out.
This is because the source code do not match the assembly, so a variable
may not be available in the source code line where it appears.
After this patch, we can lookup the accurate byte offset range of a
variable, 'INV' indicates that this variable is not valid at the given
point, but available in the scope:
$ perf probe --vars 'generic_perform_write:42' --range
Available variables at generic_perform_write:42
@<generic_perform_write+141>
[INV] ssize_t written @<generic_perform_write+[324-331]>
[INV] struct address_space_operations* a_ops @<generic_perform_write+[55-61,170-176,223-246]>
[VAL] (unknown_type) fsdata @<generic_perform_write+[70-307,346-411]>
[VAL] loff_t pos @<generic_perform_write+[0-286,286-336,346-411]>
[VAL] long int status @<generic_perform_write+[83-342,346-411]>
[VAL] long unsigned int bytes @<generic_perform_write+[122-311,320-338,346-403,403-411]>
[VAL] struct address_space* mapping @<generic_perform_write+[35-344,346-411]>
[VAL] struct iov_iter* i @<generic_perform_write+[0-340,346-411]>
[VAL] struct page* page @<generic_perform_write+[70-307,346-411]>
Then it is more clear for us to add a probe with this variable:
$ perf probe --add 'generic_perform_write+170 a_ops'
Added new event:
probe:generic_perform_write (on generic_perform_write+170 with a_ops)
Signed-off-by: He Kuang <hekuang@huawei.com>
Acked-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Wang Nan <wangnan0@huawei.com>
Link: http://lkml.kernel.org/r/1431336304-16863-2-git-send-email-hekuang@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-05-11 16:25:03 +07:00
|
|
|
int ret, ret2 = 0;
|
2010-05-20 02:57:49 +07:00
|
|
|
|
2010-10-21 17:13:16 +07:00
|
|
|
if (dwarf_attr(vr_die, DW_AT_external, &attr) != NULL)
|
|
|
|
goto static_var;
|
|
|
|
|
2010-05-20 02:57:49 +07:00
|
|
|
/* TODO: handle more than 1 exprs */
|
2013-10-11 14:10:26 +07:00
|
|
|
if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL)
|
|
|
|
return -EINVAL; /* Broken DIE ? */
|
|
|
|
if (dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0) {
|
|
|
|
ret = dwarf_entrypc(sp_die, &tmp);
|
perf probe: Add --range option to show a variable's location range
It is not easy for users to get the accurate byte offset or the line
number where a local variable can be probed.
With '--range' option, local variables in the scope of the probe point
are showed with a byte offset range, and can be added according to this
range information.
For example, there are some variables in the function
generic_perform_write():
<generic_perform_write@mm/filemap.c:0>
0 ssize_t generic_perform_write(struct file *file,
1 struct iov_iter *i, loff_t pos)
2 {
3 struct address_space *mapping = file->f_mapping;
4 const struct address_space_operations *a_ops = mapping->a_ops;
...
42 status = a_ops->write_begin(file, mapping, pos, bytes, flags,
&page, &fsdata);
44 if (unlikely(status < 0))
But we fail when we try to probe the variable 'a_ops' at line 42 or 44.
$ perf probe --add 'generic_perform_write:42 a_ops'
Failed to find the location of a_ops at this address.
Perhaps, it has been optimized out.
This is because the source code do not match the assembly, so a variable
may not be available in the source code line where it appears.
After this patch, we can lookup the accurate byte offset range of a
variable, 'INV' indicates that this variable is not valid at the given
point, but available in the scope:
$ perf probe --vars 'generic_perform_write:42' --range
Available variables at generic_perform_write:42
@<generic_perform_write+141>
[INV] ssize_t written @<generic_perform_write+[324-331]>
[INV] struct address_space_operations* a_ops @<generic_perform_write+[55-61,170-176,223-246]>
[VAL] (unknown_type) fsdata @<generic_perform_write+[70-307,346-411]>
[VAL] loff_t pos @<generic_perform_write+[0-286,286-336,346-411]>
[VAL] long int status @<generic_perform_write+[83-342,346-411]>
[VAL] long unsigned int bytes @<generic_perform_write+[122-311,320-338,346-403,403-411]>
[VAL] struct address_space* mapping @<generic_perform_write+[35-344,346-411]>
[VAL] struct iov_iter* i @<generic_perform_write+[0-340,346-411]>
[VAL] struct page* page @<generic_perform_write+[70-307,346-411]>
Then it is more clear for us to add a probe with this variable:
$ perf probe --add 'generic_perform_write+170 a_ops'
Added new event:
probe:generic_perform_write (on generic_perform_write+170 with a_ops)
Signed-off-by: He Kuang <hekuang@huawei.com>
Acked-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Wang Nan <wangnan0@huawei.com>
Link: http://lkml.kernel.org/r/1431336304-16863-2-git-send-email-hekuang@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-05-11 16:25:03 +07:00
|
|
|
if (ret)
|
|
|
|
return -ENOENT;
|
|
|
|
|
|
|
|
if (probe_conf.show_location_range &&
|
|
|
|
(dwarf_tag(vr_die) == DW_TAG_variable)) {
|
|
|
|
ret2 = -ERANGE;
|
|
|
|
} else if (addr != tmp ||
|
|
|
|
dwarf_tag(vr_die) != DW_TAG_formal_parameter) {
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = dwarf_highpc(sp_die, &tmp);
|
|
|
|
if (ret)
|
2013-10-11 14:10:26 +07:00
|
|
|
return -ENOENT;
|
|
|
|
/*
|
|
|
|
* This is fuzzed by fentry mcount. We try to find the
|
|
|
|
* parameter location at the earliest address.
|
|
|
|
*/
|
|
|
|
for (addr += 1; addr <= tmp; addr++) {
|
|
|
|
if (dwarf_getlocation_addr(&attr, addr, &op,
|
|
|
|
&nops, 1) > 0)
|
|
|
|
goto found;
|
|
|
|
}
|
2010-05-20 02:57:49 +07:00
|
|
|
return -ENOENT;
|
|
|
|
}
|
2013-10-11 14:10:26 +07:00
|
|
|
found:
|
|
|
|
if (nops == 0)
|
|
|
|
/* TODO: Support const_value */
|
|
|
|
return -ENOENT;
|
2010-05-20 02:57:49 +07:00
|
|
|
|
|
|
|
if (op->atom == DW_OP_addr) {
|
2010-10-21 17:13:16 +07:00
|
|
|
static_var:
|
2010-10-21 17:13:23 +07:00
|
|
|
if (!tvar)
|
perf probe: Add --range option to show a variable's location range
It is not easy for users to get the accurate byte offset or the line
number where a local variable can be probed.
With '--range' option, local variables in the scope of the probe point
are showed with a byte offset range, and can be added according to this
range information.
For example, there are some variables in the function
generic_perform_write():
<generic_perform_write@mm/filemap.c:0>
0 ssize_t generic_perform_write(struct file *file,
1 struct iov_iter *i, loff_t pos)
2 {
3 struct address_space *mapping = file->f_mapping;
4 const struct address_space_operations *a_ops = mapping->a_ops;
...
42 status = a_ops->write_begin(file, mapping, pos, bytes, flags,
&page, &fsdata);
44 if (unlikely(status < 0))
But we fail when we try to probe the variable 'a_ops' at line 42 or 44.
$ perf probe --add 'generic_perform_write:42 a_ops'
Failed to find the location of a_ops at this address.
Perhaps, it has been optimized out.
This is because the source code do not match the assembly, so a variable
may not be available in the source code line where it appears.
After this patch, we can lookup the accurate byte offset range of a
variable, 'INV' indicates that this variable is not valid at the given
point, but available in the scope:
$ perf probe --vars 'generic_perform_write:42' --range
Available variables at generic_perform_write:42
@<generic_perform_write+141>
[INV] ssize_t written @<generic_perform_write+[324-331]>
[INV] struct address_space_operations* a_ops @<generic_perform_write+[55-61,170-176,223-246]>
[VAL] (unknown_type) fsdata @<generic_perform_write+[70-307,346-411]>
[VAL] loff_t pos @<generic_perform_write+[0-286,286-336,346-411]>
[VAL] long int status @<generic_perform_write+[83-342,346-411]>
[VAL] long unsigned int bytes @<generic_perform_write+[122-311,320-338,346-403,403-411]>
[VAL] struct address_space* mapping @<generic_perform_write+[35-344,346-411]>
[VAL] struct iov_iter* i @<generic_perform_write+[0-340,346-411]>
[VAL] struct page* page @<generic_perform_write+[70-307,346-411]>
Then it is more clear for us to add a probe with this variable:
$ perf probe --add 'generic_perform_write+170 a_ops'
Added new event:
probe:generic_perform_write (on generic_perform_write+170 with a_ops)
Signed-off-by: He Kuang <hekuang@huawei.com>
Acked-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Wang Nan <wangnan0@huawei.com>
Link: http://lkml.kernel.org/r/1431336304-16863-2-git-send-email-hekuang@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-05-11 16:25:03 +07:00
|
|
|
return ret2;
|
2010-05-20 02:57:49 +07:00
|
|
|
/* Static variables on memory (not stack), make @varname */
|
|
|
|
ret = strlen(dwarf_diename(vr_die));
|
|
|
|
tvar->value = zalloc(ret + 2);
|
|
|
|
if (tvar->value == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
snprintf(tvar->value, ret + 2, "@%s", dwarf_diename(vr_die));
|
|
|
|
tvar->ref = alloc_trace_arg_ref((long)offs);
|
|
|
|
if (tvar->ref == NULL)
|
|
|
|
return -ENOMEM;
|
perf probe: Add --range option to show a variable's location range
It is not easy for users to get the accurate byte offset or the line
number where a local variable can be probed.
With '--range' option, local variables in the scope of the probe point
are showed with a byte offset range, and can be added according to this
range information.
For example, there are some variables in the function
generic_perform_write():
<generic_perform_write@mm/filemap.c:0>
0 ssize_t generic_perform_write(struct file *file,
1 struct iov_iter *i, loff_t pos)
2 {
3 struct address_space *mapping = file->f_mapping;
4 const struct address_space_operations *a_ops = mapping->a_ops;
...
42 status = a_ops->write_begin(file, mapping, pos, bytes, flags,
&page, &fsdata);
44 if (unlikely(status < 0))
But we fail when we try to probe the variable 'a_ops' at line 42 or 44.
$ perf probe --add 'generic_perform_write:42 a_ops'
Failed to find the location of a_ops at this address.
Perhaps, it has been optimized out.
This is because the source code do not match the assembly, so a variable
may not be available in the source code line where it appears.
After this patch, we can lookup the accurate byte offset range of a
variable, 'INV' indicates that this variable is not valid at the given
point, but available in the scope:
$ perf probe --vars 'generic_perform_write:42' --range
Available variables at generic_perform_write:42
@<generic_perform_write+141>
[INV] ssize_t written @<generic_perform_write+[324-331]>
[INV] struct address_space_operations* a_ops @<generic_perform_write+[55-61,170-176,223-246]>
[VAL] (unknown_type) fsdata @<generic_perform_write+[70-307,346-411]>
[VAL] loff_t pos @<generic_perform_write+[0-286,286-336,346-411]>
[VAL] long int status @<generic_perform_write+[83-342,346-411]>
[VAL] long unsigned int bytes @<generic_perform_write+[122-311,320-338,346-403,403-411]>
[VAL] struct address_space* mapping @<generic_perform_write+[35-344,346-411]>
[VAL] struct iov_iter* i @<generic_perform_write+[0-340,346-411]>
[VAL] struct page* page @<generic_perform_write+[70-307,346-411]>
Then it is more clear for us to add a probe with this variable:
$ perf probe --add 'generic_perform_write+170 a_ops'
Added new event:
probe:generic_perform_write (on generic_perform_write+170 with a_ops)
Signed-off-by: He Kuang <hekuang@huawei.com>
Acked-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Wang Nan <wangnan0@huawei.com>
Link: http://lkml.kernel.org/r/1431336304-16863-2-git-send-email-hekuang@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-05-11 16:25:03 +07:00
|
|
|
return ret2;
|
2010-05-20 02:57:49 +07:00
|
|
|
}
|
2009-10-09 04:17:38 +07:00
|
|
|
|
|
|
|
/* If this is based on frame buffer, set the offset */
|
2010-02-25 20:35:42 +07:00
|
|
|
if (op->atom == DW_OP_fbreg) {
|
2010-10-21 17:13:23 +07:00
|
|
|
if (fb_ops == NULL)
|
2010-04-13 00:17:35 +07:00
|
|
|
return -ENOTSUP;
|
2010-03-17 05:06:12 +07:00
|
|
|
ref = true;
|
2010-02-25 20:35:42 +07:00
|
|
|
offs = op->number;
|
2010-10-21 17:13:23 +07:00
|
|
|
op = &fb_ops[0];
|
2010-02-25 20:35:42 +07:00
|
|
|
}
|
2009-10-09 04:17:38 +07:00
|
|
|
|
2010-02-25 20:35:42 +07:00
|
|
|
if (op->atom >= DW_OP_breg0 && op->atom <= DW_OP_breg31) {
|
|
|
|
regn = op->atom - DW_OP_breg0;
|
|
|
|
offs += op->number;
|
2010-03-17 05:06:12 +07:00
|
|
|
ref = true;
|
2010-02-25 20:35:42 +07:00
|
|
|
} else if (op->atom >= DW_OP_reg0 && op->atom <= DW_OP_reg31) {
|
|
|
|
regn = op->atom - DW_OP_reg0;
|
|
|
|
} else if (op->atom == DW_OP_bregx) {
|
|
|
|
regn = op->number;
|
|
|
|
offs += op->number2;
|
2010-03-17 05:06:12 +07:00
|
|
|
ref = true;
|
2010-02-25 20:35:42 +07:00
|
|
|
} else if (op->atom == DW_OP_regx) {
|
|
|
|
regn = op->number;
|
2010-04-13 00:17:35 +07:00
|
|
|
} else {
|
2010-10-21 17:13:23 +07:00
|
|
|
pr_debug("DW_OP %x is not supported.\n", op->atom);
|
2010-04-13 00:17:35 +07:00
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
2009-10-09 04:17:38 +07:00
|
|
|
|
2010-10-21 17:13:23 +07:00
|
|
|
if (!tvar)
|
perf probe: Add --range option to show a variable's location range
It is not easy for users to get the accurate byte offset or the line
number where a local variable can be probed.
With '--range' option, local variables in the scope of the probe point
are showed with a byte offset range, and can be added according to this
range information.
For example, there are some variables in the function
generic_perform_write():
<generic_perform_write@mm/filemap.c:0>
0 ssize_t generic_perform_write(struct file *file,
1 struct iov_iter *i, loff_t pos)
2 {
3 struct address_space *mapping = file->f_mapping;
4 const struct address_space_operations *a_ops = mapping->a_ops;
...
42 status = a_ops->write_begin(file, mapping, pos, bytes, flags,
&page, &fsdata);
44 if (unlikely(status < 0))
But we fail when we try to probe the variable 'a_ops' at line 42 or 44.
$ perf probe --add 'generic_perform_write:42 a_ops'
Failed to find the location of a_ops at this address.
Perhaps, it has been optimized out.
This is because the source code do not match the assembly, so a variable
may not be available in the source code line where it appears.
After this patch, we can lookup the accurate byte offset range of a
variable, 'INV' indicates that this variable is not valid at the given
point, but available in the scope:
$ perf probe --vars 'generic_perform_write:42' --range
Available variables at generic_perform_write:42
@<generic_perform_write+141>
[INV] ssize_t written @<generic_perform_write+[324-331]>
[INV] struct address_space_operations* a_ops @<generic_perform_write+[55-61,170-176,223-246]>
[VAL] (unknown_type) fsdata @<generic_perform_write+[70-307,346-411]>
[VAL] loff_t pos @<generic_perform_write+[0-286,286-336,346-411]>
[VAL] long int status @<generic_perform_write+[83-342,346-411]>
[VAL] long unsigned int bytes @<generic_perform_write+[122-311,320-338,346-403,403-411]>
[VAL] struct address_space* mapping @<generic_perform_write+[35-344,346-411]>
[VAL] struct iov_iter* i @<generic_perform_write+[0-340,346-411]>
[VAL] struct page* page @<generic_perform_write+[70-307,346-411]>
Then it is more clear for us to add a probe with this variable:
$ perf probe --add 'generic_perform_write+170 a_ops'
Added new event:
probe:generic_perform_write (on generic_perform_write+170 with a_ops)
Signed-off-by: He Kuang <hekuang@huawei.com>
Acked-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Wang Nan <wangnan0@huawei.com>
Link: http://lkml.kernel.org/r/1431336304-16863-2-git-send-email-hekuang@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-05-11 16:25:03 +07:00
|
|
|
return ret2;
|
2010-10-21 17:13:23 +07:00
|
|
|
|
2016-08-25 23:24:57 +07:00
|
|
|
regs = get_dwarf_regstr(regn, machine);
|
2010-04-13 00:17:35 +07:00
|
|
|
if (!regs) {
|
2010-10-21 17:13:23 +07:00
|
|
|
/* This should be a bug in DWARF or this tool */
|
2010-12-17 20:12:11 +07:00
|
|
|
pr_warning("Mapping for the register number %u "
|
|
|
|
"missing on this architecture.\n", regn);
|
perf probe: Add --range option to show a variable's location range
It is not easy for users to get the accurate byte offset or the line
number where a local variable can be probed.
With '--range' option, local variables in the scope of the probe point
are showed with a byte offset range, and can be added according to this
range information.
For example, there are some variables in the function
generic_perform_write():
<generic_perform_write@mm/filemap.c:0>
0 ssize_t generic_perform_write(struct file *file,
1 struct iov_iter *i, loff_t pos)
2 {
3 struct address_space *mapping = file->f_mapping;
4 const struct address_space_operations *a_ops = mapping->a_ops;
...
42 status = a_ops->write_begin(file, mapping, pos, bytes, flags,
&page, &fsdata);
44 if (unlikely(status < 0))
But we fail when we try to probe the variable 'a_ops' at line 42 or 44.
$ perf probe --add 'generic_perform_write:42 a_ops'
Failed to find the location of a_ops at this address.
Perhaps, it has been optimized out.
This is because the source code do not match the assembly, so a variable
may not be available in the source code line where it appears.
After this patch, we can lookup the accurate byte offset range of a
variable, 'INV' indicates that this variable is not valid at the given
point, but available in the scope:
$ perf probe --vars 'generic_perform_write:42' --range
Available variables at generic_perform_write:42
@<generic_perform_write+141>
[INV] ssize_t written @<generic_perform_write+[324-331]>
[INV] struct address_space_operations* a_ops @<generic_perform_write+[55-61,170-176,223-246]>
[VAL] (unknown_type) fsdata @<generic_perform_write+[70-307,346-411]>
[VAL] loff_t pos @<generic_perform_write+[0-286,286-336,346-411]>
[VAL] long int status @<generic_perform_write+[83-342,346-411]>
[VAL] long unsigned int bytes @<generic_perform_write+[122-311,320-338,346-403,403-411]>
[VAL] struct address_space* mapping @<generic_perform_write+[35-344,346-411]>
[VAL] struct iov_iter* i @<generic_perform_write+[0-340,346-411]>
[VAL] struct page* page @<generic_perform_write+[70-307,346-411]>
Then it is more clear for us to add a probe with this variable:
$ perf probe --add 'generic_perform_write+170 a_ops'
Added new event:
probe:generic_perform_write (on generic_perform_write+170 with a_ops)
Signed-off-by: He Kuang <hekuang@huawei.com>
Acked-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Wang Nan <wangnan0@huawei.com>
Link: http://lkml.kernel.org/r/1431336304-16863-2-git-send-email-hekuang@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-05-11 16:25:03 +07:00
|
|
|
return -ENOTSUP;
|
2010-04-13 00:17:35 +07:00
|
|
|
}
|
2009-10-09 04:17:38 +07:00
|
|
|
|
2010-04-13 00:17:56 +07:00
|
|
|
tvar->value = strdup(regs);
|
|
|
|
if (tvar->value == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2010-03-17 05:06:12 +07:00
|
|
|
if (ref) {
|
2010-05-20 02:57:49 +07:00
|
|
|
tvar->ref = alloc_trace_arg_ref((long)offs);
|
2010-04-13 00:17:49 +07:00
|
|
|
if (tvar->ref == NULL)
|
|
|
|
return -ENOMEM;
|
2010-03-17 05:06:12 +07:00
|
|
|
}
|
perf probe: Add --range option to show a variable's location range
It is not easy for users to get the accurate byte offset or the line
number where a local variable can be probed.
With '--range' option, local variables in the scope of the probe point
are showed with a byte offset range, and can be added according to this
range information.
For example, there are some variables in the function
generic_perform_write():
<generic_perform_write@mm/filemap.c:0>
0 ssize_t generic_perform_write(struct file *file,
1 struct iov_iter *i, loff_t pos)
2 {
3 struct address_space *mapping = file->f_mapping;
4 const struct address_space_operations *a_ops = mapping->a_ops;
...
42 status = a_ops->write_begin(file, mapping, pos, bytes, flags,
&page, &fsdata);
44 if (unlikely(status < 0))
But we fail when we try to probe the variable 'a_ops' at line 42 or 44.
$ perf probe --add 'generic_perform_write:42 a_ops'
Failed to find the location of a_ops at this address.
Perhaps, it has been optimized out.
This is because the source code do not match the assembly, so a variable
may not be available in the source code line where it appears.
After this patch, we can lookup the accurate byte offset range of a
variable, 'INV' indicates that this variable is not valid at the given
point, but available in the scope:
$ perf probe --vars 'generic_perform_write:42' --range
Available variables at generic_perform_write:42
@<generic_perform_write+141>
[INV] ssize_t written @<generic_perform_write+[324-331]>
[INV] struct address_space_operations* a_ops @<generic_perform_write+[55-61,170-176,223-246]>
[VAL] (unknown_type) fsdata @<generic_perform_write+[70-307,346-411]>
[VAL] loff_t pos @<generic_perform_write+[0-286,286-336,346-411]>
[VAL] long int status @<generic_perform_write+[83-342,346-411]>
[VAL] long unsigned int bytes @<generic_perform_write+[122-311,320-338,346-403,403-411]>
[VAL] struct address_space* mapping @<generic_perform_write+[35-344,346-411]>
[VAL] struct iov_iter* i @<generic_perform_write+[0-340,346-411]>
[VAL] struct page* page @<generic_perform_write+[70-307,346-411]>
Then it is more clear for us to add a probe with this variable:
$ perf probe --add 'generic_perform_write+170 a_ops'
Added new event:
probe:generic_perform_write (on generic_perform_write+170 with a_ops)
Signed-off-by: He Kuang <hekuang@huawei.com>
Acked-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Wang Nan <wangnan0@huawei.com>
Link: http://lkml.kernel.org/r/1431336304-16863-2-git-send-email-hekuang@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-05-11 16:25:03 +07:00
|
|
|
return ret2;
|
2009-10-09 04:17:38 +07:00
|
|
|
}
|
|
|
|
|
2011-02-04 19:52:11 +07:00
|
|
|
#define BYTES_TO_BITS(nb) ((nb) * BITS_PER_LONG / sizeof(long))
|
|
|
|
|
2010-04-13 00:17:35 +07:00
|
|
|
static int convert_variable_type(Dwarf_Die *vr_die,
|
2010-07-29 21:13:51 +07:00
|
|
|
struct probe_trace_arg *tvar,
|
2010-05-20 02:57:35 +07:00
|
|
|
const char *cast)
|
2010-04-13 00:17:15 +07:00
|
|
|
{
|
2010-07-29 21:13:51 +07:00
|
|
|
struct probe_trace_arg_ref **ref_ptr = &tvar->ref;
|
2010-04-13 00:17:15 +07:00
|
|
|
Dwarf_Die type;
|
|
|
|
char buf[16];
|
2014-08-14 09:22:34 +07:00
|
|
|
char sbuf[STRERR_BUFSIZE];
|
2011-06-27 14:27:21 +07:00
|
|
|
int bsize, boffs, total;
|
2010-04-13 00:17:15 +07:00
|
|
|
int ret;
|
2016-08-18 15:58:47 +07:00
|
|
|
char prefix;
|
2010-04-13 00:17:15 +07:00
|
|
|
|
2010-05-20 02:57:35 +07:00
|
|
|
/* TODO: check all types */
|
2016-08-18 15:58:47 +07:00
|
|
|
if (cast && strcmp(cast, "string") != 0 && strcmp(cast, "x") != 0 &&
|
2016-08-09 09:40:08 +07:00
|
|
|
strcmp(cast, "s") != 0 && strcmp(cast, "u") != 0) {
|
2010-05-20 02:57:35 +07:00
|
|
|
/* Non string type is OK */
|
2016-08-18 15:58:47 +07:00
|
|
|
/* and respect signedness/hexadecimal cast */
|
2010-05-20 02:57:35 +07:00
|
|
|
tvar->type = strdup(cast);
|
|
|
|
return (tvar->type == NULL) ? -ENOMEM : 0;
|
|
|
|
}
|
|
|
|
|
2011-06-27 14:27:21 +07:00
|
|
|
bsize = dwarf_bitsize(vr_die);
|
|
|
|
if (bsize > 0) {
|
2011-02-04 19:52:11 +07:00
|
|
|
/* This is a bitfield */
|
2011-06-27 14:27:21 +07:00
|
|
|
boffs = dwarf_bitoffset(vr_die);
|
|
|
|
total = dwarf_bytesize(vr_die);
|
|
|
|
if (boffs < 0 || total < 0)
|
|
|
|
return -ENOENT;
|
|
|
|
ret = snprintf(buf, 16, "b%d@%d/%zd", bsize, boffs,
|
|
|
|
BYTES_TO_BITS(total));
|
2011-02-04 19:52:11 +07:00
|
|
|
goto formatted;
|
|
|
|
}
|
|
|
|
|
2010-04-13 00:17:35 +07:00
|
|
|
if (die_get_real_type(vr_die, &type) == NULL) {
|
|
|
|
pr_warning("Failed to get a type information of %s.\n",
|
|
|
|
dwarf_diename(vr_die));
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
2010-04-13 00:17:15 +07:00
|
|
|
|
2010-05-20 02:57:42 +07:00
|
|
|
pr_debug("%s type is %s.\n",
|
|
|
|
dwarf_diename(vr_die), dwarf_diename(&type));
|
|
|
|
|
2010-05-20 02:57:35 +07:00
|
|
|
if (cast && strcmp(cast, "string") == 0) { /* String type */
|
|
|
|
ret = dwarf_tag(&type);
|
|
|
|
if (ret != DW_TAG_pointer_type &&
|
|
|
|
ret != DW_TAG_array_type) {
|
|
|
|
pr_warning("Failed to cast into string: "
|
2010-12-17 20:12:11 +07:00
|
|
|
"%s(%s) is not a pointer nor array.\n",
|
2010-05-20 02:57:35 +07:00
|
|
|
dwarf_diename(vr_die), dwarf_diename(&type));
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2012-09-11 14:57:28 +07:00
|
|
|
if (die_get_real_type(&type, &type) == NULL) {
|
|
|
|
pr_warning("Failed to get a type"
|
|
|
|
" information.\n");
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
2010-05-20 02:57:35 +07:00
|
|
|
if (ret == DW_TAG_pointer_type) {
|
|
|
|
while (*ref_ptr)
|
|
|
|
ref_ptr = &(*ref_ptr)->next;
|
|
|
|
/* Add new reference with offset +0 */
|
2010-07-29 21:13:51 +07:00
|
|
|
*ref_ptr = zalloc(sizeof(struct probe_trace_arg_ref));
|
2010-05-20 02:57:35 +07:00
|
|
|
if (*ref_ptr == NULL) {
|
|
|
|
pr_warning("Out of memory error\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
}
|
2010-07-09 16:29:17 +07:00
|
|
|
if (!die_compare_name(&type, "char") &&
|
|
|
|
!die_compare_name(&type, "unsigned char")) {
|
2010-05-20 02:57:35 +07:00
|
|
|
pr_warning("Failed to cast into string: "
|
2010-12-17 20:12:11 +07:00
|
|
|
"%s is not (unsigned) char *.\n",
|
2010-05-20 02:57:35 +07:00
|
|
|
dwarf_diename(vr_die));
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
tvar->type = strdup(cast);
|
|
|
|
return (tvar->type == NULL) ? -ENOMEM : 0;
|
|
|
|
}
|
|
|
|
|
2016-08-09 09:40:08 +07:00
|
|
|
if (cast && (strcmp(cast, "u") == 0))
|
2016-08-18 15:58:47 +07:00
|
|
|
prefix = 'u';
|
2016-08-09 09:40:08 +07:00
|
|
|
else if (cast && (strcmp(cast, "s") == 0))
|
2016-08-18 15:58:47 +07:00
|
|
|
prefix = 's';
|
|
|
|
else if (cast && (strcmp(cast, "x") == 0) &&
|
|
|
|
probe_type_is_available(PROBE_TYPE_X))
|
|
|
|
prefix = 'x';
|
2016-08-09 09:40:08 +07:00
|
|
|
else
|
2016-08-18 15:59:07 +07:00
|
|
|
prefix = die_is_signed_type(&type) ? 's' :
|
|
|
|
probe_type_is_available(PROBE_TYPE_X) ? 'x' : 'u';
|
2016-08-09 09:40:08 +07:00
|
|
|
|
2011-06-27 14:27:21 +07:00
|
|
|
ret = dwarf_bytesize(&type);
|
|
|
|
if (ret <= 0)
|
2011-02-04 19:52:11 +07:00
|
|
|
/* No size ... try to use default type */
|
|
|
|
return 0;
|
2011-06-27 14:27:21 +07:00
|
|
|
ret = BYTES_TO_BITS(ret);
|
2010-04-13 00:17:15 +07:00
|
|
|
|
2011-02-04 19:52:11 +07:00
|
|
|
/* Check the bitwidth */
|
|
|
|
if (ret > MAX_BASIC_TYPE_BITS) {
|
|
|
|
pr_info("%s exceeds max-bitwidth. Cut down to %d bits.\n",
|
|
|
|
dwarf_diename(&type), MAX_BASIC_TYPE_BITS);
|
|
|
|
ret = MAX_BASIC_TYPE_BITS;
|
2010-04-13 00:17:15 +07:00
|
|
|
}
|
2016-08-18 15:58:47 +07:00
|
|
|
ret = snprintf(buf, 16, "%c%d", prefix, ret);
|
2011-02-04 19:52:11 +07:00
|
|
|
|
|
|
|
formatted:
|
|
|
|
if (ret < 0 || ret >= 16) {
|
|
|
|
if (ret >= 16)
|
|
|
|
ret = -E2BIG;
|
|
|
|
pr_warning("Failed to convert variable type: %s\n",
|
tools: Introduce str_error_r()
The tools so far have been using the strerror_r() GNU variant, that
returns a string, be it the buffer passed or something else.
But that, besides being tricky in cases where we expect that the
function using strerror_r() returns the error formatted in a provided
buffer (we have to check if it returned something else and copy that
instead), breaks the build on systems not using glibc, like Alpine
Linux, where musl libc is used.
So, introduce yet another wrapper, str_error_r(), that has the GNU
interface, but uses the portable XSI variant of strerror_r(), so that
users rest asured that the provided buffer is used and it is what is
returned.
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Wang Nan <wangnan0@huawei.com>
Link: http://lkml.kernel.org/n/tip-d4t42fnf48ytlk8rjxs822tf@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-07-06 21:56:20 +07:00
|
|
|
str_error_r(-ret, sbuf, sizeof(sbuf)));
|
2011-02-04 19:52:11 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
tvar->type = strdup(buf);
|
|
|
|
if (tvar->type == NULL)
|
|
|
|
return -ENOMEM;
|
2010-04-13 00:17:35 +07:00
|
|
|
return 0;
|
2010-04-13 00:17:15 +07:00
|
|
|
}
|
|
|
|
|
2010-04-13 00:17:35 +07:00
|
|
|
static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
|
2010-03-17 05:06:26 +07:00
|
|
|
struct perf_probe_arg_field *field,
|
2010-07-29 21:13:51 +07:00
|
|
|
struct probe_trace_arg_ref **ref_ptr,
|
2010-04-13 00:17:15 +07:00
|
|
|
Dwarf_Die *die_mem)
|
2010-03-17 05:06:26 +07:00
|
|
|
{
|
2010-07-29 21:13:51 +07:00
|
|
|
struct probe_trace_arg_ref *ref = *ref_ptr;
|
2010-03-17 05:06:26 +07:00
|
|
|
Dwarf_Die type;
|
|
|
|
Dwarf_Word offs;
|
2010-05-20 02:57:42 +07:00
|
|
|
int ret, tag;
|
2010-03-17 05:06:26 +07:00
|
|
|
|
|
|
|
pr_debug("converting %s in %s\n", field->name, varname);
|
2010-04-13 00:17:35 +07:00
|
|
|
if (die_get_real_type(vr_die, &type) == NULL) {
|
|
|
|
pr_warning("Failed to get the type of %s.\n", varname);
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
2018-03-17 19:52:25 +07:00
|
|
|
pr_debug2("Var real type: %s (%x)\n", dwarf_diename(&type),
|
|
|
|
(unsigned)dwarf_dieoffset(&type));
|
2010-05-20 02:57:42 +07:00
|
|
|
tag = dwarf_tag(&type);
|
|
|
|
|
|
|
|
if (field->name[0] == '[' &&
|
|
|
|
(tag == DW_TAG_array_type || tag == DW_TAG_pointer_type)) {
|
2018-03-17 19:52:25 +07:00
|
|
|
/* Save original type for next field or type */
|
|
|
|
memcpy(die_mem, &type, sizeof(*die_mem));
|
2010-05-20 02:57:42 +07:00
|
|
|
/* Get the type of this array */
|
|
|
|
if (die_get_real_type(&type, &type) == NULL) {
|
|
|
|
pr_warning("Failed to get the type of %s.\n", varname);
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
2018-03-17 19:52:25 +07:00
|
|
|
pr_debug2("Array real type: %s (%x)\n", dwarf_diename(&type),
|
2010-05-20 02:57:42 +07:00
|
|
|
(unsigned)dwarf_dieoffset(&type));
|
|
|
|
if (tag == DW_TAG_pointer_type) {
|
2010-07-29 21:13:51 +07:00
|
|
|
ref = zalloc(sizeof(struct probe_trace_arg_ref));
|
2010-05-20 02:57:42 +07:00
|
|
|
if (ref == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
if (*ref_ptr)
|
|
|
|
(*ref_ptr)->next = ref;
|
|
|
|
else
|
|
|
|
*ref_ptr = ref;
|
|
|
|
}
|
2011-06-27 14:27:21 +07:00
|
|
|
ref->offset += dwarf_bytesize(&type) * field->index;
|
2010-05-20 02:57:42 +07:00
|
|
|
goto next;
|
|
|
|
} else if (tag == DW_TAG_pointer_type) {
|
|
|
|
/* Check the pointer and dereference */
|
2010-04-13 00:17:35 +07:00
|
|
|
if (!field->ref) {
|
|
|
|
pr_err("Semantic error: %s must be referred by '->'\n",
|
|
|
|
field->name);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2010-03-17 05:06:26 +07:00
|
|
|
/* Get the type pointed by this pointer */
|
2010-04-13 00:17:35 +07:00
|
|
|
if (die_get_real_type(&type, &type) == NULL) {
|
|
|
|
pr_warning("Failed to get the type of %s.\n", varname);
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
2010-04-02 23:50:53 +07:00
|
|
|
/* Verify it is a data structure */
|
2012-09-12 14:57:45 +07:00
|
|
|
tag = dwarf_tag(&type);
|
|
|
|
if (tag != DW_TAG_structure_type && tag != DW_TAG_union_type) {
|
2017-02-28 05:28:49 +07:00
|
|
|
pr_warning("%s is not a data structure nor a union.\n",
|
2012-09-12 14:57:45 +07:00
|
|
|
varname);
|
2010-04-13 00:17:35 +07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2010-04-02 23:50:53 +07:00
|
|
|
|
2010-07-29 21:13:51 +07:00
|
|
|
ref = zalloc(sizeof(struct probe_trace_arg_ref));
|
2010-04-13 00:17:49 +07:00
|
|
|
if (ref == NULL)
|
|
|
|
return -ENOMEM;
|
2010-03-17 05:06:26 +07:00
|
|
|
if (*ref_ptr)
|
|
|
|
(*ref_ptr)->next = ref;
|
|
|
|
else
|
|
|
|
*ref_ptr = ref;
|
|
|
|
} else {
|
2010-04-02 23:50:53 +07:00
|
|
|
/* Verify it is a data structure */
|
2012-09-12 14:57:45 +07:00
|
|
|
if (tag != DW_TAG_structure_type && tag != DW_TAG_union_type) {
|
2017-02-28 05:28:49 +07:00
|
|
|
pr_warning("%s is not a data structure nor a union.\n",
|
2012-09-12 14:57:45 +07:00
|
|
|
varname);
|
2010-04-13 00:17:35 +07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2010-05-20 02:57:42 +07:00
|
|
|
if (field->name[0] == '[') {
|
2015-02-27 21:52:31 +07:00
|
|
|
pr_err("Semantic error: %s is not a pointer"
|
2010-12-17 20:12:11 +07:00
|
|
|
" nor array.\n", varname);
|
2010-05-20 02:57:42 +07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2015-04-02 14:33:12 +07:00
|
|
|
/* While prcessing unnamed field, we don't care about this */
|
|
|
|
if (field->ref && dwarf_diename(vr_die)) {
|
2010-04-13 00:17:35 +07:00
|
|
|
pr_err("Semantic error: %s must be referred by '.'\n",
|
|
|
|
field->name);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
if (!ref) {
|
|
|
|
pr_warning("Structure on a register is not "
|
|
|
|
"supported yet.\n");
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
2010-03-17 05:06:26 +07:00
|
|
|
}
|
|
|
|
|
2010-04-13 00:17:35 +07:00
|
|
|
if (die_find_member(&type, field->name, die_mem) == NULL) {
|
2013-10-25 03:36:31 +07:00
|
|
|
pr_warning("%s(type:%s) has no member %s.\n", varname,
|
2010-04-13 00:17:35 +07:00
|
|
|
dwarf_diename(&type), field->name);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2010-03-17 05:06:26 +07:00
|
|
|
|
|
|
|
/* Get the offset of the field */
|
2012-09-12 14:57:45 +07:00
|
|
|
if (tag == DW_TAG_union_type) {
|
|
|
|
offs = 0;
|
|
|
|
} else {
|
|
|
|
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 ret;
|
|
|
|
}
|
2010-04-13 00:17:35 +07:00
|
|
|
}
|
2010-03-17 05:06:26 +07:00
|
|
|
ref->offset += (long)offs;
|
|
|
|
|
2015-04-02 14:33:12 +07:00
|
|
|
/* If this member is unnamed, we need to reuse this field */
|
|
|
|
if (!dwarf_diename(die_mem))
|
|
|
|
return convert_variable_fields(die_mem, varname, field,
|
|
|
|
&ref, die_mem);
|
|
|
|
|
2010-05-20 02:57:42 +07:00
|
|
|
next:
|
2010-03-17 05:06:26 +07:00
|
|
|
/* Converting next field */
|
|
|
|
if (field->next)
|
2010-04-13 00:17:35 +07:00
|
|
|
return convert_variable_fields(die_mem, field->name,
|
2010-04-15 03:44:00 +07:00
|
|
|
field->next, &ref, die_mem);
|
2010-04-13 00:17:35 +07:00
|
|
|
else
|
|
|
|
return 0;
|
2010-03-17 05:06:26 +07:00
|
|
|
}
|
|
|
|
|
2009-10-09 04:17:38 +07:00
|
|
|
/* Show a variables in kprobe event format */
|
2010-04-13 00:17:35 +07:00
|
|
|
static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
|
2009-10-09 04:17:38 +07:00
|
|
|
{
|
2010-04-13 00:17:15 +07:00
|
|
|
Dwarf_Die die_mem;
|
2009-10-09 04:17:38 +07:00
|
|
|
int ret;
|
|
|
|
|
2010-05-20 02:57:49 +07:00
|
|
|
pr_debug("Converting variable %s into trace event.\n",
|
|
|
|
dwarf_diename(vr_die));
|
2010-02-25 20:35:42 +07:00
|
|
|
|
2010-10-21 17:13:23 +07:00
|
|
|
ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops,
|
2016-08-25 23:24:57 +07:00
|
|
|
&pf->sp_die, pf->machine, pf->tvar);
|
2015-05-11 16:25:04 +07:00
|
|
|
if (ret == -ENOENT || ret == -EINVAL) {
|
|
|
|
pr_err("Failed to find the location of the '%s' variable at this address.\n"
|
|
|
|
" Perhaps it has been optimized out.\n"
|
|
|
|
" Use -V with the --range option to show '%s' location range.\n",
|
|
|
|
pf->pvar->var, pf->pvar->var);
|
|
|
|
} else if (ret == -ENOTSUP)
|
2010-10-21 17:13:23 +07:00
|
|
|
pr_err("Sorry, we don't support this variable location yet.\n");
|
2014-05-29 17:52:32 +07:00
|
|
|
else if (ret == 0 && pf->pvar->field) {
|
2010-04-13 00:17:35 +07:00
|
|
|
ret = convert_variable_fields(vr_die, pf->pvar->var,
|
|
|
|
pf->pvar->field, &pf->tvar->ref,
|
|
|
|
&die_mem);
|
2010-04-13 00:17:15 +07:00
|
|
|
vr_die = &die_mem;
|
|
|
|
}
|
2010-05-20 02:57:35 +07:00
|
|
|
if (ret == 0)
|
|
|
|
ret = convert_variable_type(vr_die, pf->tvar, pf->pvar->type);
|
2010-02-25 20:35:42 +07:00
|
|
|
/* *expr will be cached in libdw. Don't free it. */
|
2010-04-13 00:17:35 +07:00
|
|
|
return ret;
|
2009-10-09 04:17:38 +07:00
|
|
|
}
|
|
|
|
|
2011-08-11 18:02:59 +07:00
|
|
|
/* Find a variable in a scope DIE */
|
|
|
|
static int find_variable(Dwarf_Die *sc_die, struct probe_finder *pf)
|
2009-10-09 04:17:38 +07:00
|
|
|
{
|
perf probe: Avoid searching variables in intermediate scopes
Fix variable searching logic to search one in inner than local scope or
global(CU) scope. In the other words, skip searching in intermediate
scopes.
e.g., in the following code,
int var1;
void inline infunc(int i)
{
i++; <--- [A]
}
void func(void)
{
int var1, var2;
infunc(var2);
}
At [A], "var1" should point the global variable "var1", however, if user
mis-typed as "var2", variable search should be failed. However, current
logic searches variable infunc() scope, global scope, and then func()
scope. Thus, it can find "var2" variable in func() scope. This may not
be what user expects.
So, it would better not search outer scopes except outermost (compile
unit) scope which contains only global variables, when it failed to find
given variable in local scope.
E.g.
Without this:
$ perf probe -V pre_schedule --externs > without.vars
With this:
$ perf probe -V pre_schedule --externs > with.vars
Check the diff:
$ diff without.vars with.vars
88d87
< int cpu
133d131
< long unsigned int* switch_count
These vars are actually in the scope of schedule(), the caller of
pre_schedule().
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Pekka Enberg <penberg@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: yrl.pp-manager.tt@hitachi.com
Link: http://lkml.kernel.org/r/20110811110305.19900.94374.stgit@fedora15
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-08-11 18:03:05 +07:00
|
|
|
Dwarf_Die vr_die;
|
2016-04-28 01:37:14 +07:00
|
|
|
char *buf, *ptr;
|
perf probe: Avoid searching variables in intermediate scopes
Fix variable searching logic to search one in inner than local scope or
global(CU) scope. In the other words, skip searching in intermediate
scopes.
e.g., in the following code,
int var1;
void inline infunc(int i)
{
i++; <--- [A]
}
void func(void)
{
int var1, var2;
infunc(var2);
}
At [A], "var1" should point the global variable "var1", however, if user
mis-typed as "var2", variable search should be failed. However, current
logic searches variable infunc() scope, global scope, and then func()
scope. Thus, it can find "var2" variable in func() scope. This may not
be what user expects.
So, it would better not search outer scopes except outermost (compile
unit) scope which contains only global variables, when it failed to find
given variable in local scope.
E.g.
Without this:
$ perf probe -V pre_schedule --externs > without.vars
With this:
$ perf probe -V pre_schedule --externs > with.vars
Check the diff:
$ diff without.vars with.vars
88d87
< int cpu
133d131
< long unsigned int* switch_count
These vars are actually in the scope of schedule(), the caller of
pre_schedule().
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Pekka Enberg <penberg@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: yrl.pp-manager.tt@hitachi.com
Link: http://lkml.kernel.org/r/20110811110305.19900.94374.stgit@fedora15
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-08-11 18:03:05 +07:00
|
|
|
int ret = 0;
|
2009-10-09 04:17:38 +07:00
|
|
|
|
2015-08-26 17:57:45 +07:00
|
|
|
/* Copy raw parameters */
|
|
|
|
if (!is_c_varname(pf->pvar->var))
|
|
|
|
return copy_to_probe_trace_arg(pf->tvar, pf->pvar);
|
2010-08-27 18:38:59 +07:00
|
|
|
|
2010-04-13 00:16:53 +07:00
|
|
|
if (pf->pvar->name)
|
2010-04-13 00:17:56 +07:00
|
|
|
pf->tvar->name = strdup(pf->pvar->name);
|
2010-04-13 00:16:53 +07:00
|
|
|
else {
|
2016-04-28 01:37:14 +07:00
|
|
|
buf = synthesize_perf_probe_arg(pf->pvar);
|
|
|
|
if (!buf)
|
|
|
|
return -ENOMEM;
|
2010-04-13 00:17:22 +07:00
|
|
|
ptr = strchr(buf, ':'); /* Change type separator to _ */
|
|
|
|
if (ptr)
|
|
|
|
*ptr = '_';
|
2016-04-28 01:37:14 +07:00
|
|
|
pf->tvar->name = buf;
|
2010-04-13 00:16:53 +07:00
|
|
|
}
|
2010-04-13 00:17:56 +07:00
|
|
|
if (pf->tvar->name == NULL)
|
|
|
|
return -ENOMEM;
|
2010-04-13 00:16:53 +07:00
|
|
|
|
perf probe: Avoid searching variables in intermediate scopes
Fix variable searching logic to search one in inner than local scope or
global(CU) scope. In the other words, skip searching in intermediate
scopes.
e.g., in the following code,
int var1;
void inline infunc(int i)
{
i++; <--- [A]
}
void func(void)
{
int var1, var2;
infunc(var2);
}
At [A], "var1" should point the global variable "var1", however, if user
mis-typed as "var2", variable search should be failed. However, current
logic searches variable infunc() scope, global scope, and then func()
scope. Thus, it can find "var2" variable in func() scope. This may not
be what user expects.
So, it would better not search outer scopes except outermost (compile
unit) scope which contains only global variables, when it failed to find
given variable in local scope.
E.g.
Without this:
$ perf probe -V pre_schedule --externs > without.vars
With this:
$ perf probe -V pre_schedule --externs > with.vars
Check the diff:
$ diff without.vars with.vars
88d87
< int cpu
133d131
< long unsigned int* switch_count
These vars are actually in the scope of schedule(), the caller of
pre_schedule().
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Pekka Enberg <penberg@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: yrl.pp-manager.tt@hitachi.com
Link: http://lkml.kernel.org/r/20110811110305.19900.94374.stgit@fedora15
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-08-11 18:03:05 +07:00
|
|
|
pr_debug("Searching '%s' variable in context.\n", pf->pvar->var);
|
2010-04-13 00:17:35 +07:00
|
|
|
/* Search child die for local variables and parameters. */
|
perf probe: Avoid searching variables in intermediate scopes
Fix variable searching logic to search one in inner than local scope or
global(CU) scope. In the other words, skip searching in intermediate
scopes.
e.g., in the following code,
int var1;
void inline infunc(int i)
{
i++; <--- [A]
}
void func(void)
{
int var1, var2;
infunc(var2);
}
At [A], "var1" should point the global variable "var1", however, if user
mis-typed as "var2", variable search should be failed. However, current
logic searches variable infunc() scope, global scope, and then func()
scope. Thus, it can find "var2" variable in func() scope. This may not
be what user expects.
So, it would better not search outer scopes except outermost (compile
unit) scope which contains only global variables, when it failed to find
given variable in local scope.
E.g.
Without this:
$ perf probe -V pre_schedule --externs > without.vars
With this:
$ perf probe -V pre_schedule --externs > with.vars
Check the diff:
$ diff without.vars with.vars
88d87
< int cpu
133d131
< long unsigned int* switch_count
These vars are actually in the scope of schedule(), the caller of
pre_schedule().
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Pekka Enberg <penberg@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: yrl.pp-manager.tt@hitachi.com
Link: http://lkml.kernel.org/r/20110811110305.19900.94374.stgit@fedora15
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-08-11 18:03:05 +07:00
|
|
|
if (!die_find_variable_at(sc_die, pf->pvar->var, pf->addr, &vr_die)) {
|
|
|
|
/* Search again in global variables */
|
2015-04-25 15:08:58 +07:00
|
|
|
if (!die_find_variable_at(&pf->cu_die, pf->pvar->var,
|
|
|
|
0, &vr_die)) {
|
2014-06-06 14:13:45 +07:00
|
|
|
pr_warning("Failed to find '%s' in this function.\n",
|
|
|
|
pf->pvar->var);
|
perf probe: Avoid searching variables in intermediate scopes
Fix variable searching logic to search one in inner than local scope or
global(CU) scope. In the other words, skip searching in intermediate
scopes.
e.g., in the following code,
int var1;
void inline infunc(int i)
{
i++; <--- [A]
}
void func(void)
{
int var1, var2;
infunc(var2);
}
At [A], "var1" should point the global variable "var1", however, if user
mis-typed as "var2", variable search should be failed. However, current
logic searches variable infunc() scope, global scope, and then func()
scope. Thus, it can find "var2" variable in func() scope. This may not
be what user expects.
So, it would better not search outer scopes except outermost (compile
unit) scope which contains only global variables, when it failed to find
given variable in local scope.
E.g.
Without this:
$ perf probe -V pre_schedule --externs > without.vars
With this:
$ perf probe -V pre_schedule --externs > with.vars
Check the diff:
$ diff without.vars with.vars
88d87
< int cpu
133d131
< long unsigned int* switch_count
These vars are actually in the scope of schedule(), the caller of
pre_schedule().
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Pekka Enberg <penberg@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: yrl.pp-manager.tt@hitachi.com
Link: http://lkml.kernel.org/r/20110811110305.19900.94374.stgit@fedora15
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-08-11 18:03:05 +07:00
|
|
|
ret = -ENOENT;
|
2015-04-25 15:08:58 +07:00
|
|
|
}
|
2010-05-20 02:57:49 +07:00
|
|
|
}
|
2011-08-20 12:39:23 +07:00
|
|
|
if (ret >= 0)
|
perf probe: Avoid searching variables in intermediate scopes
Fix variable searching logic to search one in inner than local scope or
global(CU) scope. In the other words, skip searching in intermediate
scopes.
e.g., in the following code,
int var1;
void inline infunc(int i)
{
i++; <--- [A]
}
void func(void)
{
int var1, var2;
infunc(var2);
}
At [A], "var1" should point the global variable "var1", however, if user
mis-typed as "var2", variable search should be failed. However, current
logic searches variable infunc() scope, global scope, and then func()
scope. Thus, it can find "var2" variable in func() scope. This may not
be what user expects.
So, it would better not search outer scopes except outermost (compile
unit) scope which contains only global variables, when it failed to find
given variable in local scope.
E.g.
Without this:
$ perf probe -V pre_schedule --externs > without.vars
With this:
$ perf probe -V pre_schedule --externs > with.vars
Check the diff:
$ diff without.vars with.vars
88d87
< int cpu
133d131
< long unsigned int* switch_count
These vars are actually in the scope of schedule(), the caller of
pre_schedule().
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Pekka Enberg <penberg@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: yrl.pp-manager.tt@hitachi.com
Link: http://lkml.kernel.org/r/20110811110305.19900.94374.stgit@fedora15
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-08-11 18:03:05 +07:00
|
|
|
ret = convert_variable(&vr_die, pf);
|
|
|
|
|
2010-05-20 02:57:49 +07:00
|
|
|
return ret;
|
2009-10-09 04:17:38 +07:00
|
|
|
}
|
|
|
|
|
2010-10-21 17:13:23 +07:00
|
|
|
/* Convert subprogram DIE to trace point */
|
2013-09-25 20:16:16 +07:00
|
|
|
static int convert_to_trace_point(Dwarf_Die *sp_die, Dwfl_Module *mod,
|
|
|
|
Dwarf_Addr paddr, bool retprobe,
|
2015-09-30 23:41:37 +07:00
|
|
|
const char *function,
|
2013-09-25 20:16:16 +07:00
|
|
|
struct probe_trace_point *tp)
|
2009-10-09 04:17:38 +07:00
|
|
|
{
|
2012-02-24 14:41:39 +07:00
|
|
|
Dwarf_Addr eaddr, highaddr;
|
2013-09-25 20:16:16 +07:00
|
|
|
GElf_Sym sym;
|
|
|
|
const char *symbol;
|
|
|
|
|
|
|
|
/* Verify the address is correct */
|
|
|
|
if (dwarf_entrypc(sp_die, &eaddr) != 0) {
|
|
|
|
pr_warning("Failed to get entry address of %s\n",
|
|
|
|
dwarf_diename(sp_die));
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
if (dwarf_highpc(sp_die, &highaddr) != 0) {
|
|
|
|
pr_warning("Failed to get end address of %s\n",
|
|
|
|
dwarf_diename(sp_die));
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
if (paddr > highaddr) {
|
|
|
|
pr_warning("Offset specified is greater than size of %s\n",
|
|
|
|
dwarf_diename(sp_die));
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2014-09-17 15:41:01 +07:00
|
|
|
symbol = dwarf_diename(sp_die);
|
2013-09-25 20:16:16 +07:00
|
|
|
if (!symbol) {
|
2014-09-17 15:41:01 +07:00
|
|
|
/* Try to get the symbol name from symtab */
|
|
|
|
symbol = dwfl_module_addrsym(mod, paddr, &sym, NULL);
|
|
|
|
if (!symbol) {
|
|
|
|
pr_warning("Failed to find symbol at 0x%lx\n",
|
|
|
|
(unsigned long)paddr);
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
eaddr = sym.st_value;
|
2013-09-25 20:16:16 +07:00
|
|
|
}
|
2014-09-17 15:41:01 +07:00
|
|
|
tp->offset = (unsigned long)(paddr - eaddr);
|
perf probe: Support basic dwarf-based operations on uprobe events
Support basic dwarf(debuginfo) based operations for uprobe events. With
this change, perf probe can analyze debuginfo of user application binary
to set up new uprobe event.
This allows perf-probe --add(with local variables, line numbers) and
--line works with -x option. (Actually, --vars has already accepted -x
option)
For example, the following command shows the probe-able lines of a given
user space function. Something that so far was only available in the
'perf probe' tool for kernel space functions:
# ./perf probe -x perf --line map__load
<map__load@/home/fedora/ksrc/linux-2.6/tools/perf/util/map.c:0>
0 int map__load(struct map *map, symbol_filter_t filter)
1 {
2 const char *name = map->dso->long_name;
int nr;
5 if (dso__loaded(map->dso, map->type))
6 return 0;
8 nr = dso__load(map->dso, map, filter);
9 if (nr < 0) {
10 if (map->dso->has_build_id) {
And this shows the available variables at the given line of the
function.
# ./perf probe -x perf --vars map__load:8
Available variables at map__load:8
@<map__load+96>
char* name
struct map* map
symbol_filter_t filter
@<map__find_symbol+112>
char* name
symbol_filter_t filter
@<map__find_symbol_by_name+136>
char* name
symbol_filter_t filter
@<map_groups__find_symbol_by_name+176>
char* name
struct map* map
symbol_filter_t filter
And lastly, we can now define probe(s) with all available
variables on the given line:
# ./perf probe -x perf --add 'map__load:8 $vars'
Added new events:
probe_perf:map__load (on map__load:8 with $vars)
probe_perf:map__load_1 (on map__load:8 with $vars)
probe_perf:map__load_2 (on map__load:8 with $vars)
probe_perf:map__load_3 (on map__load:8 with $vars)
You can now use it in all perf tools, such as:
perf record -e probe_perf:map__load_3 -aR sleep 1
Changes from previous version:
- Add examples in the patch description.
- Use .text section start address and dwarf symbol address
for calculating the offset of given symbol, instead of
searching the symbol in symtab again.
With this change, we can safely handle multiple local
function instances (e.g. scnprintf in perf).
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: David A. Long <dave.long@linaro.org>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: systemtap@sourceware.org
Cc: yrl.pp-manager.tt@hitachi.com
Link: http://lkml.kernel.org/r/20131226054152.22364.47021.stgit@kbuild-fedora.novalocal
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2013-12-26 12:41:53 +07:00
|
|
|
tp->address = (unsigned long)paddr;
|
2013-09-25 20:16:16 +07:00
|
|
|
tp->symbol = strdup(symbol);
|
|
|
|
if (!tp->symbol)
|
|
|
|
return -ENOMEM;
|
2010-03-17 05:06:12 +07:00
|
|
|
|
2010-08-27 18:38:53 +07:00
|
|
|
/* Return probe must be on the head of a subprogram */
|
2010-10-21 17:13:23 +07:00
|
|
|
if (retprobe) {
|
|
|
|
if (eaddr != paddr) {
|
2015-09-30 23:41:37 +07:00
|
|
|
pr_warning("Failed to find \"%s%%return\",\n"
|
|
|
|
" because %s is an inlined function and"
|
|
|
|
" has no return point.\n", function,
|
|
|
|
function);
|
2010-08-27 18:38:53 +07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
2010-10-21 17:13:23 +07:00
|
|
|
tp->retprobe = true;
|
2010-08-27 18:38:53 +07:00
|
|
|
}
|
|
|
|
|
2010-10-21 17:13:23 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-08-11 18:02:59 +07:00
|
|
|
/* Call probe_finder callback with scope DIE */
|
|
|
|
static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf)
|
2010-10-21 17:13:23 +07:00
|
|
|
{
|
|
|
|
Dwarf_Attribute fb_attr;
|
2015-11-25 17:34:32 +07:00
|
|
|
Dwarf_Frame *frame = NULL;
|
2010-10-21 17:13:23 +07:00
|
|
|
size_t nops;
|
|
|
|
int ret;
|
|
|
|
|
2011-08-11 18:02:59 +07:00
|
|
|
if (!sc_die) {
|
|
|
|
pr_err("Caller must pass a scope DIE. Program error.\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If not a real subprogram, find a real one */
|
2012-04-23 10:24:36 +07:00
|
|
|
if (!die_is_func_def(sc_die)) {
|
2011-08-11 18:02:59 +07:00
|
|
|
if (!die_find_realfunc(&pf->cu_die, pf->addr, &pf->sp_die)) {
|
2015-04-30 18:42:31 +07:00
|
|
|
if (die_find_tailfunc(&pf->cu_die, pf->addr, &pf->sp_die)) {
|
|
|
|
pr_warning("Ignoring tail call from %s\n",
|
|
|
|
dwarf_diename(&pf->sp_die));
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
pr_warning("Failed to find probe point in any "
|
|
|
|
"functions.\n");
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
2010-10-21 17:13:23 +07:00
|
|
|
}
|
2011-08-11 18:02:59 +07:00
|
|
|
} else
|
|
|
|
memcpy(&pf->sp_die, sc_die, sizeof(Dwarf_Die));
|
2009-10-09 04:17:38 +07:00
|
|
|
|
2011-08-11 18:02:59 +07:00
|
|
|
/* Get the frame base attribute/ops from subprogram */
|
|
|
|
dwarf_attr(&pf->sp_die, DW_AT_frame_base, &fb_attr);
|
2010-03-16 00:02:35 +07:00
|
|
|
ret = dwarf_getlocation_addr(&fb_attr, pf->addr, &pf->fb_ops, &nops, 1);
|
2010-04-13 00:17:29 +07:00
|
|
|
if (ret <= 0 || nops == 0) {
|
2010-02-25 20:35:42 +07:00
|
|
|
pf->fb_ops = NULL;
|
2010-05-11 00:12:07 +07:00
|
|
|
#if _ELFUTILS_PREREQ(0, 142)
|
2010-04-13 00:17:29 +07:00
|
|
|
} else if (nops == 1 && pf->fb_ops[0].atom == DW_OP_call_frame_cfa &&
|
perf probe: Search both .eh_frame and .debug_frame sections for probe location
'perf probe' through debuginfo__find_probes() in util/probe-finder.c
checks for the functions' frame descriptions in either .eh_frame section
of an ELF or the .debug_frame.
The check is based on whether either one of these sections is present.
Depending on distro, toolchain defaults, architetcutre, build flags,
etc., CFI might be found in either .eh_frame and/or .debug_frame.
Sometimes, it may happen that, .eh_frame, even if present, may not be
complete and may miss some descriptions.
Therefore, to be sure, to find the CFI covering an address we will
always have to investigate both if available.
For e.g., in powerpc, this may happen:
$ gcc -g bin.c -o bin
$ objdump --dwarf ./bin
<1><145>: Abbrev Number: 7 (DW_TAG_subprogram)
<146> DW_AT_external : 1
<146> DW_AT_name : (indirect string, offset: 0x9e): main
<14a> DW_AT_decl_file : 1
<14b> DW_AT_decl_line : 39
<14c> DW_AT_prototyped : 1
<14c> DW_AT_type : <0x57>
<150> DW_AT_low_pc : 0x100007b8
If the .eh_frame and .debug_frame are checked for the same binary, we
will find that, .eh_frame (although present) doesn't contain a
description for "main" function.
But, .debug_frame has a description:
000000d8 00000024 00000000 FDE cie=00000000 pc=100007b8..10000838
DW_CFA_advance_loc: 16 to 100007c8
DW_CFA_def_cfa_offset: 144
DW_CFA_offset_extended_sf: r65 at cfa+16
...
Due to this (since, perf checks whether .eh_frame is present and goes on
searching for that address inside that frame), perf is unable to process
the probes:
# perf probe -x ./bin main
Failed to get call frame on 0x100007b8
Error: Failed to add events.
To avoid this issue, we need to check both the sections (.eh_frame and
.debug_frame), which is done in this patch.
Note that, we can always force everything into both .eh_frame and
.debug_frame by:
$ gcc bin.c -fasynchronous-unwind-tables -fno-dwarf2-cfi-asm -g -o bin
Signed-off-by: Hemant Kumar <hemant@linux.vnet.ibm.com>
Acked-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: linuxppc-dev@lists.ozlabs.org
Cc: Mark Wielaard <mjw@redhat.com>
Cc: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Link: http://lkml.kernel.org/r/1454426806-13974-1-git-send-email-hemant@linux.vnet.ibm.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-02-02 22:26:46 +07:00
|
|
|
(pf->cfi_eh != NULL || pf->cfi_dbg != NULL)) {
|
|
|
|
if ((dwarf_cfi_addrframe(pf->cfi_eh, pf->addr, &frame) != 0 &&
|
|
|
|
(dwarf_cfi_addrframe(pf->cfi_dbg, pf->addr, &frame) != 0)) ||
|
2010-04-13 00:17:35 +07:00
|
|
|
dwarf_frame_cfa(frame, &pf->fb_ops, &nops) != 0) {
|
2010-12-17 20:12:11 +07:00
|
|
|
pr_warning("Failed to get call frame on 0x%jx\n",
|
2010-04-13 00:17:35 +07:00
|
|
|
(uintmax_t)pf->addr);
|
2015-11-25 17:34:32 +07:00
|
|
|
free(frame);
|
|
|
|
return -ENOENT;
|
2010-04-13 00:17:35 +07:00
|
|
|
}
|
2010-05-11 00:12:07 +07:00
|
|
|
#endif
|
2010-04-13 00:17:29 +07:00
|
|
|
}
|
2010-02-25 20:35:42 +07:00
|
|
|
|
2010-10-21 17:13:23 +07:00
|
|
|
/* Call finder's callback handler */
|
2015-11-25 17:34:32 +07:00
|
|
|
ret = pf->callback(sc_die, pf);
|
2010-02-25 20:35:42 +07:00
|
|
|
|
2015-11-25 17:34:32 +07:00
|
|
|
/* Since *pf->fb_ops can be a part of frame. we should free it here. */
|
|
|
|
free(frame);
|
2010-02-25 20:35:42 +07:00
|
|
|
pf->fb_ops = NULL;
|
2010-10-21 17:13:23 +07:00
|
|
|
|
|
|
|
return ret;
|
2009-10-09 04:17:38 +07:00
|
|
|
}
|
|
|
|
|
2011-08-11 18:02:59 +07:00
|
|
|
struct find_scope_param {
|
|
|
|
const char *function;
|
|
|
|
const char *file;
|
|
|
|
int line;
|
|
|
|
int diff;
|
|
|
|
Dwarf_Die *die_mem;
|
|
|
|
bool found;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int find_best_scope_cb(Dwarf_Die *fn_die, void *data)
|
|
|
|
{
|
|
|
|
struct find_scope_param *fsp = data;
|
|
|
|
const char *file;
|
|
|
|
int lno;
|
|
|
|
|
|
|
|
/* Skip if declared file name does not match */
|
|
|
|
if (fsp->file) {
|
|
|
|
file = dwarf_decl_file(fn_die);
|
|
|
|
if (!file || strcmp(fsp->file, file) != 0)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* If the function name is given, that's what user expects */
|
|
|
|
if (fsp->function) {
|
2015-05-08 08:03:35 +07:00
|
|
|
if (die_match_name(fn_die, fsp->function)) {
|
2011-08-11 18:02:59 +07:00
|
|
|
memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die));
|
|
|
|
fsp->found = true;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* With the line number, find the nearest declared DIE */
|
|
|
|
dwarf_decl_line(fn_die, &lno);
|
|
|
|
if (lno < fsp->line && fsp->diff > fsp->line - lno) {
|
|
|
|
/* Keep a candidate and continue */
|
|
|
|
fsp->diff = fsp->line - lno;
|
|
|
|
memcpy(fsp->die_mem, fn_die, sizeof(Dwarf_Die));
|
|
|
|
fsp->found = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find an appropriate scope fits to given conditions */
|
|
|
|
static Dwarf_Die *find_best_scope(struct probe_finder *pf, Dwarf_Die *die_mem)
|
|
|
|
{
|
|
|
|
struct find_scope_param fsp = {
|
|
|
|
.function = pf->pev->point.function,
|
|
|
|
.file = pf->fname,
|
|
|
|
.line = pf->lno,
|
|
|
|
.diff = INT_MAX,
|
|
|
|
.die_mem = die_mem,
|
|
|
|
.found = false,
|
|
|
|
};
|
|
|
|
|
|
|
|
cu_walk_functions_at(&pf->cu_die, pf->addr, find_best_scope_cb, &fsp);
|
|
|
|
|
|
|
|
return fsp.found ? die_mem : NULL;
|
|
|
|
}
|
|
|
|
|
2011-01-13 19:45:58 +07:00
|
|
|
static int probe_point_line_walker(const char *fname, int lineno,
|
|
|
|
Dwarf_Addr addr, void *data)
|
2009-10-09 04:17:38 +07:00
|
|
|
{
|
2011-01-13 19:45:58 +07:00
|
|
|
struct probe_finder *pf = data;
|
2011-08-11 18:02:59 +07:00
|
|
|
Dwarf_Die *sc_die, die_mem;
|
2011-01-13 19:45:58 +07:00
|
|
|
int ret;
|
2009-10-09 04:17:38 +07:00
|
|
|
|
2011-01-13 19:45:58 +07:00
|
|
|
if (lineno != pf->lno || strtailcmp(fname, pf->fname) != 0)
|
|
|
|
return 0;
|
2009-10-09 04:17:38 +07:00
|
|
|
|
2011-01-13 19:45:58 +07:00
|
|
|
pf->addr = addr;
|
2011-08-11 18:02:59 +07:00
|
|
|
sc_die = find_best_scope(pf, &die_mem);
|
|
|
|
if (!sc_die) {
|
|
|
|
pr_warning("Failed to find scope of probe point.\n");
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = call_probe_finder(sc_die, pf);
|
2009-10-28 03:43:19 +07:00
|
|
|
|
2011-01-13 19:45:58 +07:00
|
|
|
/* Continue if no error, because the line will be in inline function */
|
2011-02-21 23:23:57 +07:00
|
|
|
return ret < 0 ? ret : 0;
|
2011-01-13 19:45:58 +07:00
|
|
|
}
|
2010-02-25 20:35:42 +07:00
|
|
|
|
2011-01-13 19:45:58 +07:00
|
|
|
/* Find probe point from its line number */
|
|
|
|
static int find_probe_point_by_line(struct probe_finder *pf)
|
|
|
|
{
|
|
|
|
return die_walk_lines(&pf->cu_die, probe_point_line_walker, pf);
|
2009-10-09 04:17:38 +07:00
|
|
|
}
|
|
|
|
|
2010-02-25 20:36:12 +07:00
|
|
|
/* Find lines which match lazy pattern */
|
2014-02-06 12:32:09 +07:00
|
|
|
static int find_lazy_match_lines(struct intlist *list,
|
2010-02-25 20:36:12 +07:00
|
|
|
const char *fname, const char *pat)
|
|
|
|
{
|
2011-01-13 17:18:30 +07:00
|
|
|
FILE *fp;
|
|
|
|
char *line = NULL;
|
|
|
|
size_t line_len;
|
|
|
|
ssize_t len;
|
|
|
|
int count = 0, linenum = 1;
|
2014-08-14 09:22:34 +07:00
|
|
|
char sbuf[STRERR_BUFSIZE];
|
2011-01-13 17:18:30 +07:00
|
|
|
|
|
|
|
fp = fopen(fname, "r");
|
|
|
|
if (!fp) {
|
2014-08-14 09:22:34 +07:00
|
|
|
pr_warning("Failed to open %s: %s\n", fname,
|
tools: Introduce str_error_r()
The tools so far have been using the strerror_r() GNU variant, that
returns a string, be it the buffer passed or something else.
But that, besides being tricky in cases where we expect that the
function using strerror_r() returns the error formatted in a provided
buffer (we have to check if it returned something else and copy that
instead), breaks the build on systems not using glibc, like Alpine
Linux, where musl libc is used.
So, introduce yet another wrapper, str_error_r(), that has the GNU
interface, but uses the portable XSI variant of strerror_r(), so that
users rest asured that the provided buffer is used and it is what is
returned.
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Wang Nan <wangnan0@huawei.com>
Link: http://lkml.kernel.org/n/tip-d4t42fnf48ytlk8rjxs822tf@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-07-06 21:56:20 +07:00
|
|
|
str_error_r(errno, sbuf, sizeof(sbuf)));
|
2010-05-19 09:04:28 +07:00
|
|
|
return -errno;
|
2010-04-13 00:17:35 +07:00
|
|
|
}
|
|
|
|
|
2011-01-13 17:18:30 +07:00
|
|
|
while ((len = getline(&line, &line_len, fp)) > 0) {
|
2010-05-19 09:04:28 +07:00
|
|
|
|
2011-01-13 17:18:30 +07:00
|
|
|
if (line[len - 1] == '\n')
|
|
|
|
line[len - 1] = '\0';
|
|
|
|
|
|
|
|
if (strlazymatch(line, pat)) {
|
2014-02-06 12:32:09 +07:00
|
|
|
intlist__add(list, linenum);
|
2011-01-13 17:18:30 +07:00
|
|
|
count++;
|
2010-02-25 20:36:12 +07:00
|
|
|
}
|
2011-01-13 17:18:30 +07:00
|
|
|
linenum++;
|
2010-02-25 20:36:12 +07:00
|
|
|
}
|
2011-01-13 17:18:30 +07:00
|
|
|
|
|
|
|
if (ferror(fp))
|
|
|
|
count = -errno;
|
|
|
|
free(line);
|
|
|
|
fclose(fp);
|
|
|
|
|
|
|
|
if (count == 0)
|
|
|
|
pr_debug("No matched lines found in %s.\n", fname);
|
|
|
|
return count;
|
2010-02-25 20:36:12 +07:00
|
|
|
}
|
|
|
|
|
2011-01-13 19:45:58 +07:00
|
|
|
static int probe_point_lazy_walker(const char *fname, int lineno,
|
|
|
|
Dwarf_Addr addr, void *data)
|
|
|
|
{
|
|
|
|
struct probe_finder *pf = data;
|
2011-08-11 18:02:59 +07:00
|
|
|
Dwarf_Die *sc_die, die_mem;
|
2011-01-13 19:45:58 +07:00
|
|
|
int ret;
|
|
|
|
|
2014-02-06 12:32:09 +07:00
|
|
|
if (!intlist__has_entry(pf->lcache, lineno) ||
|
2011-01-13 19:45:58 +07:00
|
|
|
strtailcmp(fname, pf->fname) != 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
pr_debug("Probe line found: line:%d addr:0x%llx\n",
|
|
|
|
lineno, (unsigned long long)addr);
|
|
|
|
pf->addr = addr;
|
2011-08-11 18:02:59 +07:00
|
|
|
pf->lno = lineno;
|
|
|
|
sc_die = find_best_scope(pf, &die_mem);
|
|
|
|
if (!sc_die) {
|
|
|
|
pr_warning("Failed to find scope of probe point.\n");
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = call_probe_finder(sc_die, pf);
|
2011-01-13 19:45:58 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Continue if no error, because the lazy pattern will match
|
|
|
|
* to other lines
|
|
|
|
*/
|
2011-03-16 02:51:09 +07:00
|
|
|
return ret < 0 ? ret : 0;
|
2011-01-13 19:45:58 +07:00
|
|
|
}
|
|
|
|
|
2010-02-25 20:36:12 +07:00
|
|
|
/* Find probe points from lazy pattern */
|
2010-04-13 00:17:35 +07:00
|
|
|
static int find_probe_point_lazy(Dwarf_Die *sp_die, struct probe_finder *pf)
|
2010-02-25 20:36:12 +07:00
|
|
|
{
|
2010-04-13 00:17:35 +07:00
|
|
|
int ret = 0;
|
2015-03-13 12:18:40 +07:00
|
|
|
char *fpath;
|
2010-02-25 20:36:12 +07:00
|
|
|
|
2014-02-06 12:32:09 +07:00
|
|
|
if (intlist__empty(pf->lcache)) {
|
2015-03-13 12:18:40 +07:00
|
|
|
const char *comp_dir;
|
|
|
|
|
|
|
|
comp_dir = cu_get_comp_dir(&pf->cu_die);
|
|
|
|
ret = get_real_path(pf->fname, comp_dir, &fpath);
|
|
|
|
if (ret < 0) {
|
|
|
|
pr_warning("Failed to find source file path.\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-02-25 20:36:12 +07:00
|
|
|
/* Matching lazy line pattern */
|
2015-03-13 12:18:40 +07:00
|
|
|
ret = find_lazy_match_lines(pf->lcache, fpath,
|
2010-03-17 05:06:12 +07:00
|
|
|
pf->pev->point.lazy_line);
|
2015-03-13 12:18:40 +07:00
|
|
|
free(fpath);
|
2011-01-13 17:18:30 +07:00
|
|
|
if (ret <= 0)
|
2010-04-13 00:17:35 +07:00
|
|
|
return ret;
|
2010-02-25 20:36:12 +07:00
|
|
|
}
|
|
|
|
|
2011-01-13 19:45:58 +07:00
|
|
|
return die_walk_lines(sp_die, probe_point_lazy_walker, pf);
|
2010-02-25 20:36:12 +07:00
|
|
|
}
|
|
|
|
|
perf uprobe: Skip prologue if program compiled without optimization
The function prologue prepares stack and registers before executing
function logic.
When target program is compiled without optimization, function parameter
information is only valid after the prologue.
When we probe entrypc of the function, and try to record a function
parameter, it contains a garbage value.
For example:
$ vim test.c
#include <stdio.h>
void foo(int i)
{
printf("i: %d\n", i);
}
int main()
{
foo(42);
return 0;
}
$ gcc -g test.c -o test
$ objdump -dl test | less
foo():
/home/ravi/test.c:4
400536: 55 push %rbp
400537: 48 89 e5 mov %rsp,%rbp
40053a: 48 83 ec 10 sub -bashx10,%rsp
40053e: 89 7d fc mov %edi,-0x4(%rbp)
/home/ravi/test.c:5
400541: 8b 45 fc mov -0x4(%rbp),%eax
...
...
main():
/home/ravi/test.c:9
400558: 55 push %rbp
400559: 48 89 e5 mov %rsp,%rbp
/home/ravi/test.c:10
40055c: bf 2a 00 00 00 mov -bashx2a,%edi
400561: e8 d0 ff ff ff callq 400536 <foo>
$ perf probe -x ./test 'foo i'
$ cat /sys/kernel/debug/tracing/uprobe_events
p:probe_test/foo /home/ravi/test:0x0000000000000536 i=-12(%sp):s32
$ perf record -e probe_test:foo ./test
$ perf script
test 5778 [001] 4918.562027: probe_test:foo: (400536) i=0
Here variable 'i' is passed via stack which is pushed on stack at
0x40053e. But we are probing at 0x400536.
To resolve this issues, we need to probe on next instruction after
prologue. gdb and systemtap also does same thing. I've implemented this
patch based on approach systemtap has used.
After applying patch:
$ perf probe -x ./test 'foo i'
$ cat /sys/kernel/debug/tracing/uprobe_events
p:probe_test/foo /home/ravi/test:0x0000000000000541 i=-4(%bp):s32
$ perf record -e probe_test:foo ./test
$ perf script
test 6300 [001] 5877.879327: probe_test:foo: (400541) i=42
No need to skip prologue for optimized case since debug info is correct
for each instructions for -O2 -g. For more details please visit:
https://bugzilla.redhat.com/show_bug.cgi?id=612253#c6
Changes in v2:
- Skipping prologue only when any ARG is either C variable, $params or
$vars.
- Probe on line(:1) may not be always possible. Recommend only address
to force probe on function entry.
Committer notes:
Testing it with 'perf trace':
# perf probe -x ./test foo i
Added new event:
probe_test:foo (on foo in /home/acme/c/test with i)
You can now use it in all perf tools, such as:
perf record -e probe_test:foo -aR sleep 1
# cat /sys/kernel/debug/tracing/uprobe_events
p:probe_test/foo /home/acme/c/test:0x0000000000000526 i=-12(%sp):s32
# trace --no-sys --event probe_*:* ./test
i: 42
0.000 probe_test:foo:(400526) i=0)
#
After the patch:
# perf probe -d *:*
Removed event: probe_test:foo
# perf probe -x ./test foo i
Target program is compiled without optimization. Skipping prologue.
Probe on address 0x400526 to force probing at the function entry.
Added new event:
probe_test:foo (on foo in /home/acme/c/test with i)
You can now use it in all perf tools, such as:
perf record -e probe_test:foo -aR sleep 1
# cat /sys/kernel/debug/tracing/uprobe_events
p:probe_test/foo /home/acme/c/test:0x0000000000000531 i=-4(%bp):s32
# trace --no-sys --event probe_*:* ./test
i: 42
0.000 probe_test:foo:(400531) i=42)
#
Reported-by: Michael Petlan <mpetlan@redhat.com>
Report-Link: https://www.mail-archive.com/linux-perf-users@vger.kernel.org/msg02348.html
Signed-off-by: Ravi Bangoria <ravi.bangoria@linux.vnet.ibm.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Tested-by: Jiri Olsa <jolsa@kernel.org>
Acked-by: Masami Hiramatsu <mhiramat@kernel.org>
Acked-by: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Hemant Kumar <hemant@linux.vnet.ibm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Wang Nan <wangnan0@huawei.com>
Cc: Yauheni Kaliuta <yauheni.kaliuta@redhat.com>
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1299021
Link: http://lkml.kernel.org/r/1470214725-5023-2-git-send-email-ravi.bangoria@linux.vnet.ibm.com
[ Rename 'die' to 'cu_die' to avoid shadowing a die() definition on at least centos 5, Debian 7 and ubuntu:12.04.5]
[ Use PRIx64 instead of lx to format a Dwarf_Addr, aka long long unsigned int, fixing the build on 32-bit systems ]
[ dwarf_getsrclines() expects a size_t * argument ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-08-03 15:58:45 +07:00
|
|
|
static void skip_prologue(Dwarf_Die *sp_die, struct probe_finder *pf)
|
|
|
|
{
|
|
|
|
struct perf_probe_point *pp = &pf->pev->point;
|
|
|
|
|
|
|
|
/* Not uprobe? */
|
|
|
|
if (!pf->pev->uprobes)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Compiled with optimization? */
|
2016-08-30 15:39:37 +07:00
|
|
|
if (die_is_optimized_target(&pf->cu_die))
|
perf uprobe: Skip prologue if program compiled without optimization
The function prologue prepares stack and registers before executing
function logic.
When target program is compiled without optimization, function parameter
information is only valid after the prologue.
When we probe entrypc of the function, and try to record a function
parameter, it contains a garbage value.
For example:
$ vim test.c
#include <stdio.h>
void foo(int i)
{
printf("i: %d\n", i);
}
int main()
{
foo(42);
return 0;
}
$ gcc -g test.c -o test
$ objdump -dl test | less
foo():
/home/ravi/test.c:4
400536: 55 push %rbp
400537: 48 89 e5 mov %rsp,%rbp
40053a: 48 83 ec 10 sub -bashx10,%rsp
40053e: 89 7d fc mov %edi,-0x4(%rbp)
/home/ravi/test.c:5
400541: 8b 45 fc mov -0x4(%rbp),%eax
...
...
main():
/home/ravi/test.c:9
400558: 55 push %rbp
400559: 48 89 e5 mov %rsp,%rbp
/home/ravi/test.c:10
40055c: bf 2a 00 00 00 mov -bashx2a,%edi
400561: e8 d0 ff ff ff callq 400536 <foo>
$ perf probe -x ./test 'foo i'
$ cat /sys/kernel/debug/tracing/uprobe_events
p:probe_test/foo /home/ravi/test:0x0000000000000536 i=-12(%sp):s32
$ perf record -e probe_test:foo ./test
$ perf script
test 5778 [001] 4918.562027: probe_test:foo: (400536) i=0
Here variable 'i' is passed via stack which is pushed on stack at
0x40053e. But we are probing at 0x400536.
To resolve this issues, we need to probe on next instruction after
prologue. gdb and systemtap also does same thing. I've implemented this
patch based on approach systemtap has used.
After applying patch:
$ perf probe -x ./test 'foo i'
$ cat /sys/kernel/debug/tracing/uprobe_events
p:probe_test/foo /home/ravi/test:0x0000000000000541 i=-4(%bp):s32
$ perf record -e probe_test:foo ./test
$ perf script
test 6300 [001] 5877.879327: probe_test:foo: (400541) i=42
No need to skip prologue for optimized case since debug info is correct
for each instructions for -O2 -g. For more details please visit:
https://bugzilla.redhat.com/show_bug.cgi?id=612253#c6
Changes in v2:
- Skipping prologue only when any ARG is either C variable, $params or
$vars.
- Probe on line(:1) may not be always possible. Recommend only address
to force probe on function entry.
Committer notes:
Testing it with 'perf trace':
# perf probe -x ./test foo i
Added new event:
probe_test:foo (on foo in /home/acme/c/test with i)
You can now use it in all perf tools, such as:
perf record -e probe_test:foo -aR sleep 1
# cat /sys/kernel/debug/tracing/uprobe_events
p:probe_test/foo /home/acme/c/test:0x0000000000000526 i=-12(%sp):s32
# trace --no-sys --event probe_*:* ./test
i: 42
0.000 probe_test:foo:(400526) i=0)
#
After the patch:
# perf probe -d *:*
Removed event: probe_test:foo
# perf probe -x ./test foo i
Target program is compiled without optimization. Skipping prologue.
Probe on address 0x400526 to force probing at the function entry.
Added new event:
probe_test:foo (on foo in /home/acme/c/test with i)
You can now use it in all perf tools, such as:
perf record -e probe_test:foo -aR sleep 1
# cat /sys/kernel/debug/tracing/uprobe_events
p:probe_test/foo /home/acme/c/test:0x0000000000000531 i=-4(%bp):s32
# trace --no-sys --event probe_*:* ./test
i: 42
0.000 probe_test:foo:(400531) i=42)
#
Reported-by: Michael Petlan <mpetlan@redhat.com>
Report-Link: https://www.mail-archive.com/linux-perf-users@vger.kernel.org/msg02348.html
Signed-off-by: Ravi Bangoria <ravi.bangoria@linux.vnet.ibm.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Tested-by: Jiri Olsa <jolsa@kernel.org>
Acked-by: Masami Hiramatsu <mhiramat@kernel.org>
Acked-by: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Hemant Kumar <hemant@linux.vnet.ibm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Wang Nan <wangnan0@huawei.com>
Cc: Yauheni Kaliuta <yauheni.kaliuta@redhat.com>
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1299021
Link: http://lkml.kernel.org/r/1470214725-5023-2-git-send-email-ravi.bangoria@linux.vnet.ibm.com
[ Rename 'die' to 'cu_die' to avoid shadowing a die() definition on at least centos 5, Debian 7 and ubuntu:12.04.5]
[ Use PRIx64 instead of lx to format a Dwarf_Addr, aka long long unsigned int, fixing the build on 32-bit systems ]
[ dwarf_getsrclines() expects a size_t * argument ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-08-03 15:58:45 +07:00
|
|
|
return;
|
|
|
|
|
|
|
|
/* Don't know entrypc? */
|
|
|
|
if (!pf->addr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Only FUNC and FUNC@SRC are eligible. */
|
|
|
|
if (!pp->function || pp->line || pp->retprobe || pp->lazy_line ||
|
|
|
|
pp->offset || pp->abs_address)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Not interested in func parameter? */
|
|
|
|
if (!perf_probe_with_var(pf->pev))
|
|
|
|
return;
|
|
|
|
|
|
|
|
pr_info("Target program is compiled without optimization. Skipping prologue.\n"
|
|
|
|
"Probe on address 0x%" PRIx64 " to force probing at the function entry.\n\n",
|
|
|
|
pf->addr);
|
|
|
|
|
2016-08-30 15:39:37 +07:00
|
|
|
die_skip_prologue(sp_die, &pf->cu_die, &pf->addr);
|
perf uprobe: Skip prologue if program compiled without optimization
The function prologue prepares stack and registers before executing
function logic.
When target program is compiled without optimization, function parameter
information is only valid after the prologue.
When we probe entrypc of the function, and try to record a function
parameter, it contains a garbage value.
For example:
$ vim test.c
#include <stdio.h>
void foo(int i)
{
printf("i: %d\n", i);
}
int main()
{
foo(42);
return 0;
}
$ gcc -g test.c -o test
$ objdump -dl test | less
foo():
/home/ravi/test.c:4
400536: 55 push %rbp
400537: 48 89 e5 mov %rsp,%rbp
40053a: 48 83 ec 10 sub -bashx10,%rsp
40053e: 89 7d fc mov %edi,-0x4(%rbp)
/home/ravi/test.c:5
400541: 8b 45 fc mov -0x4(%rbp),%eax
...
...
main():
/home/ravi/test.c:9
400558: 55 push %rbp
400559: 48 89 e5 mov %rsp,%rbp
/home/ravi/test.c:10
40055c: bf 2a 00 00 00 mov -bashx2a,%edi
400561: e8 d0 ff ff ff callq 400536 <foo>
$ perf probe -x ./test 'foo i'
$ cat /sys/kernel/debug/tracing/uprobe_events
p:probe_test/foo /home/ravi/test:0x0000000000000536 i=-12(%sp):s32
$ perf record -e probe_test:foo ./test
$ perf script
test 5778 [001] 4918.562027: probe_test:foo: (400536) i=0
Here variable 'i' is passed via stack which is pushed on stack at
0x40053e. But we are probing at 0x400536.
To resolve this issues, we need to probe on next instruction after
prologue. gdb and systemtap also does same thing. I've implemented this
patch based on approach systemtap has used.
After applying patch:
$ perf probe -x ./test 'foo i'
$ cat /sys/kernel/debug/tracing/uprobe_events
p:probe_test/foo /home/ravi/test:0x0000000000000541 i=-4(%bp):s32
$ perf record -e probe_test:foo ./test
$ perf script
test 6300 [001] 5877.879327: probe_test:foo: (400541) i=42
No need to skip prologue for optimized case since debug info is correct
for each instructions for -O2 -g. For more details please visit:
https://bugzilla.redhat.com/show_bug.cgi?id=612253#c6
Changes in v2:
- Skipping prologue only when any ARG is either C variable, $params or
$vars.
- Probe on line(:1) may not be always possible. Recommend only address
to force probe on function entry.
Committer notes:
Testing it with 'perf trace':
# perf probe -x ./test foo i
Added new event:
probe_test:foo (on foo in /home/acme/c/test with i)
You can now use it in all perf tools, such as:
perf record -e probe_test:foo -aR sleep 1
# cat /sys/kernel/debug/tracing/uprobe_events
p:probe_test/foo /home/acme/c/test:0x0000000000000526 i=-12(%sp):s32
# trace --no-sys --event probe_*:* ./test
i: 42
0.000 probe_test:foo:(400526) i=0)
#
After the patch:
# perf probe -d *:*
Removed event: probe_test:foo
# perf probe -x ./test foo i
Target program is compiled without optimization. Skipping prologue.
Probe on address 0x400526 to force probing at the function entry.
Added new event:
probe_test:foo (on foo in /home/acme/c/test with i)
You can now use it in all perf tools, such as:
perf record -e probe_test:foo -aR sleep 1
# cat /sys/kernel/debug/tracing/uprobe_events
p:probe_test/foo /home/acme/c/test:0x0000000000000531 i=-4(%bp):s32
# trace --no-sys --event probe_*:* ./test
i: 42
0.000 probe_test:foo:(400531) i=42)
#
Reported-by: Michael Petlan <mpetlan@redhat.com>
Report-Link: https://www.mail-archive.com/linux-perf-users@vger.kernel.org/msg02348.html
Signed-off-by: Ravi Bangoria <ravi.bangoria@linux.vnet.ibm.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Tested-by: Jiri Olsa <jolsa@kernel.org>
Acked-by: Masami Hiramatsu <mhiramat@kernel.org>
Acked-by: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Hemant Kumar <hemant@linux.vnet.ibm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Wang Nan <wangnan0@huawei.com>
Cc: Yauheni Kaliuta <yauheni.kaliuta@redhat.com>
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1299021
Link: http://lkml.kernel.org/r/1470214725-5023-2-git-send-email-ravi.bangoria@linux.vnet.ibm.com
[ Rename 'die' to 'cu_die' to avoid shadowing a die() definition on at least centos 5, Debian 7 and ubuntu:12.04.5]
[ Use PRIx64 instead of lx to format a Dwarf_Addr, aka long long unsigned int, fixing the build on 32-bit systems ]
[ dwarf_getsrclines() expects a size_t * argument ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-08-03 15:58:45 +07:00
|
|
|
}
|
|
|
|
|
2010-02-25 20:35:50 +07:00
|
|
|
static int probe_point_inline_cb(Dwarf_Die *in_die, void *data)
|
|
|
|
{
|
2011-08-11 18:03:11 +07:00
|
|
|
struct probe_finder *pf = data;
|
2010-03-17 05:06:12 +07:00
|
|
|
struct perf_probe_point *pp = &pf->pev->point;
|
2010-04-13 00:17:35 +07:00
|
|
|
Dwarf_Addr addr;
|
2011-08-11 18:03:11 +07:00
|
|
|
int ret;
|
2010-02-25 20:35:50 +07:00
|
|
|
|
2010-02-25 20:36:12 +07:00
|
|
|
if (pp->lazy_line)
|
2011-08-11 18:03:11 +07:00
|
|
|
ret = find_probe_point_lazy(in_die, pf);
|
2010-02-25 20:36:12 +07:00
|
|
|
else {
|
|
|
|
/* Get probe address */
|
2010-04-13 00:17:35 +07:00
|
|
|
if (dwarf_entrypc(in_die, &addr) != 0) {
|
2010-12-17 20:12:11 +07:00
|
|
|
pr_warning("Failed to get entry address of %s.\n",
|
2010-04-13 00:17:35 +07:00
|
|
|
dwarf_diename(in_die));
|
2011-08-11 18:03:11 +07:00
|
|
|
return -ENOENT;
|
2010-04-13 00:17:35 +07:00
|
|
|
}
|
2016-09-23 22:35:07 +07:00
|
|
|
if (addr == 0) {
|
|
|
|
pr_debug("%s has no valid entry address. skipped.\n",
|
|
|
|
dwarf_diename(in_die));
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
2010-04-13 00:17:35 +07:00
|
|
|
pf->addr = addr;
|
2010-02-25 20:36:12 +07:00
|
|
|
pf->addr += pp->offset;
|
|
|
|
pr_debug("found inline addr: 0x%jx\n",
|
|
|
|
(uintmax_t)pf->addr);
|
|
|
|
|
2011-08-11 18:03:11 +07:00
|
|
|
ret = call_probe_finder(in_die, pf);
|
2010-02-25 20:36:12 +07:00
|
|
|
}
|
2010-02-25 20:35:50 +07:00
|
|
|
|
2011-08-11 18:03:11 +07:00
|
|
|
return ret;
|
2010-02-25 20:35:50 +07:00
|
|
|
}
|
2010-02-25 20:35:42 +07:00
|
|
|
|
2011-08-11 18:03:11 +07:00
|
|
|
/* Callback parameter with return value for libdw */
|
|
|
|
struct dwarf_callback_param {
|
|
|
|
void *data;
|
|
|
|
int retval;
|
|
|
|
};
|
|
|
|
|
2009-10-09 04:17:38 +07:00
|
|
|
/* Search function from function name */
|
2010-02-25 20:35:50 +07:00
|
|
|
static int probe_point_search_cb(Dwarf_Die *sp_die, void *data)
|
2009-10-09 04:17:38 +07:00
|
|
|
{
|
2010-04-13 00:17:35 +07:00
|
|
|
struct dwarf_callback_param *param = data;
|
|
|
|
struct probe_finder *pf = param->data;
|
2010-03-17 05:06:12 +07:00
|
|
|
struct perf_probe_point *pp = &pf->pev->point;
|
2009-10-09 04:17:38 +07:00
|
|
|
|
2010-02-25 20:35:50 +07:00
|
|
|
/* Check tag and diename */
|
2012-04-23 10:24:36 +07:00
|
|
|
if (!die_is_func_def(sp_die) ||
|
2015-05-08 08:03:35 +07:00
|
|
|
!die_match_name(sp_die, pp->function))
|
2010-04-13 00:17:35 +07:00
|
|
|
return DWARF_CB_OK;
|
2010-02-25 20:35:50 +07:00
|
|
|
|
2011-03-30 16:25:41 +07:00
|
|
|
/* Check declared file */
|
|
|
|
if (pp->file && strtailcmp(pp->file, dwarf_decl_file(sp_die)))
|
|
|
|
return DWARF_CB_OK;
|
|
|
|
|
2016-09-23 22:34:57 +07:00
|
|
|
pr_debug("Matched function: %s [%lx]\n", dwarf_diename(sp_die),
|
|
|
|
(unsigned long)dwarf_dieoffset(sp_die));
|
2010-02-25 20:36:12 +07:00
|
|
|
pf->fname = dwarf_decl_file(sp_die);
|
2010-02-25 20:35:50 +07:00
|
|
|
if (pp->line) { /* Function relative line */
|
|
|
|
dwarf_decl_line(sp_die, &pf->lno);
|
|
|
|
pf->lno += pp->line;
|
2010-04-13 00:17:35 +07:00
|
|
|
param->retval = find_probe_point_by_line(pf);
|
2015-01-30 16:37:44 +07:00
|
|
|
} else if (die_is_func_instance(sp_die)) {
|
|
|
|
/* Instances always have the entry address */
|
|
|
|
dwarf_entrypc(sp_die, &pf->addr);
|
2016-09-23 22:35:07 +07:00
|
|
|
/* But in some case the entry address is 0 */
|
|
|
|
if (pf->addr == 0) {
|
|
|
|
pr_debug("%s has no entry PC. Skipped\n",
|
|
|
|
dwarf_diename(sp_die));
|
|
|
|
param->retval = 0;
|
2010-02-25 20:35:50 +07:00
|
|
|
/* Real function */
|
2016-09-23 22:35:07 +07:00
|
|
|
} else if (pp->lazy_line)
|
2010-04-13 00:17:35 +07:00
|
|
|
param->retval = find_probe_point_lazy(sp_die, pf);
|
2010-02-25 20:36:12 +07:00
|
|
|
else {
|
perf uprobe: Skip prologue if program compiled without optimization
The function prologue prepares stack and registers before executing
function logic.
When target program is compiled without optimization, function parameter
information is only valid after the prologue.
When we probe entrypc of the function, and try to record a function
parameter, it contains a garbage value.
For example:
$ vim test.c
#include <stdio.h>
void foo(int i)
{
printf("i: %d\n", i);
}
int main()
{
foo(42);
return 0;
}
$ gcc -g test.c -o test
$ objdump -dl test | less
foo():
/home/ravi/test.c:4
400536: 55 push %rbp
400537: 48 89 e5 mov %rsp,%rbp
40053a: 48 83 ec 10 sub -bashx10,%rsp
40053e: 89 7d fc mov %edi,-0x4(%rbp)
/home/ravi/test.c:5
400541: 8b 45 fc mov -0x4(%rbp),%eax
...
...
main():
/home/ravi/test.c:9
400558: 55 push %rbp
400559: 48 89 e5 mov %rsp,%rbp
/home/ravi/test.c:10
40055c: bf 2a 00 00 00 mov -bashx2a,%edi
400561: e8 d0 ff ff ff callq 400536 <foo>
$ perf probe -x ./test 'foo i'
$ cat /sys/kernel/debug/tracing/uprobe_events
p:probe_test/foo /home/ravi/test:0x0000000000000536 i=-12(%sp):s32
$ perf record -e probe_test:foo ./test
$ perf script
test 5778 [001] 4918.562027: probe_test:foo: (400536) i=0
Here variable 'i' is passed via stack which is pushed on stack at
0x40053e. But we are probing at 0x400536.
To resolve this issues, we need to probe on next instruction after
prologue. gdb and systemtap also does same thing. I've implemented this
patch based on approach systemtap has used.
After applying patch:
$ perf probe -x ./test 'foo i'
$ cat /sys/kernel/debug/tracing/uprobe_events
p:probe_test/foo /home/ravi/test:0x0000000000000541 i=-4(%bp):s32
$ perf record -e probe_test:foo ./test
$ perf script
test 6300 [001] 5877.879327: probe_test:foo: (400541) i=42
No need to skip prologue for optimized case since debug info is correct
for each instructions for -O2 -g. For more details please visit:
https://bugzilla.redhat.com/show_bug.cgi?id=612253#c6
Changes in v2:
- Skipping prologue only when any ARG is either C variable, $params or
$vars.
- Probe on line(:1) may not be always possible. Recommend only address
to force probe on function entry.
Committer notes:
Testing it with 'perf trace':
# perf probe -x ./test foo i
Added new event:
probe_test:foo (on foo in /home/acme/c/test with i)
You can now use it in all perf tools, such as:
perf record -e probe_test:foo -aR sleep 1
# cat /sys/kernel/debug/tracing/uprobe_events
p:probe_test/foo /home/acme/c/test:0x0000000000000526 i=-12(%sp):s32
# trace --no-sys --event probe_*:* ./test
i: 42
0.000 probe_test:foo:(400526) i=0)
#
After the patch:
# perf probe -d *:*
Removed event: probe_test:foo
# perf probe -x ./test foo i
Target program is compiled without optimization. Skipping prologue.
Probe on address 0x400526 to force probing at the function entry.
Added new event:
probe_test:foo (on foo in /home/acme/c/test with i)
You can now use it in all perf tools, such as:
perf record -e probe_test:foo -aR sleep 1
# cat /sys/kernel/debug/tracing/uprobe_events
p:probe_test/foo /home/acme/c/test:0x0000000000000531 i=-4(%bp):s32
# trace --no-sys --event probe_*:* ./test
i: 42
0.000 probe_test:foo:(400531) i=42)
#
Reported-by: Michael Petlan <mpetlan@redhat.com>
Report-Link: https://www.mail-archive.com/linux-perf-users@vger.kernel.org/msg02348.html
Signed-off-by: Ravi Bangoria <ravi.bangoria@linux.vnet.ibm.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Tested-by: Jiri Olsa <jolsa@kernel.org>
Acked-by: Masami Hiramatsu <mhiramat@kernel.org>
Acked-by: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Hemant Kumar <hemant@linux.vnet.ibm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Wang Nan <wangnan0@huawei.com>
Cc: Yauheni Kaliuta <yauheni.kaliuta@redhat.com>
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1299021
Link: http://lkml.kernel.org/r/1470214725-5023-2-git-send-email-ravi.bangoria@linux.vnet.ibm.com
[ Rename 'die' to 'cu_die' to avoid shadowing a die() definition on at least centos 5, Debian 7 and ubuntu:12.04.5]
[ Use PRIx64 instead of lx to format a Dwarf_Addr, aka long long unsigned int, fixing the build on 32-bit systems ]
[ dwarf_getsrclines() expects a size_t * argument ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-08-03 15:58:45 +07:00
|
|
|
skip_prologue(sp_die, pf);
|
2010-02-25 20:36:12 +07:00
|
|
|
pf->addr += pp->offset;
|
|
|
|
/* TODO: Check the address in this function */
|
2010-10-21 17:13:23 +07:00
|
|
|
param->retval = call_probe_finder(sp_die, pf);
|
2010-02-25 20:36:12 +07:00
|
|
|
}
|
2015-05-08 08:03:35 +07:00
|
|
|
} else if (!probe_conf.no_inlines) {
|
2010-02-25 20:35:50 +07:00
|
|
|
/* Inlined function: search instances */
|
2011-08-11 18:03:11 +07:00
|
|
|
param->retval = die_walk_instances(sp_die,
|
|
|
|
probe_point_inline_cb, (void *)pf);
|
2015-05-08 08:03:35 +07:00
|
|
|
/* This could be a non-existed inline definition */
|
2016-09-23 22:34:57 +07:00
|
|
|
if (param->retval == -ENOENT)
|
2015-05-08 08:03:35 +07:00
|
|
|
param->retval = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We need to find other candidates */
|
|
|
|
if (strisglob(pp->function) && param->retval >= 0) {
|
|
|
|
param->retval = 0; /* We have to clear the result */
|
|
|
|
return DWARF_CB_OK;
|
|
|
|
}
|
2010-02-25 20:35:50 +07:00
|
|
|
|
2010-04-13 00:17:35 +07:00
|
|
|
return DWARF_CB_ABORT; /* Exit; no same symbol in this CU. */
|
2009-10-09 04:17:38 +07:00
|
|
|
}
|
|
|
|
|
2010-04-13 00:17:35 +07:00
|
|
|
static int find_probe_point_by_func(struct probe_finder *pf)
|
2009-10-09 04:17:38 +07:00
|
|
|
{
|
2010-04-13 00:17:35 +07:00
|
|
|
struct dwarf_callback_param _param = {.data = (void *)pf,
|
|
|
|
.retval = 0};
|
|
|
|
dwarf_getfuncs(&pf->cu_die, probe_point_search_cb, &_param, 0);
|
|
|
|
return _param.retval;
|
2009-10-09 04:17:38 +07:00
|
|
|
}
|
|
|
|
|
perf probe: Add fastpath to do lookup by function name
v3 -> v2:
- Make pubname_search_cb more generic
- Add fastpath to find_probes also
v2 -> v1:
- Don't compare file names with cu_find_realpath(...), instead, compare
them with the name returned by dwarf_decl_file(sp_die)
The vmlinux file may have thousands of CUs.
We can lookup function name from .debug_pubnames section
to avoid the slow loop on CUs.
1. Improvement data for find_line_range
./perf stat -e cycles -r 10 -- ./perf probe -k /home/mlin/vmlinux \
-s /home/mlin/linux-2.6 \
--line csum_partial_copy_to_user > tmp.log
before patch applied
=====================
847,988,276 cycles
0.355075856 seconds time elapsed
after patch applied
=====================
206,102,622 cycles
0.086883555 seconds time elapsed
2. Improvement data for find_probes
./perf stat -e cycles -r 10 -- ./perf probe -k /home/mlin/vmlinux \
-s /home/mlin/linux-2.6 \
--vars csum_partial_copy_to_user > tmp.log
before patch applied
=====================
848,490,844 cycles
0.355307901 seconds time elapsed
after patch applied
=====================
205,684,469 cycles
0.086694010 seconds time elapsed
Acked-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: linux-kernel <linux-kernel@vger.kernel.org>
LKML-Reference: <1301041668.14111.52.camel@minggr.sh.intel.com>
Signed-off-by: Lin Ming <ming.m.lin@intel.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-03-25 15:27:48 +07:00
|
|
|
struct pubname_callback_param {
|
|
|
|
char *function;
|
|
|
|
char *file;
|
|
|
|
Dwarf_Die *cu_die;
|
|
|
|
Dwarf_Die *sp_die;
|
|
|
|
int found;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int pubname_search_cb(Dwarf *dbg, Dwarf_Global *gl, void *data)
|
|
|
|
{
|
|
|
|
struct pubname_callback_param *param = data;
|
|
|
|
|
|
|
|
if (dwarf_offdie(dbg, gl->die_offset, param->sp_die)) {
|
|
|
|
if (dwarf_tag(param->sp_die) != DW_TAG_subprogram)
|
|
|
|
return DWARF_CB_OK;
|
|
|
|
|
2015-05-08 08:03:35 +07:00
|
|
|
if (die_match_name(param->sp_die, param->function)) {
|
perf probe: Add fastpath to do lookup by function name
v3 -> v2:
- Make pubname_search_cb more generic
- Add fastpath to find_probes also
v2 -> v1:
- Don't compare file names with cu_find_realpath(...), instead, compare
them with the name returned by dwarf_decl_file(sp_die)
The vmlinux file may have thousands of CUs.
We can lookup function name from .debug_pubnames section
to avoid the slow loop on CUs.
1. Improvement data for find_line_range
./perf stat -e cycles -r 10 -- ./perf probe -k /home/mlin/vmlinux \
-s /home/mlin/linux-2.6 \
--line csum_partial_copy_to_user > tmp.log
before patch applied
=====================
847,988,276 cycles
0.355075856 seconds time elapsed
after patch applied
=====================
206,102,622 cycles
0.086883555 seconds time elapsed
2. Improvement data for find_probes
./perf stat -e cycles -r 10 -- ./perf probe -k /home/mlin/vmlinux \
-s /home/mlin/linux-2.6 \
--vars csum_partial_copy_to_user > tmp.log
before patch applied
=====================
848,490,844 cycles
0.355307901 seconds time elapsed
after patch applied
=====================
205,684,469 cycles
0.086694010 seconds time elapsed
Acked-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: linux-kernel <linux-kernel@vger.kernel.org>
LKML-Reference: <1301041668.14111.52.camel@minggr.sh.intel.com>
Signed-off-by: Lin Ming <ming.m.lin@intel.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-03-25 15:27:48 +07:00
|
|
|
if (!dwarf_offdie(dbg, gl->cu_offset, param->cu_die))
|
|
|
|
return DWARF_CB_OK;
|
|
|
|
|
|
|
|
if (param->file &&
|
|
|
|
strtailcmp(param->file, dwarf_decl_file(param->sp_die)))
|
|
|
|
return DWARF_CB_OK;
|
|
|
|
|
|
|
|
param->found = 1;
|
|
|
|
return DWARF_CB_ABORT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return DWARF_CB_OK;
|
|
|
|
}
|
|
|
|
|
perf probe: Search both .eh_frame and .debug_frame sections for probe location
'perf probe' through debuginfo__find_probes() in util/probe-finder.c
checks for the functions' frame descriptions in either .eh_frame section
of an ELF or the .debug_frame.
The check is based on whether either one of these sections is present.
Depending on distro, toolchain defaults, architetcutre, build flags,
etc., CFI might be found in either .eh_frame and/or .debug_frame.
Sometimes, it may happen that, .eh_frame, even if present, may not be
complete and may miss some descriptions.
Therefore, to be sure, to find the CFI covering an address we will
always have to investigate both if available.
For e.g., in powerpc, this may happen:
$ gcc -g bin.c -o bin
$ objdump --dwarf ./bin
<1><145>: Abbrev Number: 7 (DW_TAG_subprogram)
<146> DW_AT_external : 1
<146> DW_AT_name : (indirect string, offset: 0x9e): main
<14a> DW_AT_decl_file : 1
<14b> DW_AT_decl_line : 39
<14c> DW_AT_prototyped : 1
<14c> DW_AT_type : <0x57>
<150> DW_AT_low_pc : 0x100007b8
If the .eh_frame and .debug_frame are checked for the same binary, we
will find that, .eh_frame (although present) doesn't contain a
description for "main" function.
But, .debug_frame has a description:
000000d8 00000024 00000000 FDE cie=00000000 pc=100007b8..10000838
DW_CFA_advance_loc: 16 to 100007c8
DW_CFA_def_cfa_offset: 144
DW_CFA_offset_extended_sf: r65 at cfa+16
...
Due to this (since, perf checks whether .eh_frame is present and goes on
searching for that address inside that frame), perf is unable to process
the probes:
# perf probe -x ./bin main
Failed to get call frame on 0x100007b8
Error: Failed to add events.
To avoid this issue, we need to check both the sections (.eh_frame and
.debug_frame), which is done in this patch.
Note that, we can always force everything into both .eh_frame and
.debug_frame by:
$ gcc bin.c -fasynchronous-unwind-tables -fno-dwarf2-cfi-asm -g -o bin
Signed-off-by: Hemant Kumar <hemant@linux.vnet.ibm.com>
Acked-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: linuxppc-dev@lists.ozlabs.org
Cc: Mark Wielaard <mjw@redhat.com>
Cc: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Link: http://lkml.kernel.org/r/1454426806-13974-1-git-send-email-hemant@linux.vnet.ibm.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-02-02 22:26:46 +07:00
|
|
|
static int debuginfo__find_probe_location(struct debuginfo *dbg,
|
2011-06-27 14:27:39 +07:00
|
|
|
struct probe_finder *pf)
|
2009-10-09 04:17:38 +07:00
|
|
|
{
|
2010-10-21 17:13:23 +07:00
|
|
|
struct perf_probe_point *pp = &pf->pev->point;
|
2010-02-25 20:35:42 +07:00
|
|
|
Dwarf_Off off, noff;
|
|
|
|
size_t cuhl;
|
|
|
|
Dwarf_Die *diep;
|
2010-04-13 00:17:35 +07:00
|
|
|
int ret = 0;
|
2010-02-25 20:35:42 +07:00
|
|
|
|
|
|
|
off = 0;
|
2014-02-06 12:32:09 +07:00
|
|
|
pf->lcache = intlist__new(NULL);
|
|
|
|
if (!pf->lcache)
|
|
|
|
return -ENOMEM;
|
perf probe: Add fastpath to do lookup by function name
v3 -> v2:
- Make pubname_search_cb more generic
- Add fastpath to find_probes also
v2 -> v1:
- Don't compare file names with cu_find_realpath(...), instead, compare
them with the name returned by dwarf_decl_file(sp_die)
The vmlinux file may have thousands of CUs.
We can lookup function name from .debug_pubnames section
to avoid the slow loop on CUs.
1. Improvement data for find_line_range
./perf stat -e cycles -r 10 -- ./perf probe -k /home/mlin/vmlinux \
-s /home/mlin/linux-2.6 \
--line csum_partial_copy_to_user > tmp.log
before patch applied
=====================
847,988,276 cycles
0.355075856 seconds time elapsed
after patch applied
=====================
206,102,622 cycles
0.086883555 seconds time elapsed
2. Improvement data for find_probes
./perf stat -e cycles -r 10 -- ./perf probe -k /home/mlin/vmlinux \
-s /home/mlin/linux-2.6 \
--vars csum_partial_copy_to_user > tmp.log
before patch applied
=====================
848,490,844 cycles
0.355307901 seconds time elapsed
after patch applied
=====================
205,684,469 cycles
0.086694010 seconds time elapsed
Acked-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: linux-kernel <linux-kernel@vger.kernel.org>
LKML-Reference: <1301041668.14111.52.camel@minggr.sh.intel.com>
Signed-off-by: Lin Ming <ming.m.lin@intel.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-03-25 15:27:48 +07:00
|
|
|
|
|
|
|
/* Fastpath: lookup by function name from .debug_pubnames section */
|
2015-05-08 08:03:35 +07:00
|
|
|
if (pp->function && !strisglob(pp->function)) {
|
perf probe: Add fastpath to do lookup by function name
v3 -> v2:
- Make pubname_search_cb more generic
- Add fastpath to find_probes also
v2 -> v1:
- Don't compare file names with cu_find_realpath(...), instead, compare
them with the name returned by dwarf_decl_file(sp_die)
The vmlinux file may have thousands of CUs.
We can lookup function name from .debug_pubnames section
to avoid the slow loop on CUs.
1. Improvement data for find_line_range
./perf stat -e cycles -r 10 -- ./perf probe -k /home/mlin/vmlinux \
-s /home/mlin/linux-2.6 \
--line csum_partial_copy_to_user > tmp.log
before patch applied
=====================
847,988,276 cycles
0.355075856 seconds time elapsed
after patch applied
=====================
206,102,622 cycles
0.086883555 seconds time elapsed
2. Improvement data for find_probes
./perf stat -e cycles -r 10 -- ./perf probe -k /home/mlin/vmlinux \
-s /home/mlin/linux-2.6 \
--vars csum_partial_copy_to_user > tmp.log
before patch applied
=====================
848,490,844 cycles
0.355307901 seconds time elapsed
after patch applied
=====================
205,684,469 cycles
0.086694010 seconds time elapsed
Acked-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: linux-kernel <linux-kernel@vger.kernel.org>
LKML-Reference: <1301041668.14111.52.camel@minggr.sh.intel.com>
Signed-off-by: Lin Ming <ming.m.lin@intel.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-03-25 15:27:48 +07:00
|
|
|
struct pubname_callback_param pubname_param = {
|
|
|
|
.function = pp->function,
|
|
|
|
.file = pp->file,
|
|
|
|
.cu_die = &pf->cu_die,
|
|
|
|
.sp_die = &pf->sp_die,
|
2011-04-29 15:41:57 +07:00
|
|
|
.found = 0,
|
perf probe: Add fastpath to do lookup by function name
v3 -> v2:
- Make pubname_search_cb more generic
- Add fastpath to find_probes also
v2 -> v1:
- Don't compare file names with cu_find_realpath(...), instead, compare
them with the name returned by dwarf_decl_file(sp_die)
The vmlinux file may have thousands of CUs.
We can lookup function name from .debug_pubnames section
to avoid the slow loop on CUs.
1. Improvement data for find_line_range
./perf stat -e cycles -r 10 -- ./perf probe -k /home/mlin/vmlinux \
-s /home/mlin/linux-2.6 \
--line csum_partial_copy_to_user > tmp.log
before patch applied
=====================
847,988,276 cycles
0.355075856 seconds time elapsed
after patch applied
=====================
206,102,622 cycles
0.086883555 seconds time elapsed
2. Improvement data for find_probes
./perf stat -e cycles -r 10 -- ./perf probe -k /home/mlin/vmlinux \
-s /home/mlin/linux-2.6 \
--vars csum_partial_copy_to_user > tmp.log
before patch applied
=====================
848,490,844 cycles
0.355307901 seconds time elapsed
after patch applied
=====================
205,684,469 cycles
0.086694010 seconds time elapsed
Acked-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: linux-kernel <linux-kernel@vger.kernel.org>
LKML-Reference: <1301041668.14111.52.camel@minggr.sh.intel.com>
Signed-off-by: Lin Ming <ming.m.lin@intel.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-03-25 15:27:48 +07:00
|
|
|
};
|
|
|
|
struct dwarf_callback_param probe_param = {
|
|
|
|
.data = pf,
|
|
|
|
};
|
|
|
|
|
2013-11-06 01:32:36 +07:00
|
|
|
dwarf_getpubnames(dbg->dbg, pubname_search_cb,
|
2011-06-27 14:27:39 +07:00
|
|
|
&pubname_param, 0);
|
perf probe: Add fastpath to do lookup by function name
v3 -> v2:
- Make pubname_search_cb more generic
- Add fastpath to find_probes also
v2 -> v1:
- Don't compare file names with cu_find_realpath(...), instead, compare
them with the name returned by dwarf_decl_file(sp_die)
The vmlinux file may have thousands of CUs.
We can lookup function name from .debug_pubnames section
to avoid the slow loop on CUs.
1. Improvement data for find_line_range
./perf stat -e cycles -r 10 -- ./perf probe -k /home/mlin/vmlinux \
-s /home/mlin/linux-2.6 \
--line csum_partial_copy_to_user > tmp.log
before patch applied
=====================
847,988,276 cycles
0.355075856 seconds time elapsed
after patch applied
=====================
206,102,622 cycles
0.086883555 seconds time elapsed
2. Improvement data for find_probes
./perf stat -e cycles -r 10 -- ./perf probe -k /home/mlin/vmlinux \
-s /home/mlin/linux-2.6 \
--vars csum_partial_copy_to_user > tmp.log
before patch applied
=====================
848,490,844 cycles
0.355307901 seconds time elapsed
after patch applied
=====================
205,684,469 cycles
0.086694010 seconds time elapsed
Acked-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: linux-kernel <linux-kernel@vger.kernel.org>
LKML-Reference: <1301041668.14111.52.camel@minggr.sh.intel.com>
Signed-off-by: Lin Ming <ming.m.lin@intel.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-03-25 15:27:48 +07:00
|
|
|
if (pubname_param.found) {
|
|
|
|
ret = probe_point_search_cb(&pf->sp_die, &probe_param);
|
|
|
|
if (ret)
|
|
|
|
goto found;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-02-25 20:35:42 +07:00
|
|
|
/* Loop on CUs (Compilation Unit) */
|
2013-11-06 01:32:36 +07:00
|
|
|
while (!dwarf_nextcu(dbg->dbg, off, &noff, &cuhl, NULL, NULL, NULL)) {
|
2009-10-09 04:17:38 +07:00
|
|
|
/* Get the DIE(Debugging Information Entry) of this CU */
|
2013-11-06 01:32:36 +07:00
|
|
|
diep = dwarf_offdie(dbg->dbg, off + cuhl, &pf->cu_die);
|
2010-02-25 20:35:42 +07:00
|
|
|
if (!diep)
|
|
|
|
continue;
|
2009-10-09 04:17:38 +07:00
|
|
|
|
|
|
|
/* Check if target file is included. */
|
|
|
|
if (pp->file)
|
2010-10-21 17:13:23 +07:00
|
|
|
pf->fname = cu_find_realpath(&pf->cu_die, pp->file);
|
2010-02-25 20:35:42 +07:00
|
|
|
else
|
2010-10-21 17:13:23 +07:00
|
|
|
pf->fname = NULL;
|
2009-10-09 04:17:38 +07:00
|
|
|
|
2010-10-21 17:13:23 +07:00
|
|
|
if (!pp->file || pf->fname) {
|
2009-10-09 04:17:38 +07:00
|
|
|
if (pp->function)
|
2010-10-21 17:13:23 +07:00
|
|
|
ret = find_probe_point_by_func(pf);
|
2010-02-25 20:36:12 +07:00
|
|
|
else if (pp->lazy_line)
|
2015-04-13 18:41:30 +07:00
|
|
|
ret = find_probe_point_lazy(&pf->cu_die, pf);
|
2009-10-28 03:43:19 +07:00
|
|
|
else {
|
2010-10-21 17:13:23 +07:00
|
|
|
pf->lno = pp->line;
|
|
|
|
ret = find_probe_point_by_line(pf);
|
2009-10-28 03:43:19 +07:00
|
|
|
}
|
2011-02-22 16:56:18 +07:00
|
|
|
if (ret < 0)
|
2011-02-21 23:23:57 +07:00
|
|
|
break;
|
2009-10-09 04:17:38 +07:00
|
|
|
}
|
2010-02-25 20:35:42 +07:00
|
|
|
off = noff;
|
2009-10-09 04:17:38 +07:00
|
|
|
}
|
perf probe: Add fastpath to do lookup by function name
v3 -> v2:
- Make pubname_search_cb more generic
- Add fastpath to find_probes also
v2 -> v1:
- Don't compare file names with cu_find_realpath(...), instead, compare
them with the name returned by dwarf_decl_file(sp_die)
The vmlinux file may have thousands of CUs.
We can lookup function name from .debug_pubnames section
to avoid the slow loop on CUs.
1. Improvement data for find_line_range
./perf stat -e cycles -r 10 -- ./perf probe -k /home/mlin/vmlinux \
-s /home/mlin/linux-2.6 \
--line csum_partial_copy_to_user > tmp.log
before patch applied
=====================
847,988,276 cycles
0.355075856 seconds time elapsed
after patch applied
=====================
206,102,622 cycles
0.086883555 seconds time elapsed
2. Improvement data for find_probes
./perf stat -e cycles -r 10 -- ./perf probe -k /home/mlin/vmlinux \
-s /home/mlin/linux-2.6 \
--vars csum_partial_copy_to_user > tmp.log
before patch applied
=====================
848,490,844 cycles
0.355307901 seconds time elapsed
after patch applied
=====================
205,684,469 cycles
0.086694010 seconds time elapsed
Acked-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: linux-kernel <linux-kernel@vger.kernel.org>
LKML-Reference: <1301041668.14111.52.camel@minggr.sh.intel.com>
Signed-off-by: Lin Ming <ming.m.lin@intel.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-03-25 15:27:48 +07:00
|
|
|
|
|
|
|
found:
|
2014-02-06 12:32:09 +07:00
|
|
|
intlist__delete(pf->lcache);
|
|
|
|
pf->lcache = NULL;
|
2009-10-09 04:17:38 +07:00
|
|
|
|
2010-10-21 17:13:23 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
perf probe: Search both .eh_frame and .debug_frame sections for probe location
'perf probe' through debuginfo__find_probes() in util/probe-finder.c
checks for the functions' frame descriptions in either .eh_frame section
of an ELF or the .debug_frame.
The check is based on whether either one of these sections is present.
Depending on distro, toolchain defaults, architetcutre, build flags,
etc., CFI might be found in either .eh_frame and/or .debug_frame.
Sometimes, it may happen that, .eh_frame, even if present, may not be
complete and may miss some descriptions.
Therefore, to be sure, to find the CFI covering an address we will
always have to investigate both if available.
For e.g., in powerpc, this may happen:
$ gcc -g bin.c -o bin
$ objdump --dwarf ./bin
<1><145>: Abbrev Number: 7 (DW_TAG_subprogram)
<146> DW_AT_external : 1
<146> DW_AT_name : (indirect string, offset: 0x9e): main
<14a> DW_AT_decl_file : 1
<14b> DW_AT_decl_line : 39
<14c> DW_AT_prototyped : 1
<14c> DW_AT_type : <0x57>
<150> DW_AT_low_pc : 0x100007b8
If the .eh_frame and .debug_frame are checked for the same binary, we
will find that, .eh_frame (although present) doesn't contain a
description for "main" function.
But, .debug_frame has a description:
000000d8 00000024 00000000 FDE cie=00000000 pc=100007b8..10000838
DW_CFA_advance_loc: 16 to 100007c8
DW_CFA_def_cfa_offset: 144
DW_CFA_offset_extended_sf: r65 at cfa+16
...
Due to this (since, perf checks whether .eh_frame is present and goes on
searching for that address inside that frame), perf is unable to process
the probes:
# perf probe -x ./bin main
Failed to get call frame on 0x100007b8
Error: Failed to add events.
To avoid this issue, we need to check both the sections (.eh_frame and
.debug_frame), which is done in this patch.
Note that, we can always force everything into both .eh_frame and
.debug_frame by:
$ gcc bin.c -fasynchronous-unwind-tables -fno-dwarf2-cfi-asm -g -o bin
Signed-off-by: Hemant Kumar <hemant@linux.vnet.ibm.com>
Acked-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: linuxppc-dev@lists.ozlabs.org
Cc: Mark Wielaard <mjw@redhat.com>
Cc: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Link: http://lkml.kernel.org/r/1454426806-13974-1-git-send-email-hemant@linux.vnet.ibm.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-02-02 22:26:46 +07:00
|
|
|
/* Find probe points from debuginfo */
|
|
|
|
static int debuginfo__find_probes(struct debuginfo *dbg,
|
|
|
|
struct probe_finder *pf)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
Elf *elf;
|
|
|
|
GElf_Ehdr ehdr;
|
|
|
|
|
|
|
|
if (pf->cfi_eh || pf->cfi_dbg)
|
|
|
|
return debuginfo__find_probe_location(dbg, pf);
|
|
|
|
|
|
|
|
/* Get the call frame information from this dwarf */
|
|
|
|
elf = dwarf_getelf(dbg->dbg);
|
|
|
|
if (elf == NULL)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (gelf_getehdr(elf, &ehdr) == NULL)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2016-08-25 23:24:57 +07:00
|
|
|
pf->machine = ehdr.e_machine;
|
|
|
|
|
|
|
|
#if _ELFUTILS_PREREQ(0, 142)
|
|
|
|
do {
|
|
|
|
GElf_Shdr shdr;
|
|
|
|
|
|
|
|
if (elf_section_by_name(elf, &ehdr, &shdr, ".eh_frame", NULL) &&
|
|
|
|
shdr.sh_type == SHT_PROGBITS)
|
|
|
|
pf->cfi_eh = dwarf_getcfi_elf(elf);
|
perf probe: Search both .eh_frame and .debug_frame sections for probe location
'perf probe' through debuginfo__find_probes() in util/probe-finder.c
checks for the functions' frame descriptions in either .eh_frame section
of an ELF or the .debug_frame.
The check is based on whether either one of these sections is present.
Depending on distro, toolchain defaults, architetcutre, build flags,
etc., CFI might be found in either .eh_frame and/or .debug_frame.
Sometimes, it may happen that, .eh_frame, even if present, may not be
complete and may miss some descriptions.
Therefore, to be sure, to find the CFI covering an address we will
always have to investigate both if available.
For e.g., in powerpc, this may happen:
$ gcc -g bin.c -o bin
$ objdump --dwarf ./bin
<1><145>: Abbrev Number: 7 (DW_TAG_subprogram)
<146> DW_AT_external : 1
<146> DW_AT_name : (indirect string, offset: 0x9e): main
<14a> DW_AT_decl_file : 1
<14b> DW_AT_decl_line : 39
<14c> DW_AT_prototyped : 1
<14c> DW_AT_type : <0x57>
<150> DW_AT_low_pc : 0x100007b8
If the .eh_frame and .debug_frame are checked for the same binary, we
will find that, .eh_frame (although present) doesn't contain a
description for "main" function.
But, .debug_frame has a description:
000000d8 00000024 00000000 FDE cie=00000000 pc=100007b8..10000838
DW_CFA_advance_loc: 16 to 100007c8
DW_CFA_def_cfa_offset: 144
DW_CFA_offset_extended_sf: r65 at cfa+16
...
Due to this (since, perf checks whether .eh_frame is present and goes on
searching for that address inside that frame), perf is unable to process
the probes:
# perf probe -x ./bin main
Failed to get call frame on 0x100007b8
Error: Failed to add events.
To avoid this issue, we need to check both the sections (.eh_frame and
.debug_frame), which is done in this patch.
Note that, we can always force everything into both .eh_frame and
.debug_frame by:
$ gcc bin.c -fasynchronous-unwind-tables -fno-dwarf2-cfi-asm -g -o bin
Signed-off-by: Hemant Kumar <hemant@linux.vnet.ibm.com>
Acked-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: linuxppc-dev@lists.ozlabs.org
Cc: Mark Wielaard <mjw@redhat.com>
Cc: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Link: http://lkml.kernel.org/r/1454426806-13974-1-git-send-email-hemant@linux.vnet.ibm.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-02-02 22:26:46 +07:00
|
|
|
|
2016-08-25 23:24:57 +07:00
|
|
|
pf->cfi_dbg = dwarf_getcfi(dbg->dbg);
|
|
|
|
} while (0);
|
perf probe: Search both .eh_frame and .debug_frame sections for probe location
'perf probe' through debuginfo__find_probes() in util/probe-finder.c
checks for the functions' frame descriptions in either .eh_frame section
of an ELF or the .debug_frame.
The check is based on whether either one of these sections is present.
Depending on distro, toolchain defaults, architetcutre, build flags,
etc., CFI might be found in either .eh_frame and/or .debug_frame.
Sometimes, it may happen that, .eh_frame, even if present, may not be
complete and may miss some descriptions.
Therefore, to be sure, to find the CFI covering an address we will
always have to investigate both if available.
For e.g., in powerpc, this may happen:
$ gcc -g bin.c -o bin
$ objdump --dwarf ./bin
<1><145>: Abbrev Number: 7 (DW_TAG_subprogram)
<146> DW_AT_external : 1
<146> DW_AT_name : (indirect string, offset: 0x9e): main
<14a> DW_AT_decl_file : 1
<14b> DW_AT_decl_line : 39
<14c> DW_AT_prototyped : 1
<14c> DW_AT_type : <0x57>
<150> DW_AT_low_pc : 0x100007b8
If the .eh_frame and .debug_frame are checked for the same binary, we
will find that, .eh_frame (although present) doesn't contain a
description for "main" function.
But, .debug_frame has a description:
000000d8 00000024 00000000 FDE cie=00000000 pc=100007b8..10000838
DW_CFA_advance_loc: 16 to 100007c8
DW_CFA_def_cfa_offset: 144
DW_CFA_offset_extended_sf: r65 at cfa+16
...
Due to this (since, perf checks whether .eh_frame is present and goes on
searching for that address inside that frame), perf is unable to process
the probes:
# perf probe -x ./bin main
Failed to get call frame on 0x100007b8
Error: Failed to add events.
To avoid this issue, we need to check both the sections (.eh_frame and
.debug_frame), which is done in this patch.
Note that, we can always force everything into both .eh_frame and
.debug_frame by:
$ gcc bin.c -fasynchronous-unwind-tables -fno-dwarf2-cfi-asm -g -o bin
Signed-off-by: Hemant Kumar <hemant@linux.vnet.ibm.com>
Acked-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: linuxppc-dev@lists.ozlabs.org
Cc: Mark Wielaard <mjw@redhat.com>
Cc: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
Cc: Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Link: http://lkml.kernel.org/r/1454426806-13974-1-git-send-email-hemant@linux.vnet.ibm.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-02-02 22:26:46 +07:00
|
|
|
#endif
|
|
|
|
|
|
|
|
ret = debuginfo__find_probe_location(dbg, pf);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-10-11 14:10:23 +07:00
|
|
|
struct local_vars_finder {
|
|
|
|
struct probe_finder *pf;
|
|
|
|
struct perf_probe_arg *args;
|
perf probe: Support $params special probe argument
$params is similar to $vars but matches only function parameters not
local variables.
Thus, this is useful for tracing function parameter changing or tracing
function call with parameters.
Testing it:
# perf probe tcp_sendmsg '$params'
Added new event:
probe:tcp_sendmsg (on tcp_sendmsg with $params)
You can now use it in all perf tools, such as:
perf record -e probe:tcp_sendmsg -aR sleep 1
# perf probe -l
probe:tcp_sendmsg (on tcp_sendmsg@acme/git/linux/net/ipv4/tcp.c with iocb sk msg size)
# perf record -a -e probe:*
press some random letters to generate TCP (sshd) traffic...
^C[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.223 MB perf.data (6 samples) ]
# perf script
sshd 6385 [2] 3.907529: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
sshd 6385 [2] 4.138973: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
sshd 6385 [2] 4.378966: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
sshd 6385 [2] 4.603681: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
sshd 6385 [2] 4.818455: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
sshd 6385 [2] 5.043603: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
# cat /sys/kernel/debug/tracing/events/probe/tcp_sendmsg/format
name: tcp_sendmsg
ID: 1927
format:
field:unsigned short common_type; offset:0; size:2; signed:0;
field:unsigned char common_flags; offset:2; size:1; signed:0;
field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
field:int common_pid; offset:4; size:4; signed:1;
field:unsigned long __probe_ip; offset:8; size:8; signed:0;
field:u64 iocb; offset:16; size:8; signed:0;
field:u64 sk; offset:24; size:8; signed:0;
field:u64 msg; offset:32; size:8; signed:0;
field:u64 size; offset:40; size:8; signed:0;
print fmt: "(%lx) iocb=0x%Lx sk=0x%Lx msg=0x%Lx size=0x%Lx", REC->__probe_ip, REC->iocb, REC->sk, REC->msg, REC->size
#
Do some system wide tracing of this probe + write syscalls:
# perf trace -e write --ev probe:* --filter-pids 6385
462.612 (0.010 ms): bash/19153 write(fd: 1</dev/pts/1>, buf: 0x7f7556c78000, count: 29 ) = 29
462.701 (0.027 ms): sshd/19152 write(fd: 3<socket:[63117]>, buf: 0x7f78dd12e160, count: 68 ) ...
462.701 ( ): probe:tcp_sendmsg:(ffffffff8163db30) iocb=0xffff8803ebec7e70 sk=0xffff88042196ab80 msg=0xffff8803ebec7da8 size=0x44)
462.710 (0.035 ms): sshd/19152 ... [continued]: write()) = 68
462.787 (0.009 ms): bash/19153 write(fd: 2</dev/pts/1>, buf: 0x7f7556c77000, count: 22 ) = 22
462.865 (0.002 ms): sshd/19152 write(fd: 3<socket:[63117]>, buf: 0x7f78dd12e160, count: 68 ) ...
462.865 ( ): probe:tcp_sendmsg:(ffffffff8163db30) iocb=0xffff8803ebec7e70 sk=0xffff88042196ab80 msg=0xffff8803ebec7da8 size=0x44)
462.873 (0.010 ms): sshd/19152 ... [continued]: write()) = 68
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Hemant Kumar <hemant@linux.vnet.ibm.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/20150506124653.4961.59806.stgit@localhost.localdomain
[ Add some examples to the changelog message showing how to use it ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-05-06 19:46:53 +07:00
|
|
|
bool vars;
|
2013-10-11 14:10:23 +07:00
|
|
|
int max_args;
|
|
|
|
int nargs;
|
|
|
|
int ret;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Collect available variables in this scope */
|
|
|
|
static int copy_variables_cb(Dwarf_Die *die_mem, void *data)
|
|
|
|
{
|
|
|
|
struct local_vars_finder *vf = data;
|
2013-10-11 14:10:26 +07:00
|
|
|
struct probe_finder *pf = vf->pf;
|
2013-10-11 14:10:23 +07:00
|
|
|
int tag;
|
|
|
|
|
|
|
|
tag = dwarf_tag(die_mem);
|
|
|
|
if (tag == DW_TAG_formal_parameter ||
|
perf probe: Support $params special probe argument
$params is similar to $vars but matches only function parameters not
local variables.
Thus, this is useful for tracing function parameter changing or tracing
function call with parameters.
Testing it:
# perf probe tcp_sendmsg '$params'
Added new event:
probe:tcp_sendmsg (on tcp_sendmsg with $params)
You can now use it in all perf tools, such as:
perf record -e probe:tcp_sendmsg -aR sleep 1
# perf probe -l
probe:tcp_sendmsg (on tcp_sendmsg@acme/git/linux/net/ipv4/tcp.c with iocb sk msg size)
# perf record -a -e probe:*
press some random letters to generate TCP (sshd) traffic...
^C[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.223 MB perf.data (6 samples) ]
# perf script
sshd 6385 [2] 3.907529: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
sshd 6385 [2] 4.138973: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
sshd 6385 [2] 4.378966: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
sshd 6385 [2] 4.603681: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
sshd 6385 [2] 4.818455: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
sshd 6385 [2] 5.043603: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
# cat /sys/kernel/debug/tracing/events/probe/tcp_sendmsg/format
name: tcp_sendmsg
ID: 1927
format:
field:unsigned short common_type; offset:0; size:2; signed:0;
field:unsigned char common_flags; offset:2; size:1; signed:0;
field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
field:int common_pid; offset:4; size:4; signed:1;
field:unsigned long __probe_ip; offset:8; size:8; signed:0;
field:u64 iocb; offset:16; size:8; signed:0;
field:u64 sk; offset:24; size:8; signed:0;
field:u64 msg; offset:32; size:8; signed:0;
field:u64 size; offset:40; size:8; signed:0;
print fmt: "(%lx) iocb=0x%Lx sk=0x%Lx msg=0x%Lx size=0x%Lx", REC->__probe_ip, REC->iocb, REC->sk, REC->msg, REC->size
#
Do some system wide tracing of this probe + write syscalls:
# perf trace -e write --ev probe:* --filter-pids 6385
462.612 (0.010 ms): bash/19153 write(fd: 1</dev/pts/1>, buf: 0x7f7556c78000, count: 29 ) = 29
462.701 (0.027 ms): sshd/19152 write(fd: 3<socket:[63117]>, buf: 0x7f78dd12e160, count: 68 ) ...
462.701 ( ): probe:tcp_sendmsg:(ffffffff8163db30) iocb=0xffff8803ebec7e70 sk=0xffff88042196ab80 msg=0xffff8803ebec7da8 size=0x44)
462.710 (0.035 ms): sshd/19152 ... [continued]: write()) = 68
462.787 (0.009 ms): bash/19153 write(fd: 2</dev/pts/1>, buf: 0x7f7556c77000, count: 22 ) = 22
462.865 (0.002 ms): sshd/19152 write(fd: 3<socket:[63117]>, buf: 0x7f78dd12e160, count: 68 ) ...
462.865 ( ): probe:tcp_sendmsg:(ffffffff8163db30) iocb=0xffff8803ebec7e70 sk=0xffff88042196ab80 msg=0xffff8803ebec7da8 size=0x44)
462.873 (0.010 ms): sshd/19152 ... [continued]: write()) = 68
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Hemant Kumar <hemant@linux.vnet.ibm.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/20150506124653.4961.59806.stgit@localhost.localdomain
[ Add some examples to the changelog message showing how to use it ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-05-06 19:46:53 +07:00
|
|
|
(tag == DW_TAG_variable && vf->vars)) {
|
2013-10-11 14:10:23 +07:00
|
|
|
if (convert_variable_location(die_mem, vf->pf->addr,
|
2013-10-11 14:10:26 +07:00
|
|
|
vf->pf->fb_ops, &pf->sp_die,
|
2016-08-25 23:24:57 +07:00
|
|
|
pf->machine, NULL) == 0) {
|
2013-10-11 14:10:23 +07:00
|
|
|
vf->args[vf->nargs].var = (char *)dwarf_diename(die_mem);
|
|
|
|
if (vf->args[vf->nargs].var == NULL) {
|
|
|
|
vf->ret = -ENOMEM;
|
|
|
|
return DIE_FIND_CB_END;
|
|
|
|
}
|
|
|
|
pr_debug(" %s", vf->args[vf->nargs].var);
|
|
|
|
vf->nargs++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dwarf_haspc(die_mem, vf->pf->addr))
|
|
|
|
return DIE_FIND_CB_CONTINUE;
|
|
|
|
else
|
|
|
|
return DIE_FIND_CB_SIBLING;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int expand_probe_args(Dwarf_Die *sc_die, struct probe_finder *pf,
|
|
|
|
struct perf_probe_arg *args)
|
|
|
|
{
|
|
|
|
Dwarf_Die die_mem;
|
|
|
|
int i;
|
|
|
|
int n = 0;
|
perf probe: Support $params special probe argument
$params is similar to $vars but matches only function parameters not
local variables.
Thus, this is useful for tracing function parameter changing or tracing
function call with parameters.
Testing it:
# perf probe tcp_sendmsg '$params'
Added new event:
probe:tcp_sendmsg (on tcp_sendmsg with $params)
You can now use it in all perf tools, such as:
perf record -e probe:tcp_sendmsg -aR sleep 1
# perf probe -l
probe:tcp_sendmsg (on tcp_sendmsg@acme/git/linux/net/ipv4/tcp.c with iocb sk msg size)
# perf record -a -e probe:*
press some random letters to generate TCP (sshd) traffic...
^C[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.223 MB perf.data (6 samples) ]
# perf script
sshd 6385 [2] 3.907529: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
sshd 6385 [2] 4.138973: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
sshd 6385 [2] 4.378966: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
sshd 6385 [2] 4.603681: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
sshd 6385 [2] 4.818455: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
sshd 6385 [2] 5.043603: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
# cat /sys/kernel/debug/tracing/events/probe/tcp_sendmsg/format
name: tcp_sendmsg
ID: 1927
format:
field:unsigned short common_type; offset:0; size:2; signed:0;
field:unsigned char common_flags; offset:2; size:1; signed:0;
field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
field:int common_pid; offset:4; size:4; signed:1;
field:unsigned long __probe_ip; offset:8; size:8; signed:0;
field:u64 iocb; offset:16; size:8; signed:0;
field:u64 sk; offset:24; size:8; signed:0;
field:u64 msg; offset:32; size:8; signed:0;
field:u64 size; offset:40; size:8; signed:0;
print fmt: "(%lx) iocb=0x%Lx sk=0x%Lx msg=0x%Lx size=0x%Lx", REC->__probe_ip, REC->iocb, REC->sk, REC->msg, REC->size
#
Do some system wide tracing of this probe + write syscalls:
# perf trace -e write --ev probe:* --filter-pids 6385
462.612 (0.010 ms): bash/19153 write(fd: 1</dev/pts/1>, buf: 0x7f7556c78000, count: 29 ) = 29
462.701 (0.027 ms): sshd/19152 write(fd: 3<socket:[63117]>, buf: 0x7f78dd12e160, count: 68 ) ...
462.701 ( ): probe:tcp_sendmsg:(ffffffff8163db30) iocb=0xffff8803ebec7e70 sk=0xffff88042196ab80 msg=0xffff8803ebec7da8 size=0x44)
462.710 (0.035 ms): sshd/19152 ... [continued]: write()) = 68
462.787 (0.009 ms): bash/19153 write(fd: 2</dev/pts/1>, buf: 0x7f7556c77000, count: 22 ) = 22
462.865 (0.002 ms): sshd/19152 write(fd: 3<socket:[63117]>, buf: 0x7f78dd12e160, count: 68 ) ...
462.865 ( ): probe:tcp_sendmsg:(ffffffff8163db30) iocb=0xffff8803ebec7e70 sk=0xffff88042196ab80 msg=0xffff8803ebec7da8 size=0x44)
462.873 (0.010 ms): sshd/19152 ... [continued]: write()) = 68
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Hemant Kumar <hemant@linux.vnet.ibm.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/20150506124653.4961.59806.stgit@localhost.localdomain
[ Add some examples to the changelog message showing how to use it ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-05-06 19:46:53 +07:00
|
|
|
struct local_vars_finder vf = {.pf = pf, .args = args, .vars = false,
|
2013-10-11 14:10:23 +07:00
|
|
|
.max_args = MAX_PROBE_ARGS, .ret = 0};
|
|
|
|
|
|
|
|
for (i = 0; i < pf->pev->nargs; i++) {
|
|
|
|
/* var never be NULL */
|
perf probe: Support $params special probe argument
$params is similar to $vars but matches only function parameters not
local variables.
Thus, this is useful for tracing function parameter changing or tracing
function call with parameters.
Testing it:
# perf probe tcp_sendmsg '$params'
Added new event:
probe:tcp_sendmsg (on tcp_sendmsg with $params)
You can now use it in all perf tools, such as:
perf record -e probe:tcp_sendmsg -aR sleep 1
# perf probe -l
probe:tcp_sendmsg (on tcp_sendmsg@acme/git/linux/net/ipv4/tcp.c with iocb sk msg size)
# perf record -a -e probe:*
press some random letters to generate TCP (sshd) traffic...
^C[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.223 MB perf.data (6 samples) ]
# perf script
sshd 6385 [2] 3.907529: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
sshd 6385 [2] 4.138973: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
sshd 6385 [2] 4.378966: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
sshd 6385 [2] 4.603681: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
sshd 6385 [2] 4.818455: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
sshd 6385 [2] 5.043603: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
# cat /sys/kernel/debug/tracing/events/probe/tcp_sendmsg/format
name: tcp_sendmsg
ID: 1927
format:
field:unsigned short common_type; offset:0; size:2; signed:0;
field:unsigned char common_flags; offset:2; size:1; signed:0;
field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
field:int common_pid; offset:4; size:4; signed:1;
field:unsigned long __probe_ip; offset:8; size:8; signed:0;
field:u64 iocb; offset:16; size:8; signed:0;
field:u64 sk; offset:24; size:8; signed:0;
field:u64 msg; offset:32; size:8; signed:0;
field:u64 size; offset:40; size:8; signed:0;
print fmt: "(%lx) iocb=0x%Lx sk=0x%Lx msg=0x%Lx size=0x%Lx", REC->__probe_ip, REC->iocb, REC->sk, REC->msg, REC->size
#
Do some system wide tracing of this probe + write syscalls:
# perf trace -e write --ev probe:* --filter-pids 6385
462.612 (0.010 ms): bash/19153 write(fd: 1</dev/pts/1>, buf: 0x7f7556c78000, count: 29 ) = 29
462.701 (0.027 ms): sshd/19152 write(fd: 3<socket:[63117]>, buf: 0x7f78dd12e160, count: 68 ) ...
462.701 ( ): probe:tcp_sendmsg:(ffffffff8163db30) iocb=0xffff8803ebec7e70 sk=0xffff88042196ab80 msg=0xffff8803ebec7da8 size=0x44)
462.710 (0.035 ms): sshd/19152 ... [continued]: write()) = 68
462.787 (0.009 ms): bash/19153 write(fd: 2</dev/pts/1>, buf: 0x7f7556c77000, count: 22 ) = 22
462.865 (0.002 ms): sshd/19152 write(fd: 3<socket:[63117]>, buf: 0x7f78dd12e160, count: 68 ) ...
462.865 ( ): probe:tcp_sendmsg:(ffffffff8163db30) iocb=0xffff8803ebec7e70 sk=0xffff88042196ab80 msg=0xffff8803ebec7da8 size=0x44)
462.873 (0.010 ms): sshd/19152 ... [continued]: write()) = 68
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Hemant Kumar <hemant@linux.vnet.ibm.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/20150506124653.4961.59806.stgit@localhost.localdomain
[ Add some examples to the changelog message showing how to use it ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-05-06 19:46:53 +07:00
|
|
|
if (strcmp(pf->pev->args[i].var, PROBE_ARG_VARS) == 0)
|
|
|
|
vf.vars = true;
|
|
|
|
else if (strcmp(pf->pev->args[i].var, PROBE_ARG_PARAMS) != 0) {
|
2013-10-11 14:10:23 +07:00
|
|
|
/* Copy normal argument */
|
|
|
|
args[n] = pf->pev->args[i];
|
|
|
|
n++;
|
perf probe: Support $params special probe argument
$params is similar to $vars but matches only function parameters not
local variables.
Thus, this is useful for tracing function parameter changing or tracing
function call with parameters.
Testing it:
# perf probe tcp_sendmsg '$params'
Added new event:
probe:tcp_sendmsg (on tcp_sendmsg with $params)
You can now use it in all perf tools, such as:
perf record -e probe:tcp_sendmsg -aR sleep 1
# perf probe -l
probe:tcp_sendmsg (on tcp_sendmsg@acme/git/linux/net/ipv4/tcp.c with iocb sk msg size)
# perf record -a -e probe:*
press some random letters to generate TCP (sshd) traffic...
^C[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.223 MB perf.data (6 samples) ]
# perf script
sshd 6385 [2] 3.907529: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
sshd 6385 [2] 4.138973: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
sshd 6385 [2] 4.378966: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
sshd 6385 [2] 4.603681: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
sshd 6385 [2] 4.818455: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
sshd 6385 [2] 5.043603: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
# cat /sys/kernel/debug/tracing/events/probe/tcp_sendmsg/format
name: tcp_sendmsg
ID: 1927
format:
field:unsigned short common_type; offset:0; size:2; signed:0;
field:unsigned char common_flags; offset:2; size:1; signed:0;
field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
field:int common_pid; offset:4; size:4; signed:1;
field:unsigned long __probe_ip; offset:8; size:8; signed:0;
field:u64 iocb; offset:16; size:8; signed:0;
field:u64 sk; offset:24; size:8; signed:0;
field:u64 msg; offset:32; size:8; signed:0;
field:u64 size; offset:40; size:8; signed:0;
print fmt: "(%lx) iocb=0x%Lx sk=0x%Lx msg=0x%Lx size=0x%Lx", REC->__probe_ip, REC->iocb, REC->sk, REC->msg, REC->size
#
Do some system wide tracing of this probe + write syscalls:
# perf trace -e write --ev probe:* --filter-pids 6385
462.612 (0.010 ms): bash/19153 write(fd: 1</dev/pts/1>, buf: 0x7f7556c78000, count: 29 ) = 29
462.701 (0.027 ms): sshd/19152 write(fd: 3<socket:[63117]>, buf: 0x7f78dd12e160, count: 68 ) ...
462.701 ( ): probe:tcp_sendmsg:(ffffffff8163db30) iocb=0xffff8803ebec7e70 sk=0xffff88042196ab80 msg=0xffff8803ebec7da8 size=0x44)
462.710 (0.035 ms): sshd/19152 ... [continued]: write()) = 68
462.787 (0.009 ms): bash/19153 write(fd: 2</dev/pts/1>, buf: 0x7f7556c77000, count: 22 ) = 22
462.865 (0.002 ms): sshd/19152 write(fd: 3<socket:[63117]>, buf: 0x7f78dd12e160, count: 68 ) ...
462.865 ( ): probe:tcp_sendmsg:(ffffffff8163db30) iocb=0xffff8803ebec7e70 sk=0xffff88042196ab80 msg=0xffff8803ebec7da8 size=0x44)
462.873 (0.010 ms): sshd/19152 ... [continued]: write()) = 68
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Hemant Kumar <hemant@linux.vnet.ibm.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/20150506124653.4961.59806.stgit@localhost.localdomain
[ Add some examples to the changelog message showing how to use it ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-05-06 19:46:53 +07:00
|
|
|
continue;
|
2013-10-11 14:10:23 +07:00
|
|
|
}
|
perf probe: Support $params special probe argument
$params is similar to $vars but matches only function parameters not
local variables.
Thus, this is useful for tracing function parameter changing or tracing
function call with parameters.
Testing it:
# perf probe tcp_sendmsg '$params'
Added new event:
probe:tcp_sendmsg (on tcp_sendmsg with $params)
You can now use it in all perf tools, such as:
perf record -e probe:tcp_sendmsg -aR sleep 1
# perf probe -l
probe:tcp_sendmsg (on tcp_sendmsg@acme/git/linux/net/ipv4/tcp.c with iocb sk msg size)
# perf record -a -e probe:*
press some random letters to generate TCP (sshd) traffic...
^C[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.223 MB perf.data (6 samples) ]
# perf script
sshd 6385 [2] 3.907529: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
sshd 6385 [2] 4.138973: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
sshd 6385 [2] 4.378966: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
sshd 6385 [2] 4.603681: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
sshd 6385 [2] 4.818455: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
sshd 6385 [2] 5.043603: probe:tcp_sendmsg: iocb=0xffff8800ac4cfe70 sk=0xffff88042196c140 msg=0xffff8800ac4cfda8 size=0x24
# cat /sys/kernel/debug/tracing/events/probe/tcp_sendmsg/format
name: tcp_sendmsg
ID: 1927
format:
field:unsigned short common_type; offset:0; size:2; signed:0;
field:unsigned char common_flags; offset:2; size:1; signed:0;
field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
field:int common_pid; offset:4; size:4; signed:1;
field:unsigned long __probe_ip; offset:8; size:8; signed:0;
field:u64 iocb; offset:16; size:8; signed:0;
field:u64 sk; offset:24; size:8; signed:0;
field:u64 msg; offset:32; size:8; signed:0;
field:u64 size; offset:40; size:8; signed:0;
print fmt: "(%lx) iocb=0x%Lx sk=0x%Lx msg=0x%Lx size=0x%Lx", REC->__probe_ip, REC->iocb, REC->sk, REC->msg, REC->size
#
Do some system wide tracing of this probe + write syscalls:
# perf trace -e write --ev probe:* --filter-pids 6385
462.612 (0.010 ms): bash/19153 write(fd: 1</dev/pts/1>, buf: 0x7f7556c78000, count: 29 ) = 29
462.701 (0.027 ms): sshd/19152 write(fd: 3<socket:[63117]>, buf: 0x7f78dd12e160, count: 68 ) ...
462.701 ( ): probe:tcp_sendmsg:(ffffffff8163db30) iocb=0xffff8803ebec7e70 sk=0xffff88042196ab80 msg=0xffff8803ebec7da8 size=0x44)
462.710 (0.035 ms): sshd/19152 ... [continued]: write()) = 68
462.787 (0.009 ms): bash/19153 write(fd: 2</dev/pts/1>, buf: 0x7f7556c77000, count: 22 ) = 22
462.865 (0.002 ms): sshd/19152 write(fd: 3<socket:[63117]>, buf: 0x7f78dd12e160, count: 68 ) ...
462.865 ( ): probe:tcp_sendmsg:(ffffffff8163db30) iocb=0xffff8803ebec7e70 sk=0xffff88042196ab80 msg=0xffff8803ebec7da8 size=0x44)
462.873 (0.010 ms): sshd/19152 ... [continued]: write()) = 68
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
Cc: David Ahern <dsahern@gmail.com>
Cc: Hemant Kumar <hemant@linux.vnet.ibm.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/20150506124653.4961.59806.stgit@localhost.localdomain
[ Add some examples to the changelog message showing how to use it ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-05-06 19:46:53 +07:00
|
|
|
pr_debug("Expanding %s into:", pf->pev->args[i].var);
|
|
|
|
vf.nargs = n;
|
|
|
|
/* Special local variables */
|
|
|
|
die_find_child(sc_die, copy_variables_cb, (void *)&vf,
|
|
|
|
&die_mem);
|
|
|
|
pr_debug(" (%d)\n", vf.nargs - n);
|
|
|
|
if (vf.ret < 0)
|
|
|
|
return vf.ret;
|
|
|
|
n = vf.nargs;
|
2013-10-11 14:10:23 +07:00
|
|
|
}
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
2010-10-21 17:13:23 +07:00
|
|
|
/* Add a found probe point into trace event list */
|
2011-08-11 18:02:59 +07:00
|
|
|
static int add_probe_trace_event(Dwarf_Die *sc_die, struct probe_finder *pf)
|
2010-10-21 17:13:23 +07:00
|
|
|
{
|
|
|
|
struct trace_event_finder *tf =
|
|
|
|
container_of(pf, struct trace_event_finder, pf);
|
2015-09-30 23:41:37 +07:00
|
|
|
struct perf_probe_point *pp = &pf->pev->point;
|
2010-10-21 17:13:23 +07:00
|
|
|
struct probe_trace_event *tev;
|
2015-11-13 19:29:11 +07:00
|
|
|
struct perf_probe_arg *args = NULL;
|
2010-10-21 17:13:23 +07:00
|
|
|
int ret, i;
|
|
|
|
|
|
|
|
/* Check number of tevs */
|
|
|
|
if (tf->ntevs == tf->max_tevs) {
|
|
|
|
pr_warning("Too many( > %d) probe point found.\n",
|
|
|
|
tf->max_tevs);
|
|
|
|
return -ERANGE;
|
|
|
|
}
|
|
|
|
tev = &tf->tevs[tf->ntevs++];
|
|
|
|
|
2011-08-11 18:02:59 +07:00
|
|
|
/* Trace point should be converted from subprogram DIE */
|
2013-09-25 20:16:16 +07:00
|
|
|
ret = convert_to_trace_point(&pf->sp_die, tf->mod, pf->addr,
|
2015-09-30 23:41:37 +07:00
|
|
|
pp->retprobe, pp->function, &tev->point);
|
2010-10-21 17:13:23 +07:00
|
|
|
if (ret < 0)
|
2015-11-13 19:29:11 +07:00
|
|
|
goto end;
|
2010-10-21 17:13:23 +07:00
|
|
|
|
2015-05-08 08:03:35 +07:00
|
|
|
tev->point.realname = strdup(dwarf_diename(sc_die));
|
2015-11-13 19:29:11 +07:00
|
|
|
if (!tev->point.realname) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto end;
|
|
|
|
}
|
2015-05-08 08:03:35 +07:00
|
|
|
|
2010-10-21 17:13:23 +07:00
|
|
|
pr_debug("Probe point found: %s+%lu\n", tev->point.symbol,
|
|
|
|
tev->point.offset);
|
|
|
|
|
2013-10-11 14:10:23 +07:00
|
|
|
/* Expand special probe argument if exist */
|
|
|
|
args = zalloc(sizeof(struct perf_probe_arg) * MAX_PROBE_ARGS);
|
2015-11-13 19:29:11 +07:00
|
|
|
if (args == NULL) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto end;
|
|
|
|
}
|
2013-10-11 14:10:23 +07:00
|
|
|
|
|
|
|
ret = expand_probe_args(sc_die, pf, args);
|
|
|
|
if (ret < 0)
|
|
|
|
goto end;
|
|
|
|
|
|
|
|
tev->nargs = ret;
|
|
|
|
tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
|
|
|
|
if (tev->args == NULL) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find each argument */
|
|
|
|
for (i = 0; i < tev->nargs; i++) {
|
|
|
|
pf->pvar = &args[i];
|
2010-10-21 17:13:23 +07:00
|
|
|
pf->tvar = &tev->args[i];
|
2011-08-11 18:02:59 +07:00
|
|
|
/* Variable should be found from scope DIE */
|
|
|
|
ret = find_variable(sc_die, pf);
|
2010-10-21 17:13:23 +07:00
|
|
|
if (ret != 0)
|
2013-10-11 14:10:23 +07:00
|
|
|
break;
|
2010-10-21 17:13:23 +07:00
|
|
|
}
|
|
|
|
|
2013-10-11 14:10:23 +07:00
|
|
|
end:
|
2015-11-13 19:29:11 +07:00
|
|
|
if (ret) {
|
|
|
|
clear_probe_trace_event(tev);
|
|
|
|
tf->ntevs--;
|
|
|
|
}
|
2013-10-11 14:10:23 +07:00
|
|
|
free(args);
|
|
|
|
return ret;
|
2010-10-21 17:13:23 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Find probe_trace_events specified by perf_probe_event from debuginfo */
|
2013-11-06 01:32:36 +07:00
|
|
|
int debuginfo__find_trace_events(struct debuginfo *dbg,
|
2011-06-27 14:27:39 +07:00
|
|
|
struct perf_probe_event *pev,
|
2015-05-08 08:03:31 +07:00
|
|
|
struct probe_trace_event **tevs)
|
2010-10-21 17:13:23 +07:00
|
|
|
{
|
|
|
|
struct trace_event_finder tf = {
|
|
|
|
.pf = {.pev = pev, .callback = add_probe_trace_event},
|
2015-05-08 08:03:31 +07:00
|
|
|
.max_tevs = probe_conf.max_probes, .mod = dbg->mod};
|
2015-11-13 19:29:10 +07:00
|
|
|
int ret, i;
|
2010-10-21 17:13:23 +07:00
|
|
|
|
|
|
|
/* Allocate result tevs array */
|
2015-05-08 08:03:31 +07:00
|
|
|
*tevs = zalloc(sizeof(struct probe_trace_event) * tf.max_tevs);
|
2010-10-21 17:13:23 +07:00
|
|
|
if (*tevs == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
tf.tevs = *tevs;
|
|
|
|
tf.ntevs = 0;
|
|
|
|
|
2013-11-06 01:32:36 +07:00
|
|
|
ret = debuginfo__find_probes(dbg, &tf.pf);
|
2010-10-21 17:13:23 +07:00
|
|
|
if (ret < 0) {
|
2015-11-13 19:29:10 +07:00
|
|
|
for (i = 0; i < tf.ntevs; i++)
|
|
|
|
clear_probe_trace_event(&tf.tevs[i]);
|
2013-12-27 03:41:15 +07:00
|
|
|
zfree(tevs);
|
2010-10-21 17:13:23 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (ret < 0) ? ret : tf.ntevs;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Collect available variables in this scope */
|
|
|
|
static int collect_variables_cb(Dwarf_Die *die_mem, void *data)
|
|
|
|
{
|
|
|
|
struct available_var_finder *af = data;
|
|
|
|
struct variable_list *vl;
|
2016-05-10 12:47:07 +07:00
|
|
|
struct strbuf buf = STRBUF_INIT;
|
2010-10-21 17:13:23 +07:00
|
|
|
int tag, ret;
|
|
|
|
|
|
|
|
vl = &af->vls[af->nvls - 1];
|
|
|
|
|
|
|
|
tag = dwarf_tag(die_mem);
|
|
|
|
if (tag == DW_TAG_formal_parameter ||
|
|
|
|
tag == DW_TAG_variable) {
|
|
|
|
ret = convert_variable_location(die_mem, af->pf.addr,
|
2013-10-11 14:10:26 +07:00
|
|
|
af->pf.fb_ops, &af->pf.sp_die,
|
2016-08-25 23:24:57 +07:00
|
|
|
af->pf.machine, NULL);
|
perf probe: Add --range option to show a variable's location range
It is not easy for users to get the accurate byte offset or the line
number where a local variable can be probed.
With '--range' option, local variables in the scope of the probe point
are showed with a byte offset range, and can be added according to this
range information.
For example, there are some variables in the function
generic_perform_write():
<generic_perform_write@mm/filemap.c:0>
0 ssize_t generic_perform_write(struct file *file,
1 struct iov_iter *i, loff_t pos)
2 {
3 struct address_space *mapping = file->f_mapping;
4 const struct address_space_operations *a_ops = mapping->a_ops;
...
42 status = a_ops->write_begin(file, mapping, pos, bytes, flags,
&page, &fsdata);
44 if (unlikely(status < 0))
But we fail when we try to probe the variable 'a_ops' at line 42 or 44.
$ perf probe --add 'generic_perform_write:42 a_ops'
Failed to find the location of a_ops at this address.
Perhaps, it has been optimized out.
This is because the source code do not match the assembly, so a variable
may not be available in the source code line where it appears.
After this patch, we can lookup the accurate byte offset range of a
variable, 'INV' indicates that this variable is not valid at the given
point, but available in the scope:
$ perf probe --vars 'generic_perform_write:42' --range
Available variables at generic_perform_write:42
@<generic_perform_write+141>
[INV] ssize_t written @<generic_perform_write+[324-331]>
[INV] struct address_space_operations* a_ops @<generic_perform_write+[55-61,170-176,223-246]>
[VAL] (unknown_type) fsdata @<generic_perform_write+[70-307,346-411]>
[VAL] loff_t pos @<generic_perform_write+[0-286,286-336,346-411]>
[VAL] long int status @<generic_perform_write+[83-342,346-411]>
[VAL] long unsigned int bytes @<generic_perform_write+[122-311,320-338,346-403,403-411]>
[VAL] struct address_space* mapping @<generic_perform_write+[35-344,346-411]>
[VAL] struct iov_iter* i @<generic_perform_write+[0-340,346-411]>
[VAL] struct page* page @<generic_perform_write+[70-307,346-411]>
Then it is more clear for us to add a probe with this variable:
$ perf probe --add 'generic_perform_write+170 a_ops'
Added new event:
probe:generic_perform_write (on generic_perform_write+170 with a_ops)
Signed-off-by: He Kuang <hekuang@huawei.com>
Acked-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Wang Nan <wangnan0@huawei.com>
Link: http://lkml.kernel.org/r/1431336304-16863-2-git-send-email-hekuang@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-05-11 16:25:03 +07:00
|
|
|
if (ret == 0 || ret == -ERANGE) {
|
|
|
|
int ret2;
|
|
|
|
bool externs = !af->child;
|
2015-05-11 16:25:02 +07:00
|
|
|
|
2016-05-10 12:47:07 +07:00
|
|
|
if (strbuf_init(&buf, 64) < 0)
|
|
|
|
goto error;
|
perf probe: Add --range option to show a variable's location range
It is not easy for users to get the accurate byte offset or the line
number where a local variable can be probed.
With '--range' option, local variables in the scope of the probe point
are showed with a byte offset range, and can be added according to this
range information.
For example, there are some variables in the function
generic_perform_write():
<generic_perform_write@mm/filemap.c:0>
0 ssize_t generic_perform_write(struct file *file,
1 struct iov_iter *i, loff_t pos)
2 {
3 struct address_space *mapping = file->f_mapping;
4 const struct address_space_operations *a_ops = mapping->a_ops;
...
42 status = a_ops->write_begin(file, mapping, pos, bytes, flags,
&page, &fsdata);
44 if (unlikely(status < 0))
But we fail when we try to probe the variable 'a_ops' at line 42 or 44.
$ perf probe --add 'generic_perform_write:42 a_ops'
Failed to find the location of a_ops at this address.
Perhaps, it has been optimized out.
This is because the source code do not match the assembly, so a variable
may not be available in the source code line where it appears.
After this patch, we can lookup the accurate byte offset range of a
variable, 'INV' indicates that this variable is not valid at the given
point, but available in the scope:
$ perf probe --vars 'generic_perform_write:42' --range
Available variables at generic_perform_write:42
@<generic_perform_write+141>
[INV] ssize_t written @<generic_perform_write+[324-331]>
[INV] struct address_space_operations* a_ops @<generic_perform_write+[55-61,170-176,223-246]>
[VAL] (unknown_type) fsdata @<generic_perform_write+[70-307,346-411]>
[VAL] loff_t pos @<generic_perform_write+[0-286,286-336,346-411]>
[VAL] long int status @<generic_perform_write+[83-342,346-411]>
[VAL] long unsigned int bytes @<generic_perform_write+[122-311,320-338,346-403,403-411]>
[VAL] struct address_space* mapping @<generic_perform_write+[35-344,346-411]>
[VAL] struct iov_iter* i @<generic_perform_write+[0-340,346-411]>
[VAL] struct page* page @<generic_perform_write+[70-307,346-411]>
Then it is more clear for us to add a probe with this variable:
$ perf probe --add 'generic_perform_write+170 a_ops'
Added new event:
probe:generic_perform_write (on generic_perform_write+170 with a_ops)
Signed-off-by: He Kuang <hekuang@huawei.com>
Acked-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Wang Nan <wangnan0@huawei.com>
Link: http://lkml.kernel.org/r/1431336304-16863-2-git-send-email-hekuang@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-05-11 16:25:03 +07:00
|
|
|
|
|
|
|
if (probe_conf.show_location_range) {
|
2016-05-10 12:47:07 +07:00
|
|
|
if (!externs)
|
|
|
|
ret2 = strbuf_add(&buf,
|
|
|
|
ret ? "[INV]\t" : "[VAL]\t", 6);
|
|
|
|
else
|
|
|
|
ret2 = strbuf_add(&buf, "[EXT]\t", 6);
|
|
|
|
if (ret2)
|
|
|
|
goto error;
|
perf probe: Add --range option to show a variable's location range
It is not easy for users to get the accurate byte offset or the line
number where a local variable can be probed.
With '--range' option, local variables in the scope of the probe point
are showed with a byte offset range, and can be added according to this
range information.
For example, there are some variables in the function
generic_perform_write():
<generic_perform_write@mm/filemap.c:0>
0 ssize_t generic_perform_write(struct file *file,
1 struct iov_iter *i, loff_t pos)
2 {
3 struct address_space *mapping = file->f_mapping;
4 const struct address_space_operations *a_ops = mapping->a_ops;
...
42 status = a_ops->write_begin(file, mapping, pos, bytes, flags,
&page, &fsdata);
44 if (unlikely(status < 0))
But we fail when we try to probe the variable 'a_ops' at line 42 or 44.
$ perf probe --add 'generic_perform_write:42 a_ops'
Failed to find the location of a_ops at this address.
Perhaps, it has been optimized out.
This is because the source code do not match the assembly, so a variable
may not be available in the source code line where it appears.
After this patch, we can lookup the accurate byte offset range of a
variable, 'INV' indicates that this variable is not valid at the given
point, but available in the scope:
$ perf probe --vars 'generic_perform_write:42' --range
Available variables at generic_perform_write:42
@<generic_perform_write+141>
[INV] ssize_t written @<generic_perform_write+[324-331]>
[INV] struct address_space_operations* a_ops @<generic_perform_write+[55-61,170-176,223-246]>
[VAL] (unknown_type) fsdata @<generic_perform_write+[70-307,346-411]>
[VAL] loff_t pos @<generic_perform_write+[0-286,286-336,346-411]>
[VAL] long int status @<generic_perform_write+[83-342,346-411]>
[VAL] long unsigned int bytes @<generic_perform_write+[122-311,320-338,346-403,403-411]>
[VAL] struct address_space* mapping @<generic_perform_write+[35-344,346-411]>
[VAL] struct iov_iter* i @<generic_perform_write+[0-340,346-411]>
[VAL] struct page* page @<generic_perform_write+[70-307,346-411]>
Then it is more clear for us to add a probe with this variable:
$ perf probe --add 'generic_perform_write+170 a_ops'
Added new event:
probe:generic_perform_write (on generic_perform_write+170 with a_ops)
Signed-off-by: He Kuang <hekuang@huawei.com>
Acked-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Wang Nan <wangnan0@huawei.com>
Link: http://lkml.kernel.org/r/1431336304-16863-2-git-send-email-hekuang@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-05-11 16:25:03 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
ret2 = die_get_varname(die_mem, &buf);
|
|
|
|
|
|
|
|
if (!ret2 && probe_conf.show_location_range &&
|
|
|
|
!externs) {
|
2016-05-10 12:47:07 +07:00
|
|
|
if (strbuf_addch(&buf, '\t') < 0)
|
|
|
|
goto error;
|
perf probe: Add --range option to show a variable's location range
It is not easy for users to get the accurate byte offset or the line
number where a local variable can be probed.
With '--range' option, local variables in the scope of the probe point
are showed with a byte offset range, and can be added according to this
range information.
For example, there are some variables in the function
generic_perform_write():
<generic_perform_write@mm/filemap.c:0>
0 ssize_t generic_perform_write(struct file *file,
1 struct iov_iter *i, loff_t pos)
2 {
3 struct address_space *mapping = file->f_mapping;
4 const struct address_space_operations *a_ops = mapping->a_ops;
...
42 status = a_ops->write_begin(file, mapping, pos, bytes, flags,
&page, &fsdata);
44 if (unlikely(status < 0))
But we fail when we try to probe the variable 'a_ops' at line 42 or 44.
$ perf probe --add 'generic_perform_write:42 a_ops'
Failed to find the location of a_ops at this address.
Perhaps, it has been optimized out.
This is because the source code do not match the assembly, so a variable
may not be available in the source code line where it appears.
After this patch, we can lookup the accurate byte offset range of a
variable, 'INV' indicates that this variable is not valid at the given
point, but available in the scope:
$ perf probe --vars 'generic_perform_write:42' --range
Available variables at generic_perform_write:42
@<generic_perform_write+141>
[INV] ssize_t written @<generic_perform_write+[324-331]>
[INV] struct address_space_operations* a_ops @<generic_perform_write+[55-61,170-176,223-246]>
[VAL] (unknown_type) fsdata @<generic_perform_write+[70-307,346-411]>
[VAL] loff_t pos @<generic_perform_write+[0-286,286-336,346-411]>
[VAL] long int status @<generic_perform_write+[83-342,346-411]>
[VAL] long unsigned int bytes @<generic_perform_write+[122-311,320-338,346-403,403-411]>
[VAL] struct address_space* mapping @<generic_perform_write+[35-344,346-411]>
[VAL] struct iov_iter* i @<generic_perform_write+[0-340,346-411]>
[VAL] struct page* page @<generic_perform_write+[70-307,346-411]>
Then it is more clear for us to add a probe with this variable:
$ perf probe --add 'generic_perform_write+170 a_ops'
Added new event:
probe:generic_perform_write (on generic_perform_write+170 with a_ops)
Signed-off-by: He Kuang <hekuang@huawei.com>
Acked-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Wang Nan <wangnan0@huawei.com>
Link: http://lkml.kernel.org/r/1431336304-16863-2-git-send-email-hekuang@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-05-11 16:25:03 +07:00
|
|
|
ret2 = die_get_var_range(&af->pf.sp_die,
|
|
|
|
die_mem, &buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
pr_debug("Add new var: %s\n", buf.buf);
|
|
|
|
if (ret2 == 0) {
|
2015-05-11 16:25:02 +07:00
|
|
|
strlist__add(vl->vars,
|
|
|
|
strbuf_detach(&buf, NULL));
|
2016-05-10 12:47:07 +07:00
|
|
|
}
|
|
|
|
strbuf_release(&buf);
|
2010-10-21 17:13:23 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-10-21 17:13:35 +07:00
|
|
|
if (af->child && dwarf_haspc(die_mem, af->pf.addr))
|
2010-10-21 17:13:23 +07:00
|
|
|
return DIE_FIND_CB_CONTINUE;
|
|
|
|
else
|
|
|
|
return DIE_FIND_CB_SIBLING;
|
2016-05-10 12:47:07 +07:00
|
|
|
error:
|
|
|
|
strbuf_release(&buf);
|
|
|
|
pr_debug("Error in strbuf\n");
|
|
|
|
return DIE_FIND_CB_END;
|
2010-10-21 17:13:23 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Add a found vars into available variables list */
|
2011-08-11 18:02:59 +07:00
|
|
|
static int add_available_vars(Dwarf_Die *sc_die, struct probe_finder *pf)
|
2010-10-21 17:13:23 +07:00
|
|
|
{
|
|
|
|
struct available_var_finder *af =
|
|
|
|
container_of(pf, struct available_var_finder, pf);
|
2015-09-30 23:41:37 +07:00
|
|
|
struct perf_probe_point *pp = &pf->pev->point;
|
2010-10-21 17:13:23 +07:00
|
|
|
struct variable_list *vl;
|
perf probe: Avoid searching variables in intermediate scopes
Fix variable searching logic to search one in inner than local scope or
global(CU) scope. In the other words, skip searching in intermediate
scopes.
e.g., in the following code,
int var1;
void inline infunc(int i)
{
i++; <--- [A]
}
void func(void)
{
int var1, var2;
infunc(var2);
}
At [A], "var1" should point the global variable "var1", however, if user
mis-typed as "var2", variable search should be failed. However, current
logic searches variable infunc() scope, global scope, and then func()
scope. Thus, it can find "var2" variable in func() scope. This may not
be what user expects.
So, it would better not search outer scopes except outermost (compile
unit) scope which contains only global variables, when it failed to find
given variable in local scope.
E.g.
Without this:
$ perf probe -V pre_schedule --externs > without.vars
With this:
$ perf probe -V pre_schedule --externs > with.vars
Check the diff:
$ diff without.vars with.vars
88d87
< int cpu
133d131
< long unsigned int* switch_count
These vars are actually in the scope of schedule(), the caller of
pre_schedule().
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Pekka Enberg <penberg@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: yrl.pp-manager.tt@hitachi.com
Link: http://lkml.kernel.org/r/20110811110305.19900.94374.stgit@fedora15
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-08-11 18:03:05 +07:00
|
|
|
Dwarf_Die die_mem;
|
|
|
|
int ret;
|
2010-10-21 17:13:23 +07:00
|
|
|
|
|
|
|
/* Check number of tevs */
|
|
|
|
if (af->nvls == af->max_vls) {
|
|
|
|
pr_warning("Too many( > %d) probe point found.\n", af->max_vls);
|
|
|
|
return -ERANGE;
|
|
|
|
}
|
|
|
|
vl = &af->vls[af->nvls++];
|
|
|
|
|
2011-08-11 18:02:59 +07:00
|
|
|
/* Trace point should be converted from subprogram DIE */
|
2013-09-25 20:16:16 +07:00
|
|
|
ret = convert_to_trace_point(&pf->sp_die, af->mod, pf->addr,
|
2015-09-30 23:41:37 +07:00
|
|
|
pp->retprobe, pp->function, &vl->point);
|
2010-10-21 17:13:23 +07:00
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
pr_debug("Probe point found: %s+%lu\n", vl->point.symbol,
|
|
|
|
vl->point.offset);
|
|
|
|
|
|
|
|
/* Find local variables */
|
2015-07-20 22:13:34 +07:00
|
|
|
vl->vars = strlist__new(NULL, NULL);
|
2010-10-21 17:13:23 +07:00
|
|
|
if (vl->vars == NULL)
|
|
|
|
return -ENOMEM;
|
2010-10-21 17:13:35 +07:00
|
|
|
af->child = true;
|
2011-08-11 18:02:59 +07:00
|
|
|
die_find_child(sc_die, collect_variables_cb, (void *)af, &die_mem);
|
2010-10-21 17:13:23 +07:00
|
|
|
|
2010-10-21 17:13:35 +07:00
|
|
|
/* Find external variables */
|
2015-05-08 08:03:31 +07:00
|
|
|
if (!probe_conf.show_ext_vars)
|
2010-10-21 17:13:35 +07:00
|
|
|
goto out;
|
2015-05-08 08:03:31 +07:00
|
|
|
/* Don't need to search child DIE for external vars. */
|
2010-10-21 17:13:35 +07:00
|
|
|
af->child = false;
|
perf probe: Avoid searching variables in intermediate scopes
Fix variable searching logic to search one in inner than local scope or
global(CU) scope. In the other words, skip searching in intermediate
scopes.
e.g., in the following code,
int var1;
void inline infunc(int i)
{
i++; <--- [A]
}
void func(void)
{
int var1, var2;
infunc(var2);
}
At [A], "var1" should point the global variable "var1", however, if user
mis-typed as "var2", variable search should be failed. However, current
logic searches variable infunc() scope, global scope, and then func()
scope. Thus, it can find "var2" variable in func() scope. This may not
be what user expects.
So, it would better not search outer scopes except outermost (compile
unit) scope which contains only global variables, when it failed to find
given variable in local scope.
E.g.
Without this:
$ perf probe -V pre_schedule --externs > without.vars
With this:
$ perf probe -V pre_schedule --externs > with.vars
Check the diff:
$ diff without.vars with.vars
88d87
< int cpu
133d131
< long unsigned int* switch_count
These vars are actually in the scope of schedule(), the caller of
pre_schedule().
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Pekka Enberg <penberg@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: yrl.pp-manager.tt@hitachi.com
Link: http://lkml.kernel.org/r/20110811110305.19900.94374.stgit@fedora15
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-08-11 18:03:05 +07:00
|
|
|
die_find_child(&pf->cu_die, collect_variables_cb, (void *)af, &die_mem);
|
2010-10-21 17:13:35 +07:00
|
|
|
|
|
|
|
out:
|
2010-10-21 17:13:23 +07:00
|
|
|
if (strlist__empty(vl->vars)) {
|
|
|
|
strlist__delete(vl->vars);
|
|
|
|
vl->vars = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-06-06 14:13:59 +07:00
|
|
|
/*
|
|
|
|
* Find available variables at given probe point
|
|
|
|
* Return the number of found probe points. Return 0 if there is no
|
|
|
|
* matched probe point. Return <0 if an error occurs.
|
|
|
|
*/
|
2013-11-06 01:32:36 +07:00
|
|
|
int debuginfo__find_available_vars_at(struct debuginfo *dbg,
|
2011-06-27 14:27:39 +07:00
|
|
|
struct perf_probe_event *pev,
|
2015-05-08 08:03:31 +07:00
|
|
|
struct variable_list **vls)
|
2010-10-21 17:13:23 +07:00
|
|
|
{
|
|
|
|
struct available_var_finder af = {
|
|
|
|
.pf = {.pev = pev, .callback = add_available_vars},
|
2013-11-06 01:32:36 +07:00
|
|
|
.mod = dbg->mod,
|
2015-05-08 08:03:31 +07:00
|
|
|
.max_vls = probe_conf.max_probes};
|
2010-10-21 17:13:23 +07:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Allocate result vls array */
|
2015-05-08 08:03:31 +07:00
|
|
|
*vls = zalloc(sizeof(struct variable_list) * af.max_vls);
|
2010-10-21 17:13:23 +07:00
|
|
|
if (*vls == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
af.vls = *vls;
|
|
|
|
af.nvls = 0;
|
|
|
|
|
2013-11-06 01:32:36 +07:00
|
|
|
ret = debuginfo__find_probes(dbg, &af.pf);
|
2010-10-21 17:13:23 +07:00
|
|
|
if (ret < 0) {
|
|
|
|
/* Free vlist for error */
|
|
|
|
while (af.nvls--) {
|
2013-12-28 02:55:14 +07:00
|
|
|
zfree(&af.vls[af.nvls].point.symbol);
|
2013-12-27 01:54:57 +07:00
|
|
|
strlist__delete(af.vls[af.nvls].vars);
|
2010-10-21 17:13:23 +07:00
|
|
|
}
|
2013-12-27 03:41:15 +07:00
|
|
|
zfree(vls);
|
2010-10-21 17:13:23 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (ret < 0) ? ret : af.nvls;
|
2009-10-09 04:17:38 +07:00
|
|
|
}
|
|
|
|
|
2015-09-30 23:41:33 +07:00
|
|
|
/* For the kernel module, we need a special code to get a DIE */
|
2017-01-11 13:01:57 +07:00
|
|
|
int debuginfo__get_text_offset(struct debuginfo *dbg, Dwarf_Addr *offs,
|
|
|
|
bool adjust_offset)
|
2015-09-30 23:41:33 +07:00
|
|
|
{
|
|
|
|
int n, i;
|
|
|
|
Elf32_Word shndx;
|
|
|
|
Elf_Scn *scn;
|
|
|
|
Elf *elf;
|
|
|
|
GElf_Shdr mem, *shdr;
|
|
|
|
const char *p;
|
|
|
|
|
|
|
|
elf = dwfl_module_getelf(dbg->mod, &dbg->bias);
|
|
|
|
if (!elf)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* Get the number of relocations */
|
|
|
|
n = dwfl_module_relocations(dbg->mod);
|
|
|
|
if (n < 0)
|
|
|
|
return -ENOENT;
|
|
|
|
/* Search the relocation related .text section */
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
p = dwfl_module_relocation_info(dbg->mod, i, &shndx);
|
|
|
|
if (strcmp(p, ".text") == 0) {
|
|
|
|
/* OK, get the section header */
|
|
|
|
scn = elf_getscn(elf, shndx);
|
|
|
|
if (!scn)
|
|
|
|
return -ENOENT;
|
|
|
|
shdr = gelf_getshdr(scn, &mem);
|
|
|
|
if (!shdr)
|
|
|
|
return -ENOENT;
|
|
|
|
*offs = shdr->sh_addr;
|
2017-01-11 13:01:57 +07:00
|
|
|
if (adjust_offset)
|
|
|
|
*offs -= shdr->sh_offset;
|
2015-09-30 23:41:33 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-03-17 05:06:19 +07:00
|
|
|
/* Reverse search */
|
2013-11-06 01:32:36 +07:00
|
|
|
int debuginfo__find_probe_point(struct debuginfo *dbg, unsigned long addr,
|
2011-06-27 14:27:39 +07:00
|
|
|
struct perf_probe_point *ppt)
|
2010-03-17 05:06:19 +07:00
|
|
|
{
|
|
|
|
Dwarf_Die cudie, spdie, indie;
|
perf probe: Fix to find line information for probe list
Fix to find the correct (as much as possible) line information for
listing probes. Without this fix, perf probe --list action will show
incorrect line information as below;
probe:getname_flags (on getname_flags@ksrc/linux-3/fs/namei.c)
probe:getname_flags_1 (on getname:-89@x86/include/asm/current.h)
probe:getname_flags_2 (on user_path_at_empty:-2054@x86/include/asm/current.h)
The minus line number is obviously wrong, and current.h is not related
to the probe point. Deeper investigation discovered that there were 2
issues related to this bug, and minor typos too.
The 1st issue is the rack of considering about nested inlined functions,
which causes the wrong (relative) line number.
The 2nd issue is that the dwarf line info is not correct at those
points. It points 14th line of current.h.
Since it seems that the line info includes somewhat unreliable
information, this fixes perf to try to find correct line information
from both of debuginfo and line info as below.
1) Probe address is the entry of a function instance
In this case, the line is set as the function declared line.
2) Probe address is the entry of an expanded inline function block
In this case, the line is set as the function call-site line.
This means that the line number is relative from the entry line
of caller function (which can be an inlined function if nested)
3) Probe address is inside a function instance or an expanded
inline function block
In this case, perf probe queries the line number from lineinfo
and verify the function declared file is same as the file name
queried from lineinfo.
If the file name is different, it is a failure case. The probe
address is shown as symbol+offset.
4) Probe address is not in the any function instance
This is a failure case, the probe address is shown as
symbol+offset.
With this fix, perf probe -l shows correct probe lines as below;
probe:getname_flags (on getname_flags@ksrc/linux-3/fs/namei.c)
probe:getname_flags_1 (on getname:2@ksrc/linux-3/fs/namei.c)
probe:getname_flags_2 (on user_path_at_empty:4@ksrc/linux-3/fs/namei.c)
Changes at v2:
- Fix typos in the function comments. (Thanks to Namhyung Kim)
- Use die_find_top_inlinefunc instead of die_find_inlinefunc_next.
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/20130930092144.1693.11058.stgit@udc4-manage.rcp.hitachi.co.jp
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2013-09-30 16:21:44 +07:00
|
|
|
Dwarf_Addr _addr = 0, baseaddr = 0;
|
|
|
|
const char *fname = NULL, *func = NULL, *basefunc = NULL, *tmp;
|
perf probe: Fix listing incorrect line number with inline function
Fix a bug showing incorrect line number when a probe is put on the head of an
inline function. This patch updates find_perf_probe_point() and introduces new
rules to get correct line number.
- If debuginfo doesn't have a correct file name, we shouldn't return line
number too, because, without file name, line number is meaningless.
- If the address is in a function, it stores the function name and the offset
from the function entry.
- If the address is on a line, it tries to get the relative line number from
the function entry line, except for the address is same as the entry
address of the function (in this case, the relative line number should
be 0).
- If the address is in an inline function entry (call-site), it uses the
inline function call line number as the line on which the address is.
- If the address is in an inline function body, it stores the inline
function name and offset from the inline function call site instead of the
(non-inlined) function.
Cc: 2nddept-manager@sdl.hitachi.co.jp
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Lin Ming <ming.m.lin@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
LKML-Reference: <20110330092605.2132.11629.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-03-30 16:26:05 +07:00
|
|
|
int baseline = 0, lineno = 0, ret = 0;
|
2010-03-17 05:06:19 +07:00
|
|
|
|
perf probe: Fix to show correct locations for events on modules
Fix to show correct locations for events on modules by relocating given
address instead of retrying after failure.
This happens when the module text size is big enough, bigger than
sh_addr, because the original code retries with given address + sh_addr
if it failed to find CU DIE at the given address.
Any address smaller than sh_addr always fails and it retries with the
correct address, but addresses bigger than sh_addr will get a CU DIE
which is on the given address (not adjusted by sh_addr).
In my environment(x86-64), the sh_addr of ".text" section is 0x10030.
Since i915 is a huge kernel module, we can see this issue as below.
$ grep "[Tt] .*\[i915\]" /proc/kallsyms | sort | head -n1
ffffffffc0270000 t i915_switcheroo_can_switch [i915]
ffffffffc0270000 + 0x10030 = ffffffffc0280030, so we'll check
symbols cross this boundary.
$ grep "[Tt] .*\[i915\]" /proc/kallsyms | grep -B1 ^ffffffffc028\
| head -n 2
ffffffffc027ff80 t haswell_init_clock_gating [i915]
ffffffffc0280110 t valleyview_init_clock_gating [i915]
So setup probes on both function and see what happen.
$ sudo ./perf probe -m i915 -a haswell_init_clock_gating \
-a valleyview_init_clock_gating
Added new events:
probe:haswell_init_clock_gating (on haswell_init_clock_gating in i915)
probe:valleyview_init_clock_gating (on valleyview_init_clock_gating in i915)
You can now use it in all perf tools, such as:
perf record -e probe:valleyview_init_clock_gating -aR sleep 1
$ sudo ./perf probe -l
probe:haswell_init_clock_gating (on haswell_init_clock_gating@gpu/drm/i915/intel_pm.c in i915)
probe:valleyview_init_clock_gating (on i915_vga_set_decode:4@gpu/drm/i915/i915_drv.c in i915)
As you can see, haswell_init_clock_gating is correctly shown,
but valleyview_init_clock_gating is not.
With this patch, both events are shown correctly.
$ sudo ./perf probe -l
probe:haswell_init_clock_gating (on haswell_init_clock_gating@gpu/drm/i915/intel_pm.c in i915)
probe:valleyview_init_clock_gating (on valleyview_init_clock_gating@gpu/drm/i915/intel_pm.c in i915)
Committer notes:
In my case:
# perf probe -m i915 -a haswell_init_clock_gating -a valleyview_init_clock_gating
Added new events:
probe:haswell_init_clock_gating (on haswell_init_clock_gating in i915)
probe:valleyview_init_clock_gating (on valleyview_init_clock_gating in i915)
You can now use it in all perf tools, such as:
perf record -e probe:valleyview_init_clock_gating -aR sleep 1
# perf probe -l
probe:haswell_init_clock_gating (on i915_getparam+432@gpu/drm/i915/i915_drv.c in i915)
probe:valleyview_init_clock_gating (on __i915_printk+240@gpu/drm/i915/i915_drv.c in i915)
#
# readelf -SW /lib/modules/4.9.0+/build/vmlinux | egrep -w '.text|Name'
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 1] .text PROGBITS ffffffff81000000 200000 822fd3 00 AX 0 0 4096
#
So both are b0rked, now with the fix:
# perf probe -m i915 -a haswell_init_clock_gating -a valleyview_init_clock_gating
Added new events:
probe:haswell_init_clock_gating (on haswell_init_clock_gating in i915)
probe:valleyview_init_clock_gating (on valleyview_init_clock_gating in i915)
You can now use it in all perf tools, such as:
perf record -e probe:valleyview_init_clock_gating -aR sleep 1
# perf probe -l
probe:haswell_init_clock_gating (on haswell_init_clock_gating@gpu/drm/i915/intel_pm.c in i915)
probe:valleyview_init_clock_gating (on valleyview_init_clock_gating@gpu/drm/i915/intel_pm.c in i915)
#
Both looks correct.
Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/148411436777.9978.1440275861947194930.stgit@devbox
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2017-01-11 12:59:38 +07:00
|
|
|
/* We always need to relocate the address for aranges */
|
2017-01-11 13:01:57 +07:00
|
|
|
if (debuginfo__get_text_offset(dbg, &baseaddr, false) == 0)
|
perf probe: Fix to show correct locations for events on modules
Fix to show correct locations for events on modules by relocating given
address instead of retrying after failure.
This happens when the module text size is big enough, bigger than
sh_addr, because the original code retries with given address + sh_addr
if it failed to find CU DIE at the given address.
Any address smaller than sh_addr always fails and it retries with the
correct address, but addresses bigger than sh_addr will get a CU DIE
which is on the given address (not adjusted by sh_addr).
In my environment(x86-64), the sh_addr of ".text" section is 0x10030.
Since i915 is a huge kernel module, we can see this issue as below.
$ grep "[Tt] .*\[i915\]" /proc/kallsyms | sort | head -n1
ffffffffc0270000 t i915_switcheroo_can_switch [i915]
ffffffffc0270000 + 0x10030 = ffffffffc0280030, so we'll check
symbols cross this boundary.
$ grep "[Tt] .*\[i915\]" /proc/kallsyms | grep -B1 ^ffffffffc028\
| head -n 2
ffffffffc027ff80 t haswell_init_clock_gating [i915]
ffffffffc0280110 t valleyview_init_clock_gating [i915]
So setup probes on both function and see what happen.
$ sudo ./perf probe -m i915 -a haswell_init_clock_gating \
-a valleyview_init_clock_gating
Added new events:
probe:haswell_init_clock_gating (on haswell_init_clock_gating in i915)
probe:valleyview_init_clock_gating (on valleyview_init_clock_gating in i915)
You can now use it in all perf tools, such as:
perf record -e probe:valleyview_init_clock_gating -aR sleep 1
$ sudo ./perf probe -l
probe:haswell_init_clock_gating (on haswell_init_clock_gating@gpu/drm/i915/intel_pm.c in i915)
probe:valleyview_init_clock_gating (on i915_vga_set_decode:4@gpu/drm/i915/i915_drv.c in i915)
As you can see, haswell_init_clock_gating is correctly shown,
but valleyview_init_clock_gating is not.
With this patch, both events are shown correctly.
$ sudo ./perf probe -l
probe:haswell_init_clock_gating (on haswell_init_clock_gating@gpu/drm/i915/intel_pm.c in i915)
probe:valleyview_init_clock_gating (on valleyview_init_clock_gating@gpu/drm/i915/intel_pm.c in i915)
Committer notes:
In my case:
# perf probe -m i915 -a haswell_init_clock_gating -a valleyview_init_clock_gating
Added new events:
probe:haswell_init_clock_gating (on haswell_init_clock_gating in i915)
probe:valleyview_init_clock_gating (on valleyview_init_clock_gating in i915)
You can now use it in all perf tools, such as:
perf record -e probe:valleyview_init_clock_gating -aR sleep 1
# perf probe -l
probe:haswell_init_clock_gating (on i915_getparam+432@gpu/drm/i915/i915_drv.c in i915)
probe:valleyview_init_clock_gating (on __i915_printk+240@gpu/drm/i915/i915_drv.c in i915)
#
# readelf -SW /lib/modules/4.9.0+/build/vmlinux | egrep -w '.text|Name'
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 1] .text PROGBITS ffffffff81000000 200000 822fd3 00 AX 0 0 4096
#
So both are b0rked, now with the fix:
# perf probe -m i915 -a haswell_init_clock_gating -a valleyview_init_clock_gating
Added new events:
probe:haswell_init_clock_gating (on haswell_init_clock_gating in i915)
probe:valleyview_init_clock_gating (on valleyview_init_clock_gating in i915)
You can now use it in all perf tools, such as:
perf record -e probe:valleyview_init_clock_gating -aR sleep 1
# perf probe -l
probe:haswell_init_clock_gating (on haswell_init_clock_gating@gpu/drm/i915/intel_pm.c in i915)
probe:valleyview_init_clock_gating (on valleyview_init_clock_gating@gpu/drm/i915/intel_pm.c in i915)
#
Both looks correct.
Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/148411436777.9978.1440275861947194930.stgit@devbox
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2017-01-11 12:59:38 +07:00
|
|
|
addr += baseaddr;
|
2010-03-17 05:06:19 +07:00
|
|
|
/* Find cu die */
|
2015-03-02 19:49:46 +07:00
|
|
|
if (!dwarf_addrdie(dbg->dbg, (Dwarf_Addr)addr, &cudie)) {
|
2010-12-17 20:12:11 +07:00
|
|
|
pr_warning("Failed to find debug information for address %lx\n",
|
|
|
|
addr);
|
2010-04-02 23:50:59 +07:00
|
|
|
ret = -EINVAL;
|
|
|
|
goto end;
|
|
|
|
}
|
2010-03-17 05:06:19 +07:00
|
|
|
|
perf probe: Fix listing incorrect line number with inline function
Fix a bug showing incorrect line number when a probe is put on the head of an
inline function. This patch updates find_perf_probe_point() and introduces new
rules to get correct line number.
- If debuginfo doesn't have a correct file name, we shouldn't return line
number too, because, without file name, line number is meaningless.
- If the address is in a function, it stores the function name and the offset
from the function entry.
- If the address is on a line, it tries to get the relative line number from
the function entry line, except for the address is same as the entry
address of the function (in this case, the relative line number should
be 0).
- If the address is in an inline function entry (call-site), it uses the
inline function call line number as the line on which the address is.
- If the address is in an inline function body, it stores the inline
function name and offset from the inline function call site instead of the
(non-inlined) function.
Cc: 2nddept-manager@sdl.hitachi.co.jp
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Lin Ming <ming.m.lin@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
LKML-Reference: <20110330092605.2132.11629.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-03-30 16:26:05 +07:00
|
|
|
/* Find a corresponding line (filename and lineno) */
|
|
|
|
cu_find_lineinfo(&cudie, addr, &fname, &lineno);
|
|
|
|
/* Don't care whether it failed or not */
|
2010-03-17 05:06:19 +07:00
|
|
|
|
perf probe: Fix listing incorrect line number with inline function
Fix a bug showing incorrect line number when a probe is put on the head of an
inline function. This patch updates find_perf_probe_point() and introduces new
rules to get correct line number.
- If debuginfo doesn't have a correct file name, we shouldn't return line
number too, because, without file name, line number is meaningless.
- If the address is in a function, it stores the function name and the offset
from the function entry.
- If the address is on a line, it tries to get the relative line number from
the function entry line, except for the address is same as the entry
address of the function (in this case, the relative line number should
be 0).
- If the address is in an inline function entry (call-site), it uses the
inline function call line number as the line on which the address is.
- If the address is in an inline function body, it stores the inline
function name and offset from the inline function call site instead of the
(non-inlined) function.
Cc: 2nddept-manager@sdl.hitachi.co.jp
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Lin Ming <ming.m.lin@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
LKML-Reference: <20110330092605.2132.11629.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-03-30 16:26:05 +07:00
|
|
|
/* Find a corresponding function (name, baseline and baseaddr) */
|
2011-06-27 14:27:27 +07:00
|
|
|
if (die_find_realfunc(&cudie, (Dwarf_Addr)addr, &spdie)) {
|
perf probe: Fix listing incorrect line number with inline function
Fix a bug showing incorrect line number when a probe is put on the head of an
inline function. This patch updates find_perf_probe_point() and introduces new
rules to get correct line number.
- If debuginfo doesn't have a correct file name, we shouldn't return line
number too, because, without file name, line number is meaningless.
- If the address is in a function, it stores the function name and the offset
from the function entry.
- If the address is on a line, it tries to get the relative line number from
the function entry line, except for the address is same as the entry
address of the function (in this case, the relative line number should
be 0).
- If the address is in an inline function entry (call-site), it uses the
inline function call line number as the line on which the address is.
- If the address is in an inline function body, it stores the inline
function name and offset from the inline function call site instead of the
(non-inlined) function.
Cc: 2nddept-manager@sdl.hitachi.co.jp
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Lin Ming <ming.m.lin@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
LKML-Reference: <20110330092605.2132.11629.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-03-30 16:26:05 +07:00
|
|
|
/* Get function entry information */
|
perf probe: Fix to find line information for probe list
Fix to find the correct (as much as possible) line information for
listing probes. Without this fix, perf probe --list action will show
incorrect line information as below;
probe:getname_flags (on getname_flags@ksrc/linux-3/fs/namei.c)
probe:getname_flags_1 (on getname:-89@x86/include/asm/current.h)
probe:getname_flags_2 (on user_path_at_empty:-2054@x86/include/asm/current.h)
The minus line number is obviously wrong, and current.h is not related
to the probe point. Deeper investigation discovered that there were 2
issues related to this bug, and minor typos too.
The 1st issue is the rack of considering about nested inlined functions,
which causes the wrong (relative) line number.
The 2nd issue is that the dwarf line info is not correct at those
points. It points 14th line of current.h.
Since it seems that the line info includes somewhat unreliable
information, this fixes perf to try to find correct line information
from both of debuginfo and line info as below.
1) Probe address is the entry of a function instance
In this case, the line is set as the function declared line.
2) Probe address is the entry of an expanded inline function block
In this case, the line is set as the function call-site line.
This means that the line number is relative from the entry line
of caller function (which can be an inlined function if nested)
3) Probe address is inside a function instance or an expanded
inline function block
In this case, perf probe queries the line number from lineinfo
and verify the function declared file is same as the file name
queried from lineinfo.
If the file name is different, it is a failure case. The probe
address is shown as symbol+offset.
4) Probe address is not in the any function instance
This is a failure case, the probe address is shown as
symbol+offset.
With this fix, perf probe -l shows correct probe lines as below;
probe:getname_flags (on getname_flags@ksrc/linux-3/fs/namei.c)
probe:getname_flags_1 (on getname:2@ksrc/linux-3/fs/namei.c)
probe:getname_flags_2 (on user_path_at_empty:4@ksrc/linux-3/fs/namei.c)
Changes at v2:
- Fix typos in the function comments. (Thanks to Namhyung Kim)
- Use die_find_top_inlinefunc instead of die_find_inlinefunc_next.
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/20130930092144.1693.11058.stgit@udc4-manage.rcp.hitachi.co.jp
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2013-09-30 16:21:44 +07:00
|
|
|
func = basefunc = dwarf_diename(&spdie);
|
|
|
|
if (!func ||
|
perf probe: Fix listing incorrect line number with inline function
Fix a bug showing incorrect line number when a probe is put on the head of an
inline function. This patch updates find_perf_probe_point() and introduces new
rules to get correct line number.
- If debuginfo doesn't have a correct file name, we shouldn't return line
number too, because, without file name, line number is meaningless.
- If the address is in a function, it stores the function name and the offset
from the function entry.
- If the address is on a line, it tries to get the relative line number from
the function entry line, except for the address is same as the entry
address of the function (in this case, the relative line number should
be 0).
- If the address is in an inline function entry (call-site), it uses the
inline function call line number as the line on which the address is.
- If the address is in an inline function body, it stores the inline
function name and offset from the inline function call site instead of the
(non-inlined) function.
Cc: 2nddept-manager@sdl.hitachi.co.jp
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Lin Ming <ming.m.lin@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
LKML-Reference: <20110330092605.2132.11629.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-03-30 16:26:05 +07:00
|
|
|
dwarf_entrypc(&spdie, &baseaddr) != 0 ||
|
perf probe: Fix to find line information for probe list
Fix to find the correct (as much as possible) line information for
listing probes. Without this fix, perf probe --list action will show
incorrect line information as below;
probe:getname_flags (on getname_flags@ksrc/linux-3/fs/namei.c)
probe:getname_flags_1 (on getname:-89@x86/include/asm/current.h)
probe:getname_flags_2 (on user_path_at_empty:-2054@x86/include/asm/current.h)
The minus line number is obviously wrong, and current.h is not related
to the probe point. Deeper investigation discovered that there were 2
issues related to this bug, and minor typos too.
The 1st issue is the rack of considering about nested inlined functions,
which causes the wrong (relative) line number.
The 2nd issue is that the dwarf line info is not correct at those
points. It points 14th line of current.h.
Since it seems that the line info includes somewhat unreliable
information, this fixes perf to try to find correct line information
from both of debuginfo and line info as below.
1) Probe address is the entry of a function instance
In this case, the line is set as the function declared line.
2) Probe address is the entry of an expanded inline function block
In this case, the line is set as the function call-site line.
This means that the line number is relative from the entry line
of caller function (which can be an inlined function if nested)
3) Probe address is inside a function instance or an expanded
inline function block
In this case, perf probe queries the line number from lineinfo
and verify the function declared file is same as the file name
queried from lineinfo.
If the file name is different, it is a failure case. The probe
address is shown as symbol+offset.
4) Probe address is not in the any function instance
This is a failure case, the probe address is shown as
symbol+offset.
With this fix, perf probe -l shows correct probe lines as below;
probe:getname_flags (on getname_flags@ksrc/linux-3/fs/namei.c)
probe:getname_flags_1 (on getname:2@ksrc/linux-3/fs/namei.c)
probe:getname_flags_2 (on user_path_at_empty:4@ksrc/linux-3/fs/namei.c)
Changes at v2:
- Fix typos in the function comments. (Thanks to Namhyung Kim)
- Use die_find_top_inlinefunc instead of die_find_inlinefunc_next.
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/20130930092144.1693.11058.stgit@udc4-manage.rcp.hitachi.co.jp
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2013-09-30 16:21:44 +07:00
|
|
|
dwarf_decl_line(&spdie, &baseline) != 0) {
|
|
|
|
lineno = 0;
|
perf probe: Fix listing incorrect line number with inline function
Fix a bug showing incorrect line number when a probe is put on the head of an
inline function. This patch updates find_perf_probe_point() and introduces new
rules to get correct line number.
- If debuginfo doesn't have a correct file name, we shouldn't return line
number too, because, without file name, line number is meaningless.
- If the address is in a function, it stores the function name and the offset
from the function entry.
- If the address is on a line, it tries to get the relative line number from
the function entry line, except for the address is same as the entry
address of the function (in this case, the relative line number should
be 0).
- If the address is in an inline function entry (call-site), it uses the
inline function call line number as the line on which the address is.
- If the address is in an inline function body, it stores the inline
function name and offset from the inline function call site instead of the
(non-inlined) function.
Cc: 2nddept-manager@sdl.hitachi.co.jp
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Lin Ming <ming.m.lin@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
LKML-Reference: <20110330092605.2132.11629.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-03-30 16:26:05 +07:00
|
|
|
goto post;
|
perf probe: Fix to find line information for probe list
Fix to find the correct (as much as possible) line information for
listing probes. Without this fix, perf probe --list action will show
incorrect line information as below;
probe:getname_flags (on getname_flags@ksrc/linux-3/fs/namei.c)
probe:getname_flags_1 (on getname:-89@x86/include/asm/current.h)
probe:getname_flags_2 (on user_path_at_empty:-2054@x86/include/asm/current.h)
The minus line number is obviously wrong, and current.h is not related
to the probe point. Deeper investigation discovered that there were 2
issues related to this bug, and minor typos too.
The 1st issue is the rack of considering about nested inlined functions,
which causes the wrong (relative) line number.
The 2nd issue is that the dwarf line info is not correct at those
points. It points 14th line of current.h.
Since it seems that the line info includes somewhat unreliable
information, this fixes perf to try to find correct line information
from both of debuginfo and line info as below.
1) Probe address is the entry of a function instance
In this case, the line is set as the function declared line.
2) Probe address is the entry of an expanded inline function block
In this case, the line is set as the function call-site line.
This means that the line number is relative from the entry line
of caller function (which can be an inlined function if nested)
3) Probe address is inside a function instance or an expanded
inline function block
In this case, perf probe queries the line number from lineinfo
and verify the function declared file is same as the file name
queried from lineinfo.
If the file name is different, it is a failure case. The probe
address is shown as symbol+offset.
4) Probe address is not in the any function instance
This is a failure case, the probe address is shown as
symbol+offset.
With this fix, perf probe -l shows correct probe lines as below;
probe:getname_flags (on getname_flags@ksrc/linux-3/fs/namei.c)
probe:getname_flags_1 (on getname:2@ksrc/linux-3/fs/namei.c)
probe:getname_flags_2 (on user_path_at_empty:4@ksrc/linux-3/fs/namei.c)
Changes at v2:
- Fix typos in the function comments. (Thanks to Namhyung Kim)
- Use die_find_top_inlinefunc instead of die_find_inlinefunc_next.
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/20130930092144.1693.11058.stgit@udc4-manage.rcp.hitachi.co.jp
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2013-09-30 16:21:44 +07:00
|
|
|
}
|
perf probe: Fix listing incorrect line number with inline function
Fix a bug showing incorrect line number when a probe is put on the head of an
inline function. This patch updates find_perf_probe_point() and introduces new
rules to get correct line number.
- If debuginfo doesn't have a correct file name, we shouldn't return line
number too, because, without file name, line number is meaningless.
- If the address is in a function, it stores the function name and the offset
from the function entry.
- If the address is on a line, it tries to get the relative line number from
the function entry line, except for the address is same as the entry
address of the function (in this case, the relative line number should
be 0).
- If the address is in an inline function entry (call-site), it uses the
inline function call line number as the line on which the address is.
- If the address is in an inline function body, it stores the inline
function name and offset from the inline function call site instead of the
(non-inlined) function.
Cc: 2nddept-manager@sdl.hitachi.co.jp
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Lin Ming <ming.m.lin@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
LKML-Reference: <20110330092605.2132.11629.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-03-30 16:26:05 +07:00
|
|
|
|
2013-10-11 19:23:17 +07:00
|
|
|
fname = dwarf_decl_file(&spdie);
|
perf probe: Fix to find line information for probe list
Fix to find the correct (as much as possible) line information for
listing probes. Without this fix, perf probe --list action will show
incorrect line information as below;
probe:getname_flags (on getname_flags@ksrc/linux-3/fs/namei.c)
probe:getname_flags_1 (on getname:-89@x86/include/asm/current.h)
probe:getname_flags_2 (on user_path_at_empty:-2054@x86/include/asm/current.h)
The minus line number is obviously wrong, and current.h is not related
to the probe point. Deeper investigation discovered that there were 2
issues related to this bug, and minor typos too.
The 1st issue is the rack of considering about nested inlined functions,
which causes the wrong (relative) line number.
The 2nd issue is that the dwarf line info is not correct at those
points. It points 14th line of current.h.
Since it seems that the line info includes somewhat unreliable
information, this fixes perf to try to find correct line information
from both of debuginfo and line info as below.
1) Probe address is the entry of a function instance
In this case, the line is set as the function declared line.
2) Probe address is the entry of an expanded inline function block
In this case, the line is set as the function call-site line.
This means that the line number is relative from the entry line
of caller function (which can be an inlined function if nested)
3) Probe address is inside a function instance or an expanded
inline function block
In this case, perf probe queries the line number from lineinfo
and verify the function declared file is same as the file name
queried from lineinfo.
If the file name is different, it is a failure case. The probe
address is shown as symbol+offset.
4) Probe address is not in the any function instance
This is a failure case, the probe address is shown as
symbol+offset.
With this fix, perf probe -l shows correct probe lines as below;
probe:getname_flags (on getname_flags@ksrc/linux-3/fs/namei.c)
probe:getname_flags_1 (on getname:2@ksrc/linux-3/fs/namei.c)
probe:getname_flags_2 (on user_path_at_empty:4@ksrc/linux-3/fs/namei.c)
Changes at v2:
- Fix typos in the function comments. (Thanks to Namhyung Kim)
- Use die_find_top_inlinefunc instead of die_find_inlinefunc_next.
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/20130930092144.1693.11058.stgit@udc4-manage.rcp.hitachi.co.jp
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2013-09-30 16:21:44 +07:00
|
|
|
if (addr == (unsigned long)baseaddr) {
|
perf probe: Fix listing incorrect line number with inline function
Fix a bug showing incorrect line number when a probe is put on the head of an
inline function. This patch updates find_perf_probe_point() and introduces new
rules to get correct line number.
- If debuginfo doesn't have a correct file name, we shouldn't return line
number too, because, without file name, line number is meaningless.
- If the address is in a function, it stores the function name and the offset
from the function entry.
- If the address is on a line, it tries to get the relative line number from
the function entry line, except for the address is same as the entry
address of the function (in this case, the relative line number should
be 0).
- If the address is in an inline function entry (call-site), it uses the
inline function call line number as the line on which the address is.
- If the address is in an inline function body, it stores the inline
function name and offset from the inline function call site instead of the
(non-inlined) function.
Cc: 2nddept-manager@sdl.hitachi.co.jp
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Lin Ming <ming.m.lin@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
LKML-Reference: <20110330092605.2132.11629.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-03-30 16:26:05 +07:00
|
|
|
/* Function entry - Relative line number is 0 */
|
|
|
|
lineno = baseline;
|
perf probe: Fix to find line information for probe list
Fix to find the correct (as much as possible) line information for
listing probes. Without this fix, perf probe --list action will show
incorrect line information as below;
probe:getname_flags (on getname_flags@ksrc/linux-3/fs/namei.c)
probe:getname_flags_1 (on getname:-89@x86/include/asm/current.h)
probe:getname_flags_2 (on user_path_at_empty:-2054@x86/include/asm/current.h)
The minus line number is obviously wrong, and current.h is not related
to the probe point. Deeper investigation discovered that there were 2
issues related to this bug, and minor typos too.
The 1st issue is the rack of considering about nested inlined functions,
which causes the wrong (relative) line number.
The 2nd issue is that the dwarf line info is not correct at those
points. It points 14th line of current.h.
Since it seems that the line info includes somewhat unreliable
information, this fixes perf to try to find correct line information
from both of debuginfo and line info as below.
1) Probe address is the entry of a function instance
In this case, the line is set as the function declared line.
2) Probe address is the entry of an expanded inline function block
In this case, the line is set as the function call-site line.
This means that the line number is relative from the entry line
of caller function (which can be an inlined function if nested)
3) Probe address is inside a function instance or an expanded
inline function block
In this case, perf probe queries the line number from lineinfo
and verify the function declared file is same as the file name
queried from lineinfo.
If the file name is different, it is a failure case. The probe
address is shown as symbol+offset.
4) Probe address is not in the any function instance
This is a failure case, the probe address is shown as
symbol+offset.
With this fix, perf probe -l shows correct probe lines as below;
probe:getname_flags (on getname_flags@ksrc/linux-3/fs/namei.c)
probe:getname_flags_1 (on getname:2@ksrc/linux-3/fs/namei.c)
probe:getname_flags_2 (on user_path_at_empty:4@ksrc/linux-3/fs/namei.c)
Changes at v2:
- Fix typos in the function comments. (Thanks to Namhyung Kim)
- Use die_find_top_inlinefunc instead of die_find_inlinefunc_next.
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/20130930092144.1693.11058.stgit@udc4-manage.rcp.hitachi.co.jp
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2013-09-30 16:21:44 +07:00
|
|
|
goto post;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Track down the inline functions step by step */
|
|
|
|
while (die_find_top_inlinefunc(&spdie, (Dwarf_Addr)addr,
|
|
|
|
&indie)) {
|
|
|
|
/* There is an inline function */
|
perf probe: Fix listing incorrect line number with inline function
Fix a bug showing incorrect line number when a probe is put on the head of an
inline function. This patch updates find_perf_probe_point() and introduces new
rules to get correct line number.
- If debuginfo doesn't have a correct file name, we shouldn't return line
number too, because, without file name, line number is meaningless.
- If the address is in a function, it stores the function name and the offset
from the function entry.
- If the address is on a line, it tries to get the relative line number from
the function entry line, except for the address is same as the entry
address of the function (in this case, the relative line number should
be 0).
- If the address is in an inline function entry (call-site), it uses the
inline function call line number as the line on which the address is.
- If the address is in an inline function body, it stores the inline
function name and offset from the inline function call site instead of the
(non-inlined) function.
Cc: 2nddept-manager@sdl.hitachi.co.jp
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Lin Ming <ming.m.lin@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
LKML-Reference: <20110330092605.2132.11629.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-03-30 16:26:05 +07:00
|
|
|
if (dwarf_entrypc(&indie, &_addr) == 0 &&
|
perf probe: Fix to find line information for probe list
Fix to find the correct (as much as possible) line information for
listing probes. Without this fix, perf probe --list action will show
incorrect line information as below;
probe:getname_flags (on getname_flags@ksrc/linux-3/fs/namei.c)
probe:getname_flags_1 (on getname:-89@x86/include/asm/current.h)
probe:getname_flags_2 (on user_path_at_empty:-2054@x86/include/asm/current.h)
The minus line number is obviously wrong, and current.h is not related
to the probe point. Deeper investigation discovered that there were 2
issues related to this bug, and minor typos too.
The 1st issue is the rack of considering about nested inlined functions,
which causes the wrong (relative) line number.
The 2nd issue is that the dwarf line info is not correct at those
points. It points 14th line of current.h.
Since it seems that the line info includes somewhat unreliable
information, this fixes perf to try to find correct line information
from both of debuginfo and line info as below.
1) Probe address is the entry of a function instance
In this case, the line is set as the function declared line.
2) Probe address is the entry of an expanded inline function block
In this case, the line is set as the function call-site line.
This means that the line number is relative from the entry line
of caller function (which can be an inlined function if nested)
3) Probe address is inside a function instance or an expanded
inline function block
In this case, perf probe queries the line number from lineinfo
and verify the function declared file is same as the file name
queried from lineinfo.
If the file name is different, it is a failure case. The probe
address is shown as symbol+offset.
4) Probe address is not in the any function instance
This is a failure case, the probe address is shown as
symbol+offset.
With this fix, perf probe -l shows correct probe lines as below;
probe:getname_flags (on getname_flags@ksrc/linux-3/fs/namei.c)
probe:getname_flags_1 (on getname:2@ksrc/linux-3/fs/namei.c)
probe:getname_flags_2 (on user_path_at_empty:4@ksrc/linux-3/fs/namei.c)
Changes at v2:
- Fix typos in the function comments. (Thanks to Namhyung Kim)
- Use die_find_top_inlinefunc instead of die_find_inlinefunc_next.
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/20130930092144.1693.11058.stgit@udc4-manage.rcp.hitachi.co.jp
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2013-09-30 16:21:44 +07:00
|
|
|
_addr == addr) {
|
perf probe: Fix listing incorrect line number with inline function
Fix a bug showing incorrect line number when a probe is put on the head of an
inline function. This patch updates find_perf_probe_point() and introduces new
rules to get correct line number.
- If debuginfo doesn't have a correct file name, we shouldn't return line
number too, because, without file name, line number is meaningless.
- If the address is in a function, it stores the function name and the offset
from the function entry.
- If the address is on a line, it tries to get the relative line number from
the function entry line, except for the address is same as the entry
address of the function (in this case, the relative line number should
be 0).
- If the address is in an inline function entry (call-site), it uses the
inline function call line number as the line on which the address is.
- If the address is in an inline function body, it stores the inline
function name and offset from the inline function call site instead of the
(non-inlined) function.
Cc: 2nddept-manager@sdl.hitachi.co.jp
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Lin Ming <ming.m.lin@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
LKML-Reference: <20110330092605.2132.11629.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-03-30 16:26:05 +07:00
|
|
|
/*
|
|
|
|
* addr is at an inline function entry.
|
|
|
|
* In this case, lineno should be the call-site
|
perf probe: Fix to find line information for probe list
Fix to find the correct (as much as possible) line information for
listing probes. Without this fix, perf probe --list action will show
incorrect line information as below;
probe:getname_flags (on getname_flags@ksrc/linux-3/fs/namei.c)
probe:getname_flags_1 (on getname:-89@x86/include/asm/current.h)
probe:getname_flags_2 (on user_path_at_empty:-2054@x86/include/asm/current.h)
The minus line number is obviously wrong, and current.h is not related
to the probe point. Deeper investigation discovered that there were 2
issues related to this bug, and minor typos too.
The 1st issue is the rack of considering about nested inlined functions,
which causes the wrong (relative) line number.
The 2nd issue is that the dwarf line info is not correct at those
points. It points 14th line of current.h.
Since it seems that the line info includes somewhat unreliable
information, this fixes perf to try to find correct line information
from both of debuginfo and line info as below.
1) Probe address is the entry of a function instance
In this case, the line is set as the function declared line.
2) Probe address is the entry of an expanded inline function block
In this case, the line is set as the function call-site line.
This means that the line number is relative from the entry line
of caller function (which can be an inlined function if nested)
3) Probe address is inside a function instance or an expanded
inline function block
In this case, perf probe queries the line number from lineinfo
and verify the function declared file is same as the file name
queried from lineinfo.
If the file name is different, it is a failure case. The probe
address is shown as symbol+offset.
4) Probe address is not in the any function instance
This is a failure case, the probe address is shown as
symbol+offset.
With this fix, perf probe -l shows correct probe lines as below;
probe:getname_flags (on getname_flags@ksrc/linux-3/fs/namei.c)
probe:getname_flags_1 (on getname:2@ksrc/linux-3/fs/namei.c)
probe:getname_flags_2 (on user_path_at_empty:4@ksrc/linux-3/fs/namei.c)
Changes at v2:
- Fix typos in the function comments. (Thanks to Namhyung Kim)
- Use die_find_top_inlinefunc instead of die_find_inlinefunc_next.
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/20130930092144.1693.11058.stgit@udc4-manage.rcp.hitachi.co.jp
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2013-09-30 16:21:44 +07:00
|
|
|
* line number. (overwrite lineinfo)
|
perf probe: Fix listing incorrect line number with inline function
Fix a bug showing incorrect line number when a probe is put on the head of an
inline function. This patch updates find_perf_probe_point() and introduces new
rules to get correct line number.
- If debuginfo doesn't have a correct file name, we shouldn't return line
number too, because, without file name, line number is meaningless.
- If the address is in a function, it stores the function name and the offset
from the function entry.
- If the address is on a line, it tries to get the relative line number from
the function entry line, except for the address is same as the entry
address of the function (in this case, the relative line number should
be 0).
- If the address is in an inline function entry (call-site), it uses the
inline function call line number as the line on which the address is.
- If the address is in an inline function body, it stores the inline
function name and offset from the inline function call site instead of the
(non-inlined) function.
Cc: 2nddept-manager@sdl.hitachi.co.jp
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Lin Ming <ming.m.lin@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
LKML-Reference: <20110330092605.2132.11629.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-03-30 16:26:05 +07:00
|
|
|
*/
|
|
|
|
lineno = die_get_call_lineno(&indie);
|
perf probe: Fix to find line information for probe list
Fix to find the correct (as much as possible) line information for
listing probes. Without this fix, perf probe --list action will show
incorrect line information as below;
probe:getname_flags (on getname_flags@ksrc/linux-3/fs/namei.c)
probe:getname_flags_1 (on getname:-89@x86/include/asm/current.h)
probe:getname_flags_2 (on user_path_at_empty:-2054@x86/include/asm/current.h)
The minus line number is obviously wrong, and current.h is not related
to the probe point. Deeper investigation discovered that there were 2
issues related to this bug, and minor typos too.
The 1st issue is the rack of considering about nested inlined functions,
which causes the wrong (relative) line number.
The 2nd issue is that the dwarf line info is not correct at those
points. It points 14th line of current.h.
Since it seems that the line info includes somewhat unreliable
information, this fixes perf to try to find correct line information
from both of debuginfo and line info as below.
1) Probe address is the entry of a function instance
In this case, the line is set as the function declared line.
2) Probe address is the entry of an expanded inline function block
In this case, the line is set as the function call-site line.
This means that the line number is relative from the entry line
of caller function (which can be an inlined function if nested)
3) Probe address is inside a function instance or an expanded
inline function block
In this case, perf probe queries the line number from lineinfo
and verify the function declared file is same as the file name
queried from lineinfo.
If the file name is different, it is a failure case. The probe
address is shown as symbol+offset.
4) Probe address is not in the any function instance
This is a failure case, the probe address is shown as
symbol+offset.
With this fix, perf probe -l shows correct probe lines as below;
probe:getname_flags (on getname_flags@ksrc/linux-3/fs/namei.c)
probe:getname_flags_1 (on getname:2@ksrc/linux-3/fs/namei.c)
probe:getname_flags_2 (on user_path_at_empty:4@ksrc/linux-3/fs/namei.c)
Changes at v2:
- Fix typos in the function comments. (Thanks to Namhyung Kim)
- Use die_find_top_inlinefunc instead of die_find_inlinefunc_next.
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/20130930092144.1693.11058.stgit@udc4-manage.rcp.hitachi.co.jp
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2013-09-30 16:21:44 +07:00
|
|
|
fname = die_get_call_file(&indie);
|
|
|
|
break;
|
|
|
|
} else {
|
perf probe: Fix listing incorrect line number with inline function
Fix a bug showing incorrect line number when a probe is put on the head of an
inline function. This patch updates find_perf_probe_point() and introduces new
rules to get correct line number.
- If debuginfo doesn't have a correct file name, we shouldn't return line
number too, because, without file name, line number is meaningless.
- If the address is in a function, it stores the function name and the offset
from the function entry.
- If the address is on a line, it tries to get the relative line number from
the function entry line, except for the address is same as the entry
address of the function (in this case, the relative line number should
be 0).
- If the address is in an inline function entry (call-site), it uses the
inline function call line number as the line on which the address is.
- If the address is in an inline function body, it stores the inline
function name and offset from the inline function call site instead of the
(non-inlined) function.
Cc: 2nddept-manager@sdl.hitachi.co.jp
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Lin Ming <ming.m.lin@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
LKML-Reference: <20110330092605.2132.11629.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-03-30 16:26:05 +07:00
|
|
|
/*
|
|
|
|
* addr is in an inline function body.
|
|
|
|
* Since lineno points one of the lines
|
|
|
|
* of the inline function, baseline should
|
|
|
|
* be the entry line of the inline function.
|
|
|
|
*/
|
2010-04-13 00:17:35 +07:00
|
|
|
tmp = dwarf_diename(&indie);
|
perf probe: Fix to find line information for probe list
Fix to find the correct (as much as possible) line information for
listing probes. Without this fix, perf probe --list action will show
incorrect line information as below;
probe:getname_flags (on getname_flags@ksrc/linux-3/fs/namei.c)
probe:getname_flags_1 (on getname:-89@x86/include/asm/current.h)
probe:getname_flags_2 (on user_path_at_empty:-2054@x86/include/asm/current.h)
The minus line number is obviously wrong, and current.h is not related
to the probe point. Deeper investigation discovered that there were 2
issues related to this bug, and minor typos too.
The 1st issue is the rack of considering about nested inlined functions,
which causes the wrong (relative) line number.
The 2nd issue is that the dwarf line info is not correct at those
points. It points 14th line of current.h.
Since it seems that the line info includes somewhat unreliable
information, this fixes perf to try to find correct line information
from both of debuginfo and line info as below.
1) Probe address is the entry of a function instance
In this case, the line is set as the function declared line.
2) Probe address is the entry of an expanded inline function block
In this case, the line is set as the function call-site line.
This means that the line number is relative from the entry line
of caller function (which can be an inlined function if nested)
3) Probe address is inside a function instance or an expanded
inline function block
In this case, perf probe queries the line number from lineinfo
and verify the function declared file is same as the file name
queried from lineinfo.
If the file name is different, it is a failure case. The probe
address is shown as symbol+offset.
4) Probe address is not in the any function instance
This is a failure case, the probe address is shown as
symbol+offset.
With this fix, perf probe -l shows correct probe lines as below;
probe:getname_flags (on getname_flags@ksrc/linux-3/fs/namei.c)
probe:getname_flags_1 (on getname:2@ksrc/linux-3/fs/namei.c)
probe:getname_flags_2 (on user_path_at_empty:4@ksrc/linux-3/fs/namei.c)
Changes at v2:
- Fix typos in the function comments. (Thanks to Namhyung Kim)
- Use die_find_top_inlinefunc instead of die_find_inlinefunc_next.
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/20130930092144.1693.11058.stgit@udc4-manage.rcp.hitachi.co.jp
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2013-09-30 16:21:44 +07:00
|
|
|
if (!tmp ||
|
|
|
|
dwarf_decl_line(&indie, &baseline) != 0)
|
|
|
|
break;
|
|
|
|
func = tmp;
|
|
|
|
spdie = indie;
|
2010-04-13 00:17:35 +07:00
|
|
|
}
|
2010-03-17 05:06:19 +07:00
|
|
|
}
|
perf probe: Fix to find line information for probe list
Fix to find the correct (as much as possible) line information for
listing probes. Without this fix, perf probe --list action will show
incorrect line information as below;
probe:getname_flags (on getname_flags@ksrc/linux-3/fs/namei.c)
probe:getname_flags_1 (on getname:-89@x86/include/asm/current.h)
probe:getname_flags_2 (on user_path_at_empty:-2054@x86/include/asm/current.h)
The minus line number is obviously wrong, and current.h is not related
to the probe point. Deeper investigation discovered that there were 2
issues related to this bug, and minor typos too.
The 1st issue is the rack of considering about nested inlined functions,
which causes the wrong (relative) line number.
The 2nd issue is that the dwarf line info is not correct at those
points. It points 14th line of current.h.
Since it seems that the line info includes somewhat unreliable
information, this fixes perf to try to find correct line information
from both of debuginfo and line info as below.
1) Probe address is the entry of a function instance
In this case, the line is set as the function declared line.
2) Probe address is the entry of an expanded inline function block
In this case, the line is set as the function call-site line.
This means that the line number is relative from the entry line
of caller function (which can be an inlined function if nested)
3) Probe address is inside a function instance or an expanded
inline function block
In this case, perf probe queries the line number from lineinfo
and verify the function declared file is same as the file name
queried from lineinfo.
If the file name is different, it is a failure case. The probe
address is shown as symbol+offset.
4) Probe address is not in the any function instance
This is a failure case, the probe address is shown as
symbol+offset.
With this fix, perf probe -l shows correct probe lines as below;
probe:getname_flags (on getname_flags@ksrc/linux-3/fs/namei.c)
probe:getname_flags_1 (on getname:2@ksrc/linux-3/fs/namei.c)
probe:getname_flags_2 (on user_path_at_empty:4@ksrc/linux-3/fs/namei.c)
Changes at v2:
- Fix typos in the function comments. (Thanks to Namhyung Kim)
- Use die_find_top_inlinefunc instead of die_find_inlinefunc_next.
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/20130930092144.1693.11058.stgit@udc4-manage.rcp.hitachi.co.jp
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2013-09-30 16:21:44 +07:00
|
|
|
/* Verify the lineno and baseline are in a same file */
|
|
|
|
tmp = dwarf_decl_file(&spdie);
|
|
|
|
if (!tmp || strcmp(tmp, fname) != 0)
|
|
|
|
lineno = 0;
|
perf probe: Fix listing incorrect line number with inline function
Fix a bug showing incorrect line number when a probe is put on the head of an
inline function. This patch updates find_perf_probe_point() and introduces new
rules to get correct line number.
- If debuginfo doesn't have a correct file name, we shouldn't return line
number too, because, without file name, line number is meaningless.
- If the address is in a function, it stores the function name and the offset
from the function entry.
- If the address is on a line, it tries to get the relative line number from
the function entry line, except for the address is same as the entry
address of the function (in this case, the relative line number should
be 0).
- If the address is in an inline function entry (call-site), it uses the
inline function call line number as the line on which the address is.
- If the address is in an inline function body, it stores the inline
function name and offset from the inline function call site instead of the
(non-inlined) function.
Cc: 2nddept-manager@sdl.hitachi.co.jp
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Lin Ming <ming.m.lin@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
LKML-Reference: <20110330092605.2132.11629.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-03-30 16:26:05 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
post:
|
|
|
|
/* Make a relative line number or an offset */
|
|
|
|
if (lineno)
|
|
|
|
ppt->line = lineno - baseline;
|
perf probe: Fix to find line information for probe list
Fix to find the correct (as much as possible) line information for
listing probes. Without this fix, perf probe --list action will show
incorrect line information as below;
probe:getname_flags (on getname_flags@ksrc/linux-3/fs/namei.c)
probe:getname_flags_1 (on getname:-89@x86/include/asm/current.h)
probe:getname_flags_2 (on user_path_at_empty:-2054@x86/include/asm/current.h)
The minus line number is obviously wrong, and current.h is not related
to the probe point. Deeper investigation discovered that there were 2
issues related to this bug, and minor typos too.
The 1st issue is the rack of considering about nested inlined functions,
which causes the wrong (relative) line number.
The 2nd issue is that the dwarf line info is not correct at those
points. It points 14th line of current.h.
Since it seems that the line info includes somewhat unreliable
information, this fixes perf to try to find correct line information
from both of debuginfo and line info as below.
1) Probe address is the entry of a function instance
In this case, the line is set as the function declared line.
2) Probe address is the entry of an expanded inline function block
In this case, the line is set as the function call-site line.
This means that the line number is relative from the entry line
of caller function (which can be an inlined function if nested)
3) Probe address is inside a function instance or an expanded
inline function block
In this case, perf probe queries the line number from lineinfo
and verify the function declared file is same as the file name
queried from lineinfo.
If the file name is different, it is a failure case. The probe
address is shown as symbol+offset.
4) Probe address is not in the any function instance
This is a failure case, the probe address is shown as
symbol+offset.
With this fix, perf probe -l shows correct probe lines as below;
probe:getname_flags (on getname_flags@ksrc/linux-3/fs/namei.c)
probe:getname_flags_1 (on getname:2@ksrc/linux-3/fs/namei.c)
probe:getname_flags_2 (on user_path_at_empty:4@ksrc/linux-3/fs/namei.c)
Changes at v2:
- Fix typos in the function comments. (Thanks to Namhyung Kim)
- Use die_find_top_inlinefunc instead of die_find_inlinefunc_next.
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/20130930092144.1693.11058.stgit@udc4-manage.rcp.hitachi.co.jp
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2013-09-30 16:21:44 +07:00
|
|
|
else if (basefunc) {
|
perf probe: Fix listing incorrect line number with inline function
Fix a bug showing incorrect line number when a probe is put on the head of an
inline function. This patch updates find_perf_probe_point() and introduces new
rules to get correct line number.
- If debuginfo doesn't have a correct file name, we shouldn't return line
number too, because, without file name, line number is meaningless.
- If the address is in a function, it stores the function name and the offset
from the function entry.
- If the address is on a line, it tries to get the relative line number from
the function entry line, except for the address is same as the entry
address of the function (in this case, the relative line number should
be 0).
- If the address is in an inline function entry (call-site), it uses the
inline function call line number as the line on which the address is.
- If the address is in an inline function body, it stores the inline
function name and offset from the inline function call site instead of the
(non-inlined) function.
Cc: 2nddept-manager@sdl.hitachi.co.jp
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Lin Ming <ming.m.lin@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
LKML-Reference: <20110330092605.2132.11629.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-03-30 16:26:05 +07:00
|
|
|
ppt->offset = addr - (unsigned long)baseaddr;
|
perf probe: Fix to find line information for probe list
Fix to find the correct (as much as possible) line information for
listing probes. Without this fix, perf probe --list action will show
incorrect line information as below;
probe:getname_flags (on getname_flags@ksrc/linux-3/fs/namei.c)
probe:getname_flags_1 (on getname:-89@x86/include/asm/current.h)
probe:getname_flags_2 (on user_path_at_empty:-2054@x86/include/asm/current.h)
The minus line number is obviously wrong, and current.h is not related
to the probe point. Deeper investigation discovered that there were 2
issues related to this bug, and minor typos too.
The 1st issue is the rack of considering about nested inlined functions,
which causes the wrong (relative) line number.
The 2nd issue is that the dwarf line info is not correct at those
points. It points 14th line of current.h.
Since it seems that the line info includes somewhat unreliable
information, this fixes perf to try to find correct line information
from both of debuginfo and line info as below.
1) Probe address is the entry of a function instance
In this case, the line is set as the function declared line.
2) Probe address is the entry of an expanded inline function block
In this case, the line is set as the function call-site line.
This means that the line number is relative from the entry line
of caller function (which can be an inlined function if nested)
3) Probe address is inside a function instance or an expanded
inline function block
In this case, perf probe queries the line number from lineinfo
and verify the function declared file is same as the file name
queried from lineinfo.
If the file name is different, it is a failure case. The probe
address is shown as symbol+offset.
4) Probe address is not in the any function instance
This is a failure case, the probe address is shown as
symbol+offset.
With this fix, perf probe -l shows correct probe lines as below;
probe:getname_flags (on getname_flags@ksrc/linux-3/fs/namei.c)
probe:getname_flags_1 (on getname:2@ksrc/linux-3/fs/namei.c)
probe:getname_flags_2 (on user_path_at_empty:4@ksrc/linux-3/fs/namei.c)
Changes at v2:
- Fix typos in the function comments. (Thanks to Namhyung Kim)
- Use die_find_top_inlinefunc instead of die_find_inlinefunc_next.
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: http://lkml.kernel.org/r/20130930092144.1693.11058.stgit@udc4-manage.rcp.hitachi.co.jp
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2013-09-30 16:21:44 +07:00
|
|
|
func = basefunc;
|
|
|
|
}
|
perf probe: Fix listing incorrect line number with inline function
Fix a bug showing incorrect line number when a probe is put on the head of an
inline function. This patch updates find_perf_probe_point() and introduces new
rules to get correct line number.
- If debuginfo doesn't have a correct file name, we shouldn't return line
number too, because, without file name, line number is meaningless.
- If the address is in a function, it stores the function name and the offset
from the function entry.
- If the address is on a line, it tries to get the relative line number from
the function entry line, except for the address is same as the entry
address of the function (in this case, the relative line number should
be 0).
- If the address is in an inline function entry (call-site), it uses the
inline function call line number as the line on which the address is.
- If the address is in an inline function body, it stores the inline
function name and offset from the inline function call site instead of the
(non-inlined) function.
Cc: 2nddept-manager@sdl.hitachi.co.jp
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Lin Ming <ming.m.lin@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
LKML-Reference: <20110330092605.2132.11629.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-03-30 16:26:05 +07:00
|
|
|
|
|
|
|
/* Duplicate strings */
|
|
|
|
if (func) {
|
|
|
|
ppt->function = strdup(func);
|
2010-04-13 00:17:56 +07:00
|
|
|
if (ppt->function == NULL) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto end;
|
|
|
|
}
|
2010-03-17 05:06:19 +07:00
|
|
|
}
|
perf probe: Fix listing incorrect line number with inline function
Fix a bug showing incorrect line number when a probe is put on the head of an
inline function. This patch updates find_perf_probe_point() and introduces new
rules to get correct line number.
- If debuginfo doesn't have a correct file name, we shouldn't return line
number too, because, without file name, line number is meaningless.
- If the address is in a function, it stores the function name and the offset
from the function entry.
- If the address is on a line, it tries to get the relative line number from
the function entry line, except for the address is same as the entry
address of the function (in this case, the relative line number should
be 0).
- If the address is in an inline function entry (call-site), it uses the
inline function call line number as the line on which the address is.
- If the address is in an inline function body, it stores the inline
function name and offset from the inline function call site instead of the
(non-inlined) function.
Cc: 2nddept-manager@sdl.hitachi.co.jp
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Lin Ming <ming.m.lin@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
LKML-Reference: <20110330092605.2132.11629.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-03-30 16:26:05 +07:00
|
|
|
if (fname) {
|
|
|
|
ppt->file = strdup(fname);
|
|
|
|
if (ppt->file == NULL) {
|
2013-12-27 03:41:15 +07:00
|
|
|
zfree(&ppt->function);
|
perf probe: Fix listing incorrect line number with inline function
Fix a bug showing incorrect line number when a probe is put on the head of an
inline function. This patch updates find_perf_probe_point() and introduces new
rules to get correct line number.
- If debuginfo doesn't have a correct file name, we shouldn't return line
number too, because, without file name, line number is meaningless.
- If the address is in a function, it stores the function name and the offset
from the function entry.
- If the address is on a line, it tries to get the relative line number from
the function entry line, except for the address is same as the entry
address of the function (in this case, the relative line number should
be 0).
- If the address is in an inline function entry (call-site), it uses the
inline function call line number as the line on which the address is.
- If the address is in an inline function body, it stores the inline
function name and offset from the inline function call site instead of the
(non-inlined) function.
Cc: 2nddept-manager@sdl.hitachi.co.jp
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Lin Ming <ming.m.lin@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
LKML-Reference: <20110330092605.2132.11629.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-03-30 16:26:05 +07:00
|
|
|
ret = -ENOMEM;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
}
|
2010-03-17 05:06:19 +07:00
|
|
|
end:
|
perf probe: Fix listing incorrect line number with inline function
Fix a bug showing incorrect line number when a probe is put on the head of an
inline function. This patch updates find_perf_probe_point() and introduces new
rules to get correct line number.
- If debuginfo doesn't have a correct file name, we shouldn't return line
number too, because, without file name, line number is meaningless.
- If the address is in a function, it stores the function name and the offset
from the function entry.
- If the address is on a line, it tries to get the relative line number from
the function entry line, except for the address is same as the entry
address of the function (in this case, the relative line number should
be 0).
- If the address is in an inline function entry (call-site), it uses the
inline function call line number as the line on which the address is.
- If the address is in an inline function body, it stores the inline
function name and offset from the inline function call site instead of the
(non-inlined) function.
Cc: 2nddept-manager@sdl.hitachi.co.jp
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Lin Ming <ming.m.lin@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
LKML-Reference: <20110330092605.2132.11629.stgit@ltc236.sdl.hitachi.co.jp>
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-03-30 16:26:05 +07:00
|
|
|
if (ret == 0 && (fname || func))
|
|
|
|
ret = 1; /* Found a point */
|
2010-03-17 05:06:19 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-04-15 05:40:07 +07:00
|
|
|
/* Add a line and store the src path */
|
|
|
|
static int line_range_add_line(const char *src, unsigned int lineno,
|
|
|
|
struct line_range *lr)
|
|
|
|
{
|
2010-07-09 16:28:59 +07:00
|
|
|
/* Copy source path */
|
2010-04-15 05:40:07 +07:00
|
|
|
if (!lr->path) {
|
2010-07-09 16:28:59 +07:00
|
|
|
lr->path = strdup(src);
|
|
|
|
if (lr->path == NULL)
|
|
|
|
return -ENOMEM;
|
2010-04-15 05:40:07 +07:00
|
|
|
}
|
2014-02-06 12:32:09 +07:00
|
|
|
return intlist__add(lr->line_list, lineno);
|
2010-04-15 05:40:07 +07:00
|
|
|
}
|
|
|
|
|
2011-01-13 19:45:58 +07:00
|
|
|
static int line_range_walk_cb(const char *fname, int lineno,
|
2012-09-11 05:15:03 +07:00
|
|
|
Dwarf_Addr addr __maybe_unused,
|
2011-01-13 19:45:58 +07:00
|
|
|
void *data)
|
2010-04-15 05:40:07 +07:00
|
|
|
{
|
2011-01-13 19:45:58 +07:00
|
|
|
struct line_finder *lf = data;
|
2014-04-01 11:47:57 +07:00
|
|
|
int err;
|
2010-04-15 05:40:07 +07:00
|
|
|
|
2011-01-13 19:45:58 +07:00
|
|
|
if ((strtailcmp(fname, lf->fname) != 0) ||
|
2010-04-15 05:40:07 +07:00
|
|
|
(lf->lno_s > lineno || lf->lno_e < lineno))
|
2011-01-13 19:45:58 +07:00
|
|
|
return 0;
|
2010-04-15 05:40:07 +07:00
|
|
|
|
2014-04-01 11:47:57 +07:00
|
|
|
err = line_range_add_line(fname, lineno, lf->lr);
|
|
|
|
if (err < 0 && err != -EEXIST)
|
|
|
|
return err;
|
2010-04-15 05:40:07 +07:00
|
|
|
|
2011-01-13 19:45:58 +07:00
|
|
|
return 0;
|
2010-04-15 05:40:07 +07:00
|
|
|
}
|
2010-03-17 05:06:19 +07:00
|
|
|
|
2010-01-06 21:45:34 +07:00
|
|
|
/* Find line range from its line number */
|
2010-04-13 00:17:35 +07:00
|
|
|
static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf)
|
2010-01-06 21:45:34 +07:00
|
|
|
{
|
2011-01-13 19:45:58 +07:00
|
|
|
int ret;
|
2010-04-15 05:40:07 +07:00
|
|
|
|
2011-01-13 19:45:58 +07:00
|
|
|
ret = die_walk_lines(sp_die ?: &lf->cu_die, line_range_walk_cb, lf);
|
2010-04-15 05:40:07 +07:00
|
|
|
|
2010-02-25 20:35:42 +07:00
|
|
|
/* Update status */
|
2010-04-15 05:40:07 +07:00
|
|
|
if (ret >= 0)
|
2014-02-06 12:32:09 +07:00
|
|
|
if (!intlist__empty(lf->lr->line_list))
|
2010-04-15 05:40:07 +07:00
|
|
|
ret = lf->found = 1;
|
|
|
|
else
|
|
|
|
ret = 0; /* Lines are not found */
|
2010-02-25 20:35:42 +07:00
|
|
|
else {
|
2013-12-27 03:41:15 +07:00
|
|
|
zfree(&lf->lr->path);
|
2010-02-25 20:35:42 +07:00
|
|
|
}
|
2010-04-15 05:40:07 +07:00
|
|
|
return ret;
|
2010-01-06 21:45:34 +07:00
|
|
|
}
|
|
|
|
|
2010-02-25 20:35:57 +07:00
|
|
|
static int line_range_inline_cb(Dwarf_Die *in_die, void *data)
|
|
|
|
{
|
2014-04-02 12:48:31 +07:00
|
|
|
int ret = find_line_range_by_line(in_die, data);
|
2011-08-11 18:02:47 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We have to check all instances of inlined function, because
|
|
|
|
* some execution paths can be optimized out depends on the
|
2014-04-02 12:48:31 +07:00
|
|
|
* function argument of instances. However, if an error occurs,
|
|
|
|
* it should be handled by the caller.
|
2011-08-11 18:02:47 +07:00
|
|
|
*/
|
2014-04-02 12:48:31 +07:00
|
|
|
return ret < 0 ? ret : 0;
|
2010-02-25 20:35:57 +07:00
|
|
|
}
|
|
|
|
|
2012-04-23 10:24:36 +07:00
|
|
|
/* Search function definition from function name */
|
2010-02-25 20:35:50 +07:00
|
|
|
static int line_range_search_cb(Dwarf_Die *sp_die, void *data)
|
2010-01-06 21:45:34 +07:00
|
|
|
{
|
2010-04-13 00:17:35 +07:00
|
|
|
struct dwarf_callback_param *param = data;
|
|
|
|
struct line_finder *lf = param->data;
|
2010-01-06 21:45:34 +07:00
|
|
|
struct line_range *lr = lf->lr;
|
|
|
|
|
2011-03-30 16:25:41 +07:00
|
|
|
/* Check declared file */
|
|
|
|
if (lr->file && strtailcmp(lr->file, dwarf_decl_file(sp_die)))
|
|
|
|
return DWARF_CB_OK;
|
|
|
|
|
2012-04-23 10:24:36 +07:00
|
|
|
if (die_is_func_def(sp_die) &&
|
2015-05-08 08:03:35 +07:00
|
|
|
die_match_name(sp_die, lr->function)) {
|
2010-02-25 20:35:50 +07:00
|
|
|
lf->fname = dwarf_decl_file(sp_die);
|
|
|
|
dwarf_decl_line(sp_die, &lr->offset);
|
2010-02-25 20:35:42 +07:00
|
|
|
pr_debug("fname: %s, lineno:%d\n", lf->fname, lr->offset);
|
2010-01-06 21:45:34 +07:00
|
|
|
lf->lno_s = lr->offset + lr->start;
|
2010-04-15 05:39:42 +07:00
|
|
|
if (lf->lno_s < 0) /* Overflow */
|
|
|
|
lf->lno_s = INT_MAX;
|
|
|
|
lf->lno_e = lr->offset + lr->end;
|
|
|
|
if (lf->lno_e < 0) /* Overflow */
|
2010-02-25 20:35:42 +07:00
|
|
|
lf->lno_e = INT_MAX;
|
2010-04-15 05:39:42 +07:00
|
|
|
pr_debug("New line range: %d to %d\n", lf->lno_s, lf->lno_e);
|
2010-01-06 21:45:34 +07:00
|
|
|
lr->start = lf->lno_s;
|
|
|
|
lr->end = lf->lno_e;
|
2015-01-30 16:37:44 +07:00
|
|
|
if (!die_is_func_instance(sp_die))
|
2011-08-11 18:03:11 +07:00
|
|
|
param->retval = die_walk_instances(sp_die,
|
|
|
|
line_range_inline_cb, lf);
|
|
|
|
else
|
2010-04-13 00:17:35 +07:00
|
|
|
param->retval = find_line_range_by_line(sp_die, lf);
|
|
|
|
return DWARF_CB_ABORT;
|
2010-01-06 21:45:34 +07:00
|
|
|
}
|
2010-04-13 00:17:35 +07:00
|
|
|
return DWARF_CB_OK;
|
2010-01-06 21:45:34 +07:00
|
|
|
}
|
|
|
|
|
2010-04-13 00:17:35 +07:00
|
|
|
static int find_line_range_by_func(struct line_finder *lf)
|
2010-01-06 21:45:34 +07:00
|
|
|
{
|
2010-04-13 00:17:35 +07:00
|
|
|
struct dwarf_callback_param param = {.data = (void *)lf, .retval = 0};
|
|
|
|
dwarf_getfuncs(&lf->cu_die, line_range_search_cb, ¶m, 0);
|
|
|
|
return param.retval;
|
2010-01-06 21:45:34 +07:00
|
|
|
}
|
|
|
|
|
2013-11-06 01:32:36 +07:00
|
|
|
int debuginfo__find_line_range(struct debuginfo *dbg, struct line_range *lr)
|
2010-01-06 21:45:34 +07:00
|
|
|
{
|
2010-02-25 20:35:42 +07:00
|
|
|
struct line_finder lf = {.lr = lr, .found = 0};
|
2010-04-13 00:17:35 +07:00
|
|
|
int ret = 0;
|
2010-02-25 20:35:42 +07:00
|
|
|
Dwarf_Off off = 0, noff;
|
|
|
|
size_t cuhl;
|
|
|
|
Dwarf_Die *diep;
|
2010-07-09 16:29:11 +07:00
|
|
|
const char *comp_dir;
|
2010-02-25 20:35:42 +07:00
|
|
|
|
perf probe: Add fastpath to do lookup by function name
v3 -> v2:
- Make pubname_search_cb more generic
- Add fastpath to find_probes also
v2 -> v1:
- Don't compare file names with cu_find_realpath(...), instead, compare
them with the name returned by dwarf_decl_file(sp_die)
The vmlinux file may have thousands of CUs.
We can lookup function name from .debug_pubnames section
to avoid the slow loop on CUs.
1. Improvement data for find_line_range
./perf stat -e cycles -r 10 -- ./perf probe -k /home/mlin/vmlinux \
-s /home/mlin/linux-2.6 \
--line csum_partial_copy_to_user > tmp.log
before patch applied
=====================
847,988,276 cycles
0.355075856 seconds time elapsed
after patch applied
=====================
206,102,622 cycles
0.086883555 seconds time elapsed
2. Improvement data for find_probes
./perf stat -e cycles -r 10 -- ./perf probe -k /home/mlin/vmlinux \
-s /home/mlin/linux-2.6 \
--vars csum_partial_copy_to_user > tmp.log
before patch applied
=====================
848,490,844 cycles
0.355307901 seconds time elapsed
after patch applied
=====================
205,684,469 cycles
0.086694010 seconds time elapsed
Acked-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: linux-kernel <linux-kernel@vger.kernel.org>
LKML-Reference: <1301041668.14111.52.camel@minggr.sh.intel.com>
Signed-off-by: Lin Ming <ming.m.lin@intel.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-03-25 15:27:48 +07:00
|
|
|
/* Fastpath: lookup by function name from .debug_pubnames section */
|
|
|
|
if (lr->function) {
|
|
|
|
struct pubname_callback_param pubname_param = {
|
|
|
|
.function = lr->function, .file = lr->file,
|
|
|
|
.cu_die = &lf.cu_die, .sp_die = &lf.sp_die, .found = 0};
|
|
|
|
struct dwarf_callback_param line_range_param = {
|
|
|
|
.data = (void *)&lf, .retval = 0};
|
|
|
|
|
2013-11-06 01:32:36 +07:00
|
|
|
dwarf_getpubnames(dbg->dbg, pubname_search_cb,
|
2011-06-27 14:27:39 +07:00
|
|
|
&pubname_param, 0);
|
perf probe: Add fastpath to do lookup by function name
v3 -> v2:
- Make pubname_search_cb more generic
- Add fastpath to find_probes also
v2 -> v1:
- Don't compare file names with cu_find_realpath(...), instead, compare
them with the name returned by dwarf_decl_file(sp_die)
The vmlinux file may have thousands of CUs.
We can lookup function name from .debug_pubnames section
to avoid the slow loop on CUs.
1. Improvement data for find_line_range
./perf stat -e cycles -r 10 -- ./perf probe -k /home/mlin/vmlinux \
-s /home/mlin/linux-2.6 \
--line csum_partial_copy_to_user > tmp.log
before patch applied
=====================
847,988,276 cycles
0.355075856 seconds time elapsed
after patch applied
=====================
206,102,622 cycles
0.086883555 seconds time elapsed
2. Improvement data for find_probes
./perf stat -e cycles -r 10 -- ./perf probe -k /home/mlin/vmlinux \
-s /home/mlin/linux-2.6 \
--vars csum_partial_copy_to_user > tmp.log
before patch applied
=====================
848,490,844 cycles
0.355307901 seconds time elapsed
after patch applied
=====================
205,684,469 cycles
0.086694010 seconds time elapsed
Acked-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: linux-kernel <linux-kernel@vger.kernel.org>
LKML-Reference: <1301041668.14111.52.camel@minggr.sh.intel.com>
Signed-off-by: Lin Ming <ming.m.lin@intel.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-03-25 15:27:48 +07:00
|
|
|
if (pubname_param.found) {
|
|
|
|
line_range_search_cb(&lf.sp_die, &line_range_param);
|
|
|
|
if (lf.found)
|
|
|
|
goto found;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-02-25 20:35:42 +07:00
|
|
|
/* Loop on CUs (Compilation Unit) */
|
2010-04-13 00:17:35 +07:00
|
|
|
while (!lf.found && ret >= 0) {
|
2013-11-06 01:32:36 +07:00
|
|
|
if (dwarf_nextcu(dbg->dbg, off, &noff, &cuhl,
|
2011-06-27 14:27:39 +07:00
|
|
|
NULL, NULL, NULL) != 0)
|
2010-01-06 21:45:34 +07:00
|
|
|
break;
|
|
|
|
|
|
|
|
/* Get the DIE(Debugging Information Entry) of this CU */
|
2013-11-06 01:32:36 +07:00
|
|
|
diep = dwarf_offdie(dbg->dbg, off + cuhl, &lf.cu_die);
|
2010-02-25 20:35:42 +07:00
|
|
|
if (!diep)
|
|
|
|
continue;
|
2010-01-06 21:45:34 +07:00
|
|
|
|
|
|
|
/* Check if target file is included. */
|
|
|
|
if (lr->file)
|
2010-02-25 20:36:12 +07:00
|
|
|
lf.fname = cu_find_realpath(&lf.cu_die, lr->file);
|
2010-02-25 20:35:42 +07:00
|
|
|
else
|
2010-02-25 20:36:12 +07:00
|
|
|
lf.fname = 0;
|
2010-01-06 21:45:34 +07:00
|
|
|
|
2010-02-25 20:36:12 +07:00
|
|
|
if (!lr->file || lf.fname) {
|
2010-01-06 21:45:34 +07:00
|
|
|
if (lr->function)
|
2010-04-13 00:17:35 +07:00
|
|
|
ret = find_line_range_by_func(&lf);
|
2010-01-06 21:45:34 +07:00
|
|
|
else {
|
|
|
|
lf.lno_s = lr->start;
|
2010-04-15 05:39:42 +07:00
|
|
|
lf.lno_e = lr->end;
|
2010-04-13 00:17:35 +07:00
|
|
|
ret = find_line_range_by_line(NULL, &lf);
|
2010-01-06 21:45:34 +07:00
|
|
|
}
|
|
|
|
}
|
2010-02-25 20:35:42 +07:00
|
|
|
off = noff;
|
2010-01-06 21:45:34 +07:00
|
|
|
}
|
2010-07-09 16:29:11 +07:00
|
|
|
|
perf probe: Add fastpath to do lookup by function name
v3 -> v2:
- Make pubname_search_cb more generic
- Add fastpath to find_probes also
v2 -> v1:
- Don't compare file names with cu_find_realpath(...), instead, compare
them with the name returned by dwarf_decl_file(sp_die)
The vmlinux file may have thousands of CUs.
We can lookup function name from .debug_pubnames section
to avoid the slow loop on CUs.
1. Improvement data for find_line_range
./perf stat -e cycles -r 10 -- ./perf probe -k /home/mlin/vmlinux \
-s /home/mlin/linux-2.6 \
--line csum_partial_copy_to_user > tmp.log
before patch applied
=====================
847,988,276 cycles
0.355075856 seconds time elapsed
after patch applied
=====================
206,102,622 cycles
0.086883555 seconds time elapsed
2. Improvement data for find_probes
./perf stat -e cycles -r 10 -- ./perf probe -k /home/mlin/vmlinux \
-s /home/mlin/linux-2.6 \
--vars csum_partial_copy_to_user > tmp.log
before patch applied
=====================
848,490,844 cycles
0.355307901 seconds time elapsed
after patch applied
=====================
205,684,469 cycles
0.086694010 seconds time elapsed
Acked-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: linux-kernel <linux-kernel@vger.kernel.org>
LKML-Reference: <1301041668.14111.52.camel@minggr.sh.intel.com>
Signed-off-by: Lin Ming <ming.m.lin@intel.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2011-03-25 15:27:48 +07:00
|
|
|
found:
|
2010-07-09 16:29:11 +07:00
|
|
|
/* Store comp_dir */
|
|
|
|
if (lf.found) {
|
|
|
|
comp_dir = cu_get_comp_dir(&lf.cu_die);
|
|
|
|
if (comp_dir) {
|
|
|
|
lr->comp_dir = strdup(comp_dir);
|
|
|
|
if (!lr->comp_dir)
|
|
|
|
ret = -ENOMEM;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-09 16:28:59 +07:00
|
|
|
pr_debug("path: %s\n", lr->path);
|
2010-04-13 00:17:35 +07:00
|
|
|
return (ret < 0) ? ret : lf.found;
|
2010-01-06 21:45:34 +07:00
|
|
|
}
|
|
|
|
|
2015-03-13 12:18:40 +07:00
|
|
|
/*
|
|
|
|
* Find a src file from a DWARF tag path. Prepend optional source path prefix
|
|
|
|
* and chop off leading directories that do not exist. Result is passed back as
|
|
|
|
* a newly allocated path on success.
|
|
|
|
* Return 0 if file was found and readable, -errno otherwise.
|
|
|
|
*/
|
|
|
|
int get_real_path(const char *raw_path, const char *comp_dir,
|
|
|
|
char **new_path)
|
|
|
|
{
|
|
|
|
const char *prefix = symbol_conf.source_prefix;
|
|
|
|
|
|
|
|
if (!prefix) {
|
|
|
|
if (raw_path[0] != '/' && comp_dir)
|
|
|
|
/* If not an absolute path, try to use comp_dir */
|
|
|
|
prefix = comp_dir;
|
|
|
|
else {
|
|
|
|
if (access(raw_path, R_OK) == 0) {
|
|
|
|
*new_path = strdup(raw_path);
|
|
|
|
return *new_path ? 0 : -ENOMEM;
|
|
|
|
} else
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*new_path = malloc((strlen(prefix) + strlen(raw_path) + 2));
|
|
|
|
if (!*new_path)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
sprintf(*new_path, "%s/%s", prefix, raw_path);
|
|
|
|
|
|
|
|
if (access(*new_path, R_OK) == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!symbol_conf.source_prefix) {
|
|
|
|
/* In case of searching comp_dir, don't retry */
|
|
|
|
zfree(new_path);
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (errno) {
|
|
|
|
case ENAMETOOLONG:
|
|
|
|
case ENOENT:
|
|
|
|
case EROFS:
|
|
|
|
case EFAULT:
|
|
|
|
raw_path = strchr(++raw_path, '/');
|
|
|
|
if (!raw_path) {
|
|
|
|
zfree(new_path);
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
|
|
|
|
default:
|
|
|
|
zfree(new_path);
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|