perf/core improvements:

- Automagically create a 'bpf-output' event, easing the setup of BPF
   C "scripts" that produce output via the perf ring buffer. Now it is
   just a matter of calling any perf tool, such as 'trace', with a C
   source file that references the __bpf_stdout__ output channel and
   that channel will be created and connected to the script:
 
   # trace -e nanosleep --event test_bpf_stdout.c usleep 1
     0.013 ( 0.013 ms): usleep/2818 nanosleep(rqtp: 0x7ffcead45f40                                        ) ...
     0.013 (         ): __bpf_stdout__:Raise a BPF event!..)
     0.015 (         ): perf_bpf_probe:func_begin:(ffffffff81112460))
     0.261 (         ): __bpf_stdout__:Raise a BPF event!..)
     0.262 (         ): perf_bpf_probe:func_end:(ffffffff81112460 <- ffffffff81003d92))
     0.264 ( 0.264 ms): usleep/2818  ... [continued]: nanosleep()) = 0
   #
 
   Further work is needed to reduce the number of lines in a perf bpf C source
   file, this being the part where we greatly reduce the command line setup (Wang Nan)
 
 - 'perf trace' now supports callchains, with 'trace --call-graph dwarf' using
   libunwind, just like 'perf top', to ask the kernel for stack dumps for CFI
   processing. This reduces the overhead by asking just for userspace callchains
   and also only for the syscall exit tracepoint (raw_syscalls:sys_exit)
   (Milian Wolff, Arnaldo Carvalho de Melo)
 
   Try it with, for instance:
 
      # perf trace --call dwarf ping 127.0.0.1
 
   An excerpt of a system wide 'perf trace --call dwarf" session is at:
 
    https://fedorapeople.org/~acme/perf/perf-trace--call-graph-dwarf--all-cpus.txt
 
   You may need to bump the number of mmap pages, using -m/--mmap-pages,
   but on a Broadwell machine the defaults allowed system wide tracing to
   work without losing that many records, experiment with just some
   syscalls, like:
 
     # perf trace --call dwarf -e nanosleep,futex
 
   All the targets available for 'perf record', 'perf top' (--pid, --tid, --cpu,
   etc) should work. Also --duration may be interesting to try.
 
   To get filenames from in various syscalls pointer args (open, ettc), add this
   to the mix:
 
   # perf probe 'vfs_getname=getname_flags:72 pathname=filename:string'
 
   Making this work is next in line:
 
      # trace --call dwarf --ev sched:sched_switch/call-graph=fp/ usleep 1
 
   I.e. honouring per-tracepoint callchains in 'perf trace' in addition to
   in raw_syscalls:sys_exit.
 
 Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iQIcBAABCAAGBQJXDFQyAAoJENZQFvNTUqpAZCsP/2Q1Q8XfNpNZm7+JPrZDYUkm
 KBxR3WSP8l46G8hJO2SBKHgXDv6EOCsfL/lvtLv18IHrz9pSTLZFPgl3a889iOnz
 /d2pC/ydlDQ9yPR28cELb7gKMB0OF+rUqdZIWBqSM84LnvsYHgY6CntEIejfc2wf
 jiVYHkug2dcUOmfgFpV4Jp3m6J8Okf9w9+/W4n+mkcS6o9WJvKCCiTMWoOwDDkyQ
 gfEGN7YJt2iYLg4AhsG9ZJa+XKye53znjodpFNLCVbozXbZ4YSEbogR0qKJksHfH
 5uabD2bEu2y0LiC9694xp5FLFM9tGML3Nr0JAkq6Jd230Ho4XyUy+/ZD0Lq0BHnv
 HdIR7T4+wUYVKUf/ZW8gbPShR63UJ6qrgfLE8yZMxG0WKzh3XIQtg/BcxLw8XPi8
 aF/IQt/om2KXPVEZv6SjNMp9DdmydeZ4KPrA9q2BGhbQzC2Ast7e6pHKouxbRrpb
 mOSfLgDcqPFp75ZpIbFatKdg6S8VNKtFgF8wWAGrACtLboKa5PDS3El56BSNx2IA
 6pexLuhaD8ndwvHP1F6nQQAHvFn5q4FKEg2fU0Pq8VnUN8SxrCvVjZZR3SjM+tGy
 V5GHzJ7GTn9Cm2fwllrD/tndzPWQsbFA0UuLZPwVoxq2Lt2HC0YG30+SupsAVZrx
 fCANHt3ci+qU1OCQAlIP
 =jBSC
 -----END PGP SIGNATURE-----

Merge tag 'perf-core-for-mingo-20160411' 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:

- Automagically create a 'bpf-output' event, easing the setup of BPF
  C "scripts" that produce output via the perf ring buffer. Now it is
  just a matter of calling any perf tool, such as 'trace', with a C
  source file that references the __bpf_stdout__ output channel and
  that channel will be created and connected to the script:

  # trace -e nanosleep --event test_bpf_stdout.c usleep 1
    0.013 ( 0.013 ms): usleep/2818 nanosleep(rqtp: 0x7ffcead45f40                                        ) ...
    0.013 (         ): __bpf_stdout__:Raise a BPF event!..)
    0.015 (         ): perf_bpf_probe:func_begin:(ffffffff81112460))
    0.261 (         ): __bpf_stdout__:Raise a BPF event!..)
    0.262 (         ): perf_bpf_probe:func_end:(ffffffff81112460 <- ffffffff81003d92))
    0.264 ( 0.264 ms): usleep/2818  ... [continued]: nanosleep()) = 0
  #

  Further work is needed to reduce the number of lines in a perf bpf C source
  file, this being the part where we greatly reduce the command line setup (Wang Nan)

- 'perf trace' now supports callchains, with 'trace --call-graph dwarf' using
  libunwind, just like 'perf top', to ask the kernel for stack dumps for CFI
  processing. This reduces the overhead by asking just for userspace callchains
  and also only for the syscall exit tracepoint (raw_syscalls:sys_exit)
  (Milian Wolff, Arnaldo Carvalho de Melo)

  Try it with, for instance:

     # perf trace --call dwarf ping 127.0.0.1

  An excerpt of a system wide 'perf trace --call dwarf" session is at:

   https://fedorapeople.org/~acme/perf/perf-trace--call-graph-dwarf--all-cpus.txt

  You may need to bump the number of mmap pages, using -m/--mmap-pages,
  but on a Broadwell machine the defaults allowed system wide tracing to
  work without losing that many records, experiment with just some
  syscalls, like:

    # perf trace --call dwarf -e nanosleep,futex

  All the targets available for 'perf record', 'perf top' (--pid, --tid, --cpu,
  etc) should work. Also --duration may be interesting to try.

  To get filenames from in various syscalls pointer args (open, ettc), add this
  to the mix:

  # perf probe 'vfs_getname=getname_flags:72 pathname=filename:string'

  Making this work is next in line:

     # trace --call dwarf --ev sched:sched_switch/call-graph=fp/ usleep 1

  I.e. honouring per-tracepoint callchains in 'perf trace' in addition to
  in raw_syscalls:sys_exit.

Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
Ingo Molnar 2016-04-13 09:02:07 +02:00
commit bed9441ba7
28 changed files with 487 additions and 146 deletions

View File

@ -117,6 +117,15 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs.
--syscalls:: --syscalls::
Trace system calls. This options is enabled by default. Trace system calls. This options is enabled by default.
--call-graph [mode,type,min[,limit],order[,key][,branch]]::
Setup and enable call-graph (stack chain/backtrace) recording.
See `--call-graph` section in perf-record and perf-report
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'.
--kernel-syscall-graph::
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.

View File

@ -71,7 +71,7 @@ int test__perf_time_to_tsc(int subtest __maybe_unused)
CHECK__(parse_events(evlist, "cycles:u", NULL)); CHECK__(parse_events(evlist, "cycles:u", NULL));
perf_evlist__config(evlist, &opts); perf_evlist__config(evlist, &opts, NULL);
evsel = perf_evlist__first(evlist); evsel = perf_evlist__first(evlist);

View File

@ -63,6 +63,8 @@ struct pt_regs_offset {
# define REG_OFFSET_NAME_32(n, r) {.name = n, .offset = offsetof(struct pt_regs, r)} # define REG_OFFSET_NAME_32(n, r) {.name = n, .offset = offsetof(struct pt_regs, r)}
#endif #endif
/* TODO: switching by dwarf address size */
#ifndef __x86_64__
static const struct pt_regs_offset x86_32_regoffset_table[] = { static const struct pt_regs_offset x86_32_regoffset_table[] = {
REG_OFFSET_NAME_32("%ax", eax), REG_OFFSET_NAME_32("%ax", eax),
REG_OFFSET_NAME_32("%cx", ecx), REG_OFFSET_NAME_32("%cx", ecx),
@ -75,6 +77,8 @@ static const struct pt_regs_offset x86_32_regoffset_table[] = {
REG_OFFSET_END, REG_OFFSET_END,
}; };
#define regoffset_table x86_32_regoffset_table
#else
static const struct pt_regs_offset x86_64_regoffset_table[] = { static const struct pt_regs_offset x86_64_regoffset_table[] = {
REG_OFFSET_NAME_64("%ax", rax), REG_OFFSET_NAME_64("%ax", rax),
REG_OFFSET_NAME_64("%dx", rdx), REG_OFFSET_NAME_64("%dx", rdx),
@ -95,11 +99,7 @@ static const struct pt_regs_offset x86_64_regoffset_table[] = {
REG_OFFSET_END, REG_OFFSET_END,
}; };
/* TODO: switching by dwarf address size */
#ifdef __x86_64__
#define regoffset_table x86_64_regoffset_table #define regoffset_table x86_64_regoffset_table
#else
#define regoffset_table x86_32_regoffset_table
#endif #endif
/* Minus 1 for the ending REG_OFFSET_END */ /* Minus 1 for the ending REG_OFFSET_END */

View File

@ -982,7 +982,7 @@ static int kvm_live_open_events(struct perf_kvm_stat *kvm)
struct perf_evlist *evlist = kvm->evlist; struct perf_evlist *evlist = kvm->evlist;
char sbuf[STRERR_BUFSIZE]; char sbuf[STRERR_BUFSIZE];
perf_evlist__config(evlist, &kvm->opts); perf_evlist__config(evlist, &kvm->opts, NULL);
/* /*
* Note: exclude_{guest,host} do not apply here. * Note: exclude_{guest,host} do not apply here.

View File

@ -284,7 +284,7 @@ static int record__open(struct record *rec)
struct record_opts *opts = &rec->opts; struct record_opts *opts = &rec->opts;
int rc = 0; int rc = 0;
perf_evlist__config(evlist, opts); perf_evlist__config(evlist, opts, &callchain_param);
evlist__for_each(evlist, pos) { evlist__for_each(evlist, pos) {
try_again: try_again:
@ -1276,6 +1276,14 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
if (err) if (err)
return err; return err;
err = bpf__setup_stdout(rec->evlist);
if (err) {
bpf__strerror_setup_stdout(rec->evlist, err, errbuf, sizeof(errbuf));
pr_err("ERROR: Setup BPF stdout failed: %s\n",
errbuf);
return err;
}
err = -ENOMEM; err = -ENOMEM;
symbol__init(NULL); symbol__init(NULL);

View File

@ -579,8 +579,8 @@ static void print_sample_bts(struct perf_sample *sample,
print_opts &= ~PRINT_IP_OPT_SRCLINE; print_opts &= ~PRINT_IP_OPT_SRCLINE;
} }
} }
perf_evsel__print_ip(evsel, sample, al, print_opts, perf_evsel__fprintf_sym(evsel, sample, al, 0, print_opts,
scripting_max_stack); scripting_max_stack, stdout);
} }
/* print branch_to information */ /* print branch_to information */
@ -788,9 +788,9 @@ static void process_event(struct perf_script *script,
else else
printf("\n"); printf("\n");
perf_evsel__print_ip(evsel, sample, al, perf_evsel__fprintf_sym(evsel, sample, al, 0,
output[attr->type].print_ip_opts, output[attr->type].print_ip_opts,
scripting_max_stack); scripting_max_stack, stdout);
} }
if (PRINT_FIELD(IREGS)) if (PRINT_FIELD(IREGS))
@ -1415,21 +1415,19 @@ static int is_directory(const char *base_path, const struct dirent *dent)
return S_ISDIR(st.st_mode); return S_ISDIR(st.st_mode);
} }
#define for_each_lang(scripts_path, scripts_dir, lang_dirent, lang_next)\ #define for_each_lang(scripts_path, scripts_dir, lang_dirent) \
while (!readdir_r(scripts_dir, &lang_dirent, &lang_next) && \ while ((lang_dirent = readdir(scripts_dir)) != NULL) \
lang_next) \ if ((lang_dirent->d_type == DT_DIR || \
if ((lang_dirent.d_type == DT_DIR || \ (lang_dirent->d_type == DT_UNKNOWN && \
(lang_dirent.d_type == DT_UNKNOWN && \ is_directory(scripts_path, lang_dirent))) && \
is_directory(scripts_path, &lang_dirent))) && \ (strcmp(lang_dirent->d_name, ".")) && \
(strcmp(lang_dirent.d_name, ".")) && \ (strcmp(lang_dirent->d_name, "..")))
(strcmp(lang_dirent.d_name, "..")))
#define for_each_script(lang_path, lang_dir, script_dirent, script_next)\ #define for_each_script(lang_path, lang_dir, script_dirent) \
while (!readdir_r(lang_dir, &script_dirent, &script_next) && \ while ((script_dirent = readdir(lang_dir)) != NULL) \
script_next) \ if (script_dirent->d_type != DT_DIR && \
if (script_dirent.d_type != DT_DIR && \ (script_dirent->d_type != DT_UNKNOWN || \
(script_dirent.d_type != DT_UNKNOWN || \ !is_directory(lang_path, script_dirent)))
!is_directory(lang_path, &script_dirent)))
#define RECORD_SUFFIX "-record" #define RECORD_SUFFIX "-record"
@ -1575,7 +1573,7 @@ static int list_available_scripts(const struct option *opt __maybe_unused,
const char *s __maybe_unused, const char *s __maybe_unused,
int unset __maybe_unused) int unset __maybe_unused)
{ {
struct dirent *script_next, *lang_next, script_dirent, lang_dirent; struct dirent *script_dirent, *lang_dirent;
char scripts_path[MAXPATHLEN]; char scripts_path[MAXPATHLEN];
DIR *scripts_dir, *lang_dir; DIR *scripts_dir, *lang_dir;
char script_path[MAXPATHLEN]; char script_path[MAXPATHLEN];
@ -1590,19 +1588,19 @@ static int list_available_scripts(const struct option *opt __maybe_unused,
if (!scripts_dir) if (!scripts_dir)
return -1; return -1;
for_each_lang(scripts_path, scripts_dir, lang_dirent, lang_next) { for_each_lang(scripts_path, scripts_dir, lang_dirent) {
snprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path, snprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path,
lang_dirent.d_name); lang_dirent->d_name);
lang_dir = opendir(lang_path); lang_dir = opendir(lang_path);
if (!lang_dir) if (!lang_dir)
continue; continue;
for_each_script(lang_path, lang_dir, script_dirent, script_next) { for_each_script(lang_path, lang_dir, script_dirent) {
script_root = get_script_root(&script_dirent, REPORT_SUFFIX); script_root = get_script_root(script_dirent, REPORT_SUFFIX);
if (script_root) { if (script_root) {
desc = script_desc__findnew(script_root); desc = script_desc__findnew(script_root);
snprintf(script_path, MAXPATHLEN, "%s/%s", snprintf(script_path, MAXPATHLEN, "%s/%s",
lang_path, script_dirent.d_name); lang_path, script_dirent->d_name);
read_script_info(desc, script_path); read_script_info(desc, script_path);
free(script_root); free(script_root);
} }
@ -1690,7 +1688,7 @@ static int check_ev_match(char *dir_name, char *scriptname,
*/ */
int find_scripts(char **scripts_array, char **scripts_path_array) int find_scripts(char **scripts_array, char **scripts_path_array)
{ {
struct dirent *script_next, *lang_next, script_dirent, lang_dirent; struct dirent *script_dirent, *lang_dirent;
char scripts_path[MAXPATHLEN], lang_path[MAXPATHLEN]; char scripts_path[MAXPATHLEN], lang_path[MAXPATHLEN];
DIR *scripts_dir, *lang_dir; DIR *scripts_dir, *lang_dir;
struct perf_session *session; struct perf_session *session;
@ -1713,9 +1711,9 @@ int find_scripts(char **scripts_array, char **scripts_path_array)
return -1; return -1;
} }
for_each_lang(scripts_path, scripts_dir, lang_dirent, lang_next) { for_each_lang(scripts_path, scripts_dir, lang_dirent) {
snprintf(lang_path, MAXPATHLEN, "%s/%s", scripts_path, snprintf(lang_path, MAXPATHLEN, "%s/%s", scripts_path,
lang_dirent.d_name); lang_dirent->d_name);
#ifdef NO_LIBPERL #ifdef NO_LIBPERL
if (strstr(lang_path, "perl")) if (strstr(lang_path, "perl"))
continue; continue;
@ -1729,16 +1727,16 @@ int find_scripts(char **scripts_array, char **scripts_path_array)
if (!lang_dir) if (!lang_dir)
continue; continue;
for_each_script(lang_path, lang_dir, script_dirent, script_next) { for_each_script(lang_path, lang_dir, script_dirent) {
/* Skip those real time scripts: xxxtop.p[yl] */ /* Skip those real time scripts: xxxtop.p[yl] */
if (strstr(script_dirent.d_name, "top.")) if (strstr(script_dirent->d_name, "top."))
continue; continue;
sprintf(scripts_path_array[i], "%s/%s", lang_path, sprintf(scripts_path_array[i], "%s/%s", lang_path,
script_dirent.d_name); script_dirent->d_name);
temp = strchr(script_dirent.d_name, '.'); temp = strchr(script_dirent->d_name, '.');
snprintf(scripts_array[i], snprintf(scripts_array[i],
(temp - script_dirent.d_name) + 1, (temp - script_dirent->d_name) + 1,
"%s", script_dirent.d_name); "%s", script_dirent->d_name);
if (check_ev_match(lang_path, if (check_ev_match(lang_path,
scripts_array[i], session)) scripts_array[i], session))
@ -1756,7 +1754,7 @@ int find_scripts(char **scripts_array, char **scripts_path_array)
static char *get_script_path(const char *script_root, const char *suffix) static char *get_script_path(const char *script_root, const char *suffix)
{ {
struct dirent *script_next, *lang_next, script_dirent, lang_dirent; struct dirent *script_dirent, *lang_dirent;
char scripts_path[MAXPATHLEN]; char scripts_path[MAXPATHLEN];
char script_path[MAXPATHLEN]; char script_path[MAXPATHLEN];
DIR *scripts_dir, *lang_dir; DIR *scripts_dir, *lang_dir;
@ -1769,21 +1767,21 @@ static char *get_script_path(const char *script_root, const char *suffix)
if (!scripts_dir) if (!scripts_dir)
return NULL; return NULL;
for_each_lang(scripts_path, scripts_dir, lang_dirent, lang_next) { for_each_lang(scripts_path, scripts_dir, lang_dirent) {
snprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path, snprintf(lang_path, MAXPATHLEN, "%s/%s/bin", scripts_path,
lang_dirent.d_name); lang_dirent->d_name);
lang_dir = opendir(lang_path); lang_dir = opendir(lang_path);
if (!lang_dir) if (!lang_dir)
continue; continue;
for_each_script(lang_path, lang_dir, script_dirent, script_next) { for_each_script(lang_path, lang_dir, script_dirent) {
__script_root = get_script_root(&script_dirent, suffix); __script_root = get_script_root(script_dirent, suffix);
if (__script_root && !strcmp(script_root, __script_root)) { if (__script_root && !strcmp(script_root, __script_root)) {
free(__script_root); free(__script_root);
closedir(lang_dir); closedir(lang_dir);
closedir(scripts_dir); closedir(scripts_dir);
snprintf(script_path, MAXPATHLEN, "%s/%s", snprintf(script_path, MAXPATHLEN, "%s/%s",
lang_path, script_dirent.d_name); lang_path, script_dirent->d_name);
return strdup(script_path); return strdup(script_path);
} }
free(__script_root); free(__script_root);

View File

@ -886,7 +886,7 @@ static int perf_top__start_counters(struct perf_top *top)
struct perf_evlist *evlist = top->evlist; struct perf_evlist *evlist = top->evlist;
struct record_opts *opts = &top->record_opts; struct record_opts *opts = &top->record_opts;
perf_evlist__config(evlist, opts); perf_evlist__config(evlist, opts, &callchain_param);
evlist__for_each(evlist, counter) { evlist__for_each(evlist, counter) {
try_again: try_again:

View File

@ -34,6 +34,7 @@
#include "trace-event.h" #include "trace-event.h"
#include "util/parse-events.h" #include "util/parse-events.h"
#include "util/bpf-loader.h" #include "util/bpf-loader.h"
#include "callchain.h"
#include "syscalltbl.h" #include "syscalltbl.h"
#include <libaudit.h> /* FIXME: Still needed for audit_errno_to_name */ #include <libaudit.h> /* FIXME: Still needed for audit_errno_to_name */
@ -158,6 +159,7 @@ struct trace {
bool show_comm; bool show_comm;
bool show_tool_stats; bool show_tool_stats;
bool trace_syscalls; bool trace_syscalls;
bool kernel_syscallchains;
bool force; bool force;
bool vfs_getname; bool vfs_getname;
int trace_pgfaults; int trace_pgfaults;
@ -2190,6 +2192,22 @@ signed_print:
goto signed_print; goto signed_print;
fputc('\n', trace->output); fputc('\n', trace->output);
if (sample->callchain) {
struct addr_location al;
/* TODO: user-configurable print_opts */
const unsigned int print_opts = PRINT_IP_OPT_SYM |
PRINT_IP_OPT_DSO |
PRINT_IP_OPT_UNKNOWN_AS_ADDR;
if (machine__resolve(trace->host, &al, sample) < 0) {
pr_err("problem processing %d event, skipping it.\n",
event->header.type);
goto out_put;
}
perf_evsel__fprintf_callchain(evsel, sample, &al, 38, print_opts,
scripting_max_stack, trace->output);
}
out: out:
ttrace->entry_pending = false; ttrace->entry_pending = false;
err = 0; err = 0;
@ -2645,6 +2663,15 @@ static int trace__add_syscall_newtp(struct trace *trace)
perf_evlist__add(evlist, sys_enter); perf_evlist__add(evlist, sys_enter);
perf_evlist__add(evlist, sys_exit); perf_evlist__add(evlist, sys_exit);
if (trace->opts.callgraph_set && !trace->kernel_syscallchains) {
/*
* We're interested only in the user space callchain
* leading to the syscall, allow overriding that for
* debugging reasons using --kernel_syscall_callchains
*/
sys_exit->attr.exclude_callchain_kernel = 1;
}
trace->syscalls.events.sys_enter = sys_enter; trace->syscalls.events.sys_enter = sys_enter;
trace->syscalls.events.sys_exit = sys_exit; trace->syscalls.events.sys_exit = sys_exit;
@ -2723,7 +2750,27 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
goto out_delete_evlist; goto out_delete_evlist;
} }
perf_evlist__config(evlist, &trace->opts); perf_evlist__config(evlist, &trace->opts, NULL);
if (trace->opts.callgraph_set && trace->syscalls.events.sys_exit) {
perf_evsel__config_callchain(trace->syscalls.events.sys_exit,
&trace->opts, &callchain_param);
/*
* Now we have evsels with different sample_ids, use
* PERF_SAMPLE_IDENTIFIER to map from sample to evsel
* from a fixed position in each ring buffer record.
*
* As of this the changeset introducing this comment, this
* isn't strictly needed, as the fields that can come before
* PERF_SAMPLE_ID are all used, but we'll probably disable
* some of those for things like copying the payload of
* pointer syscall arguments, and for vfs_getname we don't
* need PERF_SAMPLE_ADDR and PERF_SAMPLE_IP, so do this
* here as a warning we need to use PERF_SAMPLE_IDENTIFIER.
*/
perf_evlist__set_sample_bit(evlist, IDENTIFIER);
perf_evlist__reset_sample_bit(evlist, ID);
}
signal(SIGCHLD, sig_handler); signal(SIGCHLD, sig_handler);
signal(SIGINT, sig_handler); signal(SIGINT, sig_handler);
@ -3205,6 +3252,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
.output = stderr, .output = stderr,
.show_comm = true, .show_comm = true,
.trace_syscalls = true, .trace_syscalls = true,
.kernel_syscallchains = false,
}; };
const char *output_name = NULL; const char *output_name = NULL;
const char *ev_qualifier_str = NULL; const char *ev_qualifier_str = NULL;
@ -3250,6 +3298,11 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
"Trace pagefaults", parse_pagefaults, "maj"), "Trace pagefaults", parse_pagefaults, "maj"),
OPT_BOOLEAN(0, "syscalls", &trace.trace_syscalls, "Trace syscalls"), OPT_BOOLEAN(0, "syscalls", &trace.trace_syscalls, "Trace syscalls"),
OPT_BOOLEAN('f', "force", &trace.force, "don't complain, do it"), OPT_BOOLEAN('f', "force", &trace.force, "don't complain, do it"),
OPT_CALLBACK(0, "call-graph", &trace.opts,
"record_mode[,record_size]", record_callchain_help,
&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, "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()
@ -3273,11 +3326,21 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
argc = parse_options_subcommand(argc, argv, trace_options, trace_subcommands, argc = parse_options_subcommand(argc, argv, trace_options, trace_subcommands,
trace_usage, PARSE_OPT_STOP_AT_NON_OPTION); trace_usage, PARSE_OPT_STOP_AT_NON_OPTION);
err = bpf__setup_stdout(trace.evlist);
if (err) {
bpf__strerror_setup_stdout(trace.evlist, err, bf, sizeof(bf));
pr_err("ERROR: Setup BPF stdout failed: %s\n", bf);
goto out;
}
if (trace.trace_pgfaults) { if (trace.trace_pgfaults) {
trace.opts.sample_address = true; trace.opts.sample_address = true;
trace.opts.sample_time = true; trace.opts.sample_time = true;
} }
if (trace.opts.callgraph_set)
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);

View File

@ -138,7 +138,7 @@ static int do_test(struct bpf_object *obj, int (*func)(void),
perf_evlist__splice_list_tail(evlist, &parse_evlist.list); perf_evlist__splice_list_tail(evlist, &parse_evlist.list);
evlist->nr_groups = parse_evlist.nr_groups; evlist->nr_groups = parse_evlist.nr_groups;
perf_evlist__config(evlist, &opts); perf_evlist__config(evlist, &opts, NULL);
err = perf_evlist__open(evlist); err = perf_evlist__open(evlist);
if (err < 0) { if (err < 0) {

View File

@ -532,7 +532,7 @@ static int do_test_code_reading(bool try_kcore)
goto out_put; goto out_put;
} }
perf_evlist__config(evlist, &opts); perf_evlist__config(evlist, &opts, NULL);
evsel = perf_evlist__first(evlist); evsel = perf_evlist__first(evlist);

View File

@ -80,7 +80,7 @@ int test__keep_tracking(int subtest __maybe_unused)
CHECK__(parse_events(evlist, "dummy:u", NULL)); CHECK__(parse_events(evlist, "dummy:u", NULL));
CHECK__(parse_events(evlist, "cycles:u", NULL)); CHECK__(parse_events(evlist, "cycles:u", NULL));
perf_evlist__config(evlist, &opts); perf_evlist__config(evlist, &opts, NULL);
evsel = perf_evlist__first(evlist); evsel = perf_evlist__first(evlist);

View File

@ -44,7 +44,7 @@ int test__syscall_openat_tp_fields(int subtest __maybe_unused)
goto out_delete_evlist; goto out_delete_evlist;
} }
perf_evsel__config(evsel, &opts); perf_evsel__config(evsel, &opts, NULL);
thread_map__set_pid(evlist->threads, 0, getpid()); thread_map__set_pid(evlist->threads, 0, getpid());

View File

@ -99,7 +99,7 @@ int test__PERF_RECORD(int subtest __maybe_unused)
perf_evsel__set_sample_bit(evsel, CPU); perf_evsel__set_sample_bit(evsel, CPU);
perf_evsel__set_sample_bit(evsel, TID); perf_evsel__set_sample_bit(evsel, TID);
perf_evsel__set_sample_bit(evsel, TIME); perf_evsel__set_sample_bit(evsel, TIME);
perf_evlist__config(evlist, &opts); perf_evlist__config(evlist, &opts, NULL);
err = sched__get_first_possible_cpu(evlist->workload.pid, &cpu_mask); err = sched__get_first_possible_cpu(evlist->workload.pid, &cpu_mask);
if (err < 0) { if (err < 0) {

View File

@ -417,7 +417,7 @@ int test__switch_tracking(int subtest __maybe_unused)
perf_evsel__set_sample_bit(tracking_evsel, TIME); perf_evsel__set_sample_bit(tracking_evsel, TIME);
/* Config events */ /* Config events */
perf_evlist__config(evlist, &opts); perf_evlist__config(evlist, &opts, NULL);
/* Check moved event is still at the front */ /* Check moved event is still at the front */
if (cycles_evsel != perf_evlist__first(evlist)) { if (cycles_evsel != perf_evlist__first(evlist)) {

View File

@ -842,6 +842,58 @@ bpf_map_op__new(struct parse_events_term *term)
return op; return op;
} }
static struct bpf_map_op *
bpf_map_op__clone(struct bpf_map_op *op)
{
struct bpf_map_op *newop;
newop = memdup(op, sizeof(*op));
if (!newop) {
pr_debug("Failed to alloc bpf_map_op\n");
return NULL;
}
INIT_LIST_HEAD(&newop->list);
if (op->key_type == BPF_MAP_KEY_RANGES) {
size_t memsz = op->k.array.nr_ranges *
sizeof(op->k.array.ranges[0]);
newop->k.array.ranges = memdup(op->k.array.ranges, memsz);
if (!newop->k.array.ranges) {
pr_debug("Failed to alloc indices for map\n");
free(newop);
return NULL;
}
}
return newop;
}
static struct bpf_map_priv *
bpf_map_priv__clone(struct bpf_map_priv *priv)
{
struct bpf_map_priv *newpriv;
struct bpf_map_op *pos, *newop;
newpriv = zalloc(sizeof(*newpriv));
if (!newpriv) {
pr_debug("No enough memory to alloc map private\n");
return NULL;
}
INIT_LIST_HEAD(&newpriv->ops_list);
list_for_each_entry(pos, &priv->ops_list, list) {
newop = bpf_map_op__clone(pos);
if (!newop) {
bpf_map_priv__purge(newpriv);
return NULL;
}
list_add_tail(&newop->list, &newpriv->ops_list);
}
return newpriv;
}
static int static int
bpf_map__add_op(struct bpf_map *map, struct bpf_map_op *op) bpf_map__add_op(struct bpf_map *map, struct bpf_map_op *op)
{ {
@ -1417,6 +1469,89 @@ int bpf__apply_obj_config(void)
return 0; return 0;
} }
#define bpf__for_each_map(pos, obj, objtmp) \
bpf_object__for_each_safe(obj, objtmp) \
bpf_map__for_each(pos, obj)
#define bpf__for_each_stdout_map(pos, obj, objtmp) \
bpf__for_each_map(pos, obj, objtmp) \
if (bpf_map__get_name(pos) && \
(strcmp("__bpf_stdout__", \
bpf_map__get_name(pos)) == 0))
int bpf__setup_stdout(struct perf_evlist *evlist __maybe_unused)
{
struct bpf_map_priv *tmpl_priv = NULL;
struct bpf_object *obj, *tmp;
struct perf_evsel *evsel = NULL;
struct bpf_map *map;
int err;
bool need_init = false;
bpf__for_each_stdout_map(map, obj, tmp) {
struct bpf_map_priv *priv;
err = bpf_map__get_private(map, (void **)&priv);
if (err)
return -BPF_LOADER_ERRNO__INTERNAL;
/*
* No need to check map type: type should have been
* verified by kernel.
*/
if (!need_init && !priv)
need_init = !priv;
if (!tmpl_priv && priv)
tmpl_priv = priv;
}
if (!need_init)
return 0;
if (!tmpl_priv) {
err = parse_events(evlist, "bpf-output/no-inherit=1,name=__bpf_stdout__/",
NULL);
if (err) {
pr_debug("ERROR: failed to create bpf-output event\n");
return -err;
}
evsel = perf_evlist__last(evlist);
}
bpf__for_each_stdout_map(map, obj, tmp) {
struct bpf_map_priv *priv;
err = bpf_map__get_private(map, (void **)&priv);
if (err)
return -BPF_LOADER_ERRNO__INTERNAL;
if (priv)
continue;
if (tmpl_priv) {
priv = bpf_map_priv__clone(tmpl_priv);
if (!priv)
return -ENOMEM;
err = bpf_map__set_private(map, priv, bpf_map_priv__clear);
if (err) {
bpf_map_priv__clear(map, priv);
return err;
}
} else if (evsel) {
struct bpf_map_op *op;
op = bpf_map__add_newop(map, NULL);
if (IS_ERR(op))
return PTR_ERR(op);
op->op_type = BPF_MAP_OP_SET_EVSEL;
op->v.evsel = evsel;
}
}
return 0;
}
#define ERRNO_OFFSET(e) ((e) - __BPF_LOADER_ERRNO__START) #define ERRNO_OFFSET(e) ((e) - __BPF_LOADER_ERRNO__START)
#define ERRCODE_OFFSET(c) ERRNO_OFFSET(BPF_LOADER_ERRNO__##c) #define ERRCODE_OFFSET(c) ERRNO_OFFSET(BPF_LOADER_ERRNO__##c)
#define NR_ERRNO (__BPF_LOADER_ERRNO__END - __BPF_LOADER_ERRNO__START) #define NR_ERRNO (__BPF_LOADER_ERRNO__END - __BPF_LOADER_ERRNO__START)
@ -1590,3 +1725,11 @@ int bpf__strerror_apply_obj_config(int err, char *buf, size_t size)
bpf__strerror_end(buf, size); bpf__strerror_end(buf, size);
return 0; return 0;
} }
int bpf__strerror_setup_stdout(struct perf_evlist *evlist __maybe_unused,
int err, char *buf, size_t size)
{
bpf__strerror_head(err, buf, size);
bpf__strerror_end(buf, size);
return 0;
}

View File

@ -79,6 +79,11 @@ int bpf__strerror_config_obj(struct bpf_object *obj,
size_t size); size_t size);
int bpf__apply_obj_config(void); int bpf__apply_obj_config(void);
int bpf__strerror_apply_obj_config(int err, char *buf, size_t size); int bpf__strerror_apply_obj_config(int err, char *buf, size_t size);
int bpf__setup_stdout(struct perf_evlist *evlist);
int bpf__strerror_setup_stdout(struct perf_evlist *evlist, int err,
char *buf, size_t size);
#else #else
static inline struct bpf_object * static inline struct bpf_object *
bpf__prepare_load(const char *filename __maybe_unused, bpf__prepare_load(const char *filename __maybe_unused,
@ -124,6 +129,12 @@ bpf__apply_obj_config(void)
return 0; return 0;
} }
static inline int
bpf__setup_stdout(struct perf_evlist *evlist __maybe_unused)
{
return 0;
}
static inline int static inline int
__bpf_strerror(char *buf, size_t size) __bpf_strerror(char *buf, size_t size)
{ {
@ -177,5 +188,13 @@ bpf__strerror_apply_obj_config(int err __maybe_unused,
{ {
return __bpf_strerror(buf, size); return __bpf_strerror(buf, size);
} }
static inline int
bpf__strerror_setup_stdout(struct perf_evlist *evlist __maybe_unused,
int err __maybe_unused, char *buf,
size_t size)
{
return __bpf_strerror(buf, size);
}
#endif #endif
#endif #endif

View File

@ -434,7 +434,7 @@ static int __event__synthesize_thread(union perf_event *comm_event,
{ {
char filename[PATH_MAX]; char filename[PATH_MAX];
DIR *tasks; DIR *tasks;
struct dirent dirent, *next; struct dirent *dirent;
pid_t tgid, ppid; pid_t tgid, ppid;
int rc = 0; int rc = 0;
@ -463,11 +463,11 @@ static int __event__synthesize_thread(union perf_event *comm_event,
return 0; return 0;
} }
while (!readdir_r(tasks, &dirent, &next) && next) { while ((dirent = readdir(tasks)) != NULL) {
char *end; char *end;
pid_t _pid; pid_t _pid;
_pid = strtol(dirent.d_name, &end, 10); _pid = strtol(dirent->d_name, &end, 10);
if (*end) if (*end)
continue; continue;
@ -576,7 +576,7 @@ int perf_event__synthesize_threads(struct perf_tool *tool,
{ {
DIR *proc; DIR *proc;
char proc_path[PATH_MAX]; char proc_path[PATH_MAX];
struct dirent dirent, *next; struct dirent *dirent;
union perf_event *comm_event, *mmap_event, *fork_event; union perf_event *comm_event, *mmap_event, *fork_event;
int err = -1; int err = -1;
@ -601,9 +601,9 @@ int perf_event__synthesize_threads(struct perf_tool *tool,
if (proc == NULL) if (proc == NULL)
goto out_free_fork; goto out_free_fork;
while (!readdir_r(proc, &dirent, &next) && next) { while ((dirent = readdir(proc)) != NULL) {
char *end; char *end;
pid_t pid = strtol(dirent.d_name, &end, 10); pid_t pid = strtol(dirent->d_name, &end, 10);
if (*end) /* only interested in proper numerical dirents */ if (*end) /* only interested in proper numerical dirents */
continue; continue;

View File

@ -1192,6 +1192,24 @@ void perf_evlist__set_maps(struct perf_evlist *evlist, struct cpu_map *cpus,
perf_evlist__propagate_maps(evlist); perf_evlist__propagate_maps(evlist);
} }
void __perf_evlist__set_sample_bit(struct perf_evlist *evlist,
enum perf_event_sample_format bit)
{
struct perf_evsel *evsel;
evlist__for_each(evlist, evsel)
__perf_evsel__set_sample_bit(evsel, bit);
}
void __perf_evlist__reset_sample_bit(struct perf_evlist *evlist,
enum perf_event_sample_format bit)
{
struct perf_evsel *evsel;
evlist__for_each(evlist, evsel)
__perf_evsel__reset_sample_bit(evsel, bit);
}
int perf_evlist__apply_filters(struct perf_evlist *evlist, struct perf_evsel **err_evsel) int perf_evlist__apply_filters(struct perf_evlist *evlist, struct perf_evsel **err_evsel)
{ {
struct perf_evsel *evsel; struct perf_evsel *evsel;

View File

@ -87,6 +87,17 @@ int perf_evlist__add_dummy(struct perf_evlist *evlist);
int perf_evlist__add_newtp(struct perf_evlist *evlist, int perf_evlist__add_newtp(struct perf_evlist *evlist,
const char *sys, const char *name, void *handler); const char *sys, const char *name, void *handler);
void __perf_evlist__set_sample_bit(struct perf_evlist *evlist,
enum perf_event_sample_format bit);
void __perf_evlist__reset_sample_bit(struct perf_evlist *evlist,
enum perf_event_sample_format bit);
#define perf_evlist__set_sample_bit(evlist, bit) \
__perf_evlist__set_sample_bit(evlist, PERF_SAMPLE_##bit)
#define perf_evlist__reset_sample_bit(evlist, bit) \
__perf_evlist__reset_sample_bit(evlist, PERF_SAMPLE_##bit)
int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter); int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter);
int perf_evlist__set_filter_pid(struct perf_evlist *evlist, pid_t pid); int perf_evlist__set_filter_pid(struct perf_evlist *evlist, pid_t pid);
int perf_evlist__set_filter_pids(struct perf_evlist *evlist, size_t npids, pid_t *pids); int perf_evlist__set_filter_pids(struct perf_evlist *evlist, size_t npids, pid_t *pids);
@ -123,11 +134,14 @@ void perf_evlist__mmap_consume(struct perf_evlist *evlist, int idx);
int perf_evlist__open(struct perf_evlist *evlist); int perf_evlist__open(struct perf_evlist *evlist);
void perf_evlist__close(struct perf_evlist *evlist); void perf_evlist__close(struct perf_evlist *evlist);
struct callchain_param;
void perf_evlist__set_id_pos(struct perf_evlist *evlist); void perf_evlist__set_id_pos(struct perf_evlist *evlist);
bool perf_can_sample_identifier(void); bool perf_can_sample_identifier(void);
bool perf_can_record_switch_events(void); bool perf_can_record_switch_events(void);
bool perf_can_record_cpu_wide(void); bool perf_can_record_cpu_wide(void);
void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts); void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts,
struct callchain_param *callchain);
int record_opts__config(struct record_opts *opts); int record_opts__config(struct record_opts *opts);
int perf_evlist__prepare_workload(struct perf_evlist *evlist, int perf_evlist__prepare_workload(struct perf_evlist *evlist,

View File

@ -562,10 +562,9 @@ int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size)
return ret; return ret;
} }
static void void perf_evsel__config_callchain(struct perf_evsel *evsel,
perf_evsel__config_callgraph(struct perf_evsel *evsel, struct record_opts *opts,
struct record_opts *opts, struct callchain_param *param)
struct callchain_param *param)
{ {
bool function = perf_evsel__is_function_event(evsel); bool function = perf_evsel__is_function_event(evsel);
struct perf_event_attr *attr = &evsel->attr; struct perf_event_attr *attr = &evsel->attr;
@ -705,7 +704,7 @@ static void apply_config_terms(struct perf_evsel *evsel,
/* set perf-event callgraph */ /* set perf-event callgraph */
if (param.enabled) if (param.enabled)
perf_evsel__config_callgraph(evsel, opts, &param); perf_evsel__config_callchain(evsel, opts, &param);
} }
} }
@ -737,7 +736,8 @@ static void apply_config_terms(struct perf_evsel *evsel,
* enable/disable events specifically, as there's no * enable/disable events specifically, as there's no
* initial traced exec call. * initial traced exec call.
*/ */
void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts,
struct callchain_param *callchain)
{ {
struct perf_evsel *leader = evsel->leader; struct perf_evsel *leader = evsel->leader;
struct perf_event_attr *attr = &evsel->attr; struct perf_event_attr *attr = &evsel->attr;
@ -812,8 +812,8 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts)
if (perf_evsel__is_function_event(evsel)) if (perf_evsel__is_function_event(evsel))
evsel->attr.exclude_callchain_user = 1; evsel->attr.exclude_callchain_user = 1;
if (callchain_param.enabled && !evsel->no_aux_samples) if (callchain && callchain->enabled && !evsel->no_aux_samples)
perf_evsel__config_callgraph(evsel, opts, &callchain_param); perf_evsel__config_callchain(evsel, opts, callchain);
if (opts->sample_intr_regs) { if (opts->sample_intr_regs) {
attr->sample_regs_intr = opts->sample_intr_regs; attr->sample_regs_intr = opts->sample_intr_regs;

View File

@ -178,8 +178,14 @@ void perf_evsel__init(struct perf_evsel *evsel,
void perf_evsel__exit(struct perf_evsel *evsel); void perf_evsel__exit(struct perf_evsel *evsel);
void perf_evsel__delete(struct perf_evsel *evsel); void perf_evsel__delete(struct perf_evsel *evsel);
struct callchain_param;
void perf_evsel__config(struct perf_evsel *evsel, void perf_evsel__config(struct perf_evsel *evsel,
struct record_opts *opts); struct record_opts *opts,
struct callchain_param *callchain);
void perf_evsel__config_callchain(struct perf_evsel *evsel,
struct record_opts *opts,
struct callchain_param *callchain);
int __perf_evsel__sample_size(u64 sample_type); int __perf_evsel__sample_size(u64 sample_type);
void perf_evsel__calc_id_pos(struct perf_evsel *evsel); void perf_evsel__calc_id_pos(struct perf_evsel *evsel);
@ -381,6 +387,12 @@ struct perf_attr_details {
int perf_evsel__fprintf(struct perf_evsel *evsel, int perf_evsel__fprintf(struct perf_evsel *evsel,
struct perf_attr_details *details, FILE *fp); struct perf_attr_details *details, FILE *fp);
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);
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);
int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target, int perf_evsel__open_strerror(struct perf_evsel *evsel, struct target *target,

View File

@ -138,11 +138,11 @@ struct event_symbol event_symbols_sw[PERF_COUNT_SW_MAX] = {
#define PERF_EVENT_TYPE(config) __PERF_EVENT_FIELD(config, TYPE) #define PERF_EVENT_TYPE(config) __PERF_EVENT_FIELD(config, TYPE)
#define PERF_EVENT_ID(config) __PERF_EVENT_FIELD(config, EVENT) #define PERF_EVENT_ID(config) __PERF_EVENT_FIELD(config, EVENT)
#define for_each_subsystem(sys_dir, sys_dirent, sys_next) \ #define for_each_subsystem(sys_dir, sys_dirent) \
while (!readdir_r(sys_dir, &sys_dirent, &sys_next) && sys_next) \ while ((sys_dirent = readdir(sys_dir)) != NULL) \
if (sys_dirent.d_type == DT_DIR && \ if (sys_dirent->d_type == DT_DIR && \
(strcmp(sys_dirent.d_name, ".")) && \ (strcmp(sys_dirent->d_name, ".")) && \
(strcmp(sys_dirent.d_name, ".."))) (strcmp(sys_dirent->d_name, "..")))
static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir) static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir)
{ {
@ -159,12 +159,12 @@ static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir)
return 0; return 0;
} }
#define for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) \ #define for_each_event(sys_dirent, evt_dir, evt_dirent) \
while (!readdir_r(evt_dir, &evt_dirent, &evt_next) && evt_next) \ while ((evt_dirent = readdir(evt_dir)) != NULL) \
if (evt_dirent.d_type == DT_DIR && \ if (evt_dirent->d_type == DT_DIR && \
(strcmp(evt_dirent.d_name, ".")) && \ (strcmp(evt_dirent->d_name, ".")) && \
(strcmp(evt_dirent.d_name, "..")) && \ (strcmp(evt_dirent->d_name, "..")) && \
(!tp_event_has_id(&sys_dirent, &evt_dirent))) (!tp_event_has_id(sys_dirent, evt_dirent)))
#define MAX_EVENT_LENGTH 512 #define MAX_EVENT_LENGTH 512
@ -173,7 +173,7 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config)
{ {
struct tracepoint_path *path = NULL; struct tracepoint_path *path = NULL;
DIR *sys_dir, *evt_dir; DIR *sys_dir, *evt_dir;
struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; struct dirent *sys_dirent, *evt_dirent;
char id_buf[24]; char id_buf[24];
int fd; int fd;
u64 id; u64 id;
@ -184,18 +184,18 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config)
if (!sys_dir) if (!sys_dir)
return NULL; return NULL;
for_each_subsystem(sys_dir, sys_dirent, sys_next) { for_each_subsystem(sys_dir, sys_dirent) {
snprintf(dir_path, MAXPATHLEN, "%s/%s", tracing_events_path, snprintf(dir_path, MAXPATHLEN, "%s/%s", tracing_events_path,
sys_dirent.d_name); sys_dirent->d_name);
evt_dir = opendir(dir_path); evt_dir = opendir(dir_path);
if (!evt_dir) if (!evt_dir)
continue; continue;
for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) { for_each_event(sys_dirent, evt_dir, evt_dirent) {
snprintf(evt_path, MAXPATHLEN, "%s/%s/id", dir_path, snprintf(evt_path, MAXPATHLEN, "%s/%s/id", dir_path,
evt_dirent.d_name); evt_dirent->d_name);
fd = open(evt_path, O_RDONLY); fd = open(evt_path, O_RDONLY);
if (fd < 0) if (fd < 0)
continue; continue;
@ -220,9 +220,9 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config)
free(path); free(path);
return NULL; return NULL;
} }
strncpy(path->system, sys_dirent.d_name, strncpy(path->system, sys_dirent->d_name,
MAX_EVENT_LENGTH); MAX_EVENT_LENGTH);
strncpy(path->name, evt_dirent.d_name, strncpy(path->name, evt_dirent->d_name,
MAX_EVENT_LENGTH); MAX_EVENT_LENGTH);
return path; return path;
} }
@ -1812,7 +1812,7 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob,
bool name_only) bool name_only)
{ {
DIR *sys_dir, *evt_dir; DIR *sys_dir, *evt_dir;
struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; struct dirent *sys_dirent, *evt_dirent;
char evt_path[MAXPATHLEN]; char evt_path[MAXPATHLEN];
char dir_path[MAXPATHLEN]; char dir_path[MAXPATHLEN];
char **evt_list = NULL; char **evt_list = NULL;
@ -1830,20 +1830,20 @@ restart:
goto out_close_sys_dir; goto out_close_sys_dir;
} }
for_each_subsystem(sys_dir, sys_dirent, sys_next) { for_each_subsystem(sys_dir, sys_dirent) {
if (subsys_glob != NULL && if (subsys_glob != NULL &&
!strglobmatch(sys_dirent.d_name, subsys_glob)) !strglobmatch(sys_dirent->d_name, subsys_glob))
continue; continue;
snprintf(dir_path, MAXPATHLEN, "%s/%s", tracing_events_path, snprintf(dir_path, MAXPATHLEN, "%s/%s", tracing_events_path,
sys_dirent.d_name); sys_dirent->d_name);
evt_dir = opendir(dir_path); evt_dir = opendir(dir_path);
if (!evt_dir) if (!evt_dir)
continue; continue;
for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) { for_each_event(sys_dirent, evt_dir, evt_dirent) {
if (event_glob != NULL && if (event_glob != NULL &&
!strglobmatch(evt_dirent.d_name, event_glob)) !strglobmatch(evt_dirent->d_name, event_glob))
continue; continue;
if (!evt_num_known) { if (!evt_num_known) {
@ -1852,7 +1852,7 @@ restart:
} }
snprintf(evt_path, MAXPATHLEN, "%s:%s", snprintf(evt_path, MAXPATHLEN, "%s:%s",
sys_dirent.d_name, evt_dirent.d_name); sys_dirent->d_name, evt_dirent->d_name);
evt_list[evt_i] = strdup(evt_path); evt_list[evt_i] = strdup(evt_path);
if (evt_list[evt_i] == NULL) if (evt_list[evt_i] == NULL)
@ -1905,7 +1905,7 @@ out_close_sys_dir:
int is_valid_tracepoint(const char *event_string) int is_valid_tracepoint(const char *event_string)
{ {
DIR *sys_dir, *evt_dir; DIR *sys_dir, *evt_dir;
struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent; struct dirent *sys_dirent, *evt_dirent;
char evt_path[MAXPATHLEN]; char evt_path[MAXPATHLEN];
char dir_path[MAXPATHLEN]; char dir_path[MAXPATHLEN];
@ -1913,17 +1913,17 @@ int is_valid_tracepoint(const char *event_string)
if (!sys_dir) if (!sys_dir)
return 0; return 0;
for_each_subsystem(sys_dir, sys_dirent, sys_next) { for_each_subsystem(sys_dir, sys_dirent) {
snprintf(dir_path, MAXPATHLEN, "%s/%s", tracing_events_path, snprintf(dir_path, MAXPATHLEN, "%s/%s", tracing_events_path,
sys_dirent.d_name); sys_dirent->d_name);
evt_dir = opendir(dir_path); evt_dir = opendir(dir_path);
if (!evt_dir) if (!evt_dir)
continue; continue;
for_each_event(sys_dirent, evt_dir, evt_dirent, evt_next) { for_each_event(sys_dirent, evt_dir, evt_dirent) {
snprintf(evt_path, MAXPATHLEN, "%s:%s", snprintf(evt_path, MAXPATHLEN, "%s:%s",
sys_dirent.d_name, evt_dirent.d_name); sys_dirent->d_name, evt_dirent->d_name);
if (!strcmp(evt_path, event_string)) { if (!strcmp(evt_path, event_string)) {
closedir(evt_dir); closedir(evt_dir);
closedir(sys_dir); closedir(sys_dir);

View File

@ -129,7 +129,8 @@ bool perf_can_record_cpu_wide(void)
return true; return true;
} }
void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts) void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts,
struct callchain_param *callchain)
{ {
struct perf_evsel *evsel; struct perf_evsel *evsel;
bool use_sample_identifier = false; bool use_sample_identifier = false;
@ -148,7 +149,7 @@ void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts)
use_comm_exec = perf_can_comm_exec(); use_comm_exec = perf_can_comm_exec();
evlist__for_each(evlist, evsel) { evlist__for_each(evlist, evsel) {
perf_evsel__config(evsel, opts); perf_evsel__config(evsel, opts, callchain);
if (evsel->tracking && use_comm_exec) if (evsel->tracking && use_comm_exec)
evsel->attr.comm_exec = 1; evsel->attr.comm_exec = 1;
} }

View File

@ -1953,10 +1953,12 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,
return NULL; return NULL;
} }
void perf_evsel__print_ip(struct perf_evsel *evsel, struct perf_sample *sample, int perf_evsel__fprintf_callchain(struct perf_evsel *evsel, struct perf_sample *sample,
struct addr_location *al, struct addr_location *al, int left_alignment,
unsigned int print_opts, unsigned int stack_depth) unsigned int print_opts, unsigned int stack_depth,
FILE *fp)
{ {
int printed = 0;
struct callchain_cursor_node *node; struct callchain_cursor_node *node;
int print_ip = print_opts & PRINT_IP_OPT_IP; int print_ip = print_opts & PRINT_IP_OPT_IP;
int print_sym = print_opts & PRINT_IP_OPT_SYM; int print_sym = print_opts & PRINT_IP_OPT_SYM;
@ -1964,9 +1966,10 @@ void perf_evsel__print_ip(struct perf_evsel *evsel, struct perf_sample *sample,
int print_symoffset = print_opts & PRINT_IP_OPT_SYMOFFSET; int print_symoffset = print_opts & PRINT_IP_OPT_SYMOFFSET;
int print_oneline = print_opts & PRINT_IP_OPT_ONELINE; int print_oneline = print_opts & PRINT_IP_OPT_ONELINE;
int print_srcline = print_opts & PRINT_IP_OPT_SRCLINE; int print_srcline = print_opts & PRINT_IP_OPT_SRCLINE;
int print_unknown_as_addr = print_opts & PRINT_IP_OPT_UNKNOWN_AS_ADDR;
char s = print_oneline ? ' ' : '\t'; char s = print_oneline ? ' ' : '\t';
if (symbol_conf.use_callchain && sample->callchain) { if (sample->callchain) {
struct addr_location node_al; struct addr_location node_al;
if (thread__resolve_callchain(al->thread, evsel, if (thread__resolve_callchain(al->thread, evsel,
@ -1974,7 +1977,7 @@ void perf_evsel__print_ip(struct perf_evsel *evsel, struct perf_sample *sample,
stack_depth) != 0) { stack_depth) != 0) {
if (verbose) if (verbose)
error("Failed to resolve callchain. Skipping\n"); error("Failed to resolve callchain. Skipping\n");
return; return printed;
} }
callchain_cursor_commit(&callchain_cursor); callchain_cursor_commit(&callchain_cursor);
@ -1991,65 +1994,93 @@ void perf_evsel__print_ip(struct perf_evsel *evsel, struct perf_sample *sample,
if (node->sym && node->sym->ignore) if (node->sym && node->sym->ignore)
goto next; goto next;
printed += fprintf(fp, "%-*.*s", left_alignment, left_alignment, " ");
if (print_ip) if (print_ip)
printf("%c%16" PRIx64, s, node->ip); printed += fprintf(fp, "%c%16" PRIx64, s, node->ip);
if (node->map) if (node->map)
addr = node->map->map_ip(node->map, node->ip); addr = node->map->map_ip(node->map, node->ip);
if (print_sym) { if (print_sym) {
printf(" "); printed += fprintf(fp, " ");
node_al.addr = addr;
node_al.map = node->map;
if (print_symoffset) { if (print_symoffset) {
node_al.addr = addr; printed += __symbol__fprintf_symname_offs(node->sym, &node_al,
node_al.map = node->map; print_unknown_as_addr, fp);
symbol__fprintf_symname_offs(node->sym, &node_al, stdout); } else {
} else printed += __symbol__fprintf_symname(node->sym, &node_al,
symbol__fprintf_symname(node->sym, stdout); print_unknown_as_addr, fp);
}
} }
if (print_dso) { if (print_dso) {
printf(" ("); printed += fprintf(fp, " (");
map__fprintf_dsoname(node->map, stdout); printed += map__fprintf_dsoname(node->map, fp);
printf(")"); printed += fprintf(fp, ")");
} }
if (print_srcline) if (print_srcline)
map__fprintf_srcline(node->map, addr, "\n ", printed += map__fprintf_srcline(node->map, addr, "\n ", fp);
stdout);
if (!print_oneline) if (!print_oneline)
printf("\n"); printed += fprintf(fp, "\n");
stack_depth--; stack_depth--;
next: next:
callchain_cursor_advance(&callchain_cursor); callchain_cursor_advance(&callchain_cursor);
} }
}
} else { return printed;
if (al->sym && al->sym->ignore) }
return;
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 & PRINT_IP_OPT_IP;
int print_sym = print_opts & PRINT_IP_OPT_SYM;
int print_dso = print_opts & PRINT_IP_OPT_DSO;
int print_symoffset = print_opts & PRINT_IP_OPT_SYMOFFSET;
int print_srcline = print_opts & PRINT_IP_OPT_SRCLINE;
int print_unknown_as_addr = print_opts & PRINT_IP_OPT_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) if (print_ip)
printf("%16" PRIx64, sample->ip); printed += fprintf(fp, "%16" PRIx64, sample->ip);
if (print_sym) { if (print_sym) {
printf(" "); printed += fprintf(fp, " ");
if (print_symoffset) if (print_symoffset) {
symbol__fprintf_symname_offs(al->sym, al, printed += __symbol__fprintf_symname_offs(al->sym, al,
stdout); print_unknown_as_addr, fp);
else } else {
symbol__fprintf_symname(al->sym, stdout); printed += __symbol__fprintf_symname(al->sym, al,
print_unknown_as_addr, fp);
}
} }
if (print_dso) { if (print_dso) {
printf(" ("); printed += fprintf(fp, " (");
map__fprintf_dsoname(al->map, stdout); printed += map__fprintf_dsoname(al->map, fp);
printf(")"); printed += fprintf(fp, ")");
} }
if (print_srcline) if (print_srcline)
map__fprintf_srcline(al->map, al->addr, "\n ", stdout); printed += map__fprintf_srcline(al->map, al->addr, "\n ", fp);
} }
return printed;
} }
int perf_session__cpu_bitmap(struct perf_session *session, int perf_session__cpu_bitmap(struct perf_session *session,

View File

@ -42,6 +42,7 @@ struct perf_session {
#define PRINT_IP_OPT_SYMOFFSET (1<<3) #define PRINT_IP_OPT_SYMOFFSET (1<<3)
#define PRINT_IP_OPT_ONELINE (1<<4) #define PRINT_IP_OPT_ONELINE (1<<4)
#define PRINT_IP_OPT_SRCLINE (1<<5) #define PRINT_IP_OPT_SRCLINE (1<<5)
#define PRINT_IP_OPT_UNKNOWN_AS_ADDR (1<<6)
struct perf_tool; struct perf_tool;
@ -104,9 +105,10 @@ size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp);
struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,
unsigned int type); unsigned int type);
void perf_evsel__print_ip(struct perf_evsel *evsel, struct perf_sample *sample, int perf_evsel__fprintf_sym(struct perf_evsel *evsel, struct perf_sample *sample,
struct addr_location *al, struct addr_location *al, int left_alignment,
unsigned int print_opts, unsigned int stack_depth); unsigned int print_opts, unsigned int stack_depth,
FILE *fp);
int perf_session__cpu_bitmap(struct perf_session *session, int perf_session__cpu_bitmap(struct perf_session *session,
const char *cpu_list, unsigned long *cpu_bitmap); const char *cpu_list, unsigned long *cpu_bitmap);

View File

@ -264,8 +264,9 @@ size_t symbol__fprintf(struct symbol *sym, FILE *fp)
sym->name); sym->name);
} }
size_t symbol__fprintf_symname_offs(const struct symbol *sym, size_t __symbol__fprintf_symname_offs(const struct symbol *sym,
const struct addr_location *al, FILE *fp) const struct addr_location *al,
bool unknown_as_addr, FILE *fp)
{ {
unsigned long offset; unsigned long offset;
size_t length; size_t length;
@ -280,13 +281,29 @@ size_t symbol__fprintf_symname_offs(const struct symbol *sym,
length += fprintf(fp, "+0x%lx", offset); length += fprintf(fp, "+0x%lx", offset);
} }
return length; return length;
} else } else if (al && unknown_as_addr)
return fprintf(fp, "[%#" PRIx64 "]", al->addr);
else
return fprintf(fp, "[unknown]"); 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) size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp)
{ {
return symbol__fprintf_symname_offs(sym, NULL, fp); return __symbol__fprintf_symname_offs(sym, NULL, false, fp);
} }
void symbols__delete(struct rb_root *symbols) void symbols__delete(struct rb_root *symbols)

View File

@ -262,8 +262,14 @@ int symbol__init(struct perf_env *env);
void symbol__exit(void); void symbol__exit(void);
void symbol__elf_init(void); void symbol__elf_init(void);
struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name); struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name);
size_t __symbol__fprintf_symname_offs(const struct symbol *sym,
const struct addr_location *al,
bool unknown_as_addr, FILE *fp);
size_t symbol__fprintf_symname_offs(const struct symbol *sym, size_t symbol__fprintf_symname_offs(const struct symbol *sym,
const struct addr_location *al, FILE *fp); const struct addr_location *al, FILE *fp);
size_t __symbol__fprintf_symname(const struct symbol *sym,
const struct addr_location *al,
bool unknown_as_addr, FILE *fp);
size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp); size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp);
size_t symbol__fprintf(struct symbol *sym, FILE *fp); size_t symbol__fprintf(struct symbol *sym, FILE *fp);
bool symbol_type__is_a(char symbol_type, enum map_type map_type); bool symbol_type__is_a(char symbol_type, enum map_type map_type);

View File

@ -94,7 +94,7 @@ struct thread_map *thread_map__new_by_uid(uid_t uid)
DIR *proc; DIR *proc;
int max_threads = 32, items, i; int max_threads = 32, items, i;
char path[256]; char path[256];
struct dirent dirent, *next, **namelist = NULL; struct dirent *dirent, **namelist = NULL;
struct thread_map *threads = thread_map__alloc(max_threads); struct thread_map *threads = thread_map__alloc(max_threads);
if (threads == NULL) if (threads == NULL)
@ -107,16 +107,16 @@ struct thread_map *thread_map__new_by_uid(uid_t uid)
threads->nr = 0; threads->nr = 0;
atomic_set(&threads->refcnt, 1); atomic_set(&threads->refcnt, 1);
while (!readdir_r(proc, &dirent, &next) && next) { while ((dirent = readdir(proc)) != NULL) {
char *end; char *end;
bool grow = false; bool grow = false;
struct stat st; struct stat st;
pid_t pid = strtol(dirent.d_name, &end, 10); pid_t pid = strtol(dirent->d_name, &end, 10);
if (*end) /* only interested in proper numerical dirents */ if (*end) /* only interested in proper numerical dirents */
continue; continue;
snprintf(path, sizeof(path), "/proc/%s", dirent.d_name); snprintf(path, sizeof(path), "/proc/%s", dirent->d_name);
if (stat(path, &st) != 0) if (stat(path, &st) != 0)
continue; continue;