Commit aa30a2e0 authored by Ingo Molnar's avatar Ingo Molnar

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

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

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

  * Convert callchain children list to rbtree, greatly reducing the time
    taken for callchain processing, from Namhyung Kim.

  * Add --max-stack option to limit callchain stack scan in 'top' and 'report',
    improving callchain processing when reducing the stack depth is an option,
    from Waiman Long.

  * Compare dso's also when comparing symbols, to avoid grouping together
    symbols with the same name but on different DSOs, fix from Namhyung Kim.

  * 'perf trace' now can can use a 'perf probe' wannabe tracepoint to hook into
    the userspace -> kernel pathname copy so that it can map fds to pathnames
    without reading /proc/pid/fd/ symlinks. From Arnaldo Carvalho de Melo.

  * 'perf trace' now emits hints as to why tracing is not possible, helping the
    user to setup the system to allow tracing in the desired permission
    granularity, telling if the problem is due to debugfs not being mounted or
    with not enough permission for !root, /proc/sys/kernel/perf_event_paranoit
    value, etc. From Arnaldo Carvalho de Melo.

  * Add missing 'mmap2' in evsel debug print, from Adrian Hunter.

  * Add missing decrement in id sample parsing, not a fix per se, just to
    avoid a problem whem somebody adds another field, from Adrian Hunter.

  * Improve write_output error message in 'perf record', from Adrian Hunter.

  * Add missing sample flush for piped events, fix from Adrian Hunter.

  * Add missing members to perf_event__attr_swap(), fix from Adrian Hunter.

  * Assorted fixes for 32-bit build, from Adrian Hunter

  * Print addr by default for BTS in 'perf script', from Adrian Juntmer

  * Separating data file properties from session, code reorganization from
    Jiri Olsa.

  * Show error in 'perf list' if tracepoints not available, from Pekka Enberg.
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents 9536c8d2 5dbb6e81
...@@ -141,6 +141,14 @@ OPTIONS ...@@ -141,6 +141,14 @@ OPTIONS
Default: fractal,0.5,callee,function. Default: fractal,0.5,callee,function.
--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.
Default: 127
-G:: -G::
--inverted:: --inverted::
alias for inverted caller based call graph. alias for inverted caller based call graph.
......
...@@ -158,6 +158,14 @@ Default is to monitor all CPUS. ...@@ -158,6 +158,14 @@ Default is to monitor all CPUS.
Default: fractal,0.5,callee. Default: fractal,0.5,callee.
--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.
Default: 127
--ignore-callees=<regex>:: --ignore-callees=<regex>::
Ignore callees of the function(s) matching the given regex. Ignore callees of the function(s) matching the given regex.
This has the effect of collecting the callers of each such This has the effect of collecting the callers of each such
......
...@@ -97,6 +97,10 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs. ...@@ -97,6 +97,10 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs.
Show a summary of syscalls by thread with min, max, and average times (in Show a summary of syscalls by thread with min, max, and average times (in
msec) and relative stddev. msec) and relative stddev.
--tool_stats::
Show tool stats such as number of times fd->pathname was discovered thru
hooking the open syscall return + vfs_getname or via reading /proc/pid/fd, etc.
SEE ALSO SEE ALSO
-------- --------
linkperf:perf-record[1], linkperf:perf-script[1] linkperf:perf-record[1], linkperf:perf-script[1]
...@@ -365,6 +365,7 @@ LIB_OBJS += $(OUTPUT)util/vdso.o ...@@ -365,6 +365,7 @@ LIB_OBJS += $(OUTPUT)util/vdso.o
LIB_OBJS += $(OUTPUT)util/stat.o LIB_OBJS += $(OUTPUT)util/stat.o
LIB_OBJS += $(OUTPUT)util/record.o LIB_OBJS += $(OUTPUT)util/record.o
LIB_OBJS += $(OUTPUT)util/srcline.o LIB_OBJS += $(OUTPUT)util/srcline.o
LIB_OBJS += $(OUTPUT)util/data.o
LIB_OBJS += $(OUTPUT)ui/setup.o LIB_OBJS += $(OUTPUT)ui/setup.o
LIB_OBJS += $(OUTPUT)ui/helpline.o LIB_OBJS += $(OUTPUT)ui/helpline.o
......
...@@ -1120,7 +1120,7 @@ static void *worker_thread(void *__tdata) ...@@ -1120,7 +1120,7 @@ static void *worker_thread(void *__tdata)
/* Check whether our max runtime timed out: */ /* Check whether our max runtime timed out: */
if (g->p.nr_secs) { if (g->p.nr_secs) {
timersub(&stop, &start0, &diff); timersub(&stop, &start0, &diff);
if (diff.tv_sec >= g->p.nr_secs) { if ((u32)diff.tv_sec >= g->p.nr_secs) {
g->stop_work = true; g->stop_work = true;
break; break;
} }
...@@ -1167,7 +1167,7 @@ static void *worker_thread(void *__tdata) ...@@ -1167,7 +1167,7 @@ static void *worker_thread(void *__tdata)
runtime_ns_max += diff.tv_usec * 1000; runtime_ns_max += diff.tv_usec * 1000;
if (details >= 0) { if (details >= 0) {
printf(" #%2d / %2d: %14.2lf nsecs/op [val: %016lx]\n", printf(" #%2d / %2d: %14.2lf nsecs/op [val: %016"PRIx64"]\n",
process_nr, thread_nr, runtime_ns_max / bytes_done, val); process_nr, thread_nr, runtime_ns_max / bytes_done, val);
} }
fflush(stdout); fflush(stdout);
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "util/hist.h" #include "util/hist.h"
#include "util/session.h" #include "util/session.h"
#include "util/tool.h" #include "util/tool.h"
#include "util/data.h"
#include "arch/common.h" #include "arch/common.h"
#include <dlfcn.h> #include <dlfcn.h>
...@@ -199,9 +200,13 @@ static int __cmd_annotate(struct perf_annotate *ann) ...@@ -199,9 +200,13 @@ static int __cmd_annotate(struct perf_annotate *ann)
struct perf_session *session; struct perf_session *session;
struct perf_evsel *pos; struct perf_evsel *pos;
u64 total_nr_samples; u64 total_nr_samples;
struct perf_data_file file = {
.path = input_name,
.mode = PERF_DATA_MODE_READ,
.force = ann->force,
};
session = perf_session__new(input_name, O_RDONLY, session = perf_session__new(&file, false, &ann->tool);
ann->force, false, &ann->tool);
if (session == NULL) if (session == NULL)
return -ENOMEM; return -ENOMEM;
...@@ -254,7 +259,7 @@ static int __cmd_annotate(struct perf_annotate *ann) ...@@ -254,7 +259,7 @@ static int __cmd_annotate(struct perf_annotate *ann)
} }
if (total_nr_samples == 0) { if (total_nr_samples == 0) {
ui__error("The %s file has no samples!\n", session->filename); ui__error("The %s file has no samples!\n", file.path);
goto out_delete; goto out_delete;
} }
......
...@@ -221,8 +221,12 @@ static bool dso__missing_buildid_cache(struct dso *dso, int parm __maybe_unused) ...@@ -221,8 +221,12 @@ static bool dso__missing_buildid_cache(struct dso *dso, int parm __maybe_unused)
static int build_id_cache__fprintf_missing(const char *filename, bool force, FILE *fp) static int build_id_cache__fprintf_missing(const char *filename, bool force, FILE *fp)
{ {
struct perf_session *session = perf_session__new(filename, O_RDONLY, struct perf_data_file file = {
force, false, NULL); .path = filename,
.mode = PERF_DATA_MODE_READ,
.force = force,
};
struct perf_session *session = perf_session__new(&file, false, NULL);
if (session == NULL) if (session == NULL)
return -1; return -1;
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "util/parse-options.h" #include "util/parse-options.h"
#include "util/session.h" #include "util/session.h"
#include "util/symbol.h" #include "util/symbol.h"
#include "util/data.h"
static int sysfs__fprintf_build_id(FILE *fp) static int sysfs__fprintf_build_id(FILE *fp)
{ {
...@@ -52,6 +53,11 @@ static bool dso__skip_buildid(struct dso *dso, int with_hits) ...@@ -52,6 +53,11 @@ static bool dso__skip_buildid(struct dso *dso, int with_hits)
static int perf_session__list_build_ids(bool force, bool with_hits) static int perf_session__list_build_ids(bool force, bool with_hits)
{ {
struct perf_session *session; struct perf_session *session;
struct perf_data_file file = {
.path = input_name,
.mode = PERF_DATA_MODE_READ,
.force = force,
};
symbol__elf_init(); symbol__elf_init();
/* /*
...@@ -60,15 +66,14 @@ static int perf_session__list_build_ids(bool force, bool with_hits) ...@@ -60,15 +66,14 @@ static int perf_session__list_build_ids(bool force, bool with_hits)
if (filename__fprintf_build_id(input_name, stdout)) if (filename__fprintf_build_id(input_name, stdout))
goto out; goto out;
session = perf_session__new(input_name, O_RDONLY, force, false, session = perf_session__new(&file, false, &build_id__mark_dso_hit_ops);
&build_id__mark_dso_hit_ops);
if (session == NULL) if (session == NULL)
return -1; return -1;
/* /*
* in pipe-mode, the only way to get the buildids is to parse * in pipe-mode, the only way to get the buildids is to parse
* the record stream. Buildids are stored as RECORD_HEADER_BUILD_ID * the record stream. Buildids are stored as RECORD_HEADER_BUILD_ID
*/ */
if (with_hits || session->fd_pipe) if (with_hits || perf_data_file__is_pipe(&file))
perf_session__process_events(session, &build_id__mark_dso_hit_ops); perf_session__process_events(session, &build_id__mark_dso_hit_ops);
perf_session__fprintf_dsos_buildid(session, stdout, dso__skip_buildid, with_hits); perf_session__fprintf_dsos_buildid(session, stdout, dso__skip_buildid, with_hits);
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "util/sort.h" #include "util/sort.h"
#include "util/symbol.h" #include "util/symbol.h"
#include "util/util.h" #include "util/util.h"
#include "util/data.h"
#include <stdlib.h> #include <stdlib.h>
#include <math.h> #include <math.h>
...@@ -42,7 +43,7 @@ struct diff_hpp_fmt { ...@@ -42,7 +43,7 @@ struct diff_hpp_fmt {
struct data__file { struct data__file {
struct perf_session *session; struct perf_session *session;
const char *file; struct perf_data_file file;
int idx; int idx;
struct hists *hists; struct hists *hists;
struct diff_hpp_fmt fmt[PERF_HPP_DIFF__MAX_INDEX]; struct diff_hpp_fmt fmt[PERF_HPP_DIFF__MAX_INDEX];
...@@ -601,7 +602,7 @@ static void data__fprintf(void) ...@@ -601,7 +602,7 @@ static void data__fprintf(void)
data__for_each_file(i, d) data__for_each_file(i, d)
fprintf(stdout, "# [%d] %s %s\n", fprintf(stdout, "# [%d] %s %s\n",
d->idx, d->file, d->idx, d->file.path,
!d->idx ? "(Baseline)" : ""); !d->idx ? "(Baseline)" : "");
fprintf(stdout, "#\n"); fprintf(stdout, "#\n");
...@@ -663,17 +664,16 @@ static int __cmd_diff(void) ...@@ -663,17 +664,16 @@ static int __cmd_diff(void)
int ret = -EINVAL, i; int ret = -EINVAL, i;
data__for_each_file(i, d) { data__for_each_file(i, d) {
d->session = perf_session__new(d->file, O_RDONLY, force, d->session = perf_session__new(&d->file, false, &tool);
false, &tool);
if (!d->session) { if (!d->session) {
pr_err("Failed to open %s\n", d->file); pr_err("Failed to open %s\n", d->file.path);
ret = -ENOMEM; ret = -ENOMEM;
goto out_delete; goto out_delete;
} }
ret = perf_session__process_events(d->session, &tool); ret = perf_session__process_events(d->session, &tool);
if (ret) { if (ret) {
pr_err("Failed to process %s\n", d->file); pr_err("Failed to process %s\n", d->file.path);
goto out_delete; goto out_delete;
} }
...@@ -1016,7 +1016,12 @@ static int data_init(int argc, const char **argv) ...@@ -1016,7 +1016,12 @@ static int data_init(int argc, const char **argv)
return -ENOMEM; return -ENOMEM;
data__for_each_file(i, d) { data__for_each_file(i, d) {
d->file = use_default ? defaults[i] : argv[i]; struct perf_data_file *file = &d->file;
file->path = use_default ? defaults[i] : argv[i];
file->mode = PERF_DATA_MODE_READ,
file->force = force,
d->idx = i; d->idx = i;
} }
......
...@@ -14,13 +14,18 @@ ...@@ -14,13 +14,18 @@
#include "util/parse-events.h" #include "util/parse-events.h"
#include "util/parse-options.h" #include "util/parse-options.h"
#include "util/session.h" #include "util/session.h"
#include "util/data.h"
static int __cmd_evlist(const char *file_name, struct perf_attr_details *details) static int __cmd_evlist(const char *file_name, struct perf_attr_details *details)
{ {
struct perf_session *session; struct perf_session *session;
struct perf_evsel *pos; struct perf_evsel *pos;
struct perf_data_file file = {
.path = file_name,
.mode = PERF_DATA_MODE_READ,
};
session = perf_session__new(file_name, O_RDONLY, 0, false, NULL); session = perf_session__new(&file, 0, NULL);
if (session == NULL) if (session == NULL)
return -ENOMEM; return -ENOMEM;
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "util/tool.h" #include "util/tool.h"
#include "util/debug.h" #include "util/debug.h"
#include "util/build-id.h" #include "util/build-id.h"
#include "util/data.h"
#include "util/parse-options.h" #include "util/parse-options.h"
...@@ -345,6 +346,10 @@ static int __cmd_inject(struct perf_inject *inject) ...@@ -345,6 +346,10 @@ static int __cmd_inject(struct perf_inject *inject)
{ {
struct perf_session *session; struct perf_session *session;
int ret = -EINVAL; int ret = -EINVAL;
struct perf_data_file file = {
.path = inject->input_name,
.mode = PERF_DATA_MODE_READ,
};
signal(SIGINT, sig_handler); signal(SIGINT, sig_handler);
...@@ -355,7 +360,7 @@ static int __cmd_inject(struct perf_inject *inject) ...@@ -355,7 +360,7 @@ static int __cmd_inject(struct perf_inject *inject)
inject->tool.tracing_data = perf_event__repipe_tracing_data; inject->tool.tracing_data = perf_event__repipe_tracing_data;
} }
session = perf_session__new(inject->input_name, O_RDONLY, false, true, &inject->tool); session = perf_session__new(&file, true, &inject->tool);
if (session == NULL) if (session == NULL)
return -ENOMEM; return -ENOMEM;
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "util/parse-options.h" #include "util/parse-options.h"
#include "util/trace-event.h" #include "util/trace-event.h"
#include "util/data.h"
#include "util/debug.h" #include "util/debug.h"
...@@ -486,8 +487,12 @@ static int __cmd_kmem(void) ...@@ -486,8 +487,12 @@ static int __cmd_kmem(void)
{ "kmem:kfree", perf_evsel__process_free_event, }, { "kmem:kfree", perf_evsel__process_free_event, },
{ "kmem:kmem_cache_free", perf_evsel__process_free_event, }, { "kmem:kmem_cache_free", perf_evsel__process_free_event, },
}; };
struct perf_data_file file = {
.path = input_name,
.mode = PERF_DATA_MODE_READ,
};
session = perf_session__new(input_name, O_RDONLY, 0, false, &perf_kmem); session = perf_session__new(&file, false, &perf_kmem);
if (session == NULL) if (session == NULL)
return -ENOMEM; return -ENOMEM;
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "util/tool.h" #include "util/tool.h"
#include "util/stat.h" #include "util/stat.h"
#include "util/top.h" #include "util/top.h"
#include "util/data.h"
#include <sys/prctl.h> #include <sys/prctl.h>
#include <sys/timerfd.h> #include <sys/timerfd.h>
...@@ -1215,10 +1216,13 @@ static int read_events(struct perf_kvm_stat *kvm) ...@@ -1215,10 +1216,13 @@ static int read_events(struct perf_kvm_stat *kvm)
.comm = perf_event__process_comm, .comm = perf_event__process_comm,
.ordered_samples = true, .ordered_samples = true,
}; };
struct perf_data_file file = {
.path = input_name,
.mode = PERF_DATA_MODE_READ,
};
kvm->tool = eops; kvm->tool = eops;
kvm->session = perf_session__new(kvm->file_name, O_RDONLY, 0, false, kvm->session = perf_session__new(&file, false, &kvm->tool);
&kvm->tool);
if (!kvm->session) { if (!kvm->session) {
pr_err("Initializing perf session failed\n"); pr_err("Initializing perf session failed\n");
return -EINVAL; return -EINVAL;
...@@ -1450,6 +1454,9 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, ...@@ -1450,6 +1454,9 @@ static int kvm_events_live(struct perf_kvm_stat *kvm,
"perf kvm stat live [<options>]", "perf kvm stat live [<options>]",
NULL NULL
}; };
struct perf_data_file file = {
.mode = PERF_DATA_MODE_WRITE,
};
/* event handling */ /* event handling */
...@@ -1514,7 +1521,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, ...@@ -1514,7 +1521,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm,
/* /*
* perf session * perf session
*/ */
kvm->session = perf_session__new(NULL, O_WRONLY, false, false, &kvm->tool); kvm->session = perf_session__new(&file, false, &kvm->tool);
if (kvm->session == NULL) { if (kvm->session == NULL) {
err = -ENOMEM; err = -ENOMEM;
goto out; goto out;
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "util/debug.h" #include "util/debug.h"
#include "util/session.h" #include "util/session.h"
#include "util/tool.h" #include "util/tool.h"
#include "util/data.h"
#include <sys/types.h> #include <sys/types.h>
#include <sys/prctl.h> #include <sys/prctl.h>
...@@ -853,8 +854,12 @@ static int __cmd_report(bool display_info) ...@@ -853,8 +854,12 @@ static int __cmd_report(bool display_info)
.comm = perf_event__process_comm, .comm = perf_event__process_comm,
.ordered_samples = true, .ordered_samples = true,
}; };
struct perf_data_file file = {
.path = input_name,
.mode = PERF_DATA_MODE_READ,
};
session = perf_session__new(input_name, O_RDONLY, 0, false, &eops); session = perf_session__new(&file, false, &eops);
if (!session) { if (!session) {
pr_err("Initializing perf session failed\n"); pr_err("Initializing perf session failed\n");
return -ENOMEM; return -ENOMEM;
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "util/trace-event.h" #include "util/trace-event.h"
#include "util/tool.h" #include "util/tool.h"
#include "util/session.h" #include "util/session.h"
#include "util/data.h"
#define MEM_OPERATION_LOAD "load" #define MEM_OPERATION_LOAD "load"
#define MEM_OPERATION_STORE "store" #define MEM_OPERATION_STORE "store"
...@@ -119,10 +120,14 @@ static int process_sample_event(struct perf_tool *tool, ...@@ -119,10 +120,14 @@ static int process_sample_event(struct perf_tool *tool,
static int report_raw_events(struct perf_mem *mem) static int report_raw_events(struct perf_mem *mem)
{ {
struct perf_data_file file = {
.path = input_name,
.mode = PERF_DATA_MODE_READ,
};
int err = -EINVAL; int err = -EINVAL;
int ret; int ret;
struct perf_session *session = perf_session__new(input_name, O_RDONLY, struct perf_session *session = perf_session__new(&file, false,
0, false, &mem->tool); &mem->tool);
if (session == NULL) if (session == NULL)
return -ENOMEM; return -ENOMEM;
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include "util/symbol.h" #include "util/symbol.h"
#include "util/cpumap.h" #include "util/cpumap.h"
#include "util/thread_map.h" #include "util/thread_map.h"
#include "util/data.h"
#include <unistd.h> #include <unistd.h>
#include <sched.h> #include <sched.h>
...@@ -65,11 +66,10 @@ struct perf_record { ...@@ -65,11 +66,10 @@ struct perf_record {
struct perf_tool tool; struct perf_tool tool;
struct perf_record_opts opts; struct perf_record_opts opts;
u64 bytes_written; u64 bytes_written;
const char *output_name; struct perf_data_file file;
struct perf_evlist *evlist; struct perf_evlist *evlist;
struct perf_session *session; struct perf_session *session;
const char *progname; const char *progname;
int output;
int realtime_prio; int realtime_prio;
bool no_buildid; bool no_buildid;
bool no_buildid_cache; bool no_buildid_cache;
...@@ -84,11 +84,13 @@ static void advance_output(struct perf_record *rec, size_t size) ...@@ -84,11 +84,13 @@ static void advance_output(struct perf_record *rec, size_t size)
static int write_output(struct perf_record *rec, void *buf, size_t size) static int write_output(struct perf_record *rec, void *buf, size_t size)
{ {
struct perf_data_file *file = &rec->file;
while (size) { while (size) {
int ret = write(rec->output, buf, size); int ret = write(file->fd, buf, size);
if (ret < 0) { if (ret < 0) {
pr_err("failed to write\n"); pr_err("failed to write perf data, error: %m\n");
return -1; return -1;
} }
...@@ -248,13 +250,14 @@ static int perf_record__open(struct perf_record *rec) ...@@ -248,13 +250,14 @@ static int perf_record__open(struct perf_record *rec)
static int process_buildids(struct perf_record *rec) static int process_buildids(struct perf_record *rec)
{ {
u64 size = lseek(rec->output, 0, SEEK_CUR); struct perf_data_file *file = &rec->file;
struct perf_session *session = rec->session;
u64 size = lseek(file->fd, 0, SEEK_CUR);
if (size == 0) if (size == 0)
return 0; return 0;
rec->session->fd = rec->output; return __perf_session__process_events(session, rec->post_processing_offset,
return __perf_session__process_events(rec->session, rec->post_processing_offset,
size - rec->post_processing_offset, size - rec->post_processing_offset,
size, &build_id__mark_dso_hit_ops); size, &build_id__mark_dso_hit_ops);
} }
...@@ -262,17 +265,18 @@ static int process_buildids(struct perf_record *rec) ...@@ -262,17 +265,18 @@ static int process_buildids(struct perf_record *rec)
static void perf_record__exit(int status, void *arg) static void perf_record__exit(int status, void *arg)
{ {
struct perf_record *rec = arg; struct perf_record *rec = arg;
struct perf_data_file *file = &rec->file;
if (status != 0) if (status != 0)
return; return;
if (!rec->opts.pipe_output) { if (!file->is_pipe) {
rec->session->header.data_size += rec->bytes_written; rec->session->header.data_size += rec->bytes_written;
if (!rec->no_buildid) if (!rec->no_buildid)
process_buildids(rec); process_buildids(rec);
perf_session__write_header(rec->session, rec->evlist, perf_session__write_header(rec->session, rec->evlist,
rec->output, true); file->fd, true);
perf_session__delete(rec->session); perf_session__delete(rec->session);
perf_evlist__delete(rec->evlist); perf_evlist__delete(rec->evlist);
symbol__exit(); symbol__exit();
...@@ -340,16 +344,14 @@ static int perf_record__mmap_read_all(struct perf_record *rec) ...@@ -340,16 +344,14 @@ static int perf_record__mmap_read_all(struct perf_record *rec)
static int __cmd_record(struct perf_record *rec, int argc, const char **argv) static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
{ {
struct stat st; int err, feat;
int flags;
int err, output, feat;
unsigned long waking = 0; unsigned long waking = 0;
const bool forks = argc > 0; const bool forks = argc > 0;
struct machine *machine; struct machine *machine;
struct perf_tool *tool = &rec->tool; struct perf_tool *tool = &rec->tool;
struct perf_record_opts *opts = &rec->opts; struct perf_record_opts *opts = &rec->opts;
struct perf_evlist *evsel_list = rec->evlist; struct perf_evlist *evsel_list = rec->evlist;
const char *output_name = rec->output_name; struct perf_data_file *file = &rec->file;
struct perf_session *session; struct perf_session *session;
bool disabled = false; bool disabled = false;
...@@ -361,39 +363,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) ...@@ -361,39 +363,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
signal(SIGUSR1, sig_handler); signal(SIGUSR1, sig_handler);
signal(SIGTERM, sig_handler); signal(SIGTERM, sig_handler);
if (!output_name) { session = perf_session__new(file, false, NULL);
if (!fstat(STDOUT_FILENO, &st) && S_ISFIFO(st.st_mode))
opts->pipe_output = true;
else
rec->output_name = output_name = "perf.data";
}
if (output_name) {
if (!strcmp(output_name, "-"))
opts->pipe_output = true;
else if (!stat(output_name, &st) && st.st_size) {
char oldname[PATH_MAX];
snprintf(oldname, sizeof(oldname), "%s.old",
output_name);
unlink(oldname);
rename(output_name, oldname);
}
}
flags = O_CREAT|O_RDWR|O_TRUNC;
if (opts->pipe_output)
output = STDOUT_FILENO;
else
output = open(output_name, flags, S_IRUSR | S_IWUSR);
if (output < 0) {
perror("failed to create output file");
return -1;
}
rec->output = output;
session = perf_session__new(output_name, O_WRONLY,
true, false, NULL);
if (session == NULL) { if (session == NULL) {
pr_err("Not enough memory for reading perf file header\n"); pr_err("Not enough memory for reading perf file header\n");
return -1; return -1;
...@@ -415,7 +385,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) ...@@ -415,7 +385,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
if (forks) { if (forks) {
err = perf_evlist__prepare_workload(evsel_list, &opts->target, err = perf_evlist__prepare_workload(evsel_list, &opts->target,
argv, opts->pipe_output, argv, file->is_pipe,
true); true);
if (err < 0) { if (err < 0) {
pr_err("Couldn't run the workload!\n"); pr_err("Couldn't run the workload!\n");
...@@ -436,13 +406,13 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) ...@@ -436,13 +406,13 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
*/ */
on_exit(perf_record__exit, rec); on_exit(perf_record__exit, rec);
if (opts->pipe_output) { if (file->is_pipe) {
err = perf_header__write_pipe(output); err = perf_header__write_pipe(file->fd);
if (err < 0) if (err < 0)
goto out_delete_session; goto out_delete_session;
} else { } else {
err = perf_session__write_header(session, evsel_list, err = perf_session__write_header(session, evsel_list,
output, false); file->fd, false);
if (err < 0) if (err < 0)
goto out_delete_session; goto out_delete_session;
} }
...@@ -455,11 +425,11 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) ...@@ -455,11 +425,11 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
goto out_delete_session; goto out_delete_session;
} }
rec->post_processing_offset = lseek(output, 0, SEEK_CUR); rec->post_processing_offset = lseek(file->fd, 0, SEEK_CUR);
machine = &session->machines.host; machine = &session->machines.host;
if (opts->pipe_output) { if (file->is_pipe) {
err = perf_event__synthesize_attrs(tool, session, err = perf_event__synthesize_attrs(tool, session,
process_synthesized_event); process_synthesized_event);
if (err < 0) { if (err < 0) {
...@@ -476,7 +446,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) ...@@ -476,7 +446,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
* return this more properly and also * return this more properly and also
* propagate errors that now are calling die() * propagate errors that now are calling die()
*/ */
err = perf_event__synthesize_tracing_data(tool, output, evsel_list, err = perf_event__synthesize_tracing_data(tool, file->fd, evsel_list,
process_synthesized_event); process_synthesized_event);
if (err <= 0) { if (err <= 0) {
pr_err("Couldn't record tracing data.\n"); pr_err("Couldn't record tracing data.\n");
...@@ -583,7 +553,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) ...@@ -583,7 +553,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
fprintf(stderr, fprintf(stderr,
"[ perf record: Captured and wrote %.3f MB %s (~%" PRIu64 " samples) ]\n", "[ perf record: Captured and wrote %.3f MB %s (~%" PRIu64 " samples) ]\n",
(double)rec->bytes_written / 1024.0 / 1024.0, (double)rec->bytes_written / 1024.0 / 1024.0,
output_name, file->path,
rec->bytes_written / 24); rec->bytes_written / 24);
return 0; return 0;
...@@ -845,7 +815,7 @@ const struct option record_options[] = { ...@@ -845,7 +815,7 @@ const struct option record_options[] = {
OPT_STRING('C', "cpu", &record.opts.target.cpu_list, "cpu", OPT_STRING('C', "cpu", &record.opts.target.cpu_list, "cpu",
"list of cpus to monitor"), "list of cpus to monitor"),
OPT_U64('c', "count", &record.opts.user_interval, "event period to sample"), OPT_U64('c', "count", &record.opts.user_interval, "event period to sample"),
OPT_STRING('o', "output", &record.output_name, "file", OPT_STRING('o', "output", &record.file.path, "file",
"output file name"), "output file name"),
OPT_BOOLEAN('i', "no-inherit", &record.opts.no_inherit, OPT_BOOLEAN('i', "no-inherit", &record.opts.no_inherit,
"child tasks do not inherit counters"), "child tasks do not inherit counters"),
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include "util/thread.h" #include "util/thread.h"
#include "util/sort.h" #include "util/sort.h"
#include "util/hist.h" #include "util/hist.h"
#include "util/data.h"
#include "arch/common.h" #include "arch/common.h"
#include <dlfcn.h> #include <dlfcn.h>
...@@ -48,6 +49,7 @@ struct perf_report { ...@@ -48,6 +49,7 @@ struct perf_report {
bool show_threads; bool show_threads;
bool inverted_callchain; bool inverted_callchain;
bool mem_mode; bool mem_mode;
int max_stack;
struct perf_read_values show_threads_values; struct perf_read_values show_threads_values;
const char *pretty_printing_style; const char *pretty_printing_style;
const char *cpu_list; const char *cpu_list;
...@@ -89,7 +91,8 @@ static int perf_report__add_mem_hist_entry(struct perf_tool *tool, ...@@ -89,7 +91,8 @@ static int perf_report__add_mem_hist_entry(struct perf_tool *tool,
if ((sort__has_parent || symbol_conf.use_callchain) && if ((sort__has_parent || symbol_conf.use_callchain) &&
sample->callchain) { sample->callchain) {
err = machine__resolve_callchain(machine, evsel, al->thread, err = machine__resolve_callchain(machine, evsel, al->thread,
sample, &parent, al); sample, &parent, al,
rep->max_stack);
if (err) if (err)
return err; return err;
} }
...@@ -180,7 +183,8 @@ static int perf_report__add_branch_hist_entry(struct perf_tool *tool, ...@@ -180,7 +183,8 @@ static int perf_report__add_branch_hist_entry(struct perf_tool *tool,
if ((sort__has_parent || symbol_conf.use_callchain) if ((sort__has_parent || symbol_conf.use_callchain)
&& sample->callchain) { && sample->callchain) {
err = machine__resolve_callchain(machine, evsel, al->thread, err = machine__resolve_callchain(machine, evsel, al->thread,
sample, &parent, al); sample, &parent, al,
rep->max_stack);
if (err) if (err)
return err; return err;
} }
...@@ -243,18 +247,21 @@ static int perf_report__add_branch_hist_entry(struct perf_tool *tool, ...@@ -243,18 +247,21 @@ static int perf_report__add_branch_hist_entry(struct perf_tool *tool,
return err; return err;
} }
static int perf_evsel__add_hist_entry(struct perf_evsel *evsel, static int perf_evsel__add_hist_entry(struct perf_tool *tool,
struct perf_evsel *evsel,
struct addr_location *al, struct addr_location *al,
struct perf_sample *sample, struct perf_sample *sample,
struct machine *machine) struct machine *machine)
{ {
struct perf_report *rep = container_of(tool, struct perf_report, tool);
struct symbol *parent = NULL; struct symbol *parent = NULL;
int err = 0; int err = 0;
struct hist_entry *he; struct hist_entry *he;
if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) { if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) {
err = machine__resolve_callchain(machine, evsel, al->thread, err = machine__resolve_callchain(machine, evsel, al->thread,
sample, &parent, al); sample, &parent, al,
rep->max_stack);
if (err) if (err)
return err; return err;
} }
...@@ -331,7 +338,8 @@ static int process_sample_event(struct perf_tool *tool, ...@@ -331,7 +338,8 @@ static int process_sample_event(struct perf_tool *tool,
if (al.map != NULL) if (al.map != NULL)
al.map->dso->hit = 1; al.map->dso->hit = 1;
ret = perf_evsel__add_hist_entry(evsel, &al, sample, machine); ret = perf_evsel__add_hist_entry(tool, evsel, &al, sample,
machine);
if (ret < 0) if (ret < 0)
pr_debug("problem incrementing symbol period, skipping event\n"); pr_debug("problem incrementing symbol period, skipping event\n");
} }
...@@ -367,8 +375,9 @@ static int perf_report__setup_sample_type(struct perf_report *rep) ...@@ -367,8 +375,9 @@ static int perf_report__setup_sample_type(struct perf_report *rep)
{ {
struct perf_session *self = rep->session; struct perf_session *self = rep->session;
u64 sample_type = perf_evlist__combined_sample_type(self->evlist); u64 sample_type = perf_evlist__combined_sample_type(self->evlist);
bool is_pipe = perf_data_file__is_pipe(self->file);
if (!self->fd_pipe && !(sample_type & PERF_SAMPLE_CALLCHAIN)) { if (!is_pipe && !(sample_type & PERF_SAMPLE_CALLCHAIN)) {
if (sort__has_parent) { if (sort__has_parent) {
ui__error("Selected --sort parent, but no " ui__error("Selected --sort parent, but no "
"callchain data. Did you call " "callchain data. Did you call "
...@@ -391,7 +400,7 @@ static int perf_report__setup_sample_type(struct perf_report *rep) ...@@ -391,7 +400,7 @@ static int perf_report__setup_sample_type(struct perf_report *rep)
} }
if (sort__mode == SORT_MODE__BRANCH) { if (sort__mode == SORT_MODE__BRANCH) {
if (!self->fd_pipe && if (!is_pipe &&
!(sample_type & PERF_SAMPLE_BRANCH_STACK)) { !(sample_type & PERF_SAMPLE_BRANCH_STACK)) {
ui__error("Selected -b but no branch data. " ui__error("Selected -b but no branch data. "
"Did you call perf record without -b?\n"); "Did you call perf record without -b?\n");
...@@ -487,6 +496,7 @@ static int __cmd_report(struct perf_report *rep) ...@@ -487,6 +496,7 @@ static int __cmd_report(struct perf_report *rep)
struct map *kernel_map; struct map *kernel_map;
struct kmap *kernel_kmap; struct kmap *kernel_kmap;
const char *help = "For a higher level overview, try: perf report --sort comm,dso"; const char *help = "For a higher level overview, try: perf report --sort comm,dso";
struct perf_data_file *file = session->file;
signal(SIGINT, sig_handler); signal(SIGINT, sig_handler);
...@@ -571,7 +581,7 @@ static int __cmd_report(struct perf_report *rep) ...@@ -571,7 +581,7 @@ static int __cmd_report(struct perf_report *rep)
return 0; return 0;
if (nr_samples == 0) { if (nr_samples == 0) {
ui__error("The %s file has no samples!\n", session->filename); ui__error("The %s file has no samples!\n", file->path);
return 0; return 0;
} }
...@@ -769,6 +779,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -769,6 +779,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
.ordered_samples = true, .ordered_samples = true,
.ordering_requires_timestamps = true, .ordering_requires_timestamps = true,
}, },
.max_stack = PERF_MAX_STACK_DEPTH,
.pretty_printing_style = "normal", .pretty_printing_style = "normal",
}; };
const struct option options[] = { const struct option options[] = {
...@@ -809,6 +820,10 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -809,6 +820,10 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
OPT_CALLBACK_DEFAULT('g', "call-graph", &report, "output_type,min_percent[,print_limit],call_order", OPT_CALLBACK_DEFAULT('g', "call-graph", &report, "output_type,min_percent[,print_limit],call_order",
"Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold, optional print limit, callchain order, key (function or address). " "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold, optional print limit, callchain order, key (function or address). "
"Default: fractal,0.5,callee,function", &parse_callchain_opt, callchain_default_opt), "Default: fractal,0.5,callee,function", &parse_callchain_opt, callchain_default_opt),
OPT_INTEGER(0, "max-stack", &report.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('G', "inverted", &report.inverted_callchain, OPT_BOOLEAN('G', "inverted", &report.inverted_callchain,
"alias for inverted call graph"), "alias for inverted call graph"),
OPT_CALLBACK(0, "ignore-callees", NULL, "regex", OPT_CALLBACK(0, "ignore-callees", NULL, "regex",
...@@ -857,6 +872,9 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -857,6 +872,9 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
"Don't show entries under that percent", parse_percent_limit), "Don't show entries under that percent", parse_percent_limit),
OPT_END() OPT_END()
}; };
struct perf_data_file file = {
.mode = PERF_DATA_MODE_READ,
};
perf_config(perf_report_config, &report); perf_config(perf_report_config, &report);
...@@ -886,9 +904,11 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -886,9 +904,11 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
perf_hpp__init(); perf_hpp__init();
} }
file.path = input_name;
file.force = report.force;
repeat: repeat:
session = perf_session__new(input_name, O_RDONLY, session = perf_session__new(&file, false, &report.tool);
report.force, false, &report.tool);
if (session == NULL) if (session == NULL)
return -ENOMEM; return -ENOMEM;
......
...@@ -1446,8 +1446,12 @@ static int perf_sched__read_events(struct perf_sched *sched, ...@@ -1446,8 +1446,12 @@ static int perf_sched__read_events(struct perf_sched *sched,
{ "sched:sched_migrate_task", process_sched_migrate_task_event, }, { "sched:sched_migrate_task", process_sched_migrate_task_event, },
}; };
struct perf_session *session; struct perf_session *session;
struct perf_data_file file = {
.path = input_name,
.mode = PERF_DATA_MODE_READ,
};
session = perf_session__new(input_name, O_RDONLY, 0, false, &sched->tool); session = perf_session__new(&file, false, &sched->tool);
if (session == NULL) { if (session == NULL) {
pr_debug("No Memory for session\n"); pr_debug("No Memory for session\n");
return -1; return -1;
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "util/evlist.h" #include "util/evlist.h"
#include "util/evsel.h" #include "util/evsel.h"
#include "util/sort.h" #include "util/sort.h"
#include "util/data.h"
#include <linux/bitmap.h> #include <linux/bitmap.h>
static char const *script_name; static char const *script_name;
...@@ -409,7 +410,9 @@ static void print_sample_bts(union perf_event *event, ...@@ -409,7 +410,9 @@ static void print_sample_bts(union perf_event *event,
printf(" => "); printf(" => ");
/* print branch_to information */ /* print branch_to information */
if (PRINT_FIELD(ADDR)) if (PRINT_FIELD(ADDR) ||
((evsel->attr.sample_type & PERF_SAMPLE_ADDR) &&
!output[attr->type].user_set))
print_sample_addr(event, sample, machine, thread, attr); print_sample_addr(event, sample, machine, thread, attr);
printf("\n"); printf("\n");
...@@ -1113,10 +1116,14 @@ int find_scripts(char **scripts_array, char **scripts_path_array) ...@@ -1113,10 +1116,14 @@ int find_scripts(char **scripts_array, char **scripts_path_array)
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;
struct perf_data_file file = {
.path = input_name,
.mode = PERF_DATA_MODE_READ,
};
char *temp; char *temp;
int i = 0; int i = 0;
session = perf_session__new(input_name, O_RDONLY, 0, false, NULL); session = perf_session__new(&file, false, NULL);
if (!session) if (!session)
return -1; return -1;
...@@ -1317,12 +1324,17 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -1317,12 +1324,17 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
"perf script [<options>] <top-script> [script-args]", "perf script [<options>] <top-script> [script-args]",
NULL NULL
}; };
struct perf_data_file file = {
.mode = PERF_DATA_MODE_READ,
};
setup_scripting(); setup_scripting();
argc = parse_options(argc, argv, options, script_usage, argc = parse_options(argc, argv, options, script_usage,
PARSE_OPT_STOP_AT_NON_OPTION); PARSE_OPT_STOP_AT_NON_OPTION);
file.path = input_name;
if (argc > 1 && !strncmp(argv[0], "rec", strlen("rec"))) { if (argc > 1 && !strncmp(argv[0], "rec", strlen("rec"))) {
rec_script_path = get_script_path(argv[1], RECORD_SUFFIX); rec_script_path = get_script_path(argv[1], RECORD_SUFFIX);
if (!rec_script_path) if (!rec_script_path)
...@@ -1486,8 +1498,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -1486,8 +1498,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
if (!script_name) if (!script_name)
setup_pager(); setup_pager();
session = perf_session__new(input_name, O_RDONLY, 0, false, session = perf_session__new(&file, false, &perf_script);
&perf_script);
if (session == NULL) if (session == NULL)
return -ENOMEM; return -ENOMEM;
...@@ -1514,7 +1525,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -1514,7 +1525,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
return -1; return -1;
} }
input = open(session->filename, O_RDONLY); /* input_name */ input = open(file.path, O_RDONLY); /* input_name */
if (input < 0) { if (input < 0) {
perror("failed to open file"); perror("failed to open file");
return -1; return -1;
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include "util/session.h" #include "util/session.h"
#include "util/svghelper.h" #include "util/svghelper.h"
#include "util/tool.h" #include "util/tool.h"
#include "util/data.h"
#define SUPPORT_OLD_POWER_EVENTS 1 #define SUPPORT_OLD_POWER_EVENTS 1
#define PWR_EVENT_EXIT -1 #define PWR_EVENT_EXIT -1
...@@ -990,8 +991,13 @@ static int __cmd_timechart(const char *output_name) ...@@ -990,8 +991,13 @@ static int __cmd_timechart(const char *output_name)
{ "power:power_frequency", process_sample_power_frequency }, { "power:power_frequency", process_sample_power_frequency },
#endif #endif
}; };
struct perf_session *session = perf_session__new(input_name, O_RDONLY, struct perf_data_file file = {
0, false, &perf_timechart); .path = input_name,
.mode = PERF_DATA_MODE_READ,
};
struct perf_session *session = perf_session__new(&file, false,
&perf_timechart);
int ret = -EINVAL; int ret = -EINVAL;
if (session == NULL) if (session == NULL)
......
...@@ -770,7 +770,8 @@ static void perf_event__process_sample(struct perf_tool *tool, ...@@ -770,7 +770,8 @@ static void perf_event__process_sample(struct perf_tool *tool,
sample->callchain) { sample->callchain) {
err = machine__resolve_callchain(machine, evsel, err = machine__resolve_callchain(machine, evsel,
al.thread, sample, al.thread, sample,
&parent, &al); &parent, &al,
top->max_stack);
if (err) if (err)
return; return;
} }
...@@ -929,11 +930,8 @@ static int __cmd_top(struct perf_top *top) ...@@ -929,11 +930,8 @@ static int __cmd_top(struct perf_top *top)
struct perf_record_opts *opts = &top->record_opts; struct perf_record_opts *opts = &top->record_opts;
pthread_t thread; pthread_t thread;
int ret; int ret;
/*
* FIXME: perf_session__new should allow passing a O_MMAP, so that all this top->session = perf_session__new(NULL, false, NULL);
* mmap reading, etc is encapsulated in it. Use O_WRONLY for now.
*/
top->session = perf_session__new(NULL, O_WRONLY, false, false, NULL);
if (top->session == NULL) if (top->session == NULL)
return -ENOMEM; return -ENOMEM;
...@@ -1050,10 +1048,11 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -1050,10 +1048,11 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
.user_freq = UINT_MAX, .user_freq = UINT_MAX,
.user_interval = ULLONG_MAX, .user_interval = ULLONG_MAX,
.freq = 4000, /* 4 KHz */ .freq = 4000, /* 4 KHz */
.target = { .target = {
.uses_mmap = true, .uses_mmap = true,
}, },
}, },
.max_stack = PERF_MAX_STACK_DEPTH,
.sym_pcnt_filter = 5, .sym_pcnt_filter = 5,
}; };
struct perf_record_opts *opts = &top.record_opts; struct perf_record_opts *opts = &top.record_opts;
...@@ -1112,6 +1111,9 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -1112,6 +1111,9 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
OPT_CALLBACK_DEFAULT('G', "call-graph", &top.record_opts, OPT_CALLBACK_DEFAULT('G', "call-graph", &top.record_opts,
"mode[,dump_size]", record_callchain_help, "mode[,dump_size]", record_callchain_help,
&parse_callchain_opt, "fp"), &parse_callchain_opt, "fp"),
OPT_INTEGER(0, "max-stack", &top.max_stack,
"Set the maximum stack depth when parsing the callchain. "
"Default: " __stringify(PERF_MAX_STACK_DEPTH)),
OPT_CALLBACK(0, "ignore-callees", NULL, "regex", OPT_CALLBACK(0, "ignore-callees", NULL, "regex",
"ignore callees of these functions in call graphs", "ignore callees of these functions in call graphs",
report_parse_ignore_callees_opt), report_parse_ignore_callees_opt),
......
...@@ -951,7 +951,10 @@ static struct thread_trace *thread__trace(struct thread *thread, FILE *fp) ...@@ -951,7 +951,10 @@ static struct thread_trace *thread__trace(struct thread *thread, FILE *fp)
struct trace { struct trace {
struct perf_tool tool; struct perf_tool tool;
int audit_machine; struct {
int machine;
int open_id;
} audit;
struct { struct {
int max; int max;
struct syscall *table; struct syscall *table;
...@@ -965,40 +968,24 @@ struct trace { ...@@ -965,40 +968,24 @@ struct trace {
struct strlist *ev_qualifier; struct strlist *ev_qualifier;
bool not_ev_qualifier; bool not_ev_qualifier;
bool live; bool live;
const char *last_vfs_getname;
struct intlist *tid_list; struct intlist *tid_list;
struct intlist *pid_list; struct intlist *pid_list;
bool sched; bool sched;
bool multiple_threads; bool multiple_threads;
bool summary; bool summary;
bool show_comm; bool show_comm;
bool show_tool_stats;
double duration_filter; double duration_filter;
double runtime_ms; double runtime_ms;
struct {
u64 vfs_getname, proc_getname;
} stats;
}; };
static int thread__read_fd_path(struct thread *thread, int fd) static int trace__set_fd_pathname(struct thread *thread, int fd, const char *pathname)
{ {
struct thread_trace *ttrace = thread->priv; struct thread_trace *ttrace = thread->priv;
char linkname[PATH_MAX], pathname[PATH_MAX];
struct stat st;
int ret;
if (thread->pid_ == thread->tid) {
scnprintf(linkname, sizeof(linkname),
"/proc/%d/fd/%d", thread->pid_, fd);
} else {
scnprintf(linkname, sizeof(linkname),
"/proc/%d/task/%d/fd/%d", thread->pid_, thread->tid, fd);
}
if (lstat(linkname, &st) < 0 || st.st_size + 1 > (off_t)sizeof(pathname))
return -1;
ret = readlink(linkname, pathname, sizeof(pathname));
if (ret < 0 || ret > st.st_size)
return -1;
pathname[ret] = '\0';
if (fd > ttrace->paths.max) { if (fd > ttrace->paths.max) {
char **npath = realloc(ttrace->paths.table, (fd + 1) * sizeof(char *)); char **npath = realloc(ttrace->paths.table, (fd + 1) * sizeof(char *));
...@@ -1022,7 +1009,34 @@ static int thread__read_fd_path(struct thread *thread, int fd) ...@@ -1022,7 +1009,34 @@ static int thread__read_fd_path(struct thread *thread, int fd)
return ttrace->paths.table[fd] != NULL ? 0 : -1; return ttrace->paths.table[fd] != NULL ? 0 : -1;
} }
static const char *thread__fd_path(struct thread *thread, int fd, bool live) static int thread__read_fd_path(struct thread *thread, int fd)
{
char linkname[PATH_MAX], pathname[PATH_MAX];
struct stat st;
int ret;
if (thread->pid_ == thread->tid) {
scnprintf(linkname, sizeof(linkname),
"/proc/%d/fd/%d", thread->pid_, fd);
} else {
scnprintf(linkname, sizeof(linkname),
"/proc/%d/task/%d/fd/%d", thread->pid_, thread->tid, fd);
}
if (lstat(linkname, &st) < 0 || st.st_size + 1 > (off_t)sizeof(pathname))
return -1;
ret = readlink(linkname, pathname, sizeof(pathname));
if (ret < 0 || ret > st.st_size)
return -1;
pathname[ret] = '\0';
return trace__set_fd_pathname(thread, fd, pathname);
}
static const char *thread__fd_path(struct thread *thread, int fd,
struct trace *trace)
{ {
struct thread_trace *ttrace = thread->priv; struct thread_trace *ttrace = thread->priv;
...@@ -1032,9 +1046,13 @@ static const char *thread__fd_path(struct thread *thread, int fd, bool live) ...@@ -1032,9 +1046,13 @@ static const char *thread__fd_path(struct thread *thread, int fd, bool live)
if (fd < 0) if (fd < 0)
return NULL; return NULL;
if ((fd > ttrace->paths.max || ttrace->paths.table[fd] == NULL) && if ((fd > ttrace->paths.max || ttrace->paths.table[fd] == NULL))
(!live || thread__read_fd_path(thread, fd))) if (!trace->live)
return NULL; return NULL;
++trace->stats.proc_getname;
if (thread__read_fd_path(thread, fd)) {
return NULL;
}
return ttrace->paths.table[fd]; return ttrace->paths.table[fd];
} }
...@@ -1044,7 +1062,7 @@ static size_t syscall_arg__scnprintf_fd(char *bf, size_t size, ...@@ -1044,7 +1062,7 @@ static size_t syscall_arg__scnprintf_fd(char *bf, size_t size,
{ {
int fd = arg->val; int fd = arg->val;
size_t printed = scnprintf(bf, size, "%d", fd); size_t printed = scnprintf(bf, size, "%d", fd);
const char *path = thread__fd_path(arg->thread, fd, arg->trace->live); const char *path = thread__fd_path(arg->thread, fd, arg->trace);
if (path) if (path)
printed += scnprintf(bf + printed, size - printed, "<%s>", path); printed += scnprintf(bf + printed, size - printed, "<%s>", path);
...@@ -1080,10 +1098,12 @@ static size_t trace__fprintf_tstamp(struct trace *trace, u64 tstamp, FILE *fp) ...@@ -1080,10 +1098,12 @@ static size_t trace__fprintf_tstamp(struct trace *trace, u64 tstamp, FILE *fp)
} }
static bool done = false; static bool done = false;
static bool interrupted = false;
static void sig_handler(int sig __maybe_unused) static void sig_handler(int sig)
{ {
done = true; done = true;
interrupted = sig == SIGINT;
} }
static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thread, static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thread,
...@@ -1181,7 +1201,7 @@ static int trace__read_syscall_info(struct trace *trace, int id) ...@@ -1181,7 +1201,7 @@ static int trace__read_syscall_info(struct trace *trace, int id)
{ {
char tp_name[128]; char tp_name[128];
struct syscall *sc; struct syscall *sc;
const char *name = audit_syscall_to_name(id, trace->audit_machine); const char *name = audit_syscall_to_name(id, trace->audit.machine);
if (name == NULL) if (name == NULL)
return -1; return -1;
...@@ -1445,6 +1465,12 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, ...@@ -1445,6 +1465,12 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
ret = perf_evsel__intval(evsel, sample, "ret"); ret = perf_evsel__intval(evsel, sample, "ret");
if (id == trace->audit.open_id && ret >= 0 && trace->last_vfs_getname) {
trace__set_fd_pathname(thread, ret, trace->last_vfs_getname);
trace->last_vfs_getname = NULL;
++trace->stats.vfs_getname;
}
ttrace = thread->priv; ttrace = thread->priv;
ttrace->exit_time = sample->time; ttrace->exit_time = sample->time;
...@@ -1489,6 +1515,13 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, ...@@ -1489,6 +1515,13 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel,
return 0; return 0;
} }
static int trace__vfs_getname(struct trace *trace, struct perf_evsel *evsel,
struct perf_sample *sample)
{
trace->last_vfs_getname = perf_evsel__rawptr(evsel, sample, "pathname");
return 0;
}
static int trace__sched_stat_runtime(struct trace *trace, struct perf_evsel *evsel, static int trace__sched_stat_runtime(struct trace *trace, struct perf_evsel *evsel,
struct perf_sample *sample) struct perf_sample *sample)
{ {
...@@ -1611,6 +1644,22 @@ static int trace__record(int argc, const char **argv) ...@@ -1611,6 +1644,22 @@ static int trace__record(int argc, const char **argv)
static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp); static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp);
static void perf_evlist__add_vfs_getname(struct perf_evlist *evlist)
{
struct perf_evsel *evsel = perf_evsel__newtp("probe", "vfs_getname",
evlist->nr_entries);
if (evsel == NULL)
return;
if (perf_evsel__field(evsel, "pathname") == NULL) {
perf_evsel__delete(evsel);
return;
}
evsel->handler.func = trace__vfs_getname;
perf_evlist__add(evlist, evsel);
}
static int trace__run(struct trace *trace, int argc, const char **argv) static int trace__run(struct trace *trace, int argc, const char **argv)
{ {
struct perf_evlist *evlist = perf_evlist__new(); struct perf_evlist *evlist = perf_evlist__new();
...@@ -1630,6 +1679,8 @@ static int trace__run(struct trace *trace, int argc, const char **argv) ...@@ -1630,6 +1679,8 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_exit", trace__sys_exit)) perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_exit", trace__sys_exit))
goto out_error_tp; goto out_error_tp;
perf_evlist__add_vfs_getname(evlist);
if (trace->sched && if (trace->sched &&
perf_evlist__add_newtp(evlist, "sched", "sched_stat_runtime", perf_evlist__add_newtp(evlist, "sched", "sched_stat_runtime",
trace__sched_stat_runtime)) trace__sched_stat_runtime))
...@@ -1662,10 +1713,8 @@ static int trace__run(struct trace *trace, int argc, const char **argv) ...@@ -1662,10 +1713,8 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
} }
err = perf_evlist__open(evlist); err = perf_evlist__open(evlist);
if (err < 0) { if (err < 0)
fprintf(trace->output, "Couldn't create the events: %s\n", strerror(errno)); goto out_error_open;
goto out_delete_maps;
}
err = perf_evlist__mmap(evlist, UINT_MAX, false); err = perf_evlist__mmap(evlist, UINT_MAX, false);
if (err < 0) { if (err < 0) {
...@@ -1722,26 +1771,35 @@ static int trace__run(struct trace *trace, int argc, const char **argv) ...@@ -1722,26 +1771,35 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
handler = evsel->handler.func; handler = evsel->handler.func;
handler(trace, evsel, &sample); handler(trace, evsel, &sample);
if (done) if (interrupted)
goto out_unmap_evlist; goto out_disable;
} }
} }
if (trace->nr_events == before) { if (trace->nr_events == before) {
if (done) int timeout = done ? 100 : -1;
goto out_unmap_evlist;
poll(evlist->pollfd, evlist->nr_fds, -1); if (poll(evlist->pollfd, evlist->nr_fds, timeout) > 0)
goto again;
} else {
goto again;
} }
if (done) out_disable:
perf_evlist__disable(evlist); perf_evlist__disable(evlist);
goto again; if (!err) {
if (trace->summary)
trace__fprintf_thread_summary(trace, trace->output);
out_unmap_evlist: if (trace->show_tool_stats) {
if (!err && trace->summary) fprintf(trace->output, "Stats:\n "
trace__fprintf_thread_summary(trace, trace->output); " vfs_getname : %" PRIu64 "\n"
" proc_getname: %" PRIu64 "\n",
trace->stats.vfs_getname,
trace->stats.proc_getname);
}
}
perf_evlist__munmap(evlist); perf_evlist__munmap(evlist);
out_close_evlist: out_close_evlist:
...@@ -1753,38 +1811,33 @@ static int trace__run(struct trace *trace, int argc, const char **argv) ...@@ -1753,38 +1811,33 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
out: out:
trace->live = false; trace->live = false;
return err; return err;
{
char errbuf[BUFSIZ];
out_error_tp: out_error_tp:
switch(errno) { perf_evlist__strerror_tp(evlist, errno, errbuf, sizeof(errbuf));
case ENOENT: goto out_error;
fputs("Error:\tUnable to find debugfs\n"
"Hint:\tWas your kernel was compiled with debugfs support?\n" out_error_open:
"Hint:\tIs the debugfs filesystem mounted?\n" perf_evlist__strerror_open(evlist, errno, errbuf, sizeof(errbuf));
"Hint:\tTry 'sudo mount -t debugfs nodev /sys/kernel/debug'\n",
trace->output); out_error:
break; fprintf(trace->output, "%s\n", errbuf);
case EACCES:
fprintf(trace->output,
"Error:\tNo permissions to read %s/tracing/events/raw_syscalls\n"
"Hint:\tTry 'sudo mount -o remount,mode=755 %s'\n",
debugfs_mountpoint, debugfs_mountpoint);
break;
default: {
char bf[256];
fprintf(trace->output, "Can't trace: %s\n",
strerror_r(errno, bf, sizeof(bf)));
}
break;
}
goto out_delete_evlist; goto out_delete_evlist;
} }
}
static int trace__replay(struct trace *trace) static int trace__replay(struct trace *trace)
{ {
const struct perf_evsel_str_handler handlers[] = { const struct perf_evsel_str_handler handlers[] = {
{ "raw_syscalls:sys_enter", trace__sys_enter, }, { "raw_syscalls:sys_enter", trace__sys_enter, },
{ "raw_syscalls:sys_exit", trace__sys_exit, }, { "raw_syscalls:sys_exit", trace__sys_exit, },
{ "probe:vfs_getname", trace__vfs_getname, },
};
struct perf_data_file file = {
.path = input_name,
.mode = PERF_DATA_MODE_READ,
}; };
struct perf_session *session; struct perf_session *session;
int err = -1; int err = -1;
...@@ -1807,8 +1860,7 @@ static int trace__replay(struct trace *trace) ...@@ -1807,8 +1860,7 @@ static int trace__replay(struct trace *trace)
if (symbol__init() < 0) if (symbol__init() < 0)
return -1; return -1;
session = perf_session__new(input_name, O_RDONLY, 0, false, session = perf_session__new(&file, false, &trace->tool);
&trace->tool);
if (session == NULL) if (session == NULL)
return -ENOMEM; return -ENOMEM;
...@@ -1992,7 +2044,10 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -1992,7 +2044,10 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
NULL NULL
}; };
struct trace trace = { struct trace trace = {
.audit_machine = audit_detect_machine(), .audit = {
.machine = audit_detect_machine(),
.open_id = audit_name_to_syscall("open", trace.audit.machine),
},
.syscalls = { .syscalls = {
. max = -1, . max = -1,
}, },
...@@ -2014,6 +2069,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -2014,6 +2069,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
const struct option trace_options[] = { const struct option trace_options[] = {
OPT_BOOLEAN(0, "comm", &trace.show_comm, OPT_BOOLEAN(0, "comm", &trace.show_comm,
"show the thread COMM next to its id"), "show the thread COMM next to its id"),
OPT_BOOLEAN(0, "tool_stats", &trace.show_tool_stats, "show tool stats"),
OPT_STRING('e', "expr", &ev_qualifier_str, "expr", OPT_STRING('e', "expr", &ev_qualifier_str, "expr",
"list of events to trace"), "list of events to trace"),
OPT_STRING('o', "output", &output_name, "file", "output file name"), OPT_STRING('o', "output", &output_name, "file", "output file name"),
......
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
static void exit_fn(int status, void *__data) static void exit_fn(int status, void *__data)
{ {
......
...@@ -220,7 +220,6 @@ struct perf_record_opts { ...@@ -220,7 +220,6 @@ struct perf_record_opts {
bool no_delay; bool no_delay;
bool no_inherit; bool no_inherit;
bool no_samples; bool no_samples;
bool pipe_output;
bool raw_samples; bool raw_samples;
bool sample_address; bool sample_address;
bool sample_weight; bool sample_weight;
......
...@@ -21,12 +21,6 @@ ...@@ -21,12 +21,6 @@
__thread struct callchain_cursor callchain_cursor; __thread struct callchain_cursor callchain_cursor;
#define chain_for_each_child(child, parent) \
list_for_each_entry(child, &parent->children, siblings)
#define chain_for_each_child_safe(child, next, parent) \
list_for_each_entry_safe(child, next, &parent->children, siblings)
static void static void
rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, rb_insert_callchain(struct rb_root *root, struct callchain_node *chain,
enum chain_mode mode) enum chain_mode mode)
...@@ -71,10 +65,16 @@ static void ...@@ -71,10 +65,16 @@ static void
__sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node, __sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node,
u64 min_hit) u64 min_hit)
{ {
struct rb_node *n;
struct callchain_node *child; struct callchain_node *child;
chain_for_each_child(child, node) n = rb_first(&node->rb_root_in);
while (n) {
child = rb_entry(n, struct callchain_node, rb_node_in);
n = rb_next(n);
__sort_chain_flat(rb_root, child, min_hit); __sort_chain_flat(rb_root, child, min_hit);
}
if (node->hit && node->hit >= min_hit) if (node->hit && node->hit >= min_hit)
rb_insert_callchain(rb_root, node, CHAIN_FLAT); rb_insert_callchain(rb_root, node, CHAIN_FLAT);
...@@ -94,11 +94,16 @@ sort_chain_flat(struct rb_root *rb_root, struct callchain_root *root, ...@@ -94,11 +94,16 @@ sort_chain_flat(struct rb_root *rb_root, struct callchain_root *root,
static void __sort_chain_graph_abs(struct callchain_node *node, static void __sort_chain_graph_abs(struct callchain_node *node,
u64 min_hit) u64 min_hit)
{ {
struct rb_node *n;
struct callchain_node *child; struct callchain_node *child;
node->rb_root = RB_ROOT; node->rb_root = RB_ROOT;
n = rb_first(&node->rb_root_in);
while (n) {
child = rb_entry(n, struct callchain_node, rb_node_in);
n = rb_next(n);
chain_for_each_child(child, node) {
__sort_chain_graph_abs(child, min_hit); __sort_chain_graph_abs(child, min_hit);
if (callchain_cumul_hits(child) >= min_hit) if (callchain_cumul_hits(child) >= min_hit)
rb_insert_callchain(&node->rb_root, child, rb_insert_callchain(&node->rb_root, child,
...@@ -117,13 +122,18 @@ sort_chain_graph_abs(struct rb_root *rb_root, struct callchain_root *chain_root, ...@@ -117,13 +122,18 @@ sort_chain_graph_abs(struct rb_root *rb_root, struct callchain_root *chain_root,
static void __sort_chain_graph_rel(struct callchain_node *node, static void __sort_chain_graph_rel(struct callchain_node *node,
double min_percent) double min_percent)
{ {
struct rb_node *n;
struct callchain_node *child; struct callchain_node *child;
u64 min_hit; u64 min_hit;
node->rb_root = RB_ROOT; node->rb_root = RB_ROOT;
min_hit = ceil(node->children_hit * min_percent); min_hit = ceil(node->children_hit * min_percent);
chain_for_each_child(child, node) { n = rb_first(&node->rb_root_in);
while (n) {
child = rb_entry(n, struct callchain_node, rb_node_in);
n = rb_next(n);
__sort_chain_graph_rel(child, min_percent); __sort_chain_graph_rel(child, min_percent);
if (callchain_cumul_hits(child) >= min_hit) if (callchain_cumul_hits(child) >= min_hit)
rb_insert_callchain(&node->rb_root, child, rb_insert_callchain(&node->rb_root, child,
...@@ -173,19 +183,26 @@ create_child(struct callchain_node *parent, bool inherit_children) ...@@ -173,19 +183,26 @@ create_child(struct callchain_node *parent, bool inherit_children)
return NULL; return NULL;
} }
new->parent = parent; new->parent = parent;
INIT_LIST_HEAD(&new->children);
INIT_LIST_HEAD(&new->val); INIT_LIST_HEAD(&new->val);
if (inherit_children) { if (inherit_children) {
struct callchain_node *next; struct rb_node *n;
struct callchain_node *child;
new->rb_root_in = parent->rb_root_in;
parent->rb_root_in = RB_ROOT;
list_splice(&parent->children, &new->children); n = rb_first(&new->rb_root_in);
INIT_LIST_HEAD(&parent->children); while (n) {
child = rb_entry(n, struct callchain_node, rb_node_in);
child->parent = new;
n = rb_next(n);
}
chain_for_each_child(next, new) /* make it the first child */
next->parent = new; rb_link_node(&new->rb_node_in, NULL, &parent->rb_root_in.rb_node);
rb_insert_color(&new->rb_node_in, &parent->rb_root_in);
} }
list_add_tail(&new->siblings, &parent->children);
return new; return new;
} }
...@@ -223,7 +240,7 @@ fill_node(struct callchain_node *node, struct callchain_cursor *cursor) ...@@ -223,7 +240,7 @@ fill_node(struct callchain_node *node, struct callchain_cursor *cursor)
} }
} }
static void static struct callchain_node *
add_child(struct callchain_node *parent, add_child(struct callchain_node *parent,
struct callchain_cursor *cursor, struct callchain_cursor *cursor,
u64 period) u64 period)
...@@ -235,6 +252,19 @@ add_child(struct callchain_node *parent, ...@@ -235,6 +252,19 @@ add_child(struct callchain_node *parent,
new->children_hit = 0; new->children_hit = 0;
new->hit = period; new->hit = period;
return new;
}
static s64 match_chain(struct callchain_cursor_node *node,
struct callchain_list *cnode)
{
struct symbol *sym = node->sym;
if (cnode->ms.sym && sym &&
callchain_param.key == CCKEY_FUNCTION)
return cnode->ms.sym->start - sym->start;
else
return cnode->ip - node->ip;
} }
/* /*
...@@ -272,9 +302,33 @@ split_add_child(struct callchain_node *parent, ...@@ -272,9 +302,33 @@ split_add_child(struct callchain_node *parent,
/* create a new child for the new branch if any */ /* create a new child for the new branch if any */
if (idx_total < cursor->nr) { if (idx_total < cursor->nr) {
struct callchain_node *first;
struct callchain_list *cnode;
struct callchain_cursor_node *node;
struct rb_node *p, **pp;
parent->hit = 0; parent->hit = 0;
add_child(parent, cursor, period);
parent->children_hit += period; parent->children_hit += period;
node = callchain_cursor_current(cursor);
new = add_child(parent, cursor, period);
/*
* This is second child since we moved parent's children
* to new (first) child above.
*/
p = parent->rb_root_in.rb_node;
first = rb_entry(p, struct callchain_node, rb_node_in);
cnode = list_first_entry(&first->val, struct callchain_list,
list);
if (match_chain(node, cnode) < 0)
pp = &p->rb_left;
else
pp = &p->rb_right;
rb_link_node(&new->rb_node_in, p, pp);
rb_insert_color(&new->rb_node_in, &parent->rb_root_in);
} else { } else {
parent->hit = period; parent->hit = period;
} }
...@@ -291,16 +345,40 @@ append_chain_children(struct callchain_node *root, ...@@ -291,16 +345,40 @@ append_chain_children(struct callchain_node *root,
u64 period) u64 period)
{ {
struct callchain_node *rnode; struct callchain_node *rnode;
struct callchain_cursor_node *node;
struct rb_node **p = &root->rb_root_in.rb_node;
struct rb_node *parent = NULL;
node = callchain_cursor_current(cursor);
if (!node)
return;
/* lookup in childrens */ /* lookup in childrens */
chain_for_each_child(rnode, root) { while (*p) {
unsigned int ret = append_chain(rnode, cursor, period); s64 ret;
struct callchain_list *cnode;
if (!ret) parent = *p;
rnode = rb_entry(parent, struct callchain_node, rb_node_in);
cnode = list_first_entry(&rnode->val, struct callchain_list,
list);
/* just check first entry */
ret = match_chain(node, cnode);
if (ret == 0) {
append_chain(rnode, cursor, period);
goto inc_children_hit; goto inc_children_hit;
}
if (ret < 0)
p = &parent->rb_left;
else
p = &parent->rb_right;
} }
/* nothing in children, add to the current node */ /* nothing in children, add to the current node */
add_child(root, cursor, period); rnode = add_child(root, cursor, period);
rb_link_node(&rnode->rb_node_in, parent, p);
rb_insert_color(&rnode->rb_node_in, &root->rb_root_in);
inc_children_hit: inc_children_hit:
root->children_hit += period; root->children_hit += period;
...@@ -325,28 +403,20 @@ append_chain(struct callchain_node *root, ...@@ -325,28 +403,20 @@ append_chain(struct callchain_node *root,
*/ */
list_for_each_entry(cnode, &root->val, list) { list_for_each_entry(cnode, &root->val, list) {
struct callchain_cursor_node *node; struct callchain_cursor_node *node;
struct symbol *sym;
node = callchain_cursor_current(cursor); node = callchain_cursor_current(cursor);
if (!node) if (!node)
break; break;
sym = node->sym; if (match_chain(node, cnode) != 0)
if (cnode->ms.sym && sym &&
callchain_param.key == CCKEY_FUNCTION) {
if (cnode->ms.sym->start != sym->start)
break;
} else if (cnode->ip != node->ip)
break; break;
if (!found) found = true;
found = true;
callchain_cursor_advance(cursor); callchain_cursor_advance(cursor);
} }
/* matches not, relay on the parent */ /* matches not, relay no the parent */
if (!found) { if (!found) {
cursor->curr = curr_snap; cursor->curr = curr_snap;
cursor->pos = start; cursor->pos = start;
...@@ -395,8 +465,9 @@ merge_chain_branch(struct callchain_cursor *cursor, ...@@ -395,8 +465,9 @@ merge_chain_branch(struct callchain_cursor *cursor,
struct callchain_node *dst, struct callchain_node *src) struct callchain_node *dst, struct callchain_node *src)
{ {
struct callchain_cursor_node **old_last = cursor->last; struct callchain_cursor_node **old_last = cursor->last;
struct callchain_node *child, *next_child; struct callchain_node *child;
struct callchain_list *list, *next_list; struct callchain_list *list, *next_list;
struct rb_node *n;
int old_pos = cursor->nr; int old_pos = cursor->nr;
int err = 0; int err = 0;
...@@ -412,12 +483,16 @@ merge_chain_branch(struct callchain_cursor *cursor, ...@@ -412,12 +483,16 @@ merge_chain_branch(struct callchain_cursor *cursor,
append_chain_children(dst, cursor, src->hit); append_chain_children(dst, cursor, src->hit);
} }
chain_for_each_child_safe(child, next_child, src) { n = rb_first(&src->rb_root_in);
while (n) {
child = container_of(n, struct callchain_node, rb_node_in);
n = rb_next(n);
rb_erase(&child->rb_node_in, &src->rb_root_in);
err = merge_chain_branch(cursor, dst, child); err = merge_chain_branch(cursor, dst, child);
if (err) if (err)
break; break;
list_del(&child->siblings);
free(child); free(child);
} }
......
...@@ -21,11 +21,11 @@ enum chain_order { ...@@ -21,11 +21,11 @@ enum chain_order {
struct callchain_node { struct callchain_node {
struct callchain_node *parent; struct callchain_node *parent;
struct list_head siblings;
struct list_head children;
struct list_head val; struct list_head val;
struct rb_node rb_node; /* to sort nodes in an rbtree */ struct rb_node rb_node_in; /* to insert nodes in an rbtree */
struct rb_root rb_root; /* sorted tree of children */ struct rb_node rb_node; /* to sort nodes in an output tree */
struct rb_root rb_root_in; /* input tree of children */
struct rb_root rb_root; /* sorted output tree of children */
unsigned int val_nr; unsigned int val_nr;
u64 hit; u64 hit;
u64 children_hit; u64 children_hit;
...@@ -86,13 +86,12 @@ extern __thread struct callchain_cursor callchain_cursor; ...@@ -86,13 +86,12 @@ extern __thread struct callchain_cursor callchain_cursor;
static inline void callchain_init(struct callchain_root *root) static inline void callchain_init(struct callchain_root *root)
{ {
INIT_LIST_HEAD(&root->node.siblings);
INIT_LIST_HEAD(&root->node.children);
INIT_LIST_HEAD(&root->node.val); INIT_LIST_HEAD(&root->node.val);
root->node.parent = NULL; root->node.parent = NULL;
root->node.hit = 0; root->node.hit = 0;
root->node.children_hit = 0; root->node.children_hit = 0;
root->node.rb_root_in = RB_ROOT;
root->max_depth = 0; root->max_depth = 0;
} }
......
#include <linux/compiler.h>
#include <linux/kernel.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include "data.h"
#include "util.h"
static bool check_pipe(struct perf_data_file *file)
{
struct stat st;
bool is_pipe = false;
int fd = perf_data_file__is_read(file) ?
STDIN_FILENO : STDOUT_FILENO;
if (!file->path) {
if (!fstat(fd, &st) && S_ISFIFO(st.st_mode))
is_pipe = true;
} else {
if (!strcmp(file->path, "-"))
is_pipe = true;
}
if (is_pipe)
file->fd = fd;
return file->is_pipe = is_pipe;
}
static int check_backup(struct perf_data_file *file)
{
struct stat st;
if (!stat(file->path, &st) && st.st_size) {
/* TODO check errors properly */
char oldname[PATH_MAX];
snprintf(oldname, sizeof(oldname), "%s.old",
file->path);
unlink(oldname);
rename(file->path, oldname);
}
return 0;
}
static int open_file_read(struct perf_data_file *file)
{
struct stat st;
int fd;
fd = open(file->path, O_RDONLY);
if (fd < 0) {
int err = errno;
pr_err("failed to open %s: %s", file->path, strerror(err));
if (err == ENOENT && !strcmp(file->path, "perf.data"))
pr_err(" (try 'perf record' first)");
pr_err("\n");
return -err;
}
if (fstat(fd, &st) < 0)
goto out_close;
if (!file->force && st.st_uid && (st.st_uid != geteuid())) {
pr_err("file %s not owned by current user or root\n",
file->path);
goto out_close;
}
if (!st.st_size) {
pr_info("zero-sized file (%s), nothing to do!\n",
file->path);
goto out_close;
}
file->size = st.st_size;
return fd;
out_close:
close(fd);
return -1;
}
static int open_file_write(struct perf_data_file *file)
{
if (check_backup(file))
return -1;
return open(file->path, O_CREAT|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR);
}
static int open_file(struct perf_data_file *file)
{
int fd;
fd = perf_data_file__is_read(file) ?
open_file_read(file) : open_file_write(file);
file->fd = fd;
return fd < 0 ? -1 : 0;
}
int perf_data_file__open(struct perf_data_file *file)
{
if (check_pipe(file))
return 0;
if (!file->path)
file->path = "perf.data";
return open_file(file);
}
void perf_data_file__close(struct perf_data_file *file)
{
close(file->fd);
}
#ifndef __PERF_DATA_H
#define __PERF_DATA_H
#include <stdbool.h>
enum perf_data_mode {
PERF_DATA_MODE_WRITE,
PERF_DATA_MODE_READ,
};
struct perf_data_file {
const char *path;
int fd;
bool is_pipe;
bool force;
unsigned long size;
enum perf_data_mode mode;
};
static inline bool perf_data_file__is_read(struct perf_data_file *file)
{
return file->mode == PERF_DATA_MODE_READ;
}
static inline bool perf_data_file__is_write(struct perf_data_file *file)
{
return file->mode == PERF_DATA_MODE_WRITE;
}
static inline int perf_data_file__is_pipe(struct perf_data_file *file)
{
return file->is_pipe;
}
static inline int perf_data_file__fd(struct perf_data_file *file)
{
return file->fd;
}
static inline unsigned long perf_data_file__size(struct perf_data_file *file)
{
return file->size;
}
int perf_data_file__open(struct perf_data_file *file);
void perf_data_file__close(struct perf_data_file *file);
#endif /* __PERF_DATA_H */
...@@ -608,9 +608,36 @@ static int __perf_evlist__mmap(struct perf_evlist *evlist, ...@@ -608,9 +608,36 @@ static int __perf_evlist__mmap(struct perf_evlist *evlist,
return 0; return 0;
} }
static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, int mask) static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx,
int prot, int mask, int cpu, int thread,
int *output)
{ {
struct perf_evsel *evsel; struct perf_evsel *evsel;
list_for_each_entry(evsel, &evlist->entries, node) {
int fd = FD(evsel, cpu, thread);
if (*output == -1) {
*output = fd;
if (__perf_evlist__mmap(evlist, idx, prot, mask,
*output) < 0)
return -1;
} else {
if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, *output) != 0)
return -1;
}
if ((evsel->attr.read_format & PERF_FORMAT_ID) &&
perf_evlist__id_add_fd(evlist, evsel, cpu, thread, fd) < 0)
return -1;
}
return 0;
}
static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot,
int mask)
{
int cpu, thread; int cpu, thread;
int nr_cpus = cpu_map__nr(evlist->cpus); int nr_cpus = cpu_map__nr(evlist->cpus);
int nr_threads = thread_map__nr(evlist->threads); int nr_threads = thread_map__nr(evlist->threads);
...@@ -620,23 +647,9 @@ static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, int m ...@@ -620,23 +647,9 @@ static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, int m
int output = -1; int output = -1;
for (thread = 0; thread < nr_threads; thread++) { for (thread = 0; thread < nr_threads; thread++) {
list_for_each_entry(evsel, &evlist->entries, node) { if (perf_evlist__mmap_per_evsel(evlist, cpu, prot, mask,
int fd = FD(evsel, cpu, thread); cpu, thread, &output))
goto out_unmap;
if (output == -1) {
output = fd;
if (__perf_evlist__mmap(evlist, cpu,
prot, mask, output) < 0)
goto out_unmap;
} else {
if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, output) != 0)
goto out_unmap;
}
if ((evsel->attr.read_format & PERF_FORMAT_ID) &&
perf_evlist__id_add_fd(evlist, evsel, cpu, thread, fd) < 0)
goto out_unmap;
}
} }
} }
...@@ -648,9 +661,9 @@ static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, int m ...@@ -648,9 +661,9 @@ static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, int m
return -1; return -1;
} }
static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, int mask) static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot,
int mask)
{ {
struct perf_evsel *evsel;
int thread; int thread;
int nr_threads = thread_map__nr(evlist->threads); int nr_threads = thread_map__nr(evlist->threads);
...@@ -658,23 +671,9 @@ static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, in ...@@ -658,23 +671,9 @@ static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, in
for (thread = 0; thread < nr_threads; thread++) { for (thread = 0; thread < nr_threads; thread++) {
int output = -1; int output = -1;
list_for_each_entry(evsel, &evlist->entries, node) { if (perf_evlist__mmap_per_evsel(evlist, thread, prot, mask, 0,
int fd = FD(evsel, 0, thread); thread, &output))
goto out_unmap;
if (output == -1) {
output = fd;
if (__perf_evlist__mmap(evlist, thread,
prot, mask, output) < 0)
goto out_unmap;
} else {
if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, output) != 0)
goto out_unmap;
}
if ((evsel->attr.read_format & PERF_FORMAT_ID) &&
perf_evlist__id_add_fd(evlist, evsel, 0, thread, fd) < 0)
goto out_unmap;
}
} }
return 0; return 0;
...@@ -738,20 +737,17 @@ int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, ...@@ -738,20 +737,17 @@ int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str,
return 0; return 0;
} }
/** perf_evlist__mmap - Create per cpu maps to receive events /**
* * perf_evlist__mmap - Create mmaps to receive events.
* @evlist - list of events * @evlist: list of events
* @pages - map length in pages * @pages: map length in pages
* @overwrite - overwrite older events? * @overwrite: overwrite older events?
*
* If overwrite is false the user needs to signal event consuption using:
*
* struct perf_mmap *m = &evlist->mmap[cpu];
* unsigned int head = perf_mmap__read_head(m);
* *
* perf_mmap__write_tail(m, head) * If @overwrite is %false the user needs to signal event consumption using
* perf_mmap__write_tail(). Using perf_evlist__mmap_read() does this
* automatically.
* *
* Using perf_evlist__read_on_cpu does this automatically. * Return: %0 on success, negative error code otherwise.
*/ */
int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,
bool overwrite) bool overwrite)
...@@ -769,7 +765,7 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, ...@@ -769,7 +765,7 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,
evlist->overwrite = overwrite; evlist->overwrite = overwrite;
evlist->mmap_len = perf_evlist__mmap_size(pages); evlist->mmap_len = perf_evlist__mmap_size(pages);
pr_debug("mmap size %luB\n", evlist->mmap_len); pr_debug("mmap size %zuB\n", evlist->mmap_len);
mask = evlist->mmap_len - page_size - 1; mask = evlist->mmap_len - page_size - 1;
list_for_each_entry(evsel, &evlist->entries, node) { list_for_each_entry(evsel, &evlist->entries, node) {
...@@ -1126,3 +1122,66 @@ size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp) ...@@ -1126,3 +1122,66 @@ size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp)
return printed + fprintf(fp, "\n");; return printed + fprintf(fp, "\n");;
} }
int perf_evlist__strerror_tp(struct perf_evlist *evlist __maybe_unused,
int err, char *buf, size_t size)
{
char sbuf[128];
switch (err) {
case ENOENT:
scnprintf(buf, size, "%s",
"Error:\tUnable to find debugfs\n"
"Hint:\tWas your kernel was compiled with debugfs support?\n"
"Hint:\tIs the debugfs filesystem mounted?\n"
"Hint:\tTry 'sudo mount -t debugfs nodev /sys/kernel/debug'");
break;
case EACCES:
scnprintf(buf, size,
"Error:\tNo permissions to read %s/tracing/events/raw_syscalls\n"
"Hint:\tTry 'sudo mount -o remount,mode=755 %s'\n",
debugfs_mountpoint, debugfs_mountpoint);
break;
default:
scnprintf(buf, size, "%s", strerror_r(err, sbuf, sizeof(sbuf)));
break;
}
return 0;
}
int perf_evlist__strerror_open(struct perf_evlist *evlist __maybe_unused,
int err, char *buf, size_t size)
{
int printed, value;
char sbuf[128], *emsg = strerror_r(err, sbuf, sizeof(sbuf));
switch (err) {
case EACCES:
case EPERM:
printed = scnprintf(buf, size,
"Error:\t%s.\n"
"Hint:\tCheck /proc/sys/kernel/perf_event_paranoid setting.", emsg);
if (filename__read_int("/proc/sys/kernel/perf_event_paranoid", &value))
break;
printed += scnprintf(buf + printed, size - printed, "\nHint:\t");
if (value >= 2) {
printed += scnprintf(buf + printed, size - printed,
"For your workloads it needs to be <= 1\nHint:\t");
}
printed += scnprintf(buf + printed, size - printed,
"For system wide tracing it needs to be set to -1");
printed += scnprintf(buf + printed, size - printed,
".\nHint:\tThe current value is %d.", value);
break;
default:
scnprintf(buf, size, "%s", emsg);
break;
}
return 0;
}
...@@ -168,6 +168,9 @@ static inline struct perf_evsel *perf_evlist__last(struct perf_evlist *evlist) ...@@ -168,6 +168,9 @@ static inline struct perf_evsel *perf_evlist__last(struct perf_evlist *evlist)
size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp); size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp);
int perf_evlist__strerror_tp(struct perf_evlist *evlist, int err, char *buf, size_t size);
int perf_evlist__strerror_open(struct perf_evlist *evlist, int err, char *buf, size_t size);
static inline unsigned int perf_mmap__read_head(struct perf_mmap *mm) static inline unsigned int perf_mmap__read_head(struct perf_mmap *mm)
{ {
struct perf_event_mmap_page *pc = mm->base; struct perf_event_mmap_page *pc = mm->base;
......
...@@ -986,6 +986,7 @@ static size_t perf_event_attr__fprintf(struct perf_event_attr *attr, FILE *fp) ...@@ -986,6 +986,7 @@ static size_t perf_event_attr__fprintf(struct perf_event_attr *attr, FILE *fp)
ret += PRINT_ATTR2(exclude_host, exclude_guest); ret += PRINT_ATTR2(exclude_host, exclude_guest);
ret += PRINT_ATTR2N("excl.callchain_kern", exclude_callchain_kernel, ret += PRINT_ATTR2N("excl.callchain_kern", exclude_callchain_kernel,
"excl.callchain_user", exclude_callchain_user); "excl.callchain_user", exclude_callchain_user);
ret += PRINT_ATTR_U32(mmap2);
ret += PRINT_ATTR_U32(wakeup_events); ret += PRINT_ATTR_U32(wakeup_events);
ret += PRINT_ATTR_U32(wakeup_watermark); ret += PRINT_ATTR_U32(wakeup_watermark);
...@@ -1217,6 +1218,7 @@ static int perf_evsel__parse_id_sample(const struct perf_evsel *evsel, ...@@ -1217,6 +1218,7 @@ static int perf_evsel__parse_id_sample(const struct perf_evsel *evsel,
sample->pid = u.val32[0]; sample->pid = u.val32[0];
sample->tid = u.val32[1]; sample->tid = u.val32[1];
array--;
} }
return 0; return 0;
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "vdso.h" #include "vdso.h"
#include "strbuf.h" #include "strbuf.h"
#include "build-id.h" #include "build-id.h"
#include "data.h"
static bool no_buildid_cache = false; static bool no_buildid_cache = false;
...@@ -2189,7 +2190,7 @@ int perf_header__fprintf_info(struct perf_session *session, FILE *fp, bool full) ...@@ -2189,7 +2190,7 @@ int perf_header__fprintf_info(struct perf_session *session, FILE *fp, bool full)
{ {
struct header_print_data hd; struct header_print_data hd;
struct perf_header *header = &session->header; struct perf_header *header = &session->header;
int fd = session->fd; int fd = perf_data_file__fd(session->file);
hd.fp = fp; hd.fp = fp;
hd.full = full; hd.full = full;
...@@ -2650,7 +2651,8 @@ static int perf_header__read_pipe(struct perf_session *session) ...@@ -2650,7 +2651,8 @@ static int perf_header__read_pipe(struct perf_session *session)
struct perf_header *header = &session->header; struct perf_header *header = &session->header;
struct perf_pipe_file_header f_header; struct perf_pipe_file_header f_header;
if (perf_file_header__read_pipe(&f_header, header, session->fd, if (perf_file_header__read_pipe(&f_header, header,
perf_data_file__fd(session->file),
session->repipe) < 0) { session->repipe) < 0) {
pr_debug("incompatible file format\n"); pr_debug("incompatible file format\n");
return -EINVAL; return -EINVAL;
...@@ -2751,18 +2753,19 @@ static int perf_evlist__prepare_tracepoint_events(struct perf_evlist *evlist, ...@@ -2751,18 +2753,19 @@ static int perf_evlist__prepare_tracepoint_events(struct perf_evlist *evlist,
int perf_session__read_header(struct perf_session *session) int perf_session__read_header(struct perf_session *session)
{ {
struct perf_data_file *file = session->file;
struct perf_header *header = &session->header; struct perf_header *header = &session->header;
struct perf_file_header f_header; struct perf_file_header f_header;
struct perf_file_attr f_attr; struct perf_file_attr f_attr;
u64 f_id; u64 f_id;
int nr_attrs, nr_ids, i, j; int nr_attrs, nr_ids, i, j;
int fd = session->fd; int fd = perf_data_file__fd(file);
session->evlist = perf_evlist__new(); session->evlist = perf_evlist__new();
if (session->evlist == NULL) if (session->evlist == NULL)
return -ENOMEM; return -ENOMEM;
if (session->fd_pipe) if (perf_data_file__is_pipe(file))
return perf_header__read_pipe(session); return perf_header__read_pipe(session);
if (perf_file_header__read(&f_header, header, fd) < 0) if (perf_file_header__read(&f_header, header, fd) < 0)
...@@ -2777,7 +2780,7 @@ int perf_session__read_header(struct perf_session *session) ...@@ -2777,7 +2780,7 @@ int perf_session__read_header(struct perf_session *session)
if (f_header.data.size == 0) { if (f_header.data.size == 0) {
pr_warning("WARNING: The %s file's data size field is 0 which is unexpected.\n" pr_warning("WARNING: The %s file's data size field is 0 which is unexpected.\n"
"Was the 'perf record' command properly terminated?\n", "Was the 'perf record' command properly terminated?\n",
session->filename); file->path);
} }
nr_attrs = f_header.attrs.size / f_header.attr_size; nr_attrs = f_header.attrs.size / f_header.attr_size;
...@@ -2990,18 +2993,19 @@ int perf_event__process_tracing_data(struct perf_tool *tool __maybe_unused, ...@@ -2990,18 +2993,19 @@ int perf_event__process_tracing_data(struct perf_tool *tool __maybe_unused,
struct perf_session *session) struct perf_session *session)
{ {
ssize_t size_read, padding, size = event->tracing_data.size; ssize_t size_read, padding, size = event->tracing_data.size;
off_t offset = lseek(session->fd, 0, SEEK_CUR); int fd = perf_data_file__fd(session->file);
off_t offset = lseek(fd, 0, SEEK_CUR);
char buf[BUFSIZ]; char buf[BUFSIZ];
/* setup for reading amidst mmap */ /* setup for reading amidst mmap */
lseek(session->fd, offset + sizeof(struct tracing_data_event), lseek(fd, offset + sizeof(struct tracing_data_event),
SEEK_SET); SEEK_SET);
size_read = trace_report(session->fd, &session->pevent, size_read = trace_report(fd, &session->pevent,
session->repipe); session->repipe);
padding = PERF_ALIGN(size_read, sizeof(u64)) - size_read; padding = PERF_ALIGN(size_read, sizeof(u64)) - size_read;
if (readn(session->fd, buf, padding) < 0) { if (readn(fd, buf, padding) < 0) {
pr_err("%s: reading input file", __func__); pr_err("%s: reading input file", __func__);
return -1; return -1;
} }
......
...@@ -1253,10 +1253,12 @@ static int machine__resolve_callchain_sample(struct machine *machine, ...@@ -1253,10 +1253,12 @@ static int machine__resolve_callchain_sample(struct machine *machine,
struct thread *thread, struct thread *thread,
struct ip_callchain *chain, struct ip_callchain *chain,
struct symbol **parent, struct symbol **parent,
struct addr_location *root_al) struct addr_location *root_al,
int max_stack)
{ {
u8 cpumode = PERF_RECORD_MISC_USER; u8 cpumode = PERF_RECORD_MISC_USER;
unsigned int i; int chain_nr = min(max_stack, (int)chain->nr);
int i;
int err; int err;
callchain_cursor_reset(&callchain_cursor); callchain_cursor_reset(&callchain_cursor);
...@@ -1266,7 +1268,7 @@ static int machine__resolve_callchain_sample(struct machine *machine, ...@@ -1266,7 +1268,7 @@ static int machine__resolve_callchain_sample(struct machine *machine,
return 0; return 0;
} }
for (i = 0; i < chain->nr; i++) { for (i = 0; i < chain_nr; i++) {
u64 ip; u64 ip;
struct addr_location al; struct addr_location al;
...@@ -1338,12 +1340,14 @@ int machine__resolve_callchain(struct machine *machine, ...@@ -1338,12 +1340,14 @@ int machine__resolve_callchain(struct machine *machine,
struct thread *thread, struct thread *thread,
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 ret; int ret;
ret = machine__resolve_callchain_sample(machine, thread, ret = machine__resolve_callchain_sample(machine, thread,
sample->callchain, parent, root_al); sample->callchain, parent,
root_al, max_stack);
if (ret) if (ret)
return ret; return ret;
......
...@@ -92,7 +92,8 @@ int machine__resolve_callchain(struct machine *machine, ...@@ -92,7 +92,8 @@ int machine__resolve_callchain(struct machine *machine,
struct thread *thread, struct thread *thread,
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);
/* /*
* Default guest kernel is defined by parameter --guestkallsyms * Default guest kernel is defined by parameter --guestkallsyms
......
...@@ -998,8 +998,10 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob, ...@@ -998,8 +998,10 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob,
char evt_path[MAXPATHLEN]; char evt_path[MAXPATHLEN];
char dir_path[MAXPATHLEN]; char dir_path[MAXPATHLEN];
if (debugfs_valid_mountpoint(tracing_events_path)) if (debugfs_valid_mountpoint(tracing_events_path)) {
printf(" [ Tracepoints not available: %s ]\n", strerror(errno));
return; return;
}
sys_dir = opendir(tracing_events_path); sys_dir = opendir(tracing_events_path);
if (!sys_dir) if (!sys_dir)
......
...@@ -282,7 +282,7 @@ static void perl_process_tracepoint(union perf_event *perf_event __maybe_unused, ...@@ -282,7 +282,7 @@ static void perl_process_tracepoint(union perf_event *perf_event __maybe_unused,
event = find_cache_event(evsel); event = find_cache_event(evsel);
if (!event) if (!event)
die("ug! no event found for type %" PRIu64, evsel->attr.config); die("ug! no event found for type %" PRIu64, (u64)evsel->attr.config);
pid = raw_field_value(event, "common_pid", data); pid = raw_field_value(event, "common_pid", data);
......
...@@ -16,73 +16,34 @@ ...@@ -16,73 +16,34 @@
#include "perf_regs.h" #include "perf_regs.h"
#include "vdso.h" #include "vdso.h"
static int perf_session__open(struct perf_session *self, bool force) static int perf_session__open(struct perf_session *self)
{ {
struct stat input_stat; struct perf_data_file *file = self->file;
if (!strcmp(self->filename, "-")) {
self->fd_pipe = true;
self->fd = STDIN_FILENO;
if (perf_session__read_header(self) < 0)
pr_err("incompatible file format (rerun with -v to learn more)");
return 0;
}
self->fd = open(self->filename, O_RDONLY);
if (self->fd < 0) {
int err = errno;
pr_err("failed to open %s: %s", self->filename, strerror(err));
if (err == ENOENT && !strcmp(self->filename, "perf.data"))
pr_err(" (try 'perf record' first)");
pr_err("\n");
return -errno;
}
if (fstat(self->fd, &input_stat) < 0)
goto out_close;
if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) {
pr_err("file %s not owned by current user or root\n",
self->filename);
goto out_close;
}
if (!input_stat.st_size) {
pr_info("zero-sized file (%s), nothing to do!\n",
self->filename);
goto out_close;
}
if (perf_session__read_header(self) < 0) { if (perf_session__read_header(self) < 0) {
pr_err("incompatible file format (rerun with -v to learn more)"); pr_err("incompatible file format (rerun with -v to learn more)");
goto out_close; return -1;
} }
if (perf_data_file__is_pipe(file))
return 0;
if (!perf_evlist__valid_sample_type(self->evlist)) { if (!perf_evlist__valid_sample_type(self->evlist)) {
pr_err("non matching sample_type"); pr_err("non matching sample_type");
goto out_close; return -1;
} }
if (!perf_evlist__valid_sample_id_all(self->evlist)) { if (!perf_evlist__valid_sample_id_all(self->evlist)) {
pr_err("non matching sample_id_all"); pr_err("non matching sample_id_all");
goto out_close; return -1;
} }
if (!perf_evlist__valid_read_format(self->evlist)) { if (!perf_evlist__valid_read_format(self->evlist)) {
pr_err("non matching read_format"); pr_err("non matching read_format");
goto out_close; return -1;
} }
self->size = input_stat.st_size;
return 0; return 0;
out_close:
close(self->fd);
self->fd = -1;
return -1;
} }
void perf_session__set_id_hdr_size(struct perf_session *session) void perf_session__set_id_hdr_size(struct perf_session *session)
...@@ -106,39 +67,36 @@ static void perf_session__destroy_kernel_maps(struct perf_session *self) ...@@ -106,39 +67,36 @@ static void perf_session__destroy_kernel_maps(struct perf_session *self)
machines__destroy_kernel_maps(&self->machines); machines__destroy_kernel_maps(&self->machines);
} }
struct perf_session *perf_session__new(const char *filename, int mode, struct perf_session *perf_session__new(struct perf_data_file *file,
bool force, bool repipe, bool repipe, struct perf_tool *tool)
struct perf_tool *tool)
{ {
struct perf_session *self; struct perf_session *self;
struct stat st;
size_t len;
if (!filename || !strlen(filename)) {
if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode))
filename = "-";
else
filename = "perf.data";
}
len = strlen(filename); self = zalloc(sizeof(*self));
self = zalloc(sizeof(*self) + len); if (!self)
if (self == NULL)
goto out; goto out;
memcpy(self->filename, filename, len);
self->repipe = repipe; self->repipe = repipe;
INIT_LIST_HEAD(&self->ordered_samples.samples); INIT_LIST_HEAD(&self->ordered_samples.samples);
INIT_LIST_HEAD(&self->ordered_samples.sample_cache); INIT_LIST_HEAD(&self->ordered_samples.sample_cache);
INIT_LIST_HEAD(&self->ordered_samples.to_free); INIT_LIST_HEAD(&self->ordered_samples.to_free);
machines__init(&self->machines); machines__init(&self->machines);
if (mode == O_RDONLY) { if (file) {
if (perf_session__open(self, force) < 0) if (perf_data_file__open(file))
goto out_delete; goto out_delete;
perf_session__set_id_hdr_size(self);
} else if (mode == O_WRONLY) { self->file = file;
if (perf_data_file__is_read(file)) {
if (perf_session__open(self) < 0)
goto out_close;
perf_session__set_id_hdr_size(self);
}
}
if (!file || perf_data_file__is_write(file)) {
/* /*
* In O_RDONLY mode this will be performed when reading the * In O_RDONLY mode this will be performed when reading the
* kernel MMAP event, in perf_event__process_mmap(). * kernel MMAP event, in perf_event__process_mmap().
...@@ -153,10 +111,13 @@ struct perf_session *perf_session__new(const char *filename, int mode, ...@@ -153,10 +111,13 @@ struct perf_session *perf_session__new(const char *filename, int mode,
tool->ordered_samples = false; tool->ordered_samples = false;
} }
out:
return self; return self;
out_delete:
out_close:
perf_data_file__close(file);
out_delete:
perf_session__delete(self); perf_session__delete(self);
out:
return NULL; return NULL;
} }
...@@ -193,7 +154,8 @@ void perf_session__delete(struct perf_session *self) ...@@ -193,7 +154,8 @@ void perf_session__delete(struct perf_session *self)
perf_session__delete_threads(self); perf_session__delete_threads(self);
perf_session_env__delete(&self->header.env); perf_session_env__delete(&self->header.env);
machines__exit(&self->machines); machines__exit(&self->machines);
close(self->fd); if (self->file)
perf_data_file__close(self->file);
free(self); free(self);
vdso__exit(); vdso__exit();
} }
...@@ -453,6 +415,9 @@ void perf_event__attr_swap(struct perf_event_attr *attr) ...@@ -453,6 +415,9 @@ void perf_event__attr_swap(struct perf_event_attr *attr)
attr->bp_type = bswap_32(attr->bp_type); attr->bp_type = bswap_32(attr->bp_type);
attr->bp_addr = bswap_64(attr->bp_addr); attr->bp_addr = bswap_64(attr->bp_addr);
attr->bp_len = bswap_64(attr->bp_len); attr->bp_len = bswap_64(attr->bp_len);
attr->branch_sample_type = bswap_64(attr->branch_sample_type);
attr->sample_regs_user = bswap_64(attr->sample_regs_user);
attr->sample_stack_user = bswap_32(attr->sample_stack_user);
swap_bitfield((u8 *) (&attr->read_format + 1), sizeof(u64)); swap_bitfield((u8 *) (&attr->read_format + 1), sizeof(u64));
} }
...@@ -1047,6 +1012,7 @@ static int perf_session_deliver_event(struct perf_session *session, ...@@ -1047,6 +1012,7 @@ static int perf_session_deliver_event(struct perf_session *session,
static int perf_session__process_user_event(struct perf_session *session, union perf_event *event, static int perf_session__process_user_event(struct perf_session *session, union perf_event *event,
struct perf_tool *tool, u64 file_offset) struct perf_tool *tool, u64 file_offset)
{ {
int fd = perf_data_file__fd(session->file);
int err; int err;
dump_event(session, event, file_offset, NULL); dump_event(session, event, file_offset, NULL);
...@@ -1060,7 +1026,7 @@ static int perf_session__process_user_event(struct perf_session *session, union ...@@ -1060,7 +1026,7 @@ static int perf_session__process_user_event(struct perf_session *session, union
return err; return err;
case PERF_RECORD_HEADER_TRACING_DATA: case PERF_RECORD_HEADER_TRACING_DATA:
/* setup for reading amidst mmap */ /* setup for reading amidst mmap */
lseek(session->fd, file_offset, SEEK_SET); lseek(fd, file_offset, SEEK_SET);
return tool->tracing_data(tool, event, session); return tool->tracing_data(tool, event, session);
case PERF_RECORD_HEADER_BUILD_ID: case PERF_RECORD_HEADER_BUILD_ID:
return tool->build_id(tool, event, session); return tool->build_id(tool, event, session);
...@@ -1186,6 +1152,7 @@ volatile int session_done; ...@@ -1186,6 +1152,7 @@ volatile int session_done;
static int __perf_session__process_pipe_events(struct perf_session *self, static int __perf_session__process_pipe_events(struct perf_session *self,
struct perf_tool *tool) struct perf_tool *tool)
{ {
int fd = perf_data_file__fd(self->file);
union perf_event *event; union perf_event *event;
uint32_t size, cur_size = 0; uint32_t size, cur_size = 0;
void *buf = NULL; void *buf = NULL;
...@@ -1204,7 +1171,7 @@ static int __perf_session__process_pipe_events(struct perf_session *self, ...@@ -1204,7 +1171,7 @@ static int __perf_session__process_pipe_events(struct perf_session *self,
return -errno; return -errno;
more: more:
event = buf; event = buf;
err = readn(self->fd, event, sizeof(struct perf_event_header)); err = readn(fd, event, sizeof(struct perf_event_header));
if (err <= 0) { if (err <= 0) {
if (err == 0) if (err == 0)
goto done; goto done;
...@@ -1236,7 +1203,7 @@ static int __perf_session__process_pipe_events(struct perf_session *self, ...@@ -1236,7 +1203,7 @@ static int __perf_session__process_pipe_events(struct perf_session *self,
p += sizeof(struct perf_event_header); p += sizeof(struct perf_event_header);
if (size - sizeof(struct perf_event_header)) { if (size - sizeof(struct perf_event_header)) {
err = readn(self->fd, p, size - sizeof(struct perf_event_header)); err = readn(fd, p, size - sizeof(struct perf_event_header));
if (err <= 0) { if (err <= 0) {
if (err == 0) { if (err == 0) {
pr_err("unexpected end of event stream\n"); pr_err("unexpected end of event stream\n");
...@@ -1263,7 +1230,9 @@ static int __perf_session__process_pipe_events(struct perf_session *self, ...@@ -1263,7 +1230,9 @@ static int __perf_session__process_pipe_events(struct perf_session *self,
if (!session_done()) if (!session_done())
goto more; goto more;
done: done:
err = 0; /* do the final flush for ordered samples */
self->ordered_samples.next_flush = ULLONG_MAX;
err = flush_sample_queue(self, tool);
out_err: out_err:
free(buf); free(buf);
perf_session__warn_about_errors(self, tool); perf_session__warn_about_errors(self, tool);
...@@ -1315,6 +1284,7 @@ int __perf_session__process_events(struct perf_session *session, ...@@ -1315,6 +1284,7 @@ int __perf_session__process_events(struct perf_session *session,
u64 data_offset, u64 data_size, u64 data_offset, u64 data_size,
u64 file_size, struct perf_tool *tool) u64 file_size, struct perf_tool *tool)
{ {
int fd = perf_data_file__fd(session->file);
u64 head, page_offset, file_offset, file_pos, progress_next; u64 head, page_offset, file_offset, file_pos, progress_next;
int err, mmap_prot, mmap_flags, map_idx = 0; int err, mmap_prot, mmap_flags, map_idx = 0;
size_t mmap_size; size_t mmap_size;
...@@ -1347,7 +1317,7 @@ int __perf_session__process_events(struct perf_session *session, ...@@ -1347,7 +1317,7 @@ int __perf_session__process_events(struct perf_session *session,
mmap_flags = MAP_PRIVATE; mmap_flags = MAP_PRIVATE;
} }
remap: remap:
buf = mmap(NULL, mmap_size, mmap_prot, mmap_flags, session->fd, buf = mmap(NULL, mmap_size, mmap_prot, mmap_flags, fd,
file_offset); file_offset);
if (buf == MAP_FAILED) { if (buf == MAP_FAILED) {
pr_err("failed to mmap file\n"); pr_err("failed to mmap file\n");
...@@ -1392,13 +1362,13 @@ int __perf_session__process_events(struct perf_session *session, ...@@ -1392,13 +1362,13 @@ int __perf_session__process_events(struct perf_session *session,
"Processing events..."); "Processing events...");
} }
err = 0;
if (session_done()) if (session_done())
goto out_err; goto out;
if (file_pos < file_size) if (file_pos < file_size)
goto more; goto more;
out:
/* do the final flush for ordered samples */ /* do the final flush for ordered samples */
session->ordered_samples.next_flush = ULLONG_MAX; session->ordered_samples.next_flush = ULLONG_MAX;
err = flush_sample_queue(session, tool); err = flush_sample_queue(session, tool);
...@@ -1412,16 +1382,17 @@ int __perf_session__process_events(struct perf_session *session, ...@@ -1412,16 +1382,17 @@ int __perf_session__process_events(struct perf_session *session,
int perf_session__process_events(struct perf_session *self, int perf_session__process_events(struct perf_session *self,
struct perf_tool *tool) struct perf_tool *tool)
{ {
u64 size = perf_data_file__size(self->file);
int err; int err;
if (perf_session__register_idle_thread(self) == NULL) if (perf_session__register_idle_thread(self) == NULL)
return -ENOMEM; return -ENOMEM;
if (!self->fd_pipe) if (!perf_data_file__is_pipe(self->file))
err = __perf_session__process_events(self, err = __perf_session__process_events(self,
self->header.data_offset, self->header.data_offset,
self->header.data_size, self->header.data_size,
self->size, tool); size, tool);
else else
err = __perf_session__process_pipe_events(self, tool); err = __perf_session__process_pipe_events(self, tool);
...@@ -1541,7 +1512,8 @@ void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event, ...@@ -1541,7 +1512,8 @@ void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event,
if (symbol_conf.use_callchain && sample->callchain) { if (symbol_conf.use_callchain && sample->callchain) {
if (machine__resolve_callchain(machine, evsel, al.thread, if (machine__resolve_callchain(machine, evsel, al.thread,
sample, NULL, NULL) != 0) { sample, NULL, NULL,
PERF_MAX_STACK_DEPTH) != 0) {
if (verbose) if (verbose)
error("Failed to resolve callchain. Skipping\n"); error("Failed to resolve callchain. Skipping\n");
return; return;
...@@ -1645,13 +1617,14 @@ int perf_session__cpu_bitmap(struct perf_session *session, ...@@ -1645,13 +1617,14 @@ int perf_session__cpu_bitmap(struct perf_session *session,
void perf_session__fprintf_info(struct perf_session *session, FILE *fp, void perf_session__fprintf_info(struct perf_session *session, FILE *fp,
bool full) bool full)
{ {
int fd = perf_data_file__fd(session->file);
struct stat st; struct stat st;
int ret; int ret;
if (session == NULL || fp == NULL) if (session == NULL || fp == NULL)
return; return;
ret = fstat(session->fd, &st); ret = fstat(fd, &st);
if (ret == -1) if (ret == -1)
return; return;
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "machine.h" #include "machine.h"
#include "symbol.h" #include "symbol.h"
#include "thread.h" #include "thread.h"
#include "data.h"
#include <linux/rbtree.h> #include <linux/rbtree.h>
#include <linux/perf_event.h> #include <linux/perf_event.h>
...@@ -29,16 +30,13 @@ struct ordered_samples { ...@@ -29,16 +30,13 @@ struct ordered_samples {
struct perf_session { struct perf_session {
struct perf_header header; struct perf_header header;
unsigned long size;
struct machines machines; struct machines machines;
struct perf_evlist *evlist; struct perf_evlist *evlist;
struct pevent *pevent; struct pevent *pevent;
struct events_stats stats; struct events_stats stats;
int fd;
bool fd_pipe;
bool repipe; bool repipe;
struct ordered_samples ordered_samples; struct ordered_samples ordered_samples;
char filename[1]; struct perf_data_file *file;
}; };
#define PRINT_IP_OPT_IP (1<<0) #define PRINT_IP_OPT_IP (1<<0)
...@@ -49,9 +47,8 @@ struct perf_session { ...@@ -49,9 +47,8 @@ struct perf_session {
struct perf_tool; struct perf_tool;
struct perf_session *perf_session__new(const char *filename, int mode, struct perf_session *perf_session__new(struct perf_data_file *file,
bool force, bool repipe, bool repipe, struct perf_tool *tool);
struct perf_tool *tool);
void perf_session__delete(struct perf_session *session); void perf_session__delete(struct perf_session *session);
void perf_event_header__bswap(struct perf_event_header *self); void perf_event_header__bswap(struct perf_event_header *self);
......
...@@ -182,9 +182,19 @@ static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r) ...@@ -182,9 +182,19 @@ static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r)
static int64_t static int64_t
sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)
{ {
int64_t ret;
if (!left->ms.sym && !right->ms.sym) if (!left->ms.sym && !right->ms.sym)
return right->level - left->level; return right->level - left->level;
/*
* comparing symbol address alone is not enough since it's a
* relative address within a dso.
*/
ret = sort__dso_cmp(left, right);
if (ret != 0)
return ret;
return _sort__sym_cmp(left->ms.sym, right->ms.sym); return _sort__sym_cmp(left->ms.sym, right->ms.sym);
} }
......
...@@ -24,6 +24,7 @@ struct perf_top { ...@@ -24,6 +24,7 @@ struct perf_top {
u64 exact_samples; u64 exact_samples;
u64 guest_us_samples, guest_kernel_samples; u64 guest_us_samples, guest_kernel_samples;
int print_entries, count_filter, delay_secs; int print_entries, count_filter, delay_secs;
int max_stack;
bool hide_kernel_symbols, hide_user_symbols, zero; bool hide_kernel_symbols, hide_user_symbols, zero;
bool use_tui, use_stdio; bool use_tui, use_stdio;
bool kptr_restrict_warned; bool kptr_restrict_warned;
......
...@@ -394,3 +394,20 @@ unsigned long parse_tag_value(const char *str, struct parse_tag *tags) ...@@ -394,3 +394,20 @@ unsigned long parse_tag_value(const char *str, struct parse_tag *tags)
return (unsigned long) -1; return (unsigned long) -1;
} }
int filename__read_int(const char *filename, int *value)
{
char line[64];
int fd = open(filename, O_RDONLY), err = -1;
if (fd < 0)
return -1;
if (read(fd, line, sizeof(line)) > 0) {
*value = atoi(line);
err = 0;
}
close(fd);
return err;
}
...@@ -305,4 +305,6 @@ struct dso; ...@@ -305,4 +305,6 @@ struct dso;
char *get_srcline(struct dso *dso, unsigned long addr); char *get_srcline(struct dso *dso, unsigned long addr);
void free_srcline(char *srcline); void free_srcline(char *srcline);
int filename__read_int(const char *filename, int *value);
#endif /* GIT_COMPAT_UTIL_H */ #endif /* GIT_COMPAT_UTIL_H */
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