perf/core improvements and fixes:

User visible:
 
 - Add --range option to show a variable's location range in 'perf probe',
   helping in collecting variables in probes when there is a mismatch
   between assembly and source code (He Kuang)
 
 - Show better error message when failed to find variable in 'perf probe' (He Kuang)
 
 - Fix 'perf report --thread' handling and document it better (Namhyung Kim)
 
 Infrastructure:
 
 - Fix to get negative exit codes in 'perf test' test routines (He Kuang)
 
 - Make flex/bison calls honour V=1 (Jiri Olsa)
 
 - Ignore tail calls to probed functions in 'perf probe' (Naveen N. Rao)
 
 - Fix refcount expectations in map_group share 'perf test' (Arnaldo Carvalho de Melo)
 
 Build Fixes:
 
 - Fix 'perf kmem' build due to compiler thinking uninitialized var is
   being accessed (Arnaldo Carvalho de Melo)
 
 - Provide le16toh if not defined, to fix the libtraceevent build on
   older distros (Arnaldo Carvalho de Melo)
 
 - Fix 'perf trace' build on older distros by providing some CLOEXEC, NONBLOCK
   defines (Arnaldo Carvalho de Melo)
 
 Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJVVSMDAAoJENZQFvNTUqpAr7oQALNU82mtTInwBc3nPTjDtQoQ
 5/d7OQA2WSGi1Cm1XBerYuxFMUQUzlAGxFPl7qcDafaR1sI7WkzP2n5LqZZnvqoq
 BpUPoKPXJg3Hetw2AoSwBzn54nf3X5zBLothtozj3qa+PtXa3oE2xI2pFKFuKI0D
 qotub8G9G4HK5khI7B9WKNLZxunHmhCYWa1TteXHoIzFVSQaw9IqBa+S1Z4yRLF6
 duIEsjrT72enFGBSo3mT39/2z5gkfa3hBI4h2PnC3P5jBlQ2H0PCEROi4PWo3cJY
 9vWBSnFGNJiL+Qd+hwBJDArbujZC0ISUDix/UCkPcAqMCn0QLofG8ysRecc2/V9n
 0ZWzttuPv4ci+5fVfAw146GYaTzxAcftVExy0Xsp3chR7rnCw8n67G/iNQjdxq51
 csReveb0uqI/VpvyXXNQBWQs39Vj/qTYcaV65oudbOrfbA6kmr8GGJhnnGeeKpGQ
 5y0GHLJ8iL3L3pTwMVQlNqCiSKOXb37HohHgdaCAa0qt41WFYSNEO4lv28HdQVtf
 ftH9LP1r3JSCdtDTj18wy2slRvjO1qB6Zw55oU+LdEm56+CM9pX5i08d3LJWdYG2
 zXfcJs28utIEiAVlacvs48Dl2gPDPdb45eF5VLWWPtivSirqOaN6HitSEdgmQVi0
 OKDz6YMW1l26BlQtkdr+
 =sU6z
 -----END PGP SIGNATURE-----

Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core

Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:

User visible changes:

  - Add --range option to show a variable's location range in 'perf probe',
    helping in collecting variables in probes when there is a mismatch
    between assembly and source code (He Kuang)

  - Show better error message when failed to find variable in 'perf probe' (He Kuang)

  - Fix 'perf report --thread' handling and document it better (Namhyung Kim)

Infrastructure changes:

  - Fix to get negative exit codes in 'perf test' test routines (He Kuang)

  - Make flex/bison calls honour V=1 (Jiri Olsa)

  - Ignore tail calls to probed functions in 'perf probe' (Naveen N. Rao)

  - Fix refcount expectations in map_group share 'perf test' (Arnaldo Carvalho de Melo)

Build Fixes:

  - Fix 'perf kmem' build due to compiler thinking uninitialized var is
    being accessed (Arnaldo Carvalho de Melo)

  - Provide le16toh if not defined, to fix the libtraceevent build on
    older distros (Arnaldo Carvalho de Melo)

  - Fix 'perf trace' build on older distros by providing some CLOEXEC, NONBLOCK
    defines (Arnaldo Carvalho de Melo)

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
Ingo Molnar 2015-05-15 08:36:33 +02:00
commit aa891009ee
19 changed files with 346 additions and 94 deletions

View file

@ -4,6 +4,19 @@
#include <endian.h>
#include "event-parse.h"
/*
* From glibc endian.h, for older systems where it is not present, e.g.: RHEL5,
* Fedora6.
*/
#ifndef le16toh
# if __BYTE_ORDER == __LITTLE_ENDIAN
# define le16toh(x) (x)
# else
# define le16toh(x) __bswap_16 (x)
# endif
#endif
static unsigned long long
process___le16_to_cpup(struct trace_seq *s, unsigned long long *args)
{

View file

@ -147,7 +147,8 @@ OPTIONS
-s::
--stat::
Per thread counts.
Record per-thread event counts. Use it with 'perf report -T' to see
the values.
-d::
--data::

View file

@ -34,7 +34,8 @@ OPTIONS
-T::
--threads::
Show per-thread event counters
Show per-thread event counters. The input data file should be recorded
with -s option.
-c::
--comms=::
Only consider symbols in these comms. CSV that understands

View file

@ -713,7 +713,7 @@ static int parse_gfp_flags(struct perf_evsel *evsel, struct perf_sample *sample,
.size = sample->raw_size,
};
struct trace_seq seq;
char *str, *pos;
char *str, *pos = NULL;
if (nr_gfps) {
struct gfp_flag key = {

View file

@ -372,6 +372,8 @@ __cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
"Show accessible variables on PROBEDEF", opt_show_vars),
OPT_BOOLEAN('\0', "externs", &probe_conf.show_ext_vars,
"Show external variables too (with --vars only)"),
OPT_BOOLEAN('\0', "range", &probe_conf.show_location_range,
"Show variables location range in scope (with --vars only)"),
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
"file", "vmlinux pathname"),
OPT_STRING('s', "source", &symbol_conf.source_prefix,

View file

@ -333,15 +333,14 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,
}
if (sort_order == NULL &&
parent_pattern == default_parent_pattern) {
parent_pattern == default_parent_pattern)
fprintf(stdout, "#\n# (%s)\n#\n", help);
if (rep->show_threads) {
bool style = !strcmp(rep->pretty_printing_style, "raw");
perf_read_values_display(stdout, &rep->show_threads_values,
style);
perf_read_values_destroy(&rep->show_threads_values);
}
if (rep->show_threads) {
bool style = !strcmp(rep->pretty_printing_style, "raw");
perf_read_values_display(stdout, &rep->show_threads_values,
style);
perf_read_values_destroy(&rep->show_threads_values);
}
return 0;
@ -815,8 +814,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
goto error;
}
/* Force tty output for header output. */
if (report.header || report.header_only)
/* Force tty output for header output and per-thread stat. */
if (report.header || report.header_only || report.show_threads)
use_browser = 0;
if (strcmp(input_name, "-") != 0)

View file

@ -16,7 +16,6 @@
#include <libaudit.h>
#include <stdlib.h>
#include <sys/eventfd.h>
#include <sys/mman.h>
#include <linux/futex.h>
@ -41,6 +40,34 @@
# define EFD_SEMAPHORE 1
#endif
#ifndef EFD_NONBLOCK
# define EFD_NONBLOCK 00004000
#endif
#ifndef EFD_CLOEXEC
# define EFD_CLOEXEC 02000000
#endif
#ifndef O_CLOEXEC
# define O_CLOEXEC 02000000
#endif
#ifndef SOCK_DCCP
# define SOCK_DCCP 6
#endif
#ifndef SOCK_CLOEXEC
# define SOCK_CLOEXEC 02000000
#endif
#ifndef SOCK_NONBLOCK
# define SOCK_NONBLOCK 00004000
#endif
#ifndef MSG_CMSG_CLOEXEC
# define MSG_CMSG_CLOEXEC 0x40000000
#endif
struct tp_field {
int offset;
union {
@ -2721,11 +2748,10 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
signal(SIGFPE, sighandler_dump_stack);
trace.evlist = perf_evlist__new();
if (trace.evlist == NULL)
return -ENOMEM;
if (trace.evlist == NULL) {
pr_err("Not enough memory to run!\n");
err = -ENOMEM;
goto out;
}

View file

@ -219,7 +219,7 @@ static int run_test(struct test *test)
wait(&status);
if (WIFEXITED(status)) {
err = WEXITSTATUS(status);
err = (signed char)WEXITSTATUS(status);
pr_debug("test child finished with %d\n", err);
} else if (WIFSIGNALED(status)) {
err = -1;

View file

@ -9,6 +9,15 @@ do { \
} \
} while (0)
#define TEST_ASSERT_EQUAL(text, val, expected) \
do { \
if (val != expected) { \
pr_debug("FAILED %s:%d %s (%d != %d)\n", \
__FILE__, __LINE__, text, val, expected); \
return -1; \
} \
} while (0)
enum {
TEST_OK = 0,
TEST_FAIL = -1,

View file

@ -43,7 +43,7 @@ int test__thread_mg_share(void)
leader && t1 && t2 && t3 && other);
mg = leader->mg;
TEST_ASSERT_VAL("wrong refcnt", mg->refcnt == 4);
TEST_ASSERT_EQUAL("wrong refcnt", mg->refcnt, 4);
/* test the map groups pointer is shared */
TEST_ASSERT_VAL("map groups don't match", mg == t1->mg);
@ -58,34 +58,41 @@ int test__thread_mg_share(void)
other_leader = machine__find_thread(machine, 4, 4);
TEST_ASSERT_VAL("failed to find other leader", other_leader);
/*
* Ok, now that all the rbtree related operations were done,
* lets remove all of them from there so that we can do the
* refcounting tests.
*/
machine__remove_thread(machine, leader);
machine__remove_thread(machine, t1);
machine__remove_thread(machine, t2);
machine__remove_thread(machine, t3);
machine__remove_thread(machine, other);
machine__remove_thread(machine, other_leader);
other_mg = other->mg;
TEST_ASSERT_VAL("wrong refcnt", other_mg->refcnt == 2);
TEST_ASSERT_EQUAL("wrong refcnt", other_mg->refcnt, 2);
TEST_ASSERT_VAL("map groups don't match", other_mg == other_leader->mg);
/* release thread group */
thread__put(leader);
TEST_ASSERT_VAL("wrong refcnt", mg->refcnt == 3);
TEST_ASSERT_EQUAL("wrong refcnt", mg->refcnt, 3);
thread__put(t1);
TEST_ASSERT_VAL("wrong refcnt", mg->refcnt == 2);
TEST_ASSERT_EQUAL("wrong refcnt", mg->refcnt, 2);
thread__put(t2);
TEST_ASSERT_VAL("wrong refcnt", mg->refcnt == 1);
TEST_ASSERT_EQUAL("wrong refcnt", mg->refcnt, 1);
thread__put(t3);
/* release other group */
thread__put(other_leader);
TEST_ASSERT_VAL("wrong refcnt", other_mg->refcnt == 1);
TEST_ASSERT_EQUAL("wrong refcnt", other_mg->refcnt, 1);
thread__put(other);
/*
* Cannot call machine__delete_threads(machine) now,
* because we've already released all the threads.
*/
machines__exit(&machines);
return 0;
}

View file

@ -102,19 +102,19 @@ CFLAGS_exec_cmd.o += -DPERF_EXEC_PATH="BUILD_STR($(perfexecdir_SQ))" -DPREFIX="B
$(OUTPUT)util/parse-events-flex.c: util/parse-events.l $(OUTPUT)util/parse-events-bison.c
$(call rule_mkdir)
@$(call echo-cmd,flex)$(FLEX) -o $@ --header-file=$(OUTPUT)util/parse-events-flex.h $(PARSER_DEBUG_FLEX) util/parse-events.l
$(Q)$(call echo-cmd,flex)$(FLEX) -o $@ --header-file=$(OUTPUT)util/parse-events-flex.h $(PARSER_DEBUG_FLEX) util/parse-events.l
$(OUTPUT)util/parse-events-bison.c: util/parse-events.y
$(call rule_mkdir)
@$(call echo-cmd,bison)$(BISON) -v util/parse-events.y -d $(PARSER_DEBUG_BISON) -o $@ -p parse_events_
$(Q)$(call echo-cmd,bison)$(BISON) -v util/parse-events.y -d $(PARSER_DEBUG_BISON) -o $@ -p parse_events_
$(OUTPUT)util/pmu-flex.c: util/pmu.l $(OUTPUT)util/pmu-bison.c
$(call rule_mkdir)
@$(call echo-cmd,flex)$(FLEX) -o $@ --header-file=$(OUTPUT)util/pmu-flex.h util/pmu.l
$(Q)$(call echo-cmd,flex)$(FLEX) -o $@ --header-file=$(OUTPUT)util/pmu-flex.h util/pmu.l
$(OUTPUT)util/pmu-bison.c: util/pmu.y
$(call rule_mkdir)
@$(call echo-cmd,bison)$(BISON) -v util/pmu.y -d -o $@ -p perf_pmu_
$(Q)$(call echo-cmd,bison)$(BISON) -v util/pmu.y -d -o $@ -p perf_pmu_
CFLAGS_parse-events-flex.o += -w
CFLAGS_pmu-flex.o += -w

View file

@ -30,7 +30,6 @@ extern const char *perf_config_dirname(const char *, const char *);
/* pager.c */
extern void setup_pager(void);
extern const char *pager_program;
extern int pager_in_use(void);
extern int pager_use_color;

View file

@ -433,6 +433,43 @@ struct __addr_die_search_param {
Dwarf_Die *die_mem;
};
static int __die_search_func_tail_cb(Dwarf_Die *fn_die, void *data)
{
struct __addr_die_search_param *ad = data;
Dwarf_Addr addr = 0;
if (dwarf_tag(fn_die) == DW_TAG_subprogram &&
!dwarf_highpc(fn_die, &addr) &&
addr == ad->addr) {
memcpy(ad->die_mem, fn_die, sizeof(Dwarf_Die));
return DWARF_CB_ABORT;
}
return DWARF_CB_OK;
}
/**
* die_find_tailfunc - Search for a non-inlined function with tail call at
* given address
* @cu_die: a CU DIE which including @addr
* @addr: target address
* @die_mem: a buffer for result DIE
*
* Search for a non-inlined function DIE with tail call at @addr. Stores the
* DIE to @die_mem and returns it if found. Returns NULL if failed.
*/
Dwarf_Die *die_find_tailfunc(Dwarf_Die *cu_die, Dwarf_Addr addr,
Dwarf_Die *die_mem)
{
struct __addr_die_search_param ad;
ad.addr = addr;
ad.die_mem = die_mem;
/* dwarf_getscopes can't find subprogram. */
if (!dwarf_getfuncs(cu_die, __die_search_func_tail_cb, &ad, 0))
return NULL;
else
return die_mem;
}
/* die_find callback for non-inlined function search */
static int __die_search_func_cb(Dwarf_Die *fn_die, void *data)
{
@ -848,19 +885,17 @@ Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name,
/**
* die_get_typename - Get the name of given variable DIE
* @vr_die: a variable DIE
* @buf: a buffer for result type name
* @len: a max-length of @buf
* @buf: a strbuf for result type name
*
* Get the name of @vr_die and stores it to @buf. Return the actual length
* of type name if succeeded. Return -E2BIG if @len is not enough long, and
* Return -ENOENT if failed to find type name.
* Get the name of @vr_die and stores it to @buf. Return 0 if succeeded.
* and Return -ENOENT if failed to find type name.
* Note that the result will stores typedef name if possible, and stores
* "*(function_type)" if the type is a function pointer.
*/
int die_get_typename(Dwarf_Die *vr_die, char *buf, int len)
int die_get_typename(Dwarf_Die *vr_die, struct strbuf *buf)
{
Dwarf_Die type;
int tag, ret, ret2;
int tag, ret;
const char *tmp = "";
if (__die_get_real_type(vr_die, &type) == NULL)
@ -871,8 +906,8 @@ int die_get_typename(Dwarf_Die *vr_die, char *buf, int len)
tmp = "*";
else if (tag == DW_TAG_subroutine_type) {
/* Function pointer */
ret = snprintf(buf, len, "(function_type)");
return (ret >= len) ? -E2BIG : ret;
strbuf_addf(buf, "(function_type)");
return 0;
} else {
if (!dwarf_diename(&type))
return -ENOENT;
@ -883,39 +918,156 @@ int die_get_typename(Dwarf_Die *vr_die, char *buf, int len)
else if (tag == DW_TAG_enumeration_type)
tmp = "enum ";
/* Write a base name */
ret = snprintf(buf, len, "%s%s", tmp, dwarf_diename(&type));
return (ret >= len) ? -E2BIG : ret;
}
ret = die_get_typename(&type, buf, len);
if (ret > 0) {
ret2 = snprintf(buf + ret, len - ret, "%s", tmp);
ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret;
strbuf_addf(buf, "%s%s", tmp, dwarf_diename(&type));
return 0;
}
ret = die_get_typename(&type, buf);
if (ret == 0)
strbuf_addf(buf, "%s", tmp);
return ret;
}
/**
* die_get_varname - Get the name and type of given variable DIE
* @vr_die: a variable DIE
* @buf: a buffer for type and variable name
* @len: the max-length of @buf
* @buf: a strbuf for type and variable name
*
* Get the name and type of @vr_die and stores it in @buf as "type\tname".
*/
int die_get_varname(Dwarf_Die *vr_die, char *buf, int len)
int die_get_varname(Dwarf_Die *vr_die, struct strbuf *buf)
{
int ret, ret2;
int ret;
ret = die_get_typename(vr_die, buf, len);
ret = die_get_typename(vr_die, buf);
if (ret < 0) {
pr_debug("Failed to get type, make it unknown.\n");
ret = snprintf(buf, len, "(unknown_type)");
strbuf_addf(buf, "(unknown_type)");
}
if (ret > 0) {
ret2 = snprintf(buf + ret, len - ret, "\t%s",
dwarf_diename(vr_die));
ret = (ret2 >= len - ret) ? -E2BIG : ret2 + ret;
strbuf_addf(buf, "\t%s", dwarf_diename(vr_die));
return 0;
}
/**
* die_get_var_innermost_scope - Get innermost scope range of given variable DIE
* @sp_die: a subprogram DIE
* @vr_die: a variable DIE
* @buf: a strbuf for variable byte offset range
*
* Get the innermost scope range of @vr_die and stores it in @buf as
* "@<function_name+[NN-NN,NN-NN]>".
*/
static int die_get_var_innermost_scope(Dwarf_Die *sp_die, Dwarf_Die *vr_die,
struct strbuf *buf)
{
Dwarf_Die *scopes;
int count;
size_t offset = 0;
Dwarf_Addr base;
Dwarf_Addr start, end;
Dwarf_Addr entry;
int ret;
bool first = true;
const char *name;
ret = dwarf_entrypc(sp_die, &entry);
if (ret)
return ret;
name = dwarf_diename(sp_die);
if (!name)
return -ENOENT;
count = dwarf_getscopes_die(vr_die, &scopes);
/* (*SCOPES)[1] is the DIE for the scope containing that scope */
if (count <= 1) {
ret = -EINVAL;
goto out;
}
while ((offset = dwarf_ranges(&scopes[1], offset, &base,
&start, &end)) > 0) {
start -= entry;
end -= entry;
if (first) {
strbuf_addf(buf, "@<%s+[%lu-%lu",
name, start, end);
first = false;
} else {
strbuf_addf(buf, ",%lu-%lu",
start, end);
}
}
if (!first)
strbuf_addf(buf, "]>");
out:
free(scopes);
return ret;
}
/**
* die_get_var_range - Get byte offset range of given variable DIE
* @sp_die: a subprogram DIE
* @vr_die: a variable DIE
* @buf: a strbuf for type and variable name and byte offset range
*
* Get the byte offset range of @vr_die and stores it in @buf as
* "@<function_name+[NN-NN,NN-NN]>".
*/
int die_get_var_range(Dwarf_Die *sp_die, Dwarf_Die *vr_die, struct strbuf *buf)
{
int ret = 0;
Dwarf_Addr base;
Dwarf_Addr start, end;
Dwarf_Addr entry;
Dwarf_Op *op;
size_t nops;
size_t offset = 0;
Dwarf_Attribute attr;
bool first = true;
const char *name;
ret = dwarf_entrypc(sp_die, &entry);
if (ret)
return ret;
name = dwarf_diename(sp_die);
if (!name)
return -ENOENT;
if (dwarf_attr(vr_die, DW_AT_location, &attr) == NULL)
return -EINVAL;
while ((offset = dwarf_getlocations(
&attr, offset, &base,
&start, &end, &op, &nops)) > 0) {
if (start == 0) {
/* Single Location Descriptions */
ret = die_get_var_innermost_scope(sp_die, vr_die, buf);
return ret;
}
/* Location Lists */
start -= entry;
end -= entry;
if (first) {
strbuf_addf(buf, "@<%s+[%lu-%lu",
name, start, end);
first = false;
} else {
strbuf_addf(buf, ",%lu-%lu",
start, end);
}
}
if (!first)
strbuf_addf(buf, "]>");
return ret;
}

View file

@ -85,6 +85,10 @@ extern Dwarf_Die *die_find_child(Dwarf_Die *rt_die,
extern Dwarf_Die *die_find_realfunc(Dwarf_Die *cu_die, Dwarf_Addr addr,
Dwarf_Die *die_mem);
/* Search a non-inlined function with tail call at given address */
Dwarf_Die *die_find_tailfunc(Dwarf_Die *cu_die, Dwarf_Addr addr,
Dwarf_Die *die_mem);
/* Search the top inlined function including given address */
extern Dwarf_Die *die_find_top_inlinefunc(Dwarf_Die *sp_die, Dwarf_Addr addr,
Dwarf_Die *die_mem);
@ -117,8 +121,10 @@ extern Dwarf_Die *die_find_member(Dwarf_Die *st_die, const char *name,
Dwarf_Die *die_mem);
/* Get the name of given variable DIE */
extern int die_get_typename(Dwarf_Die *vr_die, char *buf, int len);
extern int die_get_typename(Dwarf_Die *vr_die, struct strbuf *buf);
/* Get the name and type of given variable DIE, stored as "type\tname" */
extern int die_get_varname(Dwarf_Die *vr_die, char *buf, int len);
extern int die_get_varname(Dwarf_Die *vr_die, struct strbuf *buf);
extern int die_get_var_range(Dwarf_Die *sp_die, Dwarf_Die *vr_die,
struct strbuf *buf);
#endif

View file

@ -5,5 +5,4 @@
*/
#include "cache.h"
const char *pager_program;
int pager_use_color = 1;

View file

@ -364,7 +364,7 @@ static struct thread *____machine__findnew_thread(struct machine *machine,
return th;
}
thread__zput(machine->last_match);
machine->last_match = NULL;
}
while (*p != NULL) {
@ -372,7 +372,7 @@ static struct thread *____machine__findnew_thread(struct machine *machine,
th = rb_entry(parent, struct thread, rb_node);
if (th->tid == tid) {
machine->last_match = thread__get(th);
machine->last_match = th;
machine__update_thread_pid(machine, th, pid);
return th;
}
@ -409,7 +409,7 @@ static struct thread *____machine__findnew_thread(struct machine *machine,
* It is now in the rbtree, get a ref
*/
thread__get(th);
machine->last_match = thread__get(th);
machine->last_match = th;
}
return th;
@ -1309,7 +1309,7 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event
static void __machine__remove_thread(struct machine *machine, struct thread *th, bool lock)
{
if (machine->last_match == th)
thread__zput(machine->last_match);
machine->last_match = NULL;
BUG_ON(th->refcnt.counter == 0);
if (lock)

View file

@ -50,11 +50,6 @@ void setup_pager(void)
if (!isatty(1))
return;
if (!pager) {
if (!pager_program)
perf_config(perf_default_config, NULL);
pager = pager_program;
}
if (!pager)
pager = getenv("PAGER");
if (!(pager || access("/usr/bin/pager", X_OK)))

View file

@ -9,6 +9,7 @@
/* Probe related configurations */
struct probe_conf {
bool show_ext_vars;
bool show_location_range;
bool force_add;
bool no_inlines;
int max_probes;

View file

@ -177,7 +177,7 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr,
Dwarf_Word offs = 0;
bool ref = false;
const char *regs;
int ret;
int ret, ret2 = 0;
if (dwarf_attr(vr_die, DW_AT_external, &attr) != NULL)
goto static_var;
@ -187,9 +187,19 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr,
return -EINVAL; /* Broken DIE ? */
if (dwarf_getlocation_addr(&attr, addr, &op, &nops, 1) <= 0) {
ret = dwarf_entrypc(sp_die, &tmp);
if (ret || addr != tmp ||
dwarf_tag(vr_die) != DW_TAG_formal_parameter ||
dwarf_highpc(sp_die, &tmp))
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)
return -ENOENT;
/*
* This is fuzzed by fentry mcount. We try to find the
@ -210,7 +220,7 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr,
if (op->atom == DW_OP_addr) {
static_var:
if (!tvar)
return 0;
return ret2;
/* Static variables on memory (not stack), make @varname */
ret = strlen(dwarf_diename(vr_die));
tvar->value = zalloc(ret + 2);
@ -220,7 +230,7 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr,
tvar->ref = alloc_trace_arg_ref((long)offs);
if (tvar->ref == NULL)
return -ENOMEM;
return 0;
return ret2;
}
/* If this is based on frame buffer, set the offset */
@ -250,14 +260,14 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr,
}
if (!tvar)
return 0;
return ret2;
regs = get_arch_regstr(regn);
if (!regs) {
/* This should be a bug in DWARF or this tool */
pr_warning("Mapping for the register number %u "
"missing on this architecture.\n", regn);
return -ERANGE;
return -ENOTSUP;
}
tvar->value = strdup(regs);
@ -269,7 +279,7 @@ static int convert_variable_location(Dwarf_Die *vr_die, Dwarf_Addr addr,
if (tvar->ref == NULL)
return -ENOMEM;
}
return 0;
return ret2;
}
#define BYTES_TO_BITS(nb) ((nb) * BITS_PER_LONG / sizeof(long))
@ -517,10 +527,12 @@ static int convert_variable(Dwarf_Die *vr_die, struct probe_finder *pf)
ret = convert_variable_location(vr_die, pf->addr, pf->fb_ops,
&pf->sp_die, pf->tvar);
if (ret == -ENOENT || ret == -EINVAL)
pr_err("Failed to find the location of %s at this address.\n"
" Perhaps, it has been optimized out.\n", pf->pvar->var);
else if (ret == -ENOTSUP)
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)
pr_err("Sorry, we don't support this variable location yet.\n");
else if (ret == 0 && pf->pvar->field) {
ret = convert_variable_fields(vr_die, pf->pvar->var,
@ -662,9 +674,15 @@ static int call_probe_finder(Dwarf_Die *sc_die, struct probe_finder *pf)
/* If not a real subprogram, find a real one */
if (!die_is_func_def(sc_die)) {
if (!die_find_realfunc(&pf->cu_die, pf->addr, &pf->sp_die)) {
pr_warning("Failed to find probe point in any "
"functions.\n");
return -ENOENT;
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;
}
}
} else
memcpy(&pf->sp_die, sc_die, sizeof(Dwarf_Die));
@ -1255,14 +1273,11 @@ int debuginfo__find_trace_events(struct debuginfo *dbg,
return (ret < 0) ? ret : tf.ntevs;
}
#define MAX_VAR_LEN 64
/* 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;
char buf[MAX_VAR_LEN];
int tag, ret;
vl = &af->vls[af->nvls - 1];
@ -1273,11 +1288,38 @@ static int collect_variables_cb(Dwarf_Die *die_mem, void *data)
ret = convert_variable_location(die_mem, af->pf.addr,
af->pf.fb_ops, &af->pf.sp_die,
NULL);
if (ret == 0) {
ret = die_get_varname(die_mem, buf, MAX_VAR_LEN);
pr_debug2("Add new var: %s\n", buf);
if (ret > 0)
strlist__add(vl->vars, buf);
if (ret == 0 || ret == -ERANGE) {
int ret2;
bool externs = !af->child;
struct strbuf buf;
strbuf_init(&buf, 64);
if (probe_conf.show_location_range) {
if (!externs) {
if (ret)
strbuf_addf(&buf, "[INV]\t");
else
strbuf_addf(&buf, "[VAL]\t");
} else
strbuf_addf(&buf, "[EXT]\t");
}
ret2 = die_get_varname(die_mem, &buf);
if (!ret2 && probe_conf.show_location_range &&
!externs) {
strbuf_addf(&buf, "\t");
ret2 = die_get_var_range(&af->pf.sp_die,
die_mem, &buf);
}
pr_debug("Add new var: %s\n", buf.buf);
if (ret2 == 0) {
strlist__add(vl->vars,
strbuf_detach(&buf, NULL));
}
strbuf_release(&buf);
}
}