Commit 9243ae5b authored by Ingo Molnar's avatar Ingo Molnar

Merge tag 'perf-core-for-mingo-20160415' of...

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: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents 0b22cd27 f3e459d1
...@@ -259,6 +259,16 @@ include::itrace.txt[] ...@@ -259,6 +259,16 @@ include::itrace.txt[]
--full-source-path:: --full-source-path::
Show the full path for source files for srcline output. 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:: --ns::
Use 9 decimal places when displaying time (i.e. show the nanoseconds) 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. ...@@ -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' 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'. 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:: --kernel-syscall-graph::
Show the kernel callchains on the syscall exit path. Show the kernel callchains on the syscall exit path.
--event:: --event::
Trace other events, see 'perf list' for a complete list. 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:: --proc-map-timeout::
When processing pre-existing threads /proc/XXX/mmap, it may take a long time, 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. 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) ...@@ -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); 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); callchain_cursor_commit(&callchain_cursor);
while (true) { while (true) {
......
...@@ -930,45 +930,50 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) ...@@ -930,45 +930,50 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
return status; 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" }; 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", pr_debug("callchain: stack dump size %d\n",
callchain_param.dump_size); callchain->dump_size);
} }
int record_parse_callchain_opt(const struct option *opt, int record_opts__parse_callchain(struct record_opts *record,
const char *arg, struct callchain_param *callchain,
int unset) const char *arg, bool unset)
{ {
int ret; int ret;
struct record_opts *record = (struct record_opts *)opt->value;
record->callgraph_set = true; record->callgraph_set = true;
callchain_param.enabled = !unset; callchain->enabled = !unset;
/* --no-call-graph */ /* --no-call-graph */
if (unset) { if (unset) {
callchain_param.record_mode = CALLCHAIN_NONE; callchain->record_mode = CALLCHAIN_NONE;
pr_debug("callchain: disabled\n"); pr_debug("callchain: disabled\n");
return 0; return 0;
} }
ret = parse_callchain_record_opt(arg, &callchain_param); ret = parse_callchain_record_opt(arg, callchain);
if (!ret) { if (!ret) {
/* Enable data address sampling for DWARF unwind. */ /* Enable data address sampling for DWARF unwind. */
if (callchain_param.record_mode == CALLCHAIN_DWARF) if (callchain->record_mode == CALLCHAIN_DWARF)
record->sample_address = true; record->sample_address = true;
callchain_debug(); callchain_debug(callchain);
} }
return ret; return ret;
} }
int record_parse_callchain_opt(const struct option *opt,
const char *arg,
int unset)
{
return record_opts__parse_callchain(opt->value, &callchain_param, arg, unset);
}
int record_callchain_opt(const struct option *opt, int record_callchain_opt(const struct option *opt,
const char *arg __maybe_unused, const char *arg __maybe_unused,
int unset __maybe_unused) int unset __maybe_unused)
...@@ -981,7 +986,7 @@ 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) if (callchain_param.record_mode == CALLCHAIN_NONE)
callchain_param.record_mode = CALLCHAIN_FP; callchain_param.record_mode = CALLCHAIN_FP;
callchain_debug(); callchain_debug(&callchain_param);
return 0; return 0;
} }
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "util/thread_map.h" #include "util/thread_map.h"
#include "util/stat.h" #include "util/stat.h"
#include <linux/bitmap.h> #include <linux/bitmap.h>
#include <linux/stringify.h>
#include "asm/bug.h" #include "asm/bug.h"
#include "util/mem-events.h" #include "util/mem-events.h"
...@@ -569,18 +570,23 @@ static void print_sample_bts(struct perf_sample *sample, ...@@ -569,18 +570,23 @@ static void print_sample_bts(struct perf_sample *sample,
/* print branch_from information */ /* print branch_from information */
if (PRINT_FIELD(IP)) { if (PRINT_FIELD(IP)) {
unsigned int print_opts = output[attr->type].print_ip_opts; unsigned int print_opts = output[attr->type].print_ip_opts;
struct callchain_cursor *cursor = NULL, cursor_callchain;
if (symbol_conf.use_callchain && sample->callchain) { if (symbol_conf.use_callchain && sample->callchain &&
printf("\n"); thread__resolve_callchain(al->thread, &cursor_callchain, evsel,
} else { sample, NULL, NULL, scripting_max_stack) == 0)
printf(" "); cursor = &cursor_callchain;
if (cursor == NULL) {
putchar(' ');
if (print_opts & EVSEL__PRINT_SRCLINE) { if (print_opts & EVSEL__PRINT_SRCLINE) {
print_srcline_last = true; print_srcline_last = true;
print_opts &= ~EVSEL__PRINT_SRCLINE; print_opts &= ~EVSEL__PRINT_SRCLINE;
} }
} } else
perf_evsel__fprintf_sym(evsel, sample, al, 0, print_opts, putchar('\n');
scripting_max_stack, stdout);
sample__fprintf_sym(sample, al, 0, print_opts, cursor, stdout);
} }
/* print branch_to information */ /* print branch_to information */
...@@ -783,14 +789,15 @@ static void process_event(struct perf_script *script, ...@@ -783,14 +789,15 @@ static void process_event(struct perf_script *script,
printf("%16" PRIu64, sample->weight); printf("%16" PRIu64, sample->weight);
if (PRINT_FIELD(IP)) { if (PRINT_FIELD(IP)) {
if (!symbol_conf.use_callchain) struct callchain_cursor *cursor = NULL, cursor_callchain;
printf(" ");
else if (symbol_conf.use_callchain &&
printf("\n"); thread__resolve_callchain(al->thread, &cursor_callchain, evsel,
sample, NULL, NULL, scripting_max_stack) == 0)
cursor = &cursor_callchain;
perf_evsel__fprintf_sym(evsel, sample, al, 0, putchar(cursor ? '\n' : ' ');
output[attr->type].print_ip_opts, sample__fprintf_sym(sample, al, 0, output[attr->type].print_ip_opts, cursor, stdout);
scripting_max_stack, stdout);
} }
if (PRINT_FIELD(IREGS)) if (PRINT_FIELD(IREGS))
...@@ -2021,6 +2028,10 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -2021,6 +2028,10 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
"only consider symbols in these pids"), "only consider symbols in these pids"),
OPT_STRING(0, "tid", &symbol_conf.tid_list_str, "tid[,tid...]", OPT_STRING(0, "tid", &symbol_conf.tid_list_str, "tid[,tid...]",
"only consider symbols in these tids"), "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, OPT_BOOLEAN('I', "show-info", &show_full_info,
"display extended information from perf.data file"), "display extended information from perf.data file"),
OPT_BOOLEAN('\0', "show-kernel-path", &symbol_conf.show_kernel_path, OPT_BOOLEAN('\0', "show-kernel-path", &symbol_conf.show_kernel_path,
......
...@@ -46,23 +46,12 @@ ...@@ -46,23 +46,12 @@
#include <linux/audit.h> #include <linux/audit.h>
#include <sys/ptrace.h> #include <sys/ptrace.h>
#include <linux/random.h> #include <linux/random.h>
#include <linux/stringify.h>
#ifndef O_CLOEXEC #ifndef O_CLOEXEC
# define O_CLOEXEC 02000000 # define O_CLOEXEC 02000000
#endif #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 #ifndef MSG_CMSG_CLOEXEC
# define MSG_CMSG_CLOEXEC 0x40000000 # define MSG_CMSG_CLOEXEC 0x40000000
#endif #endif
...@@ -118,6 +107,8 @@ struct trace { ...@@ -118,6 +107,8 @@ struct trace {
u64 vfs_getname, u64 vfs_getname,
proc_getname; proc_getname;
} stats; } stats;
unsigned int max_stack;
unsigned int min_stack;
bool not_ev_qualifier; bool not_ev_qualifier;
bool live; bool live;
bool full_time; bool full_time;
...@@ -538,53 +529,6 @@ static const char *socket_families[] = { ...@@ -538,53 +529,6 @@ static const char *socket_families[] = {
}; };
static DEFINE_STRARRAY(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 #ifndef MSG_PROBE
#define MSG_PROBE 0x10 #define MSG_PROBE 0x10
#endif #endif
...@@ -951,6 +895,7 @@ static size_t syscall_arg__scnprintf_getrandom_flags(char *bf, size_t size, ...@@ -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/mmap.c"
#include "trace/beauty/mode_t.c" #include "trace/beauty/mode_t.c"
#include "trace/beauty/sched_policy.c" #include "trace/beauty/sched_policy.c"
#include "trace/beauty/socket_type.c"
#include "trace/beauty/waitid_options.c" #include "trace/beauty/waitid_options.c"
static struct syscall_fmt { static struct syscall_fmt {
...@@ -1905,7 +1850,7 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, ...@@ -1905,7 +1850,7 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,
goto out_put; goto out_put;
} }
if (!trace->summary_only) if (!(trace->duration_filter || trace->summary_only || trace->min_stack))
trace__printf_interrupted_entry(trace, sample); trace__printf_interrupted_entry(trace, sample);
ttrace->entry_time = sample->time; ttrace->entry_time = sample->time;
...@@ -1916,7 +1861,7 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, ...@@ -1916,7 +1861,7 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,
args, trace, thread); args, trace, thread);
if (sc->is_exit) { 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); trace__fprintf_entry_head(trace, thread, 1, sample->time, trace->output);
fprintf(trace->output, "%-70s\n", ttrace->entry_str); fprintf(trace->output, "%-70s\n", ttrace->entry_str);
} }
...@@ -1936,26 +1881,27 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel, ...@@ -1936,26 +1881,27 @@ static int trace__sys_enter(struct trace *trace, struct perf_evsel *evsel,
return err; return err;
} }
static int trace__fprintf_callchain(struct trace *trace, struct perf_evsel *evsel, static int trace__resolve_callchain(struct trace *trace, struct perf_evsel *evsel,
struct perf_sample *sample) struct perf_sample *sample,
struct callchain_cursor *cursor)
{ {
struct addr_location al; 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 */ /* TODO: user-configurable print_opts */
const unsigned int print_opts = EVSEL__PRINT_SYM | const unsigned int print_opts = EVSEL__PRINT_SYM |
EVSEL__PRINT_DSO | EVSEL__PRINT_DSO |
EVSEL__PRINT_UNKNOWN_AS_ADDR; EVSEL__PRINT_UNKNOWN_AS_ADDR;
if (sample->callchain == NULL) return sample__fprintf_callchain(sample, 38, print_opts, &callchain_cursor, trace->output);
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);
} }
static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, 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, ...@@ -1965,7 +1911,7 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
long ret; long ret;
u64 duration = 0; u64 duration = 0;
struct thread *thread; 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 syscall *sc = trace__syscall_info(trace, evsel, id);
struct thread_trace *ttrace; struct thread_trace *ttrace;
...@@ -1997,6 +1943,15 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, ...@@ -1997,6 +1943,15 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
} else if (trace->duration_filter) } else if (trace->duration_filter)
goto out; 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) if (trace->summary_only)
goto out; goto out;
...@@ -2037,7 +1992,10 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, ...@@ -2037,7 +1992,10 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
fputc('\n', trace->output); 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: out:
ttrace->entry_pending = false; ttrace->entry_pending = false;
err = 0; err = 0;
...@@ -2186,7 +2144,10 @@ static int trace__event_handler(struct trace *trace, struct perf_evsel *evsel, ...@@ -2186,7 +2144,10 @@ static int trace__event_handler(struct trace *trace, struct perf_evsel *evsel,
fprintf(trace->output, ")\n"); 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; return 0;
} }
...@@ -3086,6 +3047,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -3086,6 +3047,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
.show_comm = true, .show_comm = true,
.trace_syscalls = true, .trace_syscalls = true,
.kernel_syscallchains = false, .kernel_syscallchains = false,
.max_stack = UINT_MAX,
}; };
const char *output_name = NULL; const char *output_name = NULL;
const char *ev_qualifier_str = NULL; const char *ev_qualifier_str = NULL;
...@@ -3136,10 +3098,19 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -3136,10 +3098,19 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
&record_parse_callchain_opt), &record_parse_callchain_opt),
OPT_BOOLEAN(0, "kernel-syscall-graph", &trace.kernel_syscallchains, OPT_BOOLEAN(0, "kernel-syscall-graph", &trace.kernel_syscallchains,
"Show the kernel callchains on the syscall exit path"), "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, OPT_UINTEGER(0, "proc-map-timeout", &trace.opts.proc_map_timeout,
"per thread proc mmap processing timeout in ms"), "per thread proc mmap processing timeout in ms"),
OPT_END() OPT_END()
}; };
bool max_stack_user_set = true;
bool mmap_pages_user_set = true;
const char * const trace_subcommands[] = { "record", NULL }; const char * const trace_subcommands[] = { "record", NULL };
int err; int err;
char bf[BUFSIZ]; char bf[BUFSIZ];
...@@ -3173,8 +3144,25 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -3173,8 +3144,25 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
trace.opts.sample_time = true; 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; symbol_conf.use_callchain = true;
}
if (trace.evlist->nr_entries > 0) if (trace.evlist->nr_entries > 0)
evlist__set_evsel_handler(trace.evlist, trace__event_handler); evlist__set_evsel_handler(trace.evlist, trace__event_handler);
......
#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 ...@@ -8,6 +8,7 @@ libperf-y += env.o
libperf-y += event.o libperf-y += event.o
libperf-y += evlist.o libperf-y += evlist.o
libperf-y += evsel.o libperf-y += evsel.o
libperf-y += evsel_fprintf.o
libperf-y += find_bit.o libperf-y += find_bit.o
libperf-y += kallsyms.o libperf-y += kallsyms.o
libperf-y += levenshtein.o libperf-y += levenshtein.o
...@@ -29,6 +30,7 @@ libperf-y += usage.o ...@@ -29,6 +30,7 @@ libperf-y += usage.o
libperf-y += wrapper.o libperf-y += wrapper.o
libperf-y += dso.o libperf-y += dso.o
libperf-y += symbol.o libperf-y += symbol.o
libperf-y += symbol_fprintf.o
libperf-y += color.o libperf-y += color.o
libperf-y += header.o libperf-y += header.o
libperf-y += callchain.o libperf-y += callchain.o
......
...@@ -788,7 +788,8 @@ int callchain_cursor_append(struct callchain_cursor *cursor, ...@@ -788,7 +788,8 @@ int callchain_cursor_append(struct callchain_cursor *cursor,
return 0; 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, struct perf_evsel *evsel, struct addr_location *al,
int max_stack) int max_stack)
{ {
...@@ -797,7 +798,7 @@ int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent ...@@ -797,7 +798,7 @@ int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent
if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain || if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain ||
sort__has_parent) { sort__has_parent) {
return thread__resolve_callchain(al->thread, evsel, sample, return thread__resolve_callchain(al->thread, cursor, evsel, sample,
parent, al, max_stack); parent, al, max_stack);
} }
return 0; return 0;
......
...@@ -212,7 +212,14 @@ struct hist_entry; ...@@ -212,7 +212,14 @@ struct hist_entry;
int record_parse_callchain_opt(const struct option *opt, const char *arg, int unset); 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 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, struct perf_evsel *evsel, struct addr_location *al,
int max_stack); int max_stack);
int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *sample); int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *sample);
......
...@@ -986,26 +986,34 @@ static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, ...@@ -986,26 +986,34 @@ static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist,
return -1; return -1;
} }
static size_t perf_evlist__mmap_size(unsigned long pages) unsigned long perf_event_mlock_kb_in_pages(void)
{ {
if (pages == UINT_MAX) { unsigned long pages;
int max; int max;
if (sysctl__read_int("kernel/perf_event_mlock_kb", &max) < 0) { if (sysctl__read_int("kernel/perf_event_mlock_kb", &max) < 0) {
/* /*
* Pick a once upon a time good value, i.e. things look * Pick a once upon a time good value, i.e. things look
* strange since we can't read a sysctl value, but lets not * strange since we can't read a sysctl value, but lets not
* die yet... * die yet...
*/ */
max = 512; max = 512;
} else { } else {
max -= (page_size / 1024); max -= (page_size / 1024);
} }
pages = (max * 1024) / page_size;
if (!is_power_of_2(pages))
pages = rounddown_pow_of_two(pages);
pages = (max * 1024) / page_size; return pages;
if (!is_power_of_2(pages)) }
pages = rounddown_pow_of_two(pages);
} else if (!is_power_of_2(pages)) static size_t perf_evlist__mmap_size(unsigned long pages)
{
if (pages == UINT_MAX)
pages = perf_event_mlock_kb_in_pages();
else if (!is_power_of_2(pages))
return 0; return 0;
return (pages + 1) * page_size; return (pages + 1) * page_size;
......
...@@ -158,6 +158,8 @@ int perf_evlist__parse_mmap_pages(const struct option *opt, ...@@ -158,6 +158,8 @@ int perf_evlist__parse_mmap_pages(const struct option *opt,
const char *str, const char *str,
int unset); int unset);
unsigned long perf_event_mlock_kb_in_pages(void);
int perf_evlist__mmap_ex(struct perf_evlist *evlist, unsigned int pages, int perf_evlist__mmap_ex(struct perf_evlist *evlist, unsigned int pages,
bool overwrite, unsigned int auxtrace_pages, bool overwrite, unsigned int auxtrace_pages,
bool auxtrace_overwrite); bool auxtrace_overwrite);
......
...@@ -2254,226 +2254,6 @@ u64 perf_evsel__intval(struct perf_evsel *evsel, struct perf_sample *sample, ...@@ -2254,226 +2254,6 @@ u64 perf_evsel__intval(struct perf_evsel *evsel, struct perf_sample *sample,
return 0; 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, bool perf_evsel__fallback(struct perf_evsel *evsel, int err,
char *msg, size_t msgsize) char *msg, size_t msgsize)
{ {
......
...@@ -395,16 +395,15 @@ int perf_evsel__fprintf(struct perf_evsel *evsel, ...@@ -395,16 +395,15 @@ int perf_evsel__fprintf(struct perf_evsel *evsel,
#define EVSEL__PRINT_SRCLINE (1<<5) #define EVSEL__PRINT_SRCLINE (1<<5)
#define EVSEL__PRINT_UNKNOWN_AS_ADDR (1<<6) #define EVSEL__PRINT_UNKNOWN_AS_ADDR (1<<6)
int perf_evsel__fprintf_callchain(struct perf_evsel *evsel, struct callchain_cursor;
struct perf_sample *sample,
struct addr_location *al, int left_alignment, int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment,
unsigned int print_opts, unsigned int print_opts,
unsigned int stack_depth, FILE *fp); struct callchain_cursor *cursor, FILE *fp);
int perf_evsel__fprintf_sym(struct perf_evsel *evsel, struct perf_sample *sample, int sample__fprintf_sym(struct perf_sample *sample, struct addr_location *al,
struct addr_location *al, int left_alignment, int left_alignment, unsigned int print_opts,
unsigned int print_opts, unsigned int stack_depth, struct callchain_cursor *cursor, FILE *fp);
FILE *fp);
bool perf_evsel__fallback(struct perf_evsel *evsel, int err, bool perf_evsel__fallback(struct perf_evsel *evsel, int err,
char *msg, size_t msgsize); char *msg, size_t msgsize);
......
#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, ...@@ -953,7 +953,7 @@ int hist_entry_iter__add(struct hist_entry_iter *iter, struct addr_location *al,
{ {
int err, err2; 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); iter->evsel, al, max_stack_depth);
if (err) if (err)
return err; return err;
......
...@@ -1599,6 +1599,7 @@ struct mem_info *sample__resolve_mem(struct perf_sample *sample, ...@@ -1599,6 +1599,7 @@ struct mem_info *sample__resolve_mem(struct perf_sample *sample,
} }
static int add_callchain_ip(struct thread *thread, static int add_callchain_ip(struct thread *thread,
struct callchain_cursor *cursor,
struct symbol **parent, struct symbol **parent,
struct addr_location *root_al, struct addr_location *root_al,
u8 *cpumode, u8 *cpumode,
...@@ -1630,7 +1631,7 @@ static int add_callchain_ip(struct thread *thread, ...@@ -1630,7 +1631,7 @@ static int add_callchain_ip(struct thread *thread,
* It seems the callchain is corrupted. * It seems the callchain is corrupted.
* Discard all. * Discard all.
*/ */
callchain_cursor_reset(&callchain_cursor); callchain_cursor_reset(cursor);
return 1; return 1;
} }
return 0; return 0;
...@@ -1648,13 +1649,13 @@ static int add_callchain_ip(struct thread *thread, ...@@ -1648,13 +1649,13 @@ static int add_callchain_ip(struct thread *thread,
/* Treat this symbol as the root, /* Treat this symbol as the root,
forgetting its callees. */ forgetting its callees. */
*root_al = al; *root_al = al;
callchain_cursor_reset(&callchain_cursor); callchain_cursor_reset(cursor);
} }
} }
if (symbol_conf.hide_unresolved && al.sym == NULL) if (symbol_conf.hide_unresolved && al.sym == NULL)
return 0; 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, struct branch_info *sample__resolve_bstack(struct perf_sample *sample,
...@@ -1724,6 +1725,7 @@ static int remove_loops(struct branch_entry *l, int nr) ...@@ -1724,6 +1725,7 @@ static int remove_loops(struct branch_entry *l, int nr)
* negative error code on other errors. * negative error code on other errors.
*/ */
static int resolve_lbr_callchain_sample(struct thread *thread, static int resolve_lbr_callchain_sample(struct thread *thread,
struct callchain_cursor *cursor,
struct perf_sample *sample, struct perf_sample *sample,
struct symbol **parent, struct symbol **parent,
struct addr_location *root_al, struct addr_location *root_al,
...@@ -1778,7 +1780,7 @@ static int resolve_lbr_callchain_sample(struct thread *thread, ...@@ -1778,7 +1780,7 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
ip = lbr_stack->entries[0].to; 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) if (err)
return (err < 0) ? err : 0; return (err < 0) ? err : 0;
} }
...@@ -1789,6 +1791,7 @@ static int resolve_lbr_callchain_sample(struct thread *thread, ...@@ -1789,6 +1791,7 @@ static int resolve_lbr_callchain_sample(struct thread *thread,
} }
static int thread__resolve_callchain_sample(struct thread *thread, static int thread__resolve_callchain_sample(struct thread *thread,
struct callchain_cursor *cursor,
struct perf_evsel *evsel, struct perf_evsel *evsel,
struct perf_sample *sample, struct perf_sample *sample,
struct symbol **parent, struct symbol **parent,
...@@ -1803,10 +1806,10 @@ static int thread__resolve_callchain_sample(struct thread *thread, ...@@ -1803,10 +1806,10 @@ static int thread__resolve_callchain_sample(struct thread *thread,
int skip_idx = -1; int skip_idx = -1;
int first_call = 0; int first_call = 0;
callchain_cursor_reset(&callchain_cursor); callchain_cursor_reset(cursor);
if (has_branch_callstack(evsel)) { 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); root_al, max_stack);
if (err) if (err)
return (err < 0) ? err : 0; return (err < 0) ? err : 0;
...@@ -1863,10 +1866,10 @@ static int thread__resolve_callchain_sample(struct thread *thread, ...@@ -1863,10 +1866,10 @@ static int thread__resolve_callchain_sample(struct thread *thread,
nr = remove_loops(be, nr); nr = remove_loops(be, nr);
for (i = 0; i < nr; i++) { 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); NULL, be[i].to);
if (!err) if (!err)
err = add_callchain_ip(thread, parent, root_al, err = add_callchain_ip(thread, cursor, parent, root_al,
NULL, be[i].from); NULL, be[i].from);
if (err == -EINVAL) if (err == -EINVAL)
break; break;
...@@ -1896,7 +1899,7 @@ static int thread__resolve_callchain_sample(struct thread *thread, ...@@ -1896,7 +1899,7 @@ static int thread__resolve_callchain_sample(struct thread *thread,
#endif #endif
ip = chain->ips[j]; 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) if (err)
return (err < 0) ? err : 0; return (err < 0) ? err : 0;
...@@ -1916,13 +1919,14 @@ static int unwind_entry(struct unwind_entry *entry, void *arg) ...@@ -1916,13 +1919,14 @@ static int unwind_entry(struct unwind_entry *entry, void *arg)
} }
int thread__resolve_callchain(struct thread *thread, int thread__resolve_callchain(struct thread *thread,
struct callchain_cursor *cursor,
struct perf_evsel *evsel, struct perf_evsel *evsel,
struct perf_sample *sample, struct perf_sample *sample,
struct symbol **parent, struct symbol **parent,
struct addr_location *root_al, struct addr_location *root_al,
int max_stack) int max_stack)
{ {
int ret = thread__resolve_callchain_sample(thread, evsel, int ret = thread__resolve_callchain_sample(thread, cursor, evsel,
sample, parent, sample, parent,
root_al, max_stack); root_al, max_stack);
if (ret) if (ret)
...@@ -1938,7 +1942,7 @@ int thread__resolve_callchain(struct thread *thread, ...@@ -1938,7 +1942,7 @@ int thread__resolve_callchain(struct thread *thread,
(!sample->user_stack.size)) (!sample->user_stack.size))
return 0; return 0;
return unwind__get_entries(unwind_entry, &callchain_cursor, return unwind__get_entries(unwind_entry, cursor,
thread, sample, max_stack); thread, sample, max_stack);
} }
......
...@@ -141,7 +141,11 @@ struct branch_info *sample__resolve_bstack(struct perf_sample *sample, ...@@ -141,7 +141,11 @@ struct branch_info *sample__resolve_bstack(struct perf_sample *sample,
struct addr_location *al); struct addr_location *al);
struct mem_info *sample__resolve_mem(struct perf_sample *sample, struct mem_info *sample__resolve_mem(struct perf_sample *sample,
struct addr_location *al); struct addr_location *al);
struct callchain_cursor;
int thread__resolve_callchain(struct thread *thread, int thread__resolve_callchain(struct thread *thread,
struct callchain_cursor *cursor,
struct perf_evsel *evsel, struct perf_evsel *evsel,
struct perf_sample *sample, struct perf_sample *sample,
struct symbol **parent, struct symbol **parent,
......
...@@ -23,3 +23,4 @@ util/strlist.c ...@@ -23,3 +23,4 @@ util/strlist.c
util/trace-event.c util/trace-event.c
../lib/rbtree.c ../lib/rbtree.c
util/string.c util/string.c
util/symbol_fprintf.c
...@@ -263,7 +263,7 @@ static SV *perl_process_callchain(struct perf_sample *sample, ...@@ -263,7 +263,7 @@ static SV *perl_process_callchain(struct perf_sample *sample,
if (!symbol_conf.use_callchain || !sample->callchain) if (!symbol_conf.use_callchain || !sample->callchain)
goto exit; goto exit;
if (thread__resolve_callchain(al->thread, evsel, if (thread__resolve_callchain(al->thread, &callchain_cursor, evsel,
sample, NULL, NULL, sample, NULL, NULL,
PERF_MAX_STACK_DEPTH) != 0) { PERF_MAX_STACK_DEPTH) != 0) {
pr_err("Failed to resolve callchain. Skipping\n"); pr_err("Failed to resolve callchain. Skipping\n");
......
...@@ -323,7 +323,7 @@ static PyObject *python_process_callchain(struct perf_sample *sample, ...@@ -323,7 +323,7 @@ static PyObject *python_process_callchain(struct perf_sample *sample,
if (!symbol_conf.use_callchain || !sample->callchain) if (!symbol_conf.use_callchain || !sample->callchain)
goto exit; goto exit;
if (thread__resolve_callchain(al->thread, evsel, if (thread__resolve_callchain(al->thread, &callchain_cursor, evsel,
sample, NULL, NULL, sample, NULL, NULL,
scripting_max_stack) != 0) { scripting_max_stack) != 0) {
pr_err("Failed to resolve callchain. Skipping\n"); pr_err("Failed to resolve callchain. Skipping\n");
......
...@@ -255,57 +255,6 @@ void symbol__delete(struct symbol *sym) ...@@ -255,57 +255,6 @@ void symbol__delete(struct symbol *sym)
free(((void *)sym) - symbol_conf.priv_size); 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) void symbols__delete(struct rb_root *symbols)
{ {
struct symbol *pos; struct symbol *pos;
...@@ -381,11 +330,6 @@ static struct symbol *symbols__next(struct symbol *sym) ...@@ -381,11 +330,6 @@ static struct symbol *symbols__next(struct symbol *sym)
return NULL; 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) static void symbols__insert_by_name(struct rb_root *symbols, struct symbol *sym)
{ {
struct rb_node **p = &symbols->rb_node; struct rb_node **p = &symbols->rb_node;
...@@ -514,21 +458,6 @@ void dso__sort_by_name(struct dso *dso, enum map_type type) ...@@ -514,21 +458,6 @@ void dso__sort_by_name(struct dso *dso, enum map_type type)
&dso->symbols[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 modules__parse(const char *filename, void *arg,
int (*process_module)(void *arg, const char *name, int (*process_module)(void *arg, const char *name,
u64 start)) u64 start))
......
...@@ -140,6 +140,11 @@ struct symbol_conf { ...@@ -140,6 +140,11 @@ struct symbol_conf {
extern struct symbol_conf 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) static inline int __symbol__join_symfs(char *bf, size_t size, const char *path)
{ {
return path__join(bf, size, symbol_conf.symfs, path); return path__join(bf, size, symbol_conf.symfs, path);
......
#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;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment