perf/core improvements:
User visible: - Wire the callchain unwinding "max-stack" know to 'perf script --max-stack', allowing to limit the depth of callchains, possibly reducing processing time (Arnaldo Carvalho de Melo) - Ditto for 'perf trace --max-stack' (Arnaldo Carvalho de Melo) - Introduce a --min-stack filter for 'perf trace', to show syscalls that had a userspace callchain leading to it at least min-stack deep (Arnaldo Carvalho de Melo) - Make 'perf trace' work with multiple threads and the --duration filter, i.e. do not print the start of an interrupted syscall followed by ... to print interrupts from other threads, as we need to wait the sys_exit syscall tracepoint to calculate the duration, duh. (Arnaldo Carvalho de Melo) System wide --duration now works as expected: [root@jouet ~]# trace --duration 100 152.393 (145.147 ms): Timer/24358 futex(uaddr: 0x7f5ed98e56cc, op: WAIT_BITSET|PRIV|CLKRT, val: 7055125, utime: 0x7f5ecdbfec30, val3: 4294967295) = -1 ETIMEDOUT Connection timed out 152.438 (145.040 ms): firefox/24321 poll(ufds: 0x7f5ec388b460, nfds: 6, timeout_msecs: 4294967295) = 1 358.580 (158.279 ms): Xorg/2025 select(n: 512, inp: 0x83a8e0, tvp: 0x7ffdcbb63610) = 0 Timeout 358.687 (148.285 ms): gnome-terminal/2711 poll(ufds: 0x55b7e6811ad0, nfds: 15, timeout_msecs: 249) = 1 370.150 (169.569 ms): gnome-shell/2287 poll(ufds: 0x55e623d65490, nfds: 86, timeout_msecs: 4294967295) = 1 - Now 'perf trace's --max-stack and --min-stack will automatically set "--call-graph dwarf", if --call-graph is not present on the command line: [root@jouet ~]# perf trace -e nanosleep --max-stack 3 usleep 1 0.299 ( 0.057 ms): usleep/29658 nanosleep(rqtp: 0x7fff80f3b230) = 0 __nanosleep+0x10 (/usr/lib64/libc-2.22.so) usleep+0x34 (/usr/lib64/libc-2.22.so) main+0x1eb (/usr/bin/usleep) [root@jouet ~]# - Bump 'perf trace --mmap-pages' for root when using callchains and not specifying --mmap-pages explicitely (Arnaldo Carvalho de Melo) Build fixes: - The python binding object had missing symbols, to some refactoring to fix that (Arnaldo Carvalho de Melo) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABCAAGBQJXEWBrAAoJENZQFvNTUqpAvvkQAIvPEkxDa6jm3nR6Y7LzE4/u j1AC5BruN/TL36RrU6Dp+zEklg0lrxmEd0fxY/H5jxqgHsyjFEkvXcTQbq4UNrmj ICrpyexoimYihvNHodqz59CWqvlu59RDQW5MUxeCEWhj0cgbUzb6Gcx9WVrlvFoW M1vR5GQaKXpmGk1t2RC7FH+jGc7Bi4Vh9duE+GLe1Pg2R5xrS7z2ORDxMlvVYLY8 7dzKfu9Z8zjDDeIYb6Xf5Z720C2JSwubGXS2s88mdSHwj3X7dHjaiPdiwwDd92hD hTBa+nbltaiqxua6++FqaNy6qOCwT8ab9xk4jhhaFmw90iGlmUlthpg7iHXzv0e4 HgZPtDB1GhlLZ1dNZOs88kdbrfb7fqQemlFV34zBMY4WAaK2DMc3lgKBKo1OKj1A hWuMTgQsDq1Xbkdd8B3R+TFuqxjVluhUjZfh+fWz47Wa9ofeAY6Fkm0VFkN64orx WJq7lFXo7yLPVpn9S5FHEc4iy57DNU0TeCkn/e0x4K8JCDfS3yt8t8mnD27O+OR0 ytZZJ11als/letF03x01EjsaoCQ4A60PKeg6SMHaCKpteV/Xajf9KZgXYqwkYH96 OF1C6LmCz+2r1IwWu/7ODkHPASyrlGktzs3124AIrM1zPEObjAKg3irpevvyNeJM sF2KbpG9GPMlz5QImtbo =+JhQ -----END PGP SIGNATURE----- Merge tag 'perf-core-for-mingo-20160415' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core Pull perf/core improvements from Arnaldo Carvalho de Melo: User visible changes: - Wire the callchain unwinding "max-stack" now to 'perf script --max-stack', allowing to limit the depth of callchains, possibly reducing processing time (Arnaldo Carvalho de Melo) - Ditto for 'perf trace --max-stack' (Arnaldo Carvalho de Melo) - Introduce a --min-stack filter for 'perf trace', to show syscalls that had a userspace callchain leading to it at least min-stack deep (Arnaldo Carvalho de Melo) - Make 'perf trace' work with multiple threads and the --duration filter, i.e. do not print the start of an interrupted syscall followed by ... to print interrupts from other threads, as we need to wait the sys_exit syscall tracepoint to calculate the duration, duh. (Arnaldo Carvalho de Melo) System wide --duration now works as expected: [root@jouet ~]# trace --duration 100 152.393 (145.147 ms): Timer/24358 futex(uaddr: 0x7f5ed98e56cc, op: WAIT_BITSET|PRIV|CLKRT, val: 7055125, utime: 0x7f5ecdbfec30, val3: 4294967295) = -1 ETIMEDOUT Connection timed out 152.438 (145.040 ms): firefox/24321 poll(ufds: 0x7f5ec388b460, nfds: 6, timeout_msecs: 4294967295) = 1 358.580 (158.279 ms): Xorg/2025 select(n: 512, inp: 0x83a8e0, tvp: 0x7ffdcbb63610) = 0 Timeout 358.687 (148.285 ms): gnome-terminal/2711 poll(ufds: 0x55b7e6811ad0, nfds: 15, timeout_msecs: 249) = 1 370.150 (169.569 ms): gnome-shell/2287 poll(ufds: 0x55e623d65490, nfds: 86, timeout_msecs: 4294967295) = 1 - Now 'perf trace's --max-stack and --min-stack will automatically set "--call-graph dwarf", if --call-graph is not present on the command line: [root@jouet ~]# perf trace -e nanosleep --max-stack 3 usleep 1 0.299 ( 0.057 ms): usleep/29658 nanosleep(rqtp: 0x7fff80f3b230) = 0 __nanosleep+0x10 (/usr/lib64/libc-2.22.so) usleep+0x34 (/usr/lib64/libc-2.22.so) main+0x1eb (/usr/bin/usleep) [root@jouet ~]# - Bump 'perf trace --mmap-pages' for root when using callchains and not specifying --mmap-pages explicitely (Arnaldo Carvalho de Melo) Build fixes: - The python binding object had missing symbols, to some refactoring to fix that (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
9243ae5b28
@ -259,6 +259,16 @@ include::itrace.txt[]
|
||||
--full-source-path::
|
||||
Show the full path for source files for srcline output.
|
||||
|
||||
--max-stack::
|
||||
Set the stack depth limit when parsing the callchain, anything
|
||||
beyond the specified depth will be ignored. This is a trade-off
|
||||
between information loss and faster processing especially for
|
||||
workloads that can have a very long callchain stack.
|
||||
Note that when using the --itrace option the synthesized callchain size
|
||||
will override this value if the synthesized callchain size is bigger.
|
||||
|
||||
Default: 127
|
||||
|
||||
--ns::
|
||||
Use 9 decimal places when displaying time (i.e. show the nanoseconds)
|
||||
|
||||
|
@ -123,12 +123,35 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs.
|
||||
man pages for details. The ones that are most useful in 'perf trace'
|
||||
are 'dwarf' and 'lbr', where available, try: 'perf trace --call-graph dwarf'.
|
||||
|
||||
Using this will, for the root user, bump the value of --mmap-pages to 4
|
||||
times the maximum for non-root users, based on the kernel.perf_event_mlock_kb
|
||||
sysctl. This is done only if the user doesn't specify a --mmap-pages value.
|
||||
|
||||
--kernel-syscall-graph::
|
||||
Show the kernel callchains on the syscall exit path.
|
||||
|
||||
--event::
|
||||
Trace other events, see 'perf list' for a complete list.
|
||||
|
||||
--max-stack::
|
||||
Set the stack depth limit when parsing the callchain, anything
|
||||
beyond the specified depth will be ignored. Note that at this point
|
||||
this is just about the presentation part, i.e. the kernel is still
|
||||
not limiting, the overhead of callchains needs to be set via the
|
||||
knobs in --call-graph dwarf.
|
||||
|
||||
Implies '--call-graph dwarf' when --call-graph not present on the
|
||||
command line, on systems where DWARF unwinding was built in.
|
||||
|
||||
Default: 127
|
||||
|
||||
--min-stack::
|
||||
Set the stack depth limit when parsing the callchain, anything
|
||||
below the specified depth will be ignored. Disabled by default.
|
||||
|
||||
Implies '--call-graph dwarf' when --call-graph not present on the
|
||||
command line, on systems where DWARF unwinding was built in.
|
||||
|
||||
--proc-map-timeout::
|
||||
When processing pre-existing threads /proc/XXX/mmap, it may take a long time,
|
||||
because the file may be huge. A time out is needed in such cases.
|
||||
|
@ -375,7 +375,7 @@ static u64 find_callsite(struct perf_evsel *evsel, struct perf_sample *sample)
|
||||
}
|
||||
|
||||
al.thread = machine__findnew_thread(machine, sample->pid, sample->tid);
|
||||
sample__resolve_callchain(sample, NULL, evsel, &al, 16);
|
||||
sample__resolve_callchain(sample, &callchain_cursor, NULL, evsel, &al, 16);
|
||||
|
||||
callchain_cursor_commit(&callchain_cursor);
|
||||
while (true) {
|
||||
|
@ -930,43 +930,48 @@ out_delete_session:
|
||||
return status;
|
||||
}
|
||||
|
||||
static void callchain_debug(void)
|
||||
static void callchain_debug(struct callchain_param *callchain)
|
||||
{
|
||||
static const char *str[CALLCHAIN_MAX] = { "NONE", "FP", "DWARF", "LBR" };
|
||||
|
||||
pr_debug("callchain: type %s\n", str[callchain_param.record_mode]);
|
||||
pr_debug("callchain: type %s\n", str[callchain->record_mode]);
|
||||
|
||||
if (callchain_param.record_mode == CALLCHAIN_DWARF)
|
||||
if (callchain->record_mode == CALLCHAIN_DWARF)
|
||||
pr_debug("callchain: stack dump size %d\n",
|
||||
callchain_param.dump_size);
|
||||
callchain->dump_size);
|
||||
}
|
||||
|
||||
int record_opts__parse_callchain(struct record_opts *record,
|
||||
struct callchain_param *callchain,
|
||||
const char *arg, bool unset)
|
||||
{
|
||||
int ret;
|
||||
record->callgraph_set = true;
|
||||
callchain->enabled = !unset;
|
||||
|
||||
/* --no-call-graph */
|
||||
if (unset) {
|
||||
callchain->record_mode = CALLCHAIN_NONE;
|
||||
pr_debug("callchain: disabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = parse_callchain_record_opt(arg, callchain);
|
||||
if (!ret) {
|
||||
/* Enable data address sampling for DWARF unwind. */
|
||||
if (callchain->record_mode == CALLCHAIN_DWARF)
|
||||
record->sample_address = true;
|
||||
callchain_debug(callchain);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int record_parse_callchain_opt(const struct option *opt,
|
||||
const char *arg,
|
||||
int unset)
|
||||
{
|
||||
int ret;
|
||||
struct record_opts *record = (struct record_opts *)opt->value;
|
||||
|
||||
record->callgraph_set = true;
|
||||
callchain_param.enabled = !unset;
|
||||
|
||||
/* --no-call-graph */
|
||||
if (unset) {
|
||||
callchain_param.record_mode = CALLCHAIN_NONE;
|
||||
pr_debug("callchain: disabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = parse_callchain_record_opt(arg, &callchain_param);
|
||||
if (!ret) {
|
||||
/* Enable data address sampling for DWARF unwind. */
|
||||
if (callchain_param.record_mode == CALLCHAIN_DWARF)
|
||||
record->sample_address = true;
|
||||
callchain_debug();
|
||||
}
|
||||
|
||||
return ret;
|
||||
return record_opts__parse_callchain(opt->value, &callchain_param, arg, unset);
|
||||
}
|
||||
|
||||
int record_callchain_opt(const struct option *opt,
|
||||
@ -981,7 +986,7 @@ int record_callchain_opt(const struct option *opt,
|
||||
if (callchain_param.record_mode == CALLCHAIN_NONE)
|
||||
callchain_param.record_mode = CALLCHAIN_FP;
|
||||
|
||||
callchain_debug();
|
||||
callchain_debug(&callchain_param);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "util/thread_map.h"
|
||||
#include "util/stat.h"
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/stringify.h>
|
||||
#include "asm/bug.h"
|
||||
#include "util/mem-events.h"
|
||||
|
||||
@ -569,18 +570,23 @@ static void print_sample_bts(struct perf_sample *sample,
|
||||
/* print branch_from information */
|
||||
if (PRINT_FIELD(IP)) {
|
||||
unsigned int print_opts = output[attr->type].print_ip_opts;
|
||||
struct callchain_cursor *cursor = NULL, cursor_callchain;
|
||||
|
||||
if (symbol_conf.use_callchain && sample->callchain) {
|
||||
printf("\n");
|
||||
} else {
|
||||
printf(" ");
|
||||
if (symbol_conf.use_callchain && sample->callchain &&
|
||||
thread__resolve_callchain(al->thread, &cursor_callchain, evsel,
|
||||
sample, NULL, NULL, scripting_max_stack) == 0)
|
||||
cursor = &cursor_callchain;
|
||||
|
||||
if (cursor == NULL) {
|
||||
putchar(' ');
|
||||
if (print_opts & EVSEL__PRINT_SRCLINE) {
|
||||
print_srcline_last = true;
|
||||
print_opts &= ~EVSEL__PRINT_SRCLINE;
|
||||
}
|
||||
}
|
||||
perf_evsel__fprintf_sym(evsel, sample, al, 0, print_opts,
|
||||
scripting_max_stack, stdout);
|
||||
} else
|
||||
putchar('\n');
|
||||
|
||||
sample__fprintf_sym(sample, al, 0, print_opts, cursor, stdout);
|
||||
}
|
||||
|
||||
/* print branch_to information */
|
||||
@ -783,14 +789,15 @@ static void process_event(struct perf_script *script,
|
||||
printf("%16" PRIu64, sample->weight);
|
||||
|
||||
if (PRINT_FIELD(IP)) {
|
||||
if (!symbol_conf.use_callchain)
|
||||
printf(" ");
|
||||
else
|
||||
printf("\n");
|
||||
struct callchain_cursor *cursor = NULL, cursor_callchain;
|
||||
|
||||
perf_evsel__fprintf_sym(evsel, sample, al, 0,
|
||||
output[attr->type].print_ip_opts,
|
||||
scripting_max_stack, stdout);
|
||||
if (symbol_conf.use_callchain &&
|
||||
thread__resolve_callchain(al->thread, &cursor_callchain, evsel,
|
||||
sample, NULL, NULL, scripting_max_stack) == 0)
|
||||
cursor = &cursor_callchain;
|
||||
|
||||
putchar(cursor ? '\n' : ' ');
|
||||
sample__fprintf_sym(sample, al, 0, output[attr->type].print_ip_opts, cursor, stdout);
|
||||
}
|
||||
|
||||
if (PRINT_FIELD(IREGS))
|
||||
@ -2021,6 +2028,10 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
"only consider symbols in these pids"),
|
||||
OPT_STRING(0, "tid", &symbol_conf.tid_list_str, "tid[,tid...]",
|
||||
"only consider symbols in these tids"),
|
||||
OPT_UINTEGER(0, "max-stack", &scripting_max_stack,
|
||||
"Set the maximum stack depth when parsing the callchain, "
|
||||
"anything beyond the specified depth will be ignored. "
|
||||
"Default: " __stringify(PERF_MAX_STACK_DEPTH)),
|
||||
OPT_BOOLEAN('I', "show-info", &show_full_info,
|
||||
"display extended information from perf.data file"),
|
||||
OPT_BOOLEAN('\0', "show-kernel-path", &symbol_conf.show_kernel_path,
|
||||
|
@ -46,23 +46,12 @@
|
||||
#include <linux/audit.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/stringify.h>
|
||||
|
||||
#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
|
||||
@ -118,6 +107,8 @@ struct trace {
|
||||
u64 vfs_getname,
|
||||
proc_getname;
|
||||
} stats;
|
||||
unsigned int max_stack;
|
||||
unsigned int min_stack;
|
||||
bool not_ev_qualifier;
|
||||
bool live;
|
||||
bool full_time;
|
||||
@ -538,53 +529,6 @@ static const char *socket_families[] = {
|
||||
};
|
||||
static DEFINE_STRARRAY(socket_families);
|
||||
|
||||
#ifndef SOCK_TYPE_MASK
|
||||
#define SOCK_TYPE_MASK 0xf
|
||||
#endif
|
||||
|
||||
static size_t syscall_arg__scnprintf_socket_type(char *bf, size_t size,
|
||||
struct syscall_arg *arg)
|
||||
{
|
||||
size_t printed;
|
||||
int type = arg->val,
|
||||
flags = type & ~SOCK_TYPE_MASK;
|
||||
|
||||
type &= SOCK_TYPE_MASK;
|
||||
/*
|
||||
* Can't use a strarray, MIPS may override for ABI reasons.
|
||||
*/
|
||||
switch (type) {
|
||||
#define P_SK_TYPE(n) case SOCK_##n: printed = scnprintf(bf, size, #n); break;
|
||||
P_SK_TYPE(STREAM);
|
||||
P_SK_TYPE(DGRAM);
|
||||
P_SK_TYPE(RAW);
|
||||
P_SK_TYPE(RDM);
|
||||
P_SK_TYPE(SEQPACKET);
|
||||
P_SK_TYPE(DCCP);
|
||||
P_SK_TYPE(PACKET);
|
||||
#undef P_SK_TYPE
|
||||
default:
|
||||
printed = scnprintf(bf, size, "%#x", type);
|
||||
}
|
||||
|
||||
#define P_SK_FLAG(n) \
|
||||
if (flags & SOCK_##n) { \
|
||||
printed += scnprintf(bf + printed, size - printed, "|%s", #n); \
|
||||
flags &= ~SOCK_##n; \
|
||||
}
|
||||
|
||||
P_SK_FLAG(CLOEXEC);
|
||||
P_SK_FLAG(NONBLOCK);
|
||||
#undef P_SK_FLAG
|
||||
|
||||
if (flags)
|
||||
printed += scnprintf(bf + printed, size - printed, "|%#x", flags);
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
#define SCA_SK_TYPE syscall_arg__scnprintf_socket_type
|
||||
|
||||
#ifndef MSG_PROBE
|
||||
#define MSG_PROBE 0x10
|
||||
#endif
|
||||
@ -951,6 +895,7 @@ static size_t syscall_arg__scnprintf_getrandom_flags(char *bf, size_t size,
|
||||
#include "trace/beauty/mmap.c"
|
||||
#include "trace/beauty/mode_t.c"
|
||||
#include "trace/beauty/sched_policy.c"
|
||||
#include "trace/beauty/socket_type.c"
|
||||
#include "trace/beauty/waitid_options.c"
|
||||
|
||||
static struct syscall_fmt {
|
||||
@ -1905,7 +1850,7 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
if (!trace->summary_only)
|
||||
if (!(trace->duration_filter || trace->summary_only || trace->min_stack))
|
||||
trace__printf_interrupted_entry(trace, sample);
|
||||
|
||||
ttrace->entry_time = sample->time;
|
||||
@ -1916,7 +1861,7 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,
|
||||
args, trace, thread);
|
||||
|
||||
if (sc->is_exit) {
|
||||
if (!trace->duration_filter && !trace->summary_only) {
|
||||
if (!(trace->duration_filter || trace->summary_only || trace->min_stack)) {
|
||||
trace__fprintf_entry_head(trace, thread, 1, sample->time, trace->output);
|
||||
fprintf(trace->output, "%-70s\n", ttrace->entry_str);
|
||||
}
|
||||
@ -1936,26 +1881,27 @@ out_put:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int trace__fprintf_callchain(struct trace *trace, struct perf_evsel *evsel,
|
||||
struct perf_sample *sample)
|
||||
static int trace__resolve_callchain(struct trace *trace, struct perf_evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct callchain_cursor *cursor)
|
||||
{
|
||||
struct addr_location al;
|
||||
|
||||
if (machine__resolve(trace->host, &al, sample) < 0 ||
|
||||
thread__resolve_callchain(al.thread, cursor, evsel, sample, NULL, NULL, trace->max_stack))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int trace__fprintf_callchain(struct trace *trace, struct perf_sample *sample)
|
||||
{
|
||||
/* TODO: user-configurable print_opts */
|
||||
const unsigned int print_opts = EVSEL__PRINT_SYM |
|
||||
EVSEL__PRINT_DSO |
|
||||
EVSEL__PRINT_UNKNOWN_AS_ADDR;
|
||||
|
||||
if (sample->callchain == NULL)
|
||||
return 0;
|
||||
|
||||
if (machine__resolve(trace->host, &al, sample) < 0) {
|
||||
pr_err("Problem processing %s callchain, skipping...\n",
|
||||
perf_evsel__name(evsel));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return perf_evsel__fprintf_callchain(evsel, sample, &al, 38, print_opts,
|
||||
scripting_max_stack, trace->output);
|
||||
return sample__fprintf_callchain(sample, 38, print_opts, &callchain_cursor, trace->output);
|
||||
}
|
||||
|
||||
static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
|
||||
@ -1965,7 +1911,7 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
|
||||
long ret;
|
||||
u64 duration = 0;
|
||||
struct thread *thread;
|
||||
int id = perf_evsel__sc_tp_uint(evsel, id, sample), err = -1;
|
||||
int id = perf_evsel__sc_tp_uint(evsel, id, sample), err = -1, callchain_ret = 0;
|
||||
struct syscall *sc = trace__syscall_info(trace, evsel, id);
|
||||
struct thread_trace *ttrace;
|
||||
|
||||
@ -1997,6 +1943,15 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
|
||||
} else if (trace->duration_filter)
|
||||
goto out;
|
||||
|
||||
if (sample->callchain) {
|
||||
callchain_ret = trace__resolve_callchain(trace, evsel, sample, &callchain_cursor);
|
||||
if (callchain_ret == 0) {
|
||||
if (callchain_cursor.nr < trace->min_stack)
|
||||
goto out;
|
||||
callchain_ret = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (trace->summary_only)
|
||||
goto out;
|
||||
|
||||
@ -2037,7 +1992,10 @@ signed_print:
|
||||
|
||||
fputc('\n', trace->output);
|
||||
|
||||
trace__fprintf_callchain(trace, evsel, sample);
|
||||
if (callchain_ret > 0)
|
||||
trace__fprintf_callchain(trace, sample);
|
||||
else if (callchain_ret < 0)
|
||||
pr_err("Problem processing %s callchain, skipping...\n", perf_evsel__name(evsel));
|
||||
out:
|
||||
ttrace->entry_pending = false;
|
||||
err = 0;
|
||||
@ -2186,7 +2144,10 @@ static int trace__event_handler(struct trace *trace, struct perf_evsel *evsel,
|
||||
|
||||
fprintf(trace->output, ")\n");
|
||||
|
||||
trace__fprintf_callchain(trace, evsel, sample);
|
||||
if (sample->callchain) {
|
||||
if (trace__resolve_callchain(trace, evsel, sample, &callchain_cursor) == 0)
|
||||
trace__fprintf_callchain(trace, sample);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -3086,6 +3047,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
.show_comm = true,
|
||||
.trace_syscalls = true,
|
||||
.kernel_syscallchains = false,
|
||||
.max_stack = UINT_MAX,
|
||||
};
|
||||
const char *output_name = NULL;
|
||||
const char *ev_qualifier_str = NULL;
|
||||
@ -3136,10 +3098,19 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
&record_parse_callchain_opt),
|
||||
OPT_BOOLEAN(0, "kernel-syscall-graph", &trace.kernel_syscallchains,
|
||||
"Show the kernel callchains on the syscall exit path"),
|
||||
OPT_UINTEGER(0, "min-stack", &trace.min_stack,
|
||||
"Set the minimum stack depth when parsing the callchain, "
|
||||
"anything below the specified depth will be ignored."),
|
||||
OPT_UINTEGER(0, "max-stack", &trace.max_stack,
|
||||
"Set the maximum stack depth when parsing the callchain, "
|
||||
"anything beyond the specified depth will be ignored. "
|
||||
"Default: " __stringify(PERF_MAX_STACK_DEPTH)),
|
||||
OPT_UINTEGER(0, "proc-map-timeout", &trace.opts.proc_map_timeout,
|
||||
"per thread proc mmap processing timeout in ms"),
|
||||
OPT_END()
|
||||
};
|
||||
bool max_stack_user_set = true;
|
||||
bool mmap_pages_user_set = true;
|
||||
const char * const trace_subcommands[] = { "record", NULL };
|
||||
int err;
|
||||
char bf[BUFSIZ];
|
||||
@ -3173,8 +3144,25 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
trace.opts.sample_time = true;
|
||||
}
|
||||
|
||||
if (trace.opts.callgraph_set)
|
||||
if (trace.opts.mmap_pages == UINT_MAX)
|
||||
mmap_pages_user_set = false;
|
||||
|
||||
if (trace.max_stack == UINT_MAX) {
|
||||
trace.max_stack = PERF_MAX_STACK_DEPTH;
|
||||
max_stack_user_set = false;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DWARF_UNWIND_SUPPORT
|
||||
if ((trace.min_stack || max_stack_user_set) && !trace.opts.callgraph_set)
|
||||
record_opts__parse_callchain(&trace.opts, &callchain_param, "dwarf", false);
|
||||
#endif
|
||||
|
||||
if (trace.opts.callgraph_set) {
|
||||
if (!mmap_pages_user_set && geteuid() == 0)
|
||||
trace.opts.mmap_pages = perf_event_mlock_kb_in_pages() * 4;
|
||||
|
||||
symbol_conf.use_callchain = true;
|
||||
}
|
||||
|
||||
if (trace.evlist->nr_entries > 0)
|
||||
evlist__set_evsel_handler(trace.evlist, trace__event_handler);
|
||||
|
60
tools/perf/trace/beauty/socket_type.c
Normal file
60
tools/perf/trace/beauty/socket_type.c
Normal file
@ -0,0 +1,60 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#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 SOCK_TYPE_MASK
|
||||
#define SOCK_TYPE_MASK 0xf
|
||||
#endif
|
||||
|
||||
static size_t syscall_arg__scnprintf_socket_type(char *bf, size_t size, struct syscall_arg *arg)
|
||||
{
|
||||
size_t printed;
|
||||
int type = arg->val,
|
||||
flags = type & ~SOCK_TYPE_MASK;
|
||||
|
||||
type &= SOCK_TYPE_MASK;
|
||||
/*
|
||||
* Can't use a strarray, MIPS may override for ABI reasons.
|
||||
*/
|
||||
switch (type) {
|
||||
#define P_SK_TYPE(n) case SOCK_##n: printed = scnprintf(bf, size, #n); break;
|
||||
P_SK_TYPE(STREAM);
|
||||
P_SK_TYPE(DGRAM);
|
||||
P_SK_TYPE(RAW);
|
||||
P_SK_TYPE(RDM);
|
||||
P_SK_TYPE(SEQPACKET);
|
||||
P_SK_TYPE(DCCP);
|
||||
P_SK_TYPE(PACKET);
|
||||
#undef P_SK_TYPE
|
||||
default:
|
||||
printed = scnprintf(bf, size, "%#x", type);
|
||||
}
|
||||
|
||||
#define P_SK_FLAG(n) \
|
||||
if (flags & SOCK_##n) { \
|
||||
printed += scnprintf(bf + printed, size - printed, "|%s", #n); \
|
||||
flags &= ~SOCK_##n; \
|
||||
}
|
||||
|
||||
P_SK_FLAG(CLOEXEC);
|
||||
P_SK_FLAG(NONBLOCK);
|
||||
#undef P_SK_FLAG
|
||||
|
||||
if (flags)
|
||||
printed += scnprintf(bf + printed, size - printed, "|%#x", flags);
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
#define SCA_SK_TYPE syscall_arg__scnprintf_socket_type
|
@ -8,6 +8,7 @@ libperf-y += env.o
|
||||
libperf-y += event.o
|
||||
libperf-y += evlist.o
|
||||
libperf-y += evsel.o
|
||||
libperf-y += evsel_fprintf.o
|
||||
libperf-y += find_bit.o
|
||||
libperf-y += kallsyms.o
|
||||
libperf-y += levenshtein.o
|
||||
@ -29,6 +30,7 @@ libperf-y += usage.o
|
||||
libperf-y += wrapper.o
|
||||
libperf-y += dso.o
|
||||
libperf-y += symbol.o
|
||||
libperf-y += symbol_fprintf.o
|
||||
libperf-y += color.o
|
||||
libperf-y += header.o
|
||||
libperf-y += callchain.o
|
||||
|
@ -788,7 +788,8 @@ int callchain_cursor_append(struct callchain_cursor *cursor,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent,
|
||||
int sample__resolve_callchain(struct perf_sample *sample,
|
||||
struct callchain_cursor *cursor, struct symbol **parent,
|
||||
struct perf_evsel *evsel, struct addr_location *al,
|
||||
int max_stack)
|
||||
{
|
||||
@ -797,7 +798,7 @@ int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent
|
||||
|
||||
if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain ||
|
||||
sort__has_parent) {
|
||||
return thread__resolve_callchain(al->thread, evsel, sample,
|
||||
return thread__resolve_callchain(al->thread, cursor, evsel, sample,
|
||||
parent, al, max_stack);
|
||||
}
|
||||
return 0;
|
||||
|
@ -212,7 +212,14 @@ struct hist_entry;
|
||||
int record_parse_callchain_opt(const struct option *opt, const char *arg, int unset);
|
||||
int record_callchain_opt(const struct option *opt, const char *arg, int unset);
|
||||
|
||||
int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent,
|
||||
struct record_opts;
|
||||
|
||||
int record_opts__parse_callchain(struct record_opts *record,
|
||||
struct callchain_param *callchain,
|
||||
const char *arg, bool unset);
|
||||
|
||||
int sample__resolve_callchain(struct perf_sample *sample,
|
||||
struct callchain_cursor *cursor, struct symbol **parent,
|
||||
struct perf_evsel *evsel, struct addr_location *al,
|
||||
int max_stack);
|
||||
int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *sample);
|
||||
|
@ -986,26 +986,34 @@ out_unmap:
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsigned long perf_event_mlock_kb_in_pages(void)
|
||||
{
|
||||
unsigned long pages;
|
||||
int max;
|
||||
|
||||
if (sysctl__read_int("kernel/perf_event_mlock_kb", &max) < 0) {
|
||||
/*
|
||||
* Pick a once upon a time good value, i.e. things look
|
||||
* strange since we can't read a sysctl value, but lets not
|
||||
* die yet...
|
||||
*/
|
||||
max = 512;
|
||||
} else {
|
||||
max -= (page_size / 1024);
|
||||
}
|
||||
|
||||
pages = (max * 1024) / page_size;
|
||||
if (!is_power_of_2(pages))
|
||||
pages = rounddown_pow_of_two(pages);
|
||||
|
||||
return pages;
|
||||
}
|
||||
|
||||
static size_t perf_evlist__mmap_size(unsigned long pages)
|
||||
{
|
||||
if (pages == UINT_MAX) {
|
||||
int max;
|
||||
|
||||
if (sysctl__read_int("kernel/perf_event_mlock_kb", &max) < 0) {
|
||||
/*
|
||||
* Pick a once upon a time good value, i.e. things look
|
||||
* strange since we can't read a sysctl value, but lets not
|
||||
* die yet...
|
||||
*/
|
||||
max = 512;
|
||||
} else {
|
||||
max -= (page_size / 1024);
|
||||
}
|
||||
|
||||
pages = (max * 1024) / page_size;
|
||||
if (!is_power_of_2(pages))
|
||||
pages = rounddown_pow_of_two(pages);
|
||||
} else if (!is_power_of_2(pages))
|
||||
if (pages == UINT_MAX)
|
||||
pages = perf_event_mlock_kb_in_pages();
|
||||
else if (!is_power_of_2(pages))
|
||||
return 0;
|
||||
|
||||
return (pages + 1) * page_size;
|
||||
|
@ -158,6 +158,8 @@ int perf_evlist__parse_mmap_pages(const struct option *opt,
|
||||
const char *str,
|
||||
int unset);
|
||||
|
||||
unsigned long perf_event_mlock_kb_in_pages(void);
|
||||
|
||||
int perf_evlist__mmap_ex(struct perf_evlist *evlist, unsigned int pages,
|
||||
bool overwrite, unsigned int auxtrace_pages,
|
||||
bool auxtrace_overwrite);
|
||||
|
@ -2254,226 +2254,6 @@ u64 perf_evsel__intval(struct perf_evsel *evsel, struct perf_sample *sample,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int comma_fprintf(FILE *fp, bool *first, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int ret = 0;
|
||||
|
||||
if (!*first) {
|
||||
ret += fprintf(fp, ",");
|
||||
} else {
|
||||
ret += fprintf(fp, ":");
|
||||
*first = false;
|
||||
}
|
||||
|
||||
va_start(args, fmt);
|
||||
ret += vfprintf(fp, fmt, args);
|
||||
va_end(args);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __print_attr__fprintf(FILE *fp, const char *name, const char *val, void *priv)
|
||||
{
|
||||
return comma_fprintf(fp, (bool *)priv, " %s: %s", name, val);
|
||||
}
|
||||
|
||||
int perf_evsel__fprintf(struct perf_evsel *evsel,
|
||||
struct perf_attr_details *details, FILE *fp)
|
||||
{
|
||||
bool first = true;
|
||||
int printed = 0;
|
||||
|
||||
if (details->event_group) {
|
||||
struct perf_evsel *pos;
|
||||
|
||||
if (!perf_evsel__is_group_leader(evsel))
|
||||
return 0;
|
||||
|
||||
if (evsel->nr_members > 1)
|
||||
printed += fprintf(fp, "%s{", evsel->group_name ?: "");
|
||||
|
||||
printed += fprintf(fp, "%s", perf_evsel__name(evsel));
|
||||
for_each_group_member(pos, evsel)
|
||||
printed += fprintf(fp, ",%s", perf_evsel__name(pos));
|
||||
|
||||
if (evsel->nr_members > 1)
|
||||
printed += fprintf(fp, "}");
|
||||
goto out;
|
||||
}
|
||||
|
||||
printed += fprintf(fp, "%s", perf_evsel__name(evsel));
|
||||
|
||||
if (details->verbose) {
|
||||
printed += perf_event_attr__fprintf(fp, &evsel->attr,
|
||||
__print_attr__fprintf, &first);
|
||||
} else if (details->freq) {
|
||||
const char *term = "sample_freq";
|
||||
|
||||
if (!evsel->attr.freq)
|
||||
term = "sample_period";
|
||||
|
||||
printed += comma_fprintf(fp, &first, " %s=%" PRIu64,
|
||||
term, (u64)evsel->attr.sample_freq);
|
||||
}
|
||||
|
||||
if (details->trace_fields) {
|
||||
struct format_field *field;
|
||||
|
||||
if (evsel->attr.type != PERF_TYPE_TRACEPOINT) {
|
||||
printed += comma_fprintf(fp, &first, " (not a tracepoint)");
|
||||
goto out;
|
||||
}
|
||||
|
||||
field = evsel->tp_format->format.fields;
|
||||
if (field == NULL) {
|
||||
printed += comma_fprintf(fp, &first, " (no trace field)");
|
||||
goto out;
|
||||
}
|
||||
|
||||
printed += comma_fprintf(fp, &first, " trace_fields: %s", field->name);
|
||||
|
||||
field = field->next;
|
||||
while (field) {
|
||||
printed += comma_fprintf(fp, &first, "%s", field->name);
|
||||
field = field->next;
|
||||
}
|
||||
}
|
||||
out:
|
||||
fputc('\n', fp);
|
||||
return ++printed;
|
||||
}
|
||||
|
||||
int perf_evsel__fprintf_callchain(struct perf_evsel *evsel, struct perf_sample *sample,
|
||||
struct addr_location *al, int left_alignment,
|
||||
unsigned int print_opts, unsigned int stack_depth,
|
||||
FILE *fp)
|
||||
{
|
||||
int printed = 0;
|
||||
struct callchain_cursor_node *node;
|
||||
int print_ip = print_opts & EVSEL__PRINT_IP;
|
||||
int print_sym = print_opts & EVSEL__PRINT_SYM;
|
||||
int print_dso = print_opts & EVSEL__PRINT_DSO;
|
||||
int print_symoffset = print_opts & EVSEL__PRINT_SYMOFFSET;
|
||||
int print_oneline = print_opts & EVSEL__PRINT_ONELINE;
|
||||
int print_srcline = print_opts & EVSEL__PRINT_SRCLINE;
|
||||
int print_unknown_as_addr = print_opts & EVSEL__PRINT_UNKNOWN_AS_ADDR;
|
||||
char s = print_oneline ? ' ' : '\t';
|
||||
|
||||
if (sample->callchain) {
|
||||
struct addr_location node_al;
|
||||
|
||||
if (thread__resolve_callchain(al->thread, evsel,
|
||||
sample, NULL, NULL,
|
||||
stack_depth) != 0) {
|
||||
if (verbose)
|
||||
error("Failed to resolve callchain. Skipping\n");
|
||||
return printed;
|
||||
}
|
||||
callchain_cursor_commit(&callchain_cursor);
|
||||
|
||||
if (print_symoffset)
|
||||
node_al = *al;
|
||||
|
||||
while (stack_depth) {
|
||||
u64 addr = 0;
|
||||
|
||||
node = callchain_cursor_current(&callchain_cursor);
|
||||
if (!node)
|
||||
break;
|
||||
|
||||
if (node->sym && node->sym->ignore)
|
||||
goto next;
|
||||
|
||||
printed += fprintf(fp, "%-*.*s", left_alignment, left_alignment, " ");
|
||||
|
||||
if (print_ip)
|
||||
printed += fprintf(fp, "%c%16" PRIx64, s, node->ip);
|
||||
|
||||
if (node->map)
|
||||
addr = node->map->map_ip(node->map, node->ip);
|
||||
|
||||
if (print_sym) {
|
||||
printed += fprintf(fp, " ");
|
||||
node_al.addr = addr;
|
||||
node_al.map = node->map;
|
||||
|
||||
if (print_symoffset) {
|
||||
printed += __symbol__fprintf_symname_offs(node->sym, &node_al,
|
||||
print_unknown_as_addr, fp);
|
||||
} else {
|
||||
printed += __symbol__fprintf_symname(node->sym, &node_al,
|
||||
print_unknown_as_addr, fp);
|
||||
}
|
||||
}
|
||||
|
||||
if (print_dso) {
|
||||
printed += fprintf(fp, " (");
|
||||
printed += map__fprintf_dsoname(node->map, fp);
|
||||
printed += fprintf(fp, ")");
|
||||
}
|
||||
|
||||
if (print_srcline)
|
||||
printed += map__fprintf_srcline(node->map, addr, "\n ", fp);
|
||||
|
||||
if (!print_oneline)
|
||||
printed += fprintf(fp, "\n");
|
||||
|
||||
stack_depth--;
|
||||
next:
|
||||
callchain_cursor_advance(&callchain_cursor);
|
||||
}
|
||||
}
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
int perf_evsel__fprintf_sym(struct perf_evsel *evsel, struct perf_sample *sample,
|
||||
struct addr_location *al, int left_alignment,
|
||||
unsigned int print_opts, unsigned int stack_depth,
|
||||
FILE *fp)
|
||||
{
|
||||
int printed = 0;
|
||||
int print_ip = print_opts & EVSEL__PRINT_IP;
|
||||
int print_sym = print_opts & EVSEL__PRINT_SYM;
|
||||
int print_dso = print_opts & EVSEL__PRINT_DSO;
|
||||
int print_symoffset = print_opts & EVSEL__PRINT_SYMOFFSET;
|
||||
int print_srcline = print_opts & EVSEL__PRINT_SRCLINE;
|
||||
int print_unknown_as_addr = print_opts & EVSEL__PRINT_UNKNOWN_AS_ADDR;
|
||||
|
||||
if (symbol_conf.use_callchain && sample->callchain) {
|
||||
printed += perf_evsel__fprintf_callchain(evsel, sample, al, left_alignment,
|
||||
print_opts, stack_depth, fp);
|
||||
} else if (!(al->sym && al->sym->ignore)) {
|
||||
printed += fprintf(fp, "%-*.*s", left_alignment, left_alignment, " ");
|
||||
|
||||
if (print_ip)
|
||||
printed += fprintf(fp, "%16" PRIx64, sample->ip);
|
||||
|
||||
if (print_sym) {
|
||||
printed += fprintf(fp, " ");
|
||||
if (print_symoffset) {
|
||||
printed += __symbol__fprintf_symname_offs(al->sym, al,
|
||||
print_unknown_as_addr, fp);
|
||||
} else {
|
||||
printed += __symbol__fprintf_symname(al->sym, al,
|
||||
print_unknown_as_addr, fp);
|
||||
}
|
||||
}
|
||||
|
||||
if (print_dso) {
|
||||
printed += fprintf(fp, " (");
|
||||
printed += map__fprintf_dsoname(al->map, fp);
|
||||
printed += fprintf(fp, ")");
|
||||
}
|
||||
|
||||
if (print_srcline)
|
||||
printed += map__fprintf_srcline(al->map, al->addr, "\n ", fp);
|
||||
}
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
|
||||
bool perf_evsel__fallback(struct perf_evsel *evsel, int err,
|
||||
char *msg, size_t msgsize)
|
||||
{
|
||||
|
@ -395,16 +395,15 @@ int perf_evsel__fprintf(struct perf_evsel *evsel,
|
||||
#define EVSEL__PRINT_SRCLINE (1<<5)
|
||||
#define EVSEL__PRINT_UNKNOWN_AS_ADDR (1<<6)
|
||||
|
||||
int perf_evsel__fprintf_callchain(struct perf_evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct addr_location *al, int left_alignment,
|
||||
unsigned int print_opts,
|
||||
unsigned int stack_depth, FILE *fp);
|
||||
struct callchain_cursor;
|
||||
|
||||
int perf_evsel__fprintf_sym(struct perf_evsel *evsel, struct perf_sample *sample,
|
||||
struct addr_location *al, int left_alignment,
|
||||
unsigned int print_opts, unsigned int stack_depth,
|
||||
FILE *fp);
|
||||
int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment,
|
||||
unsigned int print_opts,
|
||||
struct callchain_cursor *cursor, FILE *fp);
|
||||
|
||||
int sample__fprintf_sym(struct perf_sample *sample, struct addr_location *al,
|
||||
int left_alignment, unsigned int print_opts,
|
||||
struct callchain_cursor *cursor, FILE *fp);
|
||||
|
||||
bool perf_evsel__fallback(struct perf_evsel *evsel, int err,
|
||||
char *msg, size_t msgsize);
|
||||
|
212
tools/perf/util/evsel_fprintf.c
Normal file
212
tools/perf/util/evsel_fprintf.c
Normal file
@ -0,0 +1,212 @@
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <traceevent/event-parse.h>
|
||||
#include "evsel.h"
|
||||
#include "callchain.h"
|
||||
#include "map.h"
|
||||
#include "symbol.h"
|
||||
|
||||
static int comma_fprintf(FILE *fp, bool *first, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
int ret = 0;
|
||||
|
||||
if (!*first) {
|
||||
ret += fprintf(fp, ",");
|
||||
} else {
|
||||
ret += fprintf(fp, ":");
|
||||
*first = false;
|
||||
}
|
||||
|
||||
va_start(args, fmt);
|
||||
ret += vfprintf(fp, fmt, args);
|
||||
va_end(args);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __print_attr__fprintf(FILE *fp, const char *name, const char *val, void *priv)
|
||||
{
|
||||
return comma_fprintf(fp, (bool *)priv, " %s: %s", name, val);
|
||||
}
|
||||
|
||||
int perf_evsel__fprintf(struct perf_evsel *evsel,
|
||||
struct perf_attr_details *details, FILE *fp)
|
||||
{
|
||||
bool first = true;
|
||||
int printed = 0;
|
||||
|
||||
if (details->event_group) {
|
||||
struct perf_evsel *pos;
|
||||
|
||||
if (!perf_evsel__is_group_leader(evsel))
|
||||
return 0;
|
||||
|
||||
if (evsel->nr_members > 1)
|
||||
printed += fprintf(fp, "%s{", evsel->group_name ?: "");
|
||||
|
||||
printed += fprintf(fp, "%s", perf_evsel__name(evsel));
|
||||
for_each_group_member(pos, evsel)
|
||||
printed += fprintf(fp, ",%s", perf_evsel__name(pos));
|
||||
|
||||
if (evsel->nr_members > 1)
|
||||
printed += fprintf(fp, "}");
|
||||
goto out;
|
||||
}
|
||||
|
||||
printed += fprintf(fp, "%s", perf_evsel__name(evsel));
|
||||
|
||||
if (details->verbose) {
|
||||
printed += perf_event_attr__fprintf(fp, &evsel->attr,
|
||||
__print_attr__fprintf, &first);
|
||||
} else if (details->freq) {
|
||||
const char *term = "sample_freq";
|
||||
|
||||
if (!evsel->attr.freq)
|
||||
term = "sample_period";
|
||||
|
||||
printed += comma_fprintf(fp, &first, " %s=%" PRIu64,
|
||||
term, (u64)evsel->attr.sample_freq);
|
||||
}
|
||||
|
||||
if (details->trace_fields) {
|
||||
struct format_field *field;
|
||||
|
||||
if (evsel->attr.type != PERF_TYPE_TRACEPOINT) {
|
||||
printed += comma_fprintf(fp, &first, " (not a tracepoint)");
|
||||
goto out;
|
||||
}
|
||||
|
||||
field = evsel->tp_format->format.fields;
|
||||
if (field == NULL) {
|
||||
printed += comma_fprintf(fp, &first, " (no trace field)");
|
||||
goto out;
|
||||
}
|
||||
|
||||
printed += comma_fprintf(fp, &first, " trace_fields: %s", field->name);
|
||||
|
||||
field = field->next;
|
||||
while (field) {
|
||||
printed += comma_fprintf(fp, &first, "%s", field->name);
|
||||
field = field->next;
|
||||
}
|
||||
}
|
||||
out:
|
||||
fputc('\n', fp);
|
||||
return ++printed;
|
||||
}
|
||||
|
||||
int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment,
|
||||
unsigned int print_opts, struct callchain_cursor *cursor,
|
||||
FILE *fp)
|
||||
{
|
||||
int printed = 0;
|
||||
struct callchain_cursor_node *node;
|
||||
int print_ip = print_opts & EVSEL__PRINT_IP;
|
||||
int print_sym = print_opts & EVSEL__PRINT_SYM;
|
||||
int print_dso = print_opts & EVSEL__PRINT_DSO;
|
||||
int print_symoffset = print_opts & EVSEL__PRINT_SYMOFFSET;
|
||||
int print_oneline = print_opts & EVSEL__PRINT_ONELINE;
|
||||
int print_srcline = print_opts & EVSEL__PRINT_SRCLINE;
|
||||
int print_unknown_as_addr = print_opts & EVSEL__PRINT_UNKNOWN_AS_ADDR;
|
||||
char s = print_oneline ? ' ' : '\t';
|
||||
|
||||
if (sample->callchain) {
|
||||
struct addr_location node_al;
|
||||
|
||||
callchain_cursor_commit(cursor);
|
||||
|
||||
while (1) {
|
||||
u64 addr = 0;
|
||||
|
||||
node = callchain_cursor_current(cursor);
|
||||
if (!node)
|
||||
break;
|
||||
|
||||
if (node->sym && node->sym->ignore)
|
||||
goto next;
|
||||
|
||||
printed += fprintf(fp, "%-*.*s", left_alignment, left_alignment, " ");
|
||||
|
||||
if (print_ip)
|
||||
printed += fprintf(fp, "%c%16" PRIx64, s, node->ip);
|
||||
|
||||
if (node->map)
|
||||
addr = node->map->map_ip(node->map, node->ip);
|
||||
|
||||
if (print_sym) {
|
||||
printed += fprintf(fp, " ");
|
||||
node_al.addr = addr;
|
||||
node_al.map = node->map;
|
||||
|
||||
if (print_symoffset) {
|
||||
printed += __symbol__fprintf_symname_offs(node->sym, &node_al,
|
||||
print_unknown_as_addr, fp);
|
||||
} else {
|
||||
printed += __symbol__fprintf_symname(node->sym, &node_al,
|
||||
print_unknown_as_addr, fp);
|
||||
}
|
||||
}
|
||||
|
||||
if (print_dso) {
|
||||
printed += fprintf(fp, " (");
|
||||
printed += map__fprintf_dsoname(node->map, fp);
|
||||
printed += fprintf(fp, ")");
|
||||
}
|
||||
|
||||
if (print_srcline)
|
||||
printed += map__fprintf_srcline(node->map, addr, "\n ", fp);
|
||||
|
||||
if (!print_oneline)
|
||||
printed += fprintf(fp, "\n");
|
||||
next:
|
||||
callchain_cursor_advance(cursor);
|
||||
}
|
||||
}
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
int sample__fprintf_sym(struct perf_sample *sample, struct addr_location *al,
|
||||
int left_alignment, unsigned int print_opts,
|
||||
struct callchain_cursor *cursor, FILE *fp)
|
||||
{
|
||||
int printed = 0;
|
||||
int print_ip = print_opts & EVSEL__PRINT_IP;
|
||||
int print_sym = print_opts & EVSEL__PRINT_SYM;
|
||||
int print_dso = print_opts & EVSEL__PRINT_DSO;
|
||||
int print_symoffset = print_opts & EVSEL__PRINT_SYMOFFSET;
|
||||
int print_srcline = print_opts & EVSEL__PRINT_SRCLINE;
|
||||
int print_unknown_as_addr = print_opts & EVSEL__PRINT_UNKNOWN_AS_ADDR;
|
||||
|
||||
if (cursor != NULL) {
|
||||
printed += sample__fprintf_callchain(sample, left_alignment,
|
||||
print_opts, cursor, fp);
|
||||
} else if (!(al->sym && al->sym->ignore)) {
|
||||
printed += fprintf(fp, "%-*.*s", left_alignment, left_alignment, " ");
|
||||
|
||||
if (print_ip)
|
||||
printed += fprintf(fp, "%16" PRIx64, sample->ip);
|
||||
|
||||
if (print_sym) {
|
||||
printed += fprintf(fp, " ");
|
||||
if (print_symoffset) {
|
||||
printed += __symbol__fprintf_symname_offs(al->sym, al,
|
||||
print_unknown_as_addr, fp);
|
||||
} else {
|
||||
printed += __symbol__fprintf_symname(al->sym, al,
|
||||
print_unknown_as_addr, fp);
|
||||
}
|
||||
}
|
||||
|
||||
if (print_dso) {
|
||||
printed += fprintf(fp, " (");
|
||||
printed += map__fprintf_dsoname(al->map, fp);
|
||||
printed += fprintf(fp, ")");
|
||||
}
|
||||
|
||||
if (print_srcline)
|
||||
printed += map__fprintf_srcline(al->map, al->addr, "\n ", fp);
|
||||
}
|
||||
|
||||
return printed;
|
||||
}
|
@ -953,7 +953,7 @@ int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al,
|
||||
{
|
||||
int err, err2;
|
||||
|
||||
err = sample__resolve_callchain(iter->sample, &iter->parent,
|
||||
err = sample__resolve_callchain(iter->sample, &callchain_cursor, &iter->parent,
|
||||
iter->evsel, al, max_stack_depth);
|
||||
if (err)
|
||||
return err;
|
||||
|
@ -1599,6 +1599,7 @@ struct mem_info *sample__resolve_mem(struct perf_sample *sample,
|
||||
}
|
||||
|
||||
static int add_callchain_ip(struct thread *thread,
|
||||
struct callchain_cursor *cursor,
|
||||
struct symbol **parent,
|
||||
struct addr_location *root_al,
|
||||
u8 *cpumode,
|
||||
@ -1630,7 +1631,7 @@ static int add_callchain_ip(struct thread *thread,
|
||||
* It seems the callchain is corrupted.
|
||||
* Discard all.
|
||||
*/
|
||||
callchain_cursor_reset(&callchain_cursor);
|
||||
callchain_cursor_reset(cursor);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@ -1648,13 +1649,13 @@ static int add_callchain_ip(struct thread *thread,
|
||||
/* Treat this symbol as the root,
|
||||
forgetting its callees. */
|
||||
*root_al = al;
|
||||
callchain_cursor_reset(&callchain_cursor);
|
||||
callchain_cursor_reset(cursor);
|
||||
}
|
||||
}
|
||||
|
||||
if (symbol_conf.hide_unresolved && al.sym == NULL)
|
||||
return 0;
|
||||
return callchain_cursor_append(&callchain_cursor, al.addr, al.map, al.sym);
|
||||
return callchain_cursor_append(cursor, al.addr, al.map, al.sym);
|
||||
}
|
||||
|
||||
struct branch_info *sample__resolve_bstack(struct perf_sample *sample,
|
||||
@ -1724,6 +1725,7 @@ static int remove_loops(struct branch_entry *l, int nr)
|
||||
* negative error code on other errors.
|
||||
*/
|
||||
static int resolve_lbr_callchain_sample(struct thread *thread,
|
||||
struct callchain_cursor *cursor,
|
||||
struct perf_sample *sample,
|
||||
struct symbol **parent,
|
||||
struct addr_location *root_al,
|
||||
@ -1778,7 +1780,7 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
|
||||
ip = lbr_stack->entries[0].to;
|
||||
}
|
||||
|
||||
err = add_callchain_ip(thread, parent, root_al, &cpumode, ip);
|
||||
err = add_callchain_ip(thread, cursor, parent, root_al, &cpumode, ip);
|
||||
if (err)
|
||||
return (err < 0) ? err : 0;
|
||||
}
|
||||
@ -1789,6 +1791,7 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
|
||||
}
|
||||
|
||||
static int thread__resolve_callchain_sample(struct thread *thread,
|
||||
struct callchain_cursor *cursor,
|
||||
struct perf_evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct symbol **parent,
|
||||
@ -1803,10 +1806,10 @@ static int thread__resolve_callchain_sample(struct thread *thread,
|
||||
int skip_idx = -1;
|
||||
int first_call = 0;
|
||||
|
||||
callchain_cursor_reset(&callchain_cursor);
|
||||
callchain_cursor_reset(cursor);
|
||||
|
||||
if (has_branch_callstack(evsel)) {
|
||||
err = resolve_lbr_callchain_sample(thread, sample, parent,
|
||||
err = resolve_lbr_callchain_sample(thread, cursor, sample, parent,
|
||||
root_al, max_stack);
|
||||
if (err)
|
||||
return (err < 0) ? err : 0;
|
||||
@ -1863,10 +1866,10 @@ static int thread__resolve_callchain_sample(struct thread *thread,
|
||||
nr = remove_loops(be, nr);
|
||||
|
||||
for (i = 0; i < nr; i++) {
|
||||
err = add_callchain_ip(thread, parent, root_al,
|
||||
err = add_callchain_ip(thread, cursor, parent, root_al,
|
||||
NULL, be[i].to);
|
||||
if (!err)
|
||||
err = add_callchain_ip(thread, parent, root_al,
|
||||
err = add_callchain_ip(thread, cursor, parent, root_al,
|
||||
NULL, be[i].from);
|
||||
if (err == -EINVAL)
|
||||
break;
|
||||
@ -1896,7 +1899,7 @@ check_calls:
|
||||
#endif
|
||||
ip = chain->ips[j];
|
||||
|
||||
err = add_callchain_ip(thread, parent, root_al, &cpumode, ip);
|
||||
err = add_callchain_ip(thread, cursor, parent, root_al, &cpumode, ip);
|
||||
|
||||
if (err)
|
||||
return (err < 0) ? err : 0;
|
||||
@ -1916,13 +1919,14 @@ static int unwind_entry(struct unwind_entry *entry, void *arg)
|
||||
}
|
||||
|
||||
int thread__resolve_callchain(struct thread *thread,
|
||||
struct callchain_cursor *cursor,
|
||||
struct perf_evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct symbol **parent,
|
||||
struct addr_location *root_al,
|
||||
int max_stack)
|
||||
{
|
||||
int ret = thread__resolve_callchain_sample(thread, evsel,
|
||||
int ret = thread__resolve_callchain_sample(thread, cursor, evsel,
|
||||
sample, parent,
|
||||
root_al, max_stack);
|
||||
if (ret)
|
||||
@ -1938,7 +1942,7 @@ int thread__resolve_callchain(struct thread *thread,
|
||||
(!sample->user_stack.size))
|
||||
return 0;
|
||||
|
||||
return unwind__get_entries(unwind_entry, &callchain_cursor,
|
||||
return unwind__get_entries(unwind_entry, cursor,
|
||||
thread, sample, max_stack);
|
||||
|
||||
}
|
||||
|
@ -141,7 +141,11 @@ struct branch_info *sample__resolve_bstack(struct perf_sample *sample,
|
||||
struct addr_location *al);
|
||||
struct mem_info *sample__resolve_mem(struct perf_sample *sample,
|
||||
struct addr_location *al);
|
||||
|
||||
struct callchain_cursor;
|
||||
|
||||
int thread__resolve_callchain(struct thread *thread,
|
||||
struct callchain_cursor *cursor,
|
||||
struct perf_evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct symbol **parent,
|
||||
|
@ -23,3 +23,4 @@ util/strlist.c
|
||||
util/trace-event.c
|
||||
../lib/rbtree.c
|
||||
util/string.c
|
||||
util/symbol_fprintf.c
|
||||
|
@ -263,7 +263,7 @@ static SV *perl_process_callchain(struct perf_sample *sample,
|
||||
if (!symbol_conf.use_callchain || !sample->callchain)
|
||||
goto exit;
|
||||
|
||||
if (thread__resolve_callchain(al->thread, evsel,
|
||||
if (thread__resolve_callchain(al->thread, &callchain_cursor, evsel,
|
||||
sample, NULL, NULL,
|
||||
PERF_MAX_STACK_DEPTH) != 0) {
|
||||
pr_err("Failed to resolve callchain. Skipping\n");
|
||||
|
@ -323,7 +323,7 @@ static PyObject *python_process_callchain(struct perf_sample *sample,
|
||||
if (!symbol_conf.use_callchain || !sample->callchain)
|
||||
goto exit;
|
||||
|
||||
if (thread__resolve_callchain(al->thread, evsel,
|
||||
if (thread__resolve_callchain(al->thread, &callchain_cursor, evsel,
|
||||
sample, NULL, NULL,
|
||||
scripting_max_stack) != 0) {
|
||||
pr_err("Failed to resolve callchain. Skipping\n");
|
||||
|
@ -255,57 +255,6 @@ void symbol__delete(struct symbol *sym)
|
||||
free(((void *)sym) - symbol_conf.priv_size);
|
||||
}
|
||||
|
||||
size_t symbol__fprintf(struct symbol *sym, FILE *fp)
|
||||
{
|
||||
return fprintf(fp, " %" PRIx64 "-%" PRIx64 " %c %s\n",
|
||||
sym->start, sym->end,
|
||||
sym->binding == STB_GLOBAL ? 'g' :
|
||||
sym->binding == STB_LOCAL ? 'l' : 'w',
|
||||
sym->name);
|
||||
}
|
||||
|
||||
size_t __symbol__fprintf_symname_offs(const struct symbol *sym,
|
||||
const struct addr_location *al,
|
||||
bool unknown_as_addr, FILE *fp)
|
||||
{
|
||||
unsigned long offset;
|
||||
size_t length;
|
||||
|
||||
if (sym && sym->name) {
|
||||
length = fprintf(fp, "%s", sym->name);
|
||||
if (al) {
|
||||
if (al->addr < sym->end)
|
||||
offset = al->addr - sym->start;
|
||||
else
|
||||
offset = al->addr - al->map->start - sym->start;
|
||||
length += fprintf(fp, "+0x%lx", offset);
|
||||
}
|
||||
return length;
|
||||
} else if (al && unknown_as_addr)
|
||||
return fprintf(fp, "[%#" PRIx64 "]", al->addr);
|
||||
else
|
||||
return fprintf(fp, "[unknown]");
|
||||
}
|
||||
|
||||
size_t symbol__fprintf_symname_offs(const struct symbol *sym,
|
||||
const struct addr_location *al,
|
||||
FILE *fp)
|
||||
{
|
||||
return __symbol__fprintf_symname_offs(sym, al, false, fp);
|
||||
}
|
||||
|
||||
size_t __symbol__fprintf_symname(const struct symbol *sym,
|
||||
const struct addr_location *al,
|
||||
bool unknown_as_addr, FILE *fp)
|
||||
{
|
||||
return __symbol__fprintf_symname_offs(sym, al, unknown_as_addr, fp);
|
||||
}
|
||||
|
||||
size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp)
|
||||
{
|
||||
return __symbol__fprintf_symname_offs(sym, NULL, false, fp);
|
||||
}
|
||||
|
||||
void symbols__delete(struct rb_root *symbols)
|
||||
{
|
||||
struct symbol *pos;
|
||||
@ -381,11 +330,6 @@ static struct symbol *symbols__next(struct symbol *sym)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct symbol_name_rb_node {
|
||||
struct rb_node rb_node;
|
||||
struct symbol sym;
|
||||
};
|
||||
|
||||
static void symbols__insert_by_name(struct rb_root *symbols, struct symbol *sym)
|
||||
{
|
||||
struct rb_node **p = &symbols->rb_node;
|
||||
@ -514,21 +458,6 @@ void dso__sort_by_name(struct dso *dso, enum map_type type)
|
||||
&dso->symbols[type]);
|
||||
}
|
||||
|
||||
size_t dso__fprintf_symbols_by_name(struct dso *dso,
|
||||
enum map_type type, FILE *fp)
|
||||
{
|
||||
size_t ret = 0;
|
||||
struct rb_node *nd;
|
||||
struct symbol_name_rb_node *pos;
|
||||
|
||||
for (nd = rb_first(&dso->symbol_names[type]); nd; nd = rb_next(nd)) {
|
||||
pos = rb_entry(nd, struct symbol_name_rb_node, rb_node);
|
||||
fprintf(fp, "%s\n", pos->sym.name);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int modules__parse(const char *filename, void *arg,
|
||||
int (*process_module)(void *arg, const char *name,
|
||||
u64 start))
|
||||
|
@ -140,6 +140,11 @@ struct symbol_conf {
|
||||
|
||||
extern struct symbol_conf symbol_conf;
|
||||
|
||||
struct symbol_name_rb_node {
|
||||
struct rb_node rb_node;
|
||||
struct symbol sym;
|
||||
};
|
||||
|
||||
static inline int __symbol__join_symfs(char *bf, size_t size, const char *path)
|
||||
{
|
||||
return path__join(bf, size, symbol_conf.symfs, path);
|
||||
|
71
tools/perf/util/symbol_fprintf.c
Normal file
71
tools/perf/util/symbol_fprintf.c
Normal file
@ -0,0 +1,71 @@
|
||||
#include <elf.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "symbol.h"
|
||||
|
||||
size_t symbol__fprintf(struct symbol *sym, FILE *fp)
|
||||
{
|
||||
return fprintf(fp, " %" PRIx64 "-%" PRIx64 " %c %s\n",
|
||||
sym->start, sym->end,
|
||||
sym->binding == STB_GLOBAL ? 'g' :
|
||||
sym->binding == STB_LOCAL ? 'l' : 'w',
|
||||
sym->name);
|
||||
}
|
||||
|
||||
size_t __symbol__fprintf_symname_offs(const struct symbol *sym,
|
||||
const struct addr_location *al,
|
||||
bool unknown_as_addr, FILE *fp)
|
||||
{
|
||||
unsigned long offset;
|
||||
size_t length;
|
||||
|
||||
if (sym && sym->name) {
|
||||
length = fprintf(fp, "%s", sym->name);
|
||||
if (al) {
|
||||
if (al->addr < sym->end)
|
||||
offset = al->addr - sym->start;
|
||||
else
|
||||
offset = al->addr - al->map->start - sym->start;
|
||||
length += fprintf(fp, "+0x%lx", offset);
|
||||
}
|
||||
return length;
|
||||
} else if (al && unknown_as_addr)
|
||||
return fprintf(fp, "[%#" PRIx64 "]", al->addr);
|
||||
else
|
||||
return fprintf(fp, "[unknown]");
|
||||
}
|
||||
|
||||
size_t symbol__fprintf_symname_offs(const struct symbol *sym,
|
||||
const struct addr_location *al,
|
||||
FILE *fp)
|
||||
{
|
||||
return __symbol__fprintf_symname_offs(sym, al, false, fp);
|
||||
}
|
||||
|
||||
size_t __symbol__fprintf_symname(const struct symbol *sym,
|
||||
const struct addr_location *al,
|
||||
bool unknown_as_addr, FILE *fp)
|
||||
{
|
||||
return __symbol__fprintf_symname_offs(sym, al, unknown_as_addr, fp);
|
||||
}
|
||||
|
||||
size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp)
|
||||
{
|
||||
return __symbol__fprintf_symname_offs(sym, NULL, false, fp);
|
||||
}
|
||||
|
||||
size_t dso__fprintf_symbols_by_name(struct dso *dso,
|
||||
enum map_type type, FILE *fp)
|
||||
{
|
||||
size_t ret = 0;
|
||||
struct rb_node *nd;
|
||||
struct symbol_name_rb_node *pos;
|
||||
|
||||
for (nd = rb_first(&dso->symbol_names[type]); nd; nd = rb_next(nd)) {
|
||||
pos = rb_entry(nd, struct symbol_name_rb_node, rb_node);
|
||||
fprintf(fp, "%s\n", pos->sym.name);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
Loading…
Reference in New Issue
Block a user