mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2025-01-24 18:09:30 +07:00
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:
commit
aa891009ee
@ -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)
|
||||
{
|
||||
|
@ -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::
|
||||
|
@ -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
|
||||
|
@ -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 = {
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -5,5 +5,4 @@
|
||||
*/
|
||||
#include "cache.h"
|
||||
|
||||
const char *pager_program;
|
||||
int pager_use_color = 1;
|
||||
|
@ -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)
|
||||
|
@ -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)))
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user