diff options
42 files changed, 834 insertions, 376 deletions
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index be5ad87b6c3d..10a279871251 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt | |||
| @@ -141,6 +141,14 @@ OPTIONS | |||
| 141 | 141 | ||
| 142 | Default: fractal,0.5,callee,function. | 142 | Default: fractal,0.5,callee,function. |
| 143 | 143 | ||
| 144 | --max-stack:: | ||
| 145 | Set the stack depth limit when parsing the callchain, anything | ||
| 146 | beyond the specified depth will be ignored. This is a trade-off | ||
| 147 | between information loss and faster processing especially for | ||
| 148 | workloads that can have a very long callchain stack. | ||
| 149 | |||
| 150 | Default: 127 | ||
| 151 | |||
| 144 | -G:: | 152 | -G:: |
| 145 | --inverted:: | 153 | --inverted:: |
| 146 | alias for inverted caller based call graph. | 154 | alias for inverted caller based call graph. |
diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index f65777c1f723..c16a09e2f182 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt | |||
| @@ -158,6 +158,14 @@ Default is to monitor all CPUS. | |||
| 158 | 158 | ||
| 159 | Default: fractal,0.5,callee. | 159 | Default: fractal,0.5,callee. |
| 160 | 160 | ||
| 161 | --max-stack:: | ||
| 162 | Set the stack depth limit when parsing the callchain, anything | ||
| 163 | beyond the specified depth will be ignored. This is a trade-off | ||
| 164 | between information loss and faster processing especially for | ||
| 165 | workloads that can have a very long callchain stack. | ||
| 166 | |||
| 167 | Default: 127 | ||
| 168 | |||
| 161 | --ignore-callees=<regex>:: | 169 | --ignore-callees=<regex>:: |
| 162 | Ignore callees of the function(s) matching the given regex. | 170 | Ignore callees of the function(s) matching the given regex. |
| 163 | This has the effect of collecting the callers of each such | 171 | This has the effect of collecting the callers of each such |
diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt index 54139c6457f8..7b0497f95a75 100644 --- a/tools/perf/Documentation/perf-trace.txt +++ b/tools/perf/Documentation/perf-trace.txt | |||
| @@ -97,6 +97,10 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs. | |||
| 97 | Show a summary of syscalls by thread with min, max, and average times (in | 97 | Show a summary of syscalls by thread with min, max, and average times (in |
| 98 | msec) and relative stddev. | 98 | msec) and relative stddev. |
| 99 | 99 | ||
| 100 | --tool_stats:: | ||
| 101 | Show tool stats such as number of times fd->pathname was discovered thru | ||
| 102 | hooking the open syscall return + vfs_getname or via reading /proc/pid/fd, etc. | ||
| 103 | |||
| 100 | SEE ALSO | 104 | SEE ALSO |
| 101 | -------- | 105 | -------- |
| 102 | linkperf:perf-record[1], linkperf:perf-script[1] | 106 | linkperf:perf-record[1], linkperf:perf-script[1] |
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index c873e039aafb..326a26e5fc1c 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf | |||
| @@ -365,6 +365,7 @@ LIB_OBJS += $(OUTPUT)util/vdso.o | |||
| 365 | LIB_OBJS += $(OUTPUT)util/stat.o | 365 | LIB_OBJS += $(OUTPUT)util/stat.o |
| 366 | LIB_OBJS += $(OUTPUT)util/record.o | 366 | LIB_OBJS += $(OUTPUT)util/record.o |
| 367 | LIB_OBJS += $(OUTPUT)util/srcline.o | 367 | LIB_OBJS += $(OUTPUT)util/srcline.o |
| 368 | LIB_OBJS += $(OUTPUT)util/data.o | ||
| 368 | 369 | ||
| 369 | LIB_OBJS += $(OUTPUT)ui/setup.o | 370 | LIB_OBJS += $(OUTPUT)ui/setup.o |
| 370 | LIB_OBJS += $(OUTPUT)ui/helpline.o | 371 | LIB_OBJS += $(OUTPUT)ui/helpline.o |
diff --git a/tools/perf/bench/numa.c b/tools/perf/bench/numa.c index 64fa01cfc34d..d4c83c60b9b2 100644 --- a/tools/perf/bench/numa.c +++ b/tools/perf/bench/numa.c | |||
| @@ -1120,7 +1120,7 @@ static void *worker_thread(void *__tdata) | |||
| 1120 | /* Check whether our max runtime timed out: */ | 1120 | /* Check whether our max runtime timed out: */ |
| 1121 | if (g->p.nr_secs) { | 1121 | if (g->p.nr_secs) { |
| 1122 | timersub(&stop, &start0, &diff); | 1122 | timersub(&stop, &start0, &diff); |
| 1123 | if (diff.tv_sec >= g->p.nr_secs) { | 1123 | if ((u32)diff.tv_sec >= g->p.nr_secs) { |
| 1124 | g->stop_work = true; | 1124 | g->stop_work = true; |
| 1125 | break; | 1125 | break; |
| 1126 | } | 1126 | } |
| @@ -1167,7 +1167,7 @@ static void *worker_thread(void *__tdata) | |||
| 1167 | runtime_ns_max += diff.tv_usec * 1000; | 1167 | runtime_ns_max += diff.tv_usec * 1000; |
| 1168 | 1168 | ||
| 1169 | if (details >= 0) { | 1169 | if (details >= 0) { |
| 1170 | printf(" #%2d / %2d: %14.2lf nsecs/op [val: %016lx]\n", | 1170 | printf(" #%2d / %2d: %14.2lf nsecs/op [val: %016"PRIx64"]\n", |
| 1171 | process_nr, thread_nr, runtime_ns_max / bytes_done, val); | 1171 | process_nr, thread_nr, runtime_ns_max / bytes_done, val); |
| 1172 | } | 1172 | } |
| 1173 | fflush(stdout); | 1173 | fflush(stdout); |
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 94f9a8e78117..03cfa592071f 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c | |||
| @@ -28,6 +28,7 @@ | |||
| 28 | #include "util/hist.h" | 28 | #include "util/hist.h" |
| 29 | #include "util/session.h" | 29 | #include "util/session.h" |
| 30 | #include "util/tool.h" | 30 | #include "util/tool.h" |
| 31 | #include "util/data.h" | ||
| 31 | #include "arch/common.h" | 32 | #include "arch/common.h" |
| 32 | 33 | ||
| 33 | #include <dlfcn.h> | 34 | #include <dlfcn.h> |
| @@ -199,9 +200,13 @@ static int __cmd_annotate(struct perf_annotate *ann) | |||
| 199 | struct perf_session *session; | 200 | struct perf_session *session; |
| 200 | struct perf_evsel *pos; | 201 | struct perf_evsel *pos; |
| 201 | u64 total_nr_samples; | 202 | u64 total_nr_samples; |
| 203 | struct perf_data_file file = { | ||
| 204 | .path = input_name, | ||
| 205 | .mode = PERF_DATA_MODE_READ, | ||
| 206 | .force = ann->force, | ||
| 207 | }; | ||
| 202 | 208 | ||
| 203 | session = perf_session__new(input_name, O_RDONLY, | 209 | session = perf_session__new(&file, false, &ann->tool); |
| 204 | ann->force, false, &ann->tool); | ||
| 205 | if (session == NULL) | 210 | if (session == NULL) |
| 206 | return -ENOMEM; | 211 | return -ENOMEM; |
| 207 | 212 | ||
| @@ -254,7 +259,7 @@ static int __cmd_annotate(struct perf_annotate *ann) | |||
| 254 | } | 259 | } |
| 255 | 260 | ||
| 256 | if (total_nr_samples == 0) { | 261 | if (total_nr_samples == 0) { |
| 257 | ui__error("The %s file has no samples!\n", session->filename); | 262 | ui__error("The %s file has no samples!\n", file.path); |
| 258 | goto out_delete; | 263 | goto out_delete; |
| 259 | } | 264 | } |
| 260 | 265 | ||
diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c index 8140b7b249fa..cfede86161d8 100644 --- a/tools/perf/builtin-buildid-cache.c +++ b/tools/perf/builtin-buildid-cache.c | |||
| @@ -221,8 +221,12 @@ static bool dso__missing_buildid_cache(struct dso *dso, int parm __maybe_unused) | |||
| 221 | 221 | ||
| 222 | static int build_id_cache__fprintf_missing(const char *filename, bool force, FILE *fp) | 222 | static int build_id_cache__fprintf_missing(const char *filename, bool force, FILE *fp) |
| 223 | { | 223 | { |
| 224 | struct perf_session *session = perf_session__new(filename, O_RDONLY, | 224 | struct perf_data_file file = { |
| 225 | force, false, NULL); | 225 | .path = filename, |
| 226 | .mode = PERF_DATA_MODE_READ, | ||
| 227 | .force = force, | ||
| 228 | }; | ||
| 229 | struct perf_session *session = perf_session__new(&file, false, NULL); | ||
| 226 | if (session == NULL) | 230 | if (session == NULL) |
| 227 | return -1; | 231 | return -1; |
| 228 | 232 | ||
diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index e74366a13218..ed3873b3e238 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include "util/parse-options.h" | 15 | #include "util/parse-options.h" |
| 16 | #include "util/session.h" | 16 | #include "util/session.h" |
| 17 | #include "util/symbol.h" | 17 | #include "util/symbol.h" |
| 18 | #include "util/data.h" | ||
| 18 | 19 | ||
| 19 | static int sysfs__fprintf_build_id(FILE *fp) | 20 | static int sysfs__fprintf_build_id(FILE *fp) |
| 20 | { | 21 | { |
| @@ -52,6 +53,11 @@ static bool dso__skip_buildid(struct dso *dso, int with_hits) | |||
| 52 | static int perf_session__list_build_ids(bool force, bool with_hits) | 53 | static int perf_session__list_build_ids(bool force, bool with_hits) |
| 53 | { | 54 | { |
| 54 | struct perf_session *session; | 55 | struct perf_session *session; |
| 56 | struct perf_data_file file = { | ||
| 57 | .path = input_name, | ||
| 58 | .mode = PERF_DATA_MODE_READ, | ||
| 59 | .force = force, | ||
| 60 | }; | ||
| 55 | 61 | ||
| 56 | symbol__elf_init(); | 62 | symbol__elf_init(); |
| 57 | /* | 63 | /* |
| @@ -60,15 +66,14 @@ static int perf_session__list_build_ids(bool force, bool with_hits) | |||
| 60 | if (filename__fprintf_build_id(input_name, stdout)) | 66 | if (filename__fprintf_build_id(input_name, stdout)) |
| 61 | goto out; | 67 | goto out; |
| 62 | 68 | ||
| 63 | session = perf_session__new(input_name, O_RDONLY, force, false, | 69 | session = perf_session__new(&file, false, &build_id__mark_dso_hit_ops); |
| 64 | &build_id__mark_dso_hit_ops); | ||
| 65 | if (session == NULL) | 70 | if (session == NULL) |
| 66 | return -1; | 71 | return -1; |
| 67 | /* | 72 | /* |
| 68 | * in pipe-mode, the only way to get the buildids is to parse | 73 | * in pipe-mode, the only way to get the buildids is to parse |
| 69 | * the record stream. Buildids are stored as RECORD_HEADER_BUILD_ID | 74 | * the record stream. Buildids are stored as RECORD_HEADER_BUILD_ID |
| 70 | */ | 75 | */ |
| 71 | if (with_hits || session->fd_pipe) | 76 | if (with_hits || perf_data_file__is_pipe(&file)) |
| 72 | perf_session__process_events(session, &build_id__mark_dso_hit_ops); | 77 | perf_session__process_events(session, &build_id__mark_dso_hit_ops); |
| 73 | 78 | ||
| 74 | perf_session__fprintf_dsos_buildid(session, stdout, dso__skip_buildid, with_hits); | 79 | perf_session__fprintf_dsos_buildid(session, stdout, dso__skip_buildid, with_hits); |
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 2a78dc806c39..419d27dd708b 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c | |||
| @@ -16,6 +16,7 @@ | |||
| 16 | #include "util/sort.h" | 16 | #include "util/sort.h" |
| 17 | #include "util/symbol.h" | 17 | #include "util/symbol.h" |
| 18 | #include "util/util.h" | 18 | #include "util/util.h" |
| 19 | #include "util/data.h" | ||
| 19 | 20 | ||
| 20 | #include <stdlib.h> | 21 | #include <stdlib.h> |
| 21 | #include <math.h> | 22 | #include <math.h> |
| @@ -42,7 +43,7 @@ struct diff_hpp_fmt { | |||
| 42 | 43 | ||
| 43 | struct data__file { | 44 | struct data__file { |
| 44 | struct perf_session *session; | 45 | struct perf_session *session; |
| 45 | const char *file; | 46 | struct perf_data_file file; |
| 46 | int idx; | 47 | int idx; |
| 47 | struct hists *hists; | 48 | struct hists *hists; |
| 48 | struct diff_hpp_fmt fmt[PERF_HPP_DIFF__MAX_INDEX]; | 49 | struct diff_hpp_fmt fmt[PERF_HPP_DIFF__MAX_INDEX]; |
| @@ -601,7 +602,7 @@ static void data__fprintf(void) | |||
| 601 | 602 | ||
| 602 | data__for_each_file(i, d) | 603 | data__for_each_file(i, d) |
| 603 | fprintf(stdout, "# [%d] %s %s\n", | 604 | fprintf(stdout, "# [%d] %s %s\n", |
| 604 | d->idx, d->file, | 605 | d->idx, d->file.path, |
| 605 | !d->idx ? "(Baseline)" : ""); | 606 | !d->idx ? "(Baseline)" : ""); |
| 606 | 607 | ||
| 607 | fprintf(stdout, "#\n"); | 608 | fprintf(stdout, "#\n"); |
| @@ -663,17 +664,16 @@ static int __cmd_diff(void) | |||
| 663 | int ret = -EINVAL, i; | 664 | int ret = -EINVAL, i; |
| 664 | 665 | ||
| 665 | data__for_each_file(i, d) { | 666 | data__for_each_file(i, d) { |
| 666 | d->session = perf_session__new(d->file, O_RDONLY, force, | 667 | d->session = perf_session__new(&d->file, false, &tool); |
| 667 | false, &tool); | ||
| 668 | if (!d->session) { | 668 | if (!d->session) { |
| 669 | pr_err("Failed to open %s\n", d->file); | 669 | pr_err("Failed to open %s\n", d->file.path); |
| 670 | ret = -ENOMEM; | 670 | ret = -ENOMEM; |
| 671 | goto out_delete; | 671 | goto out_delete; |
| 672 | } | 672 | } |
| 673 | 673 | ||
| 674 | ret = perf_session__process_events(d->session, &tool); | 674 | ret = perf_session__process_events(d->session, &tool); |
| 675 | if (ret) { | 675 | if (ret) { |
| 676 | pr_err("Failed to process %s\n", d->file); | 676 | pr_err("Failed to process %s\n", d->file.path); |
| 677 | goto out_delete; | 677 | goto out_delete; |
| 678 | } | 678 | } |
| 679 | 679 | ||
| @@ -1016,7 +1016,12 @@ static int data_init(int argc, const char **argv) | |||
| 1016 | return -ENOMEM; | 1016 | return -ENOMEM; |
| 1017 | 1017 | ||
| 1018 | data__for_each_file(i, d) { | 1018 | data__for_each_file(i, d) { |
| 1019 | d->file = use_default ? defaults[i] : argv[i]; | 1019 | struct perf_data_file *file = &d->file; |
| 1020 | |||
| 1021 | file->path = use_default ? defaults[i] : argv[i]; | ||
| 1022 | file->mode = PERF_DATA_MODE_READ, | ||
| 1023 | file->force = force, | ||
| 1024 | |||
| 1020 | d->idx = i; | 1025 | d->idx = i; |
| 1021 | } | 1026 | } |
| 1022 | 1027 | ||
diff --git a/tools/perf/builtin-evlist.c b/tools/perf/builtin-evlist.c index 05bd9dfe875c..20b0f12763b0 100644 --- a/tools/perf/builtin-evlist.c +++ b/tools/perf/builtin-evlist.c | |||
| @@ -14,13 +14,18 @@ | |||
| 14 | #include "util/parse-events.h" | 14 | #include "util/parse-events.h" |
| 15 | #include "util/parse-options.h" | 15 | #include "util/parse-options.h" |
| 16 | #include "util/session.h" | 16 | #include "util/session.h" |
| 17 | #include "util/data.h" | ||
| 17 | 18 | ||
| 18 | static int __cmd_evlist(const char *file_name, struct perf_attr_details *details) | 19 | static int __cmd_evlist(const char *file_name, struct perf_attr_details *details) |
| 19 | { | 20 | { |
| 20 | struct perf_session *session; | 21 | struct perf_session *session; |
| 21 | struct perf_evsel *pos; | 22 | struct perf_evsel *pos; |
| 23 | struct perf_data_file file = { | ||
| 24 | .path = file_name, | ||
| 25 | .mode = PERF_DATA_MODE_READ, | ||
| 26 | }; | ||
| 22 | 27 | ||
| 23 | session = perf_session__new(file_name, O_RDONLY, 0, false, NULL); | 28 | session = perf_session__new(&file, 0, NULL); |
| 24 | if (session == NULL) | 29 | if (session == NULL) |
| 25 | return -ENOMEM; | 30 | return -ENOMEM; |
| 26 | 31 | ||
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index f51a9637f69b..4aa6d7850bcc 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include "util/tool.h" | 15 | #include "util/tool.h" |
| 16 | #include "util/debug.h" | 16 | #include "util/debug.h" |
| 17 | #include "util/build-id.h" | 17 | #include "util/build-id.h" |
| 18 | #include "util/data.h" | ||
| 18 | 19 | ||
| 19 | #include "util/parse-options.h" | 20 | #include "util/parse-options.h" |
| 20 | 21 | ||
| @@ -345,6 +346,10 @@ static int __cmd_inject(struct perf_inject *inject) | |||
| 345 | { | 346 | { |
| 346 | struct perf_session *session; | 347 | struct perf_session *session; |
| 347 | int ret = -EINVAL; | 348 | int ret = -EINVAL; |
| 349 | struct perf_data_file file = { | ||
| 350 | .path = inject->input_name, | ||
| 351 | .mode = PERF_DATA_MODE_READ, | ||
| 352 | }; | ||
| 348 | 353 | ||
| 349 | signal(SIGINT, sig_handler); | 354 | signal(SIGINT, sig_handler); |
| 350 | 355 | ||
| @@ -355,7 +360,7 @@ static int __cmd_inject(struct perf_inject *inject) | |||
| 355 | inject->tool.tracing_data = perf_event__repipe_tracing_data; | 360 | inject->tool.tracing_data = perf_event__repipe_tracing_data; |
| 356 | } | 361 | } |
| 357 | 362 | ||
| 358 | session = perf_session__new(inject->input_name, O_RDONLY, false, true, &inject->tool); | 363 | session = perf_session__new(&file, true, &inject->tool); |
| 359 | if (session == NULL) | 364 | if (session == NULL) |
| 360 | return -ENOMEM; | 365 | return -ENOMEM; |
| 361 | 366 | ||
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 9b5f077fee5b..1126382659a9 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | 13 | ||
| 14 | #include "util/parse-options.h" | 14 | #include "util/parse-options.h" |
| 15 | #include "util/trace-event.h" | 15 | #include "util/trace-event.h" |
| 16 | #include "util/data.h" | ||
| 16 | 17 | ||
| 17 | #include "util/debug.h" | 18 | #include "util/debug.h" |
| 18 | 19 | ||
| @@ -486,8 +487,12 @@ static int __cmd_kmem(void) | |||
| 486 | { "kmem:kfree", perf_evsel__process_free_event, }, | 487 | { "kmem:kfree", perf_evsel__process_free_event, }, |
| 487 | { "kmem:kmem_cache_free", perf_evsel__process_free_event, }, | 488 | { "kmem:kmem_cache_free", perf_evsel__process_free_event, }, |
| 488 | }; | 489 | }; |
| 490 | struct perf_data_file file = { | ||
| 491 | .path = input_name, | ||
| 492 | .mode = PERF_DATA_MODE_READ, | ||
| 493 | }; | ||
| 489 | 494 | ||
| 490 | session = perf_session__new(input_name, O_RDONLY, 0, false, &perf_kmem); | 495 | session = perf_session__new(&file, false, &perf_kmem); |
| 491 | if (session == NULL) | 496 | if (session == NULL) |
| 492 | return -ENOMEM; | 497 | return -ENOMEM; |
| 493 | 498 | ||
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index cfa0b7979914..188bb29b373f 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include "util/tool.h" | 17 | #include "util/tool.h" |
| 18 | #include "util/stat.h" | 18 | #include "util/stat.h" |
| 19 | #include "util/top.h" | 19 | #include "util/top.h" |
| 20 | #include "util/data.h" | ||
| 20 | 21 | ||
| 21 | #include <sys/prctl.h> | 22 | #include <sys/prctl.h> |
| 22 | #include <sys/timerfd.h> | 23 | #include <sys/timerfd.h> |
| @@ -1215,10 +1216,13 @@ static int read_events(struct perf_kvm_stat *kvm) | |||
| 1215 | .comm = perf_event__process_comm, | 1216 | .comm = perf_event__process_comm, |
| 1216 | .ordered_samples = true, | 1217 | .ordered_samples = true, |
| 1217 | }; | 1218 | }; |
| 1219 | struct perf_data_file file = { | ||
| 1220 | .path = input_name, | ||
| 1221 | .mode = PERF_DATA_MODE_READ, | ||
| 1222 | }; | ||
| 1218 | 1223 | ||
| 1219 | kvm->tool = eops; | 1224 | kvm->tool = eops; |
| 1220 | kvm->session = perf_session__new(kvm->file_name, O_RDONLY, 0, false, | 1225 | kvm->session = perf_session__new(&file, false, &kvm->tool); |
| 1221 | &kvm->tool); | ||
| 1222 | if (!kvm->session) { | 1226 | if (!kvm->session) { |
| 1223 | pr_err("Initializing perf session failed\n"); | 1227 | pr_err("Initializing perf session failed\n"); |
| 1224 | return -EINVAL; | 1228 | return -EINVAL; |
| @@ -1450,6 +1454,9 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, | |||
| 1450 | "perf kvm stat live [<options>]", | 1454 | "perf kvm stat live [<options>]", |
| 1451 | NULL | 1455 | NULL |
| 1452 | }; | 1456 | }; |
| 1457 | struct perf_data_file file = { | ||
| 1458 | .mode = PERF_DATA_MODE_WRITE, | ||
| 1459 | }; | ||
| 1453 | 1460 | ||
| 1454 | 1461 | ||
| 1455 | /* event handling */ | 1462 | /* event handling */ |
| @@ -1514,7 +1521,7 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, | |||
| 1514 | /* | 1521 | /* |
| 1515 | * perf session | 1522 | * perf session |
| 1516 | */ | 1523 | */ |
| 1517 | kvm->session = perf_session__new(NULL, O_WRONLY, false, false, &kvm->tool); | 1524 | kvm->session = perf_session__new(&file, false, &kvm->tool); |
| 1518 | if (kvm->session == NULL) { | 1525 | if (kvm->session == NULL) { |
| 1519 | err = -ENOMEM; | 1526 | err = -ENOMEM; |
| 1520 | goto out; | 1527 | goto out; |
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 6a9076f165f4..33c7253295b9 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include "util/debug.h" | 15 | #include "util/debug.h" |
| 16 | #include "util/session.h" | 16 | #include "util/session.h" |
| 17 | #include "util/tool.h" | 17 | #include "util/tool.h" |
| 18 | #include "util/data.h" | ||
| 18 | 19 | ||
| 19 | #include <sys/types.h> | 20 | #include <sys/types.h> |
| 20 | #include <sys/prctl.h> | 21 | #include <sys/prctl.h> |
| @@ -853,8 +854,12 @@ static int __cmd_report(bool display_info) | |||
| 853 | .comm = perf_event__process_comm, | 854 | .comm = perf_event__process_comm, |
| 854 | .ordered_samples = true, | 855 | .ordered_samples = true, |
| 855 | }; | 856 | }; |
| 857 | struct perf_data_file file = { | ||
| 858 | .path = input_name, | ||
| 859 | .mode = PERF_DATA_MODE_READ, | ||
| 860 | }; | ||
| 856 | 861 | ||
| 857 | session = perf_session__new(input_name, O_RDONLY, 0, false, &eops); | 862 | session = perf_session__new(&file, false, &eops); |
| 858 | if (!session) { | 863 | if (!session) { |
| 859 | pr_err("Initializing perf session failed\n"); | 864 | pr_err("Initializing perf session failed\n"); |
| 860 | return -ENOMEM; | 865 | return -ENOMEM; |
diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c index 253133a6251d..31c00f186da1 100644 --- a/tools/perf/builtin-mem.c +++ b/tools/perf/builtin-mem.c | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include "util/trace-event.h" | 5 | #include "util/trace-event.h" |
| 6 | #include "util/tool.h" | 6 | #include "util/tool.h" |
| 7 | #include "util/session.h" | 7 | #include "util/session.h" |
| 8 | #include "util/data.h" | ||
| 8 | 9 | ||
| 9 | #define MEM_OPERATION_LOAD "load" | 10 | #define MEM_OPERATION_LOAD "load" |
| 10 | #define MEM_OPERATION_STORE "store" | 11 | #define MEM_OPERATION_STORE "store" |
| @@ -119,10 +120,14 @@ static int process_sample_event(struct perf_tool *tool, | |||
| 119 | 120 | ||
| 120 | static int report_raw_events(struct perf_mem *mem) | 121 | static int report_raw_events(struct perf_mem *mem) |
| 121 | { | 122 | { |
| 123 | struct perf_data_file file = { | ||
| 124 | .path = input_name, | ||
| 125 | .mode = PERF_DATA_MODE_READ, | ||
| 126 | }; | ||
| 122 | int err = -EINVAL; | 127 | int err = -EINVAL; |
| 123 | int ret; | 128 | int ret; |
| 124 | struct perf_session *session = perf_session__new(input_name, O_RDONLY, | 129 | struct perf_session *session = perf_session__new(&file, false, |
| 125 | 0, false, &mem->tool); | 130 | &mem->tool); |
| 126 | 131 | ||
| 127 | if (session == NULL) | 132 | if (session == NULL) |
| 128 | return -ENOMEM; | 133 | return -ENOMEM; |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 92ca5419073b..ab8d15e6e8cc 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
| @@ -24,6 +24,7 @@ | |||
| 24 | #include "util/symbol.h" | 24 | #include "util/symbol.h" |
| 25 | #include "util/cpumap.h" | 25 | #include "util/cpumap.h" |
| 26 | #include "util/thread_map.h" | 26 | #include "util/thread_map.h" |
| 27 | #include "util/data.h" | ||
| 27 | 28 | ||
| 28 | #include <unistd.h> | 29 | #include <unistd.h> |
| 29 | #include <sched.h> | 30 | #include <sched.h> |
| @@ -65,11 +66,10 @@ struct perf_record { | |||
| 65 | struct perf_tool tool; | 66 | struct perf_tool tool; |
| 66 | struct perf_record_opts opts; | 67 | struct perf_record_opts opts; |
| 67 | u64 bytes_written; | 68 | u64 bytes_written; |
| 68 | const char *output_name; | 69 | struct perf_data_file file; |
| 69 | struct perf_evlist *evlist; | 70 | struct perf_evlist *evlist; |
| 70 | struct perf_session *session; | 71 | struct perf_session *session; |
| 71 | const char *progname; | 72 | const char *progname; |
| 72 | int output; | ||
| 73 | int realtime_prio; | 73 | int realtime_prio; |
| 74 | bool no_buildid; | 74 | bool no_buildid; |
| 75 | bool no_buildid_cache; | 75 | bool no_buildid_cache; |
| @@ -84,11 +84,13 @@ static void advance_output(struct perf_record *rec, size_t size) | |||
| 84 | 84 | ||
| 85 | static int write_output(struct perf_record *rec, void *buf, size_t size) | 85 | static int write_output(struct perf_record *rec, void *buf, size_t size) |
| 86 | { | 86 | { |
| 87 | struct perf_data_file *file = &rec->file; | ||
| 88 | |||
| 87 | while (size) { | 89 | while (size) { |
| 88 | int ret = write(rec->output, buf, size); | 90 | int ret = write(file->fd, buf, size); |
| 89 | 91 | ||
| 90 | if (ret < 0) { | 92 | if (ret < 0) { |
| 91 | pr_err("failed to write\n"); | 93 | pr_err("failed to write perf data, error: %m\n"); |
| 92 | return -1; | 94 | return -1; |
| 93 | } | 95 | } |
| 94 | 96 | ||
| @@ -248,13 +250,14 @@ out: | |||
| 248 | 250 | ||
| 249 | static int process_buildids(struct perf_record *rec) | 251 | static int process_buildids(struct perf_record *rec) |
| 250 | { | 252 | { |
| 251 | u64 size = lseek(rec->output, 0, SEEK_CUR); | 253 | struct perf_data_file *file = &rec->file; |
| 254 | struct perf_session *session = rec->session; | ||
| 252 | 255 | ||
| 256 | u64 size = lseek(file->fd, 0, SEEK_CUR); | ||
| 253 | if (size == 0) | 257 | if (size == 0) |
| 254 | return 0; | 258 | return 0; |
| 255 | 259 | ||
| 256 | rec->session->fd = rec->output; | 260 | return __perf_session__process_events(session, rec->post_processing_offset, |
| 257 | return __perf_session__process_events(rec->session, rec->post_processing_offset, | ||
| 258 | size - rec->post_processing_offset, | 261 | size - rec->post_processing_offset, |
| 259 | size, &build_id__mark_dso_hit_ops); | 262 | size, &build_id__mark_dso_hit_ops); |
| 260 | } | 263 | } |
| @@ -262,17 +265,18 @@ static int process_buildids(struct perf_record *rec) | |||
| 262 | static void perf_record__exit(int status, void *arg) | 265 | static void perf_record__exit(int status, void *arg) |
| 263 | { | 266 | { |
| 264 | struct perf_record *rec = arg; | 267 | struct perf_record *rec = arg; |
| 268 | struct perf_data_file *file = &rec->file; | ||
| 265 | 269 | ||
| 266 | if (status != 0) | 270 | if (status != 0) |
| 267 | return; | 271 | return; |
| 268 | 272 | ||
| 269 | if (!rec->opts.pipe_output) { | 273 | if (!file->is_pipe) { |
| 270 | rec->session->header.data_size += rec->bytes_written; | 274 | rec->session->header.data_size += rec->bytes_written; |
| 271 | 275 | ||
| 272 | if (!rec->no_buildid) | 276 | if (!rec->no_buildid) |
| 273 | process_buildids(rec); | 277 | process_buildids(rec); |
| 274 | perf_session__write_header(rec->session, rec->evlist, | 278 | perf_session__write_header(rec->session, rec->evlist, |
| 275 | rec->output, true); | 279 | file->fd, true); |
| 276 | perf_session__delete(rec->session); | 280 | perf_session__delete(rec->session); |
| 277 | perf_evlist__delete(rec->evlist); | 281 | perf_evlist__delete(rec->evlist); |
| 278 | symbol__exit(); | 282 | symbol__exit(); |
| @@ -340,16 +344,14 @@ out: | |||
| 340 | 344 | ||
| 341 | static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | 345 | static int __cmd_record(struct perf_record *rec, int argc, const char **argv) |
| 342 | { | 346 | { |
| 343 | struct stat st; | 347 | int err, feat; |
| 344 | int flags; | ||
| 345 | int err, output, feat; | ||
| 346 | unsigned long waking = 0; | 348 | unsigned long waking = 0; |
| 347 | const bool forks = argc > 0; | 349 | const bool forks = argc > 0; |
| 348 | struct machine *machine; | 350 | struct machine *machine; |
| 349 | struct perf_tool *tool = &rec->tool; | 351 | struct perf_tool *tool = &rec->tool; |
| 350 | struct perf_record_opts *opts = &rec->opts; | 352 | struct perf_record_opts *opts = &rec->opts; |
| 351 | struct perf_evlist *evsel_list = rec->evlist; | 353 | struct perf_evlist *evsel_list = rec->evlist; |
| 352 | const char *output_name = rec->output_name; | 354 | struct perf_data_file *file = &rec->file; |
| 353 | struct perf_session *session; | 355 | struct perf_session *session; |
| 354 | bool disabled = false; | 356 | bool disabled = false; |
| 355 | 357 | ||
| @@ -361,39 +363,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
| 361 | signal(SIGUSR1, sig_handler); | 363 | signal(SIGUSR1, sig_handler); |
| 362 | signal(SIGTERM, sig_handler); | 364 | signal(SIGTERM, sig_handler); |
| 363 | 365 | ||
| 364 | if (!output_name) { | 366 | session = perf_session__new(file, false, NULL); |
| 365 | if (!fstat(STDOUT_FILENO, &st) && S_ISFIFO(st.st_mode)) | ||
| 366 | opts->pipe_output = true; | ||
| 367 | else | ||
| 368 | rec->output_name = output_name = "perf.data"; | ||
| 369 | } | ||
| 370 | if (output_name) { | ||
| 371 | if (!strcmp(output_name, "-")) | ||
| 372 | opts->pipe_output = true; | ||
| 373 | else if (!stat(output_name, &st) && st.st_size) { | ||
| 374 | char oldname[PATH_MAX]; | ||
| 375 | snprintf(oldname, sizeof(oldname), "%s.old", | ||
| 376 | output_name); | ||
| 377 | unlink(oldname); | ||
| 378 | rename(output_name, oldname); | ||
| 379 | } | ||
| 380 | } | ||
| 381 | |||
| 382 | flags = O_CREAT|O_RDWR|O_TRUNC; | ||
| 383 | |||
| 384 | if (opts->pipe_output) | ||
| 385 | output = STDOUT_FILENO; | ||
| 386 | else | ||
| 387 | output = open(output_name, flags, S_IRUSR | S_IWUSR); | ||
| 388 | if (output < 0) { | ||
| 389 | perror("failed to create output file"); | ||
| 390 | return -1; | ||
| 391 | } | ||
| 392 | |||
| 393 | rec->output = output; | ||
| 394 | |||
| 395 | session = perf_session__new(output_name, O_WRONLY, | ||
| 396 | true, false, NULL); | ||
| 397 | if (session == NULL) { | 367 | if (session == NULL) { |
| 398 | pr_err("Not enough memory for reading perf file header\n"); | 368 | pr_err("Not enough memory for reading perf file header\n"); |
| 399 | return -1; | 369 | return -1; |
| @@ -415,7 +385,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
| 415 | 385 | ||
| 416 | if (forks) { | 386 | if (forks) { |
| 417 | err = perf_evlist__prepare_workload(evsel_list, &opts->target, | 387 | err = perf_evlist__prepare_workload(evsel_list, &opts->target, |
| 418 | argv, opts->pipe_output, | 388 | argv, file->is_pipe, |
| 419 | true); | 389 | true); |
| 420 | if (err < 0) { | 390 | if (err < 0) { |
| 421 | pr_err("Couldn't run the workload!\n"); | 391 | 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 | */ | 406 | */ |
| 437 | on_exit(perf_record__exit, rec); | 407 | on_exit(perf_record__exit, rec); |
| 438 | 408 | ||
| 439 | if (opts->pipe_output) { | 409 | if (file->is_pipe) { |
| 440 | err = perf_header__write_pipe(output); | 410 | err = perf_header__write_pipe(file->fd); |
| 441 | if (err < 0) | 411 | if (err < 0) |
| 442 | goto out_delete_session; | 412 | goto out_delete_session; |
| 443 | } else { | 413 | } else { |
| 444 | err = perf_session__write_header(session, evsel_list, | 414 | err = perf_session__write_header(session, evsel_list, |
| 445 | output, false); | 415 | file->fd, false); |
| 446 | if (err < 0) | 416 | if (err < 0) |
| 447 | goto out_delete_session; | 417 | goto out_delete_session; |
| 448 | } | 418 | } |
| @@ -455,11 +425,11 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
| 455 | goto out_delete_session; | 425 | goto out_delete_session; |
| 456 | } | 426 | } |
| 457 | 427 | ||
| 458 | rec->post_processing_offset = lseek(output, 0, SEEK_CUR); | 428 | rec->post_processing_offset = lseek(file->fd, 0, SEEK_CUR); |
| 459 | 429 | ||
| 460 | machine = &session->machines.host; | 430 | machine = &session->machines.host; |
| 461 | 431 | ||
| 462 | if (opts->pipe_output) { | 432 | if (file->is_pipe) { |
| 463 | err = perf_event__synthesize_attrs(tool, session, | 433 | err = perf_event__synthesize_attrs(tool, session, |
| 464 | process_synthesized_event); | 434 | process_synthesized_event); |
| 465 | if (err < 0) { | 435 | if (err < 0) { |
| @@ -476,7 +446,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) | |||
| 476 | * return this more properly and also | 446 | * return this more properly and also |
| 477 | * propagate errors that now are calling die() | 447 | * propagate errors that now are calling die() |
| 478 | */ | 448 | */ |
| 479 | err = perf_event__synthesize_tracing_data(tool, output, evsel_list, | 449 | err = perf_event__synthesize_tracing_data(tool, file->fd, evsel_list, |
| 480 | process_synthesized_event); | 450 | process_synthesized_event); |
| 481 | if (err <= 0) { | 451 | if (err <= 0) { |
| 482 | pr_err("Couldn't record tracing data.\n"); | 452 | 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 | fprintf(stderr, | 553 | fprintf(stderr, |
| 584 | "[ perf record: Captured and wrote %.3f MB %s (~%" PRIu64 " samples) ]\n", | 554 | "[ perf record: Captured and wrote %.3f MB %s (~%" PRIu64 " samples) ]\n", |
| 585 | (double)rec->bytes_written / 1024.0 / 1024.0, | 555 | (double)rec->bytes_written / 1024.0 / 1024.0, |
| 586 | output_name, | 556 | file->path, |
| 587 | rec->bytes_written / 24); | 557 | rec->bytes_written / 24); |
| 588 | 558 | ||
| 589 | return 0; | 559 | return 0; |
| @@ -845,7 +815,7 @@ const struct option record_options[] = { | |||
| 845 | OPT_STRING('C', "cpu", &record.opts.target.cpu_list, "cpu", | 815 | OPT_STRING('C', "cpu", &record.opts.target.cpu_list, "cpu", |
| 846 | "list of cpus to monitor"), | 816 | "list of cpus to monitor"), |
| 847 | OPT_U64('c', "count", &record.opts.user_interval, "event period to sample"), | 817 | OPT_U64('c', "count", &record.opts.user_interval, "event period to sample"), |
| 848 | OPT_STRING('o', "output", &record.output_name, "file", | 818 | OPT_STRING('o', "output", &record.file.path, "file", |
| 849 | "output file name"), | 819 | "output file name"), |
| 850 | OPT_BOOLEAN('i', "no-inherit", &record.opts.no_inherit, | 820 | OPT_BOOLEAN('i', "no-inherit", &record.opts.no_inherit, |
| 851 | "child tasks do not inherit counters"), | 821 | "child tasks do not inherit counters"), |
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 21b5c2f54c2a..81addcabb356 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c | |||
| @@ -33,6 +33,7 @@ | |||
| 33 | #include "util/thread.h" | 33 | #include "util/thread.h" |
| 34 | #include "util/sort.h" | 34 | #include "util/sort.h" |
| 35 | #include "util/hist.h" | 35 | #include "util/hist.h" |
| 36 | #include "util/data.h" | ||
| 36 | #include "arch/common.h" | 37 | #include "arch/common.h" |
| 37 | 38 | ||
| 38 | #include <dlfcn.h> | 39 | #include <dlfcn.h> |
| @@ -48,6 +49,7 @@ struct perf_report { | |||
| 48 | bool show_threads; | 49 | bool show_threads; |
| 49 | bool inverted_callchain; | 50 | bool inverted_callchain; |
| 50 | bool mem_mode; | 51 | bool mem_mode; |
| 52 | int max_stack; | ||
| 51 | struct perf_read_values show_threads_values; | 53 | struct perf_read_values show_threads_values; |
| 52 | const char *pretty_printing_style; | 54 | const char *pretty_printing_style; |
| 53 | const char *cpu_list; | 55 | const char *cpu_list; |
| @@ -89,7 +91,8 @@ static int perf_report__add_mem_hist_entry(struct perf_tool *tool, | |||
| 89 | if ((sort__has_parent || symbol_conf.use_callchain) && | 91 | if ((sort__has_parent || symbol_conf.use_callchain) && |
| 90 | sample->callchain) { | 92 | sample->callchain) { |
| 91 | err = machine__resolve_callchain(machine, evsel, al->thread, | 93 | err = machine__resolve_callchain(machine, evsel, al->thread, |
| 92 | sample, &parent, al); | 94 | sample, &parent, al, |
| 95 | rep->max_stack); | ||
| 93 | if (err) | 96 | if (err) |
| 94 | return err; | 97 | return err; |
| 95 | } | 98 | } |
| @@ -180,7 +183,8 @@ static int perf_report__add_branch_hist_entry(struct perf_tool *tool, | |||
| 180 | if ((sort__has_parent || symbol_conf.use_callchain) | 183 | if ((sort__has_parent || symbol_conf.use_callchain) |
| 181 | && sample->callchain) { | 184 | && sample->callchain) { |
| 182 | err = machine__resolve_callchain(machine, evsel, al->thread, | 185 | err = machine__resolve_callchain(machine, evsel, al->thread, |
| 183 | sample, &parent, al); | 186 | sample, &parent, al, |
| 187 | rep->max_stack); | ||
| 184 | if (err) | 188 | if (err) |
| 185 | return err; | 189 | return err; |
| 186 | } | 190 | } |
| @@ -243,18 +247,21 @@ out: | |||
| 243 | return err; | 247 | return err; |
| 244 | } | 248 | } |
| 245 | 249 | ||
| 246 | static int perf_evsel__add_hist_entry(struct perf_evsel *evsel, | 250 | static int perf_evsel__add_hist_entry(struct perf_tool *tool, |
| 251 | struct perf_evsel *evsel, | ||
| 247 | struct addr_location *al, | 252 | struct addr_location *al, |
| 248 | struct perf_sample *sample, | 253 | struct perf_sample *sample, |
| 249 | struct machine *machine) | 254 | struct machine *machine) |
| 250 | { | 255 | { |
| 256 | struct perf_report *rep = container_of(tool, struct perf_report, tool); | ||
| 251 | struct symbol *parent = NULL; | 257 | struct symbol *parent = NULL; |
| 252 | int err = 0; | 258 | int err = 0; |
| 253 | struct hist_entry *he; | 259 | struct hist_entry *he; |
| 254 | 260 | ||
| 255 | if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) { | 261 | if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) { |
| 256 | err = machine__resolve_callchain(machine, evsel, al->thread, | 262 | err = machine__resolve_callchain(machine, evsel, al->thread, |
| 257 | sample, &parent, al); | 263 | sample, &parent, al, |
| 264 | rep->max_stack); | ||
| 258 | if (err) | 265 | if (err) |
| 259 | return err; | 266 | return err; |
| 260 | } | 267 | } |
| @@ -331,7 +338,8 @@ static int process_sample_event(struct perf_tool *tool, | |||
| 331 | if (al.map != NULL) | 338 | if (al.map != NULL) |
| 332 | al.map->dso->hit = 1; | 339 | al.map->dso->hit = 1; |
| 333 | 340 | ||
| 334 | ret = perf_evsel__add_hist_entry(evsel, &al, sample, machine); | 341 | ret = perf_evsel__add_hist_entry(tool, evsel, &al, sample, |
| 342 | machine); | ||
| 335 | if (ret < 0) | 343 | if (ret < 0) |
| 336 | pr_debug("problem incrementing symbol period, skipping event\n"); | 344 | pr_debug("problem incrementing symbol period, skipping event\n"); |
| 337 | } | 345 | } |
| @@ -367,8 +375,9 @@ static int perf_report__setup_sample_type(struct perf_report *rep) | |||
| 367 | { | 375 | { |
| 368 | struct perf_session *self = rep->session; | 376 | struct perf_session *self = rep->session; |
| 369 | u64 sample_type = perf_evlist__combined_sample_type(self->evlist); | 377 | u64 sample_type = perf_evlist__combined_sample_type(self->evlist); |
| 378 | bool is_pipe = perf_data_file__is_pipe(self->file); | ||
| 370 | 379 | ||
| 371 | if (!self->fd_pipe && !(sample_type & PERF_SAMPLE_CALLCHAIN)) { | 380 | if (!is_pipe && !(sample_type & PERF_SAMPLE_CALLCHAIN)) { |
| 372 | if (sort__has_parent) { | 381 | if (sort__has_parent) { |
| 373 | ui__error("Selected --sort parent, but no " | 382 | ui__error("Selected --sort parent, but no " |
| 374 | "callchain data. Did you call " | 383 | "callchain data. Did you call " |
| @@ -391,7 +400,7 @@ static int perf_report__setup_sample_type(struct perf_report *rep) | |||
| 391 | } | 400 | } |
| 392 | 401 | ||
| 393 | if (sort__mode == SORT_MODE__BRANCH) { | 402 | if (sort__mode == SORT_MODE__BRANCH) { |
| 394 | if (!self->fd_pipe && | 403 | if (!is_pipe && |
| 395 | !(sample_type & PERF_SAMPLE_BRANCH_STACK)) { | 404 | !(sample_type & PERF_SAMPLE_BRANCH_STACK)) { |
| 396 | ui__error("Selected -b but no branch data. " | 405 | ui__error("Selected -b but no branch data. " |
| 397 | "Did you call perf record without -b?\n"); | 406 | "Did you call perf record without -b?\n"); |
| @@ -487,6 +496,7 @@ static int __cmd_report(struct perf_report *rep) | |||
| 487 | struct map *kernel_map; | 496 | struct map *kernel_map; |
| 488 | struct kmap *kernel_kmap; | 497 | struct kmap *kernel_kmap; |
| 489 | const char *help = "For a higher level overview, try: perf report --sort comm,dso"; | 498 | const char *help = "For a higher level overview, try: perf report --sort comm,dso"; |
| 499 | struct perf_data_file *file = session->file; | ||
| 490 | 500 | ||
| 491 | signal(SIGINT, sig_handler); | 501 | signal(SIGINT, sig_handler); |
| 492 | 502 | ||
| @@ -571,7 +581,7 @@ static int __cmd_report(struct perf_report *rep) | |||
| 571 | return 0; | 581 | return 0; |
| 572 | 582 | ||
| 573 | if (nr_samples == 0) { | 583 | if (nr_samples == 0) { |
| 574 | ui__error("The %s file has no samples!\n", session->filename); | 584 | ui__error("The %s file has no samples!\n", file->path); |
| 575 | return 0; | 585 | return 0; |
| 576 | } | 586 | } |
| 577 | 587 | ||
| @@ -769,6 +779,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 769 | .ordered_samples = true, | 779 | .ordered_samples = true, |
| 770 | .ordering_requires_timestamps = true, | 780 | .ordering_requires_timestamps = true, |
| 771 | }, | 781 | }, |
| 782 | .max_stack = PERF_MAX_STACK_DEPTH, | ||
| 772 | .pretty_printing_style = "normal", | 783 | .pretty_printing_style = "normal", |
| 773 | }; | 784 | }; |
| 774 | const struct option options[] = { | 785 | const struct option options[] = { |
| @@ -809,6 +820,10 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 809 | OPT_CALLBACK_DEFAULT('g', "call-graph", &report, "output_type,min_percent[,print_limit],call_order", | 820 | OPT_CALLBACK_DEFAULT('g', "call-graph", &report, "output_type,min_percent[,print_limit],call_order", |
| 810 | "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold, optional print limit, callchain order, key (function or address). " | 821 | "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold, optional print limit, callchain order, key (function or address). " |
| 811 | "Default: fractal,0.5,callee,function", &parse_callchain_opt, callchain_default_opt), | 822 | "Default: fractal,0.5,callee,function", &parse_callchain_opt, callchain_default_opt), |
| 823 | OPT_INTEGER(0, "max-stack", &report.max_stack, | ||
| 824 | "Set the maximum stack depth when parsing the callchain, " | ||
| 825 | "anything beyond the specified depth will be ignored. " | ||
| 826 | "Default: " __stringify(PERF_MAX_STACK_DEPTH)), | ||
| 812 | OPT_BOOLEAN('G', "inverted", &report.inverted_callchain, | 827 | OPT_BOOLEAN('G', "inverted", &report.inverted_callchain, |
| 813 | "alias for inverted call graph"), | 828 | "alias for inverted call graph"), |
| 814 | OPT_CALLBACK(0, "ignore-callees", NULL, "regex", | 829 | 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 | "Don't show entries under that percent", parse_percent_limit), | 872 | "Don't show entries under that percent", parse_percent_limit), |
| 858 | OPT_END() | 873 | OPT_END() |
| 859 | }; | 874 | }; |
| 875 | struct perf_data_file file = { | ||
| 876 | .mode = PERF_DATA_MODE_READ, | ||
| 877 | }; | ||
| 860 | 878 | ||
| 861 | perf_config(perf_report_config, &report); | 879 | perf_config(perf_report_config, &report); |
| 862 | 880 | ||
| @@ -886,9 +904,11 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 886 | perf_hpp__init(); | 904 | perf_hpp__init(); |
| 887 | } | 905 | } |
| 888 | 906 | ||
| 907 | file.path = input_name; | ||
| 908 | file.force = report.force; | ||
| 909 | |||
| 889 | repeat: | 910 | repeat: |
| 890 | session = perf_session__new(input_name, O_RDONLY, | 911 | session = perf_session__new(&file, false, &report.tool); |
| 891 | report.force, false, &report.tool); | ||
| 892 | if (session == NULL) | 912 | if (session == NULL) |
| 893 | return -ENOMEM; | 913 | return -ENOMEM; |
| 894 | 914 | ||
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index d8c51b2f263f..5a46b102eb08 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c | |||
| @@ -1446,8 +1446,12 @@ static int perf_sched__read_events(struct perf_sched *sched, | |||
| 1446 | { "sched:sched_migrate_task", process_sched_migrate_task_event, }, | 1446 | { "sched:sched_migrate_task", process_sched_migrate_task_event, }, |
| 1447 | }; | 1447 | }; |
| 1448 | struct perf_session *session; | 1448 | struct perf_session *session; |
| 1449 | struct perf_data_file file = { | ||
| 1450 | .path = input_name, | ||
| 1451 | .mode = PERF_DATA_MODE_READ, | ||
| 1452 | }; | ||
| 1449 | 1453 | ||
| 1450 | session = perf_session__new(input_name, O_RDONLY, 0, false, &sched->tool); | 1454 | session = perf_session__new(&file, false, &sched->tool); |
| 1451 | if (session == NULL) { | 1455 | if (session == NULL) { |
| 1452 | pr_debug("No Memory for session\n"); | 1456 | pr_debug("No Memory for session\n"); |
| 1453 | return -1; | 1457 | return -1; |
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 9c333ff3dfeb..27de6068049d 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include "util/evlist.h" | 15 | #include "util/evlist.h" |
| 16 | #include "util/evsel.h" | 16 | #include "util/evsel.h" |
| 17 | #include "util/sort.h" | 17 | #include "util/sort.h" |
| 18 | #include "util/data.h" | ||
| 18 | #include <linux/bitmap.h> | 19 | #include <linux/bitmap.h> |
| 19 | 20 | ||
| 20 | static char const *script_name; | 21 | static char const *script_name; |
| @@ -409,7 +410,9 @@ static void print_sample_bts(union perf_event *event, | |||
| 409 | printf(" => "); | 410 | printf(" => "); |
| 410 | 411 | ||
| 411 | /* print branch_to information */ | 412 | /* print branch_to information */ |
| 412 | if (PRINT_FIELD(ADDR)) | 413 | if (PRINT_FIELD(ADDR) || |
| 414 | ((evsel->attr.sample_type & PERF_SAMPLE_ADDR) && | ||
| 415 | !output[attr->type].user_set)) | ||
| 413 | print_sample_addr(event, sample, machine, thread, attr); | 416 | print_sample_addr(event, sample, machine, thread, attr); |
| 414 | 417 | ||
| 415 | printf("\n"); | 418 | printf("\n"); |
| @@ -1113,10 +1116,14 @@ int find_scripts(char **scripts_array, char **scripts_path_array) | |||
| 1113 | char scripts_path[MAXPATHLEN], lang_path[MAXPATHLEN]; | 1116 | char scripts_path[MAXPATHLEN], lang_path[MAXPATHLEN]; |
| 1114 | DIR *scripts_dir, *lang_dir; | 1117 | DIR *scripts_dir, *lang_dir; |
| 1115 | struct perf_session *session; | 1118 | struct perf_session *session; |
| 1119 | struct perf_data_file file = { | ||
| 1120 | .path = input_name, | ||
| 1121 | .mode = PERF_DATA_MODE_READ, | ||
| 1122 | }; | ||
| 1116 | char *temp; | 1123 | char *temp; |
| 1117 | int i = 0; | 1124 | int i = 0; |
| 1118 | 1125 | ||
| 1119 | session = perf_session__new(input_name, O_RDONLY, 0, false, NULL); | 1126 | session = perf_session__new(&file, false, NULL); |
| 1120 | if (!session) | 1127 | if (!session) |
| 1121 | return -1; | 1128 | return -1; |
| 1122 | 1129 | ||
| @@ -1317,12 +1324,17 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1317 | "perf script [<options>] <top-script> [script-args]", | 1324 | "perf script [<options>] <top-script> [script-args]", |
| 1318 | NULL | 1325 | NULL |
| 1319 | }; | 1326 | }; |
| 1327 | struct perf_data_file file = { | ||
| 1328 | .mode = PERF_DATA_MODE_READ, | ||
| 1329 | }; | ||
| 1320 | 1330 | ||
| 1321 | setup_scripting(); | 1331 | setup_scripting(); |
| 1322 | 1332 | ||
| 1323 | argc = parse_options(argc, argv, options, script_usage, | 1333 | argc = parse_options(argc, argv, options, script_usage, |
| 1324 | PARSE_OPT_STOP_AT_NON_OPTION); | 1334 | PARSE_OPT_STOP_AT_NON_OPTION); |
| 1325 | 1335 | ||
| 1336 | file.path = input_name; | ||
| 1337 | |||
| 1326 | if (argc > 1 && !strncmp(argv[0], "rec", strlen("rec"))) { | 1338 | if (argc > 1 && !strncmp(argv[0], "rec", strlen("rec"))) { |
| 1327 | rec_script_path = get_script_path(argv[1], RECORD_SUFFIX); | 1339 | rec_script_path = get_script_path(argv[1], RECORD_SUFFIX); |
| 1328 | if (!rec_script_path) | 1340 | if (!rec_script_path) |
| @@ -1486,8 +1498,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1486 | if (!script_name) | 1498 | if (!script_name) |
| 1487 | setup_pager(); | 1499 | setup_pager(); |
| 1488 | 1500 | ||
| 1489 | session = perf_session__new(input_name, O_RDONLY, 0, false, | 1501 | session = perf_session__new(&file, false, &perf_script); |
| 1490 | &perf_script); | ||
| 1491 | if (session == NULL) | 1502 | if (session == NULL) |
| 1492 | return -ENOMEM; | 1503 | return -ENOMEM; |
| 1493 | 1504 | ||
| @@ -1514,7 +1525,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1514 | return -1; | 1525 | return -1; |
| 1515 | } | 1526 | } |
| 1516 | 1527 | ||
| 1517 | input = open(session->filename, O_RDONLY); /* input_name */ | 1528 | input = open(file.path, O_RDONLY); /* input_name */ |
| 1518 | if (input < 0) { | 1529 | if (input < 0) { |
| 1519 | perror("failed to open file"); | 1530 | perror("failed to open file"); |
| 1520 | return -1; | 1531 | return -1; |
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index c2e02319347a..e11c61d9bda4 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c | |||
| @@ -36,6 +36,7 @@ | |||
| 36 | #include "util/session.h" | 36 | #include "util/session.h" |
| 37 | #include "util/svghelper.h" | 37 | #include "util/svghelper.h" |
| 38 | #include "util/tool.h" | 38 | #include "util/tool.h" |
| 39 | #include "util/data.h" | ||
| 39 | 40 | ||
| 40 | #define SUPPORT_OLD_POWER_EVENTS 1 | 41 | #define SUPPORT_OLD_POWER_EVENTS 1 |
| 41 | #define PWR_EVENT_EXIT -1 | 42 | #define PWR_EVENT_EXIT -1 |
| @@ -990,8 +991,13 @@ static int __cmd_timechart(const char *output_name) | |||
| 990 | { "power:power_frequency", process_sample_power_frequency }, | 991 | { "power:power_frequency", process_sample_power_frequency }, |
| 991 | #endif | 992 | #endif |
| 992 | }; | 993 | }; |
| 993 | struct perf_session *session = perf_session__new(input_name, O_RDONLY, | 994 | struct perf_data_file file = { |
| 994 | 0, false, &perf_timechart); | 995 | .path = input_name, |
| 996 | .mode = PERF_DATA_MODE_READ, | ||
| 997 | }; | ||
| 998 | |||
| 999 | struct perf_session *session = perf_session__new(&file, false, | ||
| 1000 | &perf_timechart); | ||
| 995 | int ret = -EINVAL; | 1001 | int ret = -EINVAL; |
| 996 | 1002 | ||
| 997 | if (session == NULL) | 1003 | if (session == NULL) |
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 65c49b2f51c1..386d83324a8d 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c | |||
| @@ -770,7 +770,8 @@ static void perf_event__process_sample(struct perf_tool *tool, | |||
| 770 | sample->callchain) { | 770 | sample->callchain) { |
| 771 | err = machine__resolve_callchain(machine, evsel, | 771 | err = machine__resolve_callchain(machine, evsel, |
| 772 | al.thread, sample, | 772 | al.thread, sample, |
| 773 | &parent, &al); | 773 | &parent, &al, |
| 774 | top->max_stack); | ||
| 774 | if (err) | 775 | if (err) |
| 775 | return; | 776 | return; |
| 776 | } | 777 | } |
| @@ -929,11 +930,8 @@ static int __cmd_top(struct perf_top *top) | |||
| 929 | struct perf_record_opts *opts = &top->record_opts; | 930 | struct perf_record_opts *opts = &top->record_opts; |
| 930 | pthread_t thread; | 931 | pthread_t thread; |
| 931 | int ret; | 932 | int ret; |
| 932 | /* | 933 | |
| 933 | * FIXME: perf_session__new should allow passing a O_MMAP, so that all this | 934 | top->session = perf_session__new(NULL, false, NULL); |
| 934 | * mmap reading, etc is encapsulated in it. Use O_WRONLY for now. | ||
| 935 | */ | ||
| 936 | top->session = perf_session__new(NULL, O_WRONLY, false, false, NULL); | ||
| 937 | if (top->session == NULL) | 935 | if (top->session == NULL) |
| 938 | return -ENOMEM; | 936 | return -ENOMEM; |
| 939 | 937 | ||
| @@ -1050,10 +1048,11 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1050 | .user_freq = UINT_MAX, | 1048 | .user_freq = UINT_MAX, |
| 1051 | .user_interval = ULLONG_MAX, | 1049 | .user_interval = ULLONG_MAX, |
| 1052 | .freq = 4000, /* 4 KHz */ | 1050 | .freq = 4000, /* 4 KHz */ |
| 1053 | .target = { | 1051 | .target = { |
| 1054 | .uses_mmap = true, | 1052 | .uses_mmap = true, |
| 1055 | }, | 1053 | }, |
| 1056 | }, | 1054 | }, |
| 1055 | .max_stack = PERF_MAX_STACK_DEPTH, | ||
| 1057 | .sym_pcnt_filter = 5, | 1056 | .sym_pcnt_filter = 5, |
| 1058 | }; | 1057 | }; |
| 1059 | struct perf_record_opts *opts = &top.record_opts; | 1058 | 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 | OPT_CALLBACK_DEFAULT('G', "call-graph", &top.record_opts, | 1111 | OPT_CALLBACK_DEFAULT('G', "call-graph", &top.record_opts, |
| 1113 | "mode[,dump_size]", record_callchain_help, | 1112 | "mode[,dump_size]", record_callchain_help, |
| 1114 | &parse_callchain_opt, "fp"), | 1113 | &parse_callchain_opt, "fp"), |
| 1114 | OPT_INTEGER(0, "max-stack", &top.max_stack, | ||
| 1115 | "Set the maximum stack depth when parsing the callchain. " | ||
| 1116 | "Default: " __stringify(PERF_MAX_STACK_DEPTH)), | ||
| 1115 | OPT_CALLBACK(0, "ignore-callees", NULL, "regex", | 1117 | OPT_CALLBACK(0, "ignore-callees", NULL, "regex", |
| 1116 | "ignore callees of these functions in call graphs", | 1118 | "ignore callees of these functions in call graphs", |
| 1117 | report_parse_ignore_callees_opt), | 1119 | report_parse_ignore_callees_opt), |
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index d0f91fe755a3..fa620bc1db69 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
| @@ -951,7 +951,10 @@ fail: | |||
| 951 | 951 | ||
| 952 | struct trace { | 952 | struct trace { |
| 953 | struct perf_tool tool; | 953 | struct perf_tool tool; |
| 954 | int audit_machine; | 954 | struct { |
| 955 | int machine; | ||
| 956 | int open_id; | ||
| 957 | } audit; | ||
| 955 | struct { | 958 | struct { |
| 956 | int max; | 959 | int max; |
| 957 | struct syscall *table; | 960 | struct syscall *table; |
| @@ -965,40 +968,24 @@ struct trace { | |||
| 965 | struct strlist *ev_qualifier; | 968 | struct strlist *ev_qualifier; |
| 966 | bool not_ev_qualifier; | 969 | bool not_ev_qualifier; |
| 967 | bool live; | 970 | bool live; |
| 971 | const char *last_vfs_getname; | ||
| 968 | struct intlist *tid_list; | 972 | struct intlist *tid_list; |
| 969 | struct intlist *pid_list; | 973 | struct intlist *pid_list; |
| 970 | bool sched; | 974 | bool sched; |
| 971 | bool multiple_threads; | 975 | bool multiple_threads; |
| 972 | bool summary; | 976 | bool summary; |
| 973 | bool show_comm; | 977 | bool show_comm; |
| 978 | bool show_tool_stats; | ||
| 974 | double duration_filter; | 979 | double duration_filter; |
| 975 | double runtime_ms; | 980 | double runtime_ms; |
| 981 | struct { | ||
| 982 | u64 vfs_getname, proc_getname; | ||
| 983 | } stats; | ||
| 976 | }; | 984 | }; |
| 977 | 985 | ||
| 978 | static int thread__read_fd_path(struct thread *thread, int fd) | 986 | static int trace__set_fd_pathname(struct thread *thread, int fd, const char *pathname) |
| 979 | { | 987 | { |
| 980 | struct thread_trace *ttrace = thread->priv; | 988 | struct thread_trace *ttrace = thread->priv; |
| 981 | char linkname[PATH_MAX], pathname[PATH_MAX]; | ||
| 982 | struct stat st; | ||
| 983 | int ret; | ||
| 984 | |||
| 985 | if (thread->pid_ == thread->tid) { | ||
| 986 | scnprintf(linkname, sizeof(linkname), | ||
| 987 | "/proc/%d/fd/%d", thread->pid_, fd); | ||
| 988 | } else { | ||
| 989 | scnprintf(linkname, sizeof(linkname), | ||
| 990 | "/proc/%d/task/%d/fd/%d", thread->pid_, thread->tid, fd); | ||
| 991 | } | ||
| 992 | |||
| 993 | if (lstat(linkname, &st) < 0 || st.st_size + 1 > (off_t)sizeof(pathname)) | ||
| 994 | return -1; | ||
| 995 | |||
| 996 | ret = readlink(linkname, pathname, sizeof(pathname)); | ||
| 997 | |||
| 998 | if (ret < 0 || ret > st.st_size) | ||
| 999 | return -1; | ||
| 1000 | |||
| 1001 | pathname[ret] = '\0'; | ||
| 1002 | 989 | ||
| 1003 | if (fd > ttrace->paths.max) { | 990 | if (fd > ttrace->paths.max) { |
| 1004 | char **npath = realloc(ttrace->paths.table, (fd + 1) * sizeof(char *)); | 991 | 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 | return ttrace->paths.table[fd] != NULL ? 0 : -1; | 1009 | return ttrace->paths.table[fd] != NULL ? 0 : -1; |
| 1023 | } | 1010 | } |
| 1024 | 1011 | ||
| 1025 | static const char *thread__fd_path(struct thread *thread, int fd, bool live) | 1012 | static int thread__read_fd_path(struct thread *thread, int fd) |
| 1013 | { | ||
| 1014 | char linkname[PATH_MAX], pathname[PATH_MAX]; | ||
| 1015 | struct stat st; | ||
| 1016 | int ret; | ||
| 1017 | |||
| 1018 | if (thread->pid_ == thread->tid) { | ||
| 1019 | scnprintf(linkname, sizeof(linkname), | ||
| 1020 | "/proc/%d/fd/%d", thread->pid_, fd); | ||
| 1021 | } else { | ||
| 1022 | scnprintf(linkname, sizeof(linkname), | ||
| 1023 | "/proc/%d/task/%d/fd/%d", thread->pid_, thread->tid, fd); | ||
| 1024 | } | ||
| 1025 | |||
| 1026 | if (lstat(linkname, &st) < 0 || st.st_size + 1 > (off_t)sizeof(pathname)) | ||
| 1027 | return -1; | ||
| 1028 | |||
| 1029 | ret = readlink(linkname, pathname, sizeof(pathname)); | ||
| 1030 | |||
| 1031 | if (ret < 0 || ret > st.st_size) | ||
| 1032 | return -1; | ||
| 1033 | |||
| 1034 | pathname[ret] = '\0'; | ||
| 1035 | return trace__set_fd_pathname(thread, fd, pathname); | ||
| 1036 | } | ||
| 1037 | |||
| 1038 | static const char *thread__fd_path(struct thread *thread, int fd, | ||
| 1039 | struct trace *trace) | ||
| 1026 | { | 1040 | { |
| 1027 | struct thread_trace *ttrace = thread->priv; | 1041 | struct thread_trace *ttrace = thread->priv; |
| 1028 | 1042 | ||
| @@ -1032,9 +1046,13 @@ static const char *thread__fd_path(struct thread *thread, int fd, bool live) | |||
| 1032 | if (fd < 0) | 1046 | if (fd < 0) |
| 1033 | return NULL; | 1047 | return NULL; |
| 1034 | 1048 | ||
| 1035 | if ((fd > ttrace->paths.max || ttrace->paths.table[fd] == NULL) && | 1049 | if ((fd > ttrace->paths.max || ttrace->paths.table[fd] == NULL)) |
| 1036 | (!live || thread__read_fd_path(thread, fd))) | 1050 | if (!trace->live) |
| 1037 | return NULL; | 1051 | return NULL; |
| 1052 | ++trace->stats.proc_getname; | ||
| 1053 | if (thread__read_fd_path(thread, fd)) { | ||
| 1054 | return NULL; | ||
| 1055 | } | ||
| 1038 | 1056 | ||
| 1039 | return ttrace->paths.table[fd]; | 1057 | return ttrace->paths.table[fd]; |
| 1040 | } | 1058 | } |
| @@ -1044,7 +1062,7 @@ static size_t syscall_arg__scnprintf_fd(char *bf, size_t size, | |||
| 1044 | { | 1062 | { |
| 1045 | int fd = arg->val; | 1063 | int fd = arg->val; |
| 1046 | size_t printed = scnprintf(bf, size, "%d", fd); | 1064 | size_t printed = scnprintf(bf, size, "%d", fd); |
| 1047 | const char *path = thread__fd_path(arg->thread, fd, arg->trace->live); | 1065 | const char *path = thread__fd_path(arg->thread, fd, arg->trace); |
| 1048 | 1066 | ||
| 1049 | if (path) | 1067 | if (path) |
| 1050 | printed += scnprintf(bf + printed, size - printed, "<%s>", path); | 1068 | 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 | } | 1098 | } |
| 1081 | 1099 | ||
| 1082 | static bool done = false; | 1100 | static bool done = false; |
| 1101 | static bool interrupted = false; | ||
| 1083 | 1102 | ||
| 1084 | static void sig_handler(int sig __maybe_unused) | 1103 | static void sig_handler(int sig) |
| 1085 | { | 1104 | { |
| 1086 | done = true; | 1105 | done = true; |
| 1106 | interrupted = sig == SIGINT; | ||
| 1087 | } | 1107 | } |
| 1088 | 1108 | ||
| 1089 | static size_t trace__fprintf_entry_head(struct trace *trace, struct thread *thread, | 1109 | 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 | { | 1201 | { |
| 1182 | char tp_name[128]; | 1202 | char tp_name[128]; |
| 1183 | struct syscall *sc; | 1203 | struct syscall *sc; |
| 1184 | const char *name = audit_syscall_to_name(id, trace->audit_machine); | 1204 | const char *name = audit_syscall_to_name(id, trace->audit.machine); |
| 1185 | 1205 | ||
| 1186 | if (name == NULL) | 1206 | if (name == NULL) |
| 1187 | return -1; | 1207 | return -1; |
| @@ -1445,6 +1465,12 @@ static int trace__sys_exit(struct trace *trace, struct perf_evsel *evsel, | |||
| 1445 | 1465 | ||
| 1446 | ret = perf_evsel__intval(evsel, sample, "ret"); | 1466 | ret = perf_evsel__intval(evsel, sample, "ret"); |
| 1447 | 1467 | ||
| 1468 | if (id == trace->audit.open_id && ret >= 0 && trace->last_vfs_getname) { | ||
| 1469 | trace__set_fd_pathname(thread, ret, trace->last_vfs_getname); | ||
| 1470 | trace->last_vfs_getname = NULL; | ||
| 1471 | ++trace->stats.vfs_getname; | ||
| 1472 | } | ||
| 1473 | |||
| 1448 | ttrace = thread->priv; | 1474 | ttrace = thread->priv; |
| 1449 | 1475 | ||
| 1450 | ttrace->exit_time = sample->time; | 1476 | ttrace->exit_time = sample->time; |
| @@ -1489,6 +1515,13 @@ out: | |||
| 1489 | return 0; | 1515 | return 0; |
| 1490 | } | 1516 | } |
| 1491 | 1517 | ||
| 1518 | static int trace__vfs_getname(struct trace *trace, struct perf_evsel *evsel, | ||
| 1519 | struct perf_sample *sample) | ||
| 1520 | { | ||
| 1521 | trace->last_vfs_getname = perf_evsel__rawptr(evsel, sample, "pathname"); | ||
| 1522 | return 0; | ||
| 1523 | } | ||
| 1524 | |||
| 1492 | static int trace__sched_stat_runtime(struct trace *trace, struct perf_evsel *evsel, | 1525 | static int trace__sched_stat_runtime(struct trace *trace, struct perf_evsel *evsel, |
| 1493 | struct perf_sample *sample) | 1526 | struct perf_sample *sample) |
| 1494 | { | 1527 | { |
| @@ -1611,6 +1644,22 @@ static int trace__record(int argc, const char **argv) | |||
| 1611 | 1644 | ||
| 1612 | static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp); | 1645 | static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp); |
| 1613 | 1646 | ||
| 1647 | static void perf_evlist__add_vfs_getname(struct perf_evlist *evlist) | ||
| 1648 | { | ||
| 1649 | struct perf_evsel *evsel = perf_evsel__newtp("probe", "vfs_getname", | ||
| 1650 | evlist->nr_entries); | ||
| 1651 | if (evsel == NULL) | ||
| 1652 | return; | ||
| 1653 | |||
| 1654 | if (perf_evsel__field(evsel, "pathname") == NULL) { | ||
| 1655 | perf_evsel__delete(evsel); | ||
| 1656 | return; | ||
| 1657 | } | ||
| 1658 | |||
| 1659 | evsel->handler.func = trace__vfs_getname; | ||
| 1660 | perf_evlist__add(evlist, evsel); | ||
| 1661 | } | ||
| 1662 | |||
| 1614 | static int trace__run(struct trace *trace, int argc, const char **argv) | 1663 | static int trace__run(struct trace *trace, int argc, const char **argv) |
| 1615 | { | 1664 | { |
| 1616 | struct perf_evlist *evlist = perf_evlist__new(); | 1665 | struct perf_evlist *evlist = perf_evlist__new(); |
| @@ -1630,6 +1679,8 @@ static int trace__run(struct trace *trace, int argc, const char **argv) | |||
| 1630 | perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_exit", trace__sys_exit)) | 1679 | perf_evlist__add_newtp(evlist, "raw_syscalls", "sys_exit", trace__sys_exit)) |
| 1631 | goto out_error_tp; | 1680 | goto out_error_tp; |
| 1632 | 1681 | ||
| 1682 | perf_evlist__add_vfs_getname(evlist); | ||
| 1683 | |||
| 1633 | if (trace->sched && | 1684 | if (trace->sched && |
| 1634 | perf_evlist__add_newtp(evlist, "sched", "sched_stat_runtime", | 1685 | perf_evlist__add_newtp(evlist, "sched", "sched_stat_runtime", |
| 1635 | trace__sched_stat_runtime)) | 1686 | trace__sched_stat_runtime)) |
| @@ -1662,10 +1713,8 @@ static int trace__run(struct trace *trace, int argc, const char **argv) | |||
| 1662 | } | 1713 | } |
| 1663 | 1714 | ||
| 1664 | err = perf_evlist__open(evlist); | 1715 | err = perf_evlist__open(evlist); |
| 1665 | if (err < 0) { | 1716 | if (err < 0) |
| 1666 | fprintf(trace->output, "Couldn't create the events: %s\n", strerror(errno)); | 1717 | goto out_error_open; |
| 1667 | goto out_delete_maps; | ||
| 1668 | } | ||
| 1669 | 1718 | ||
| 1670 | err = perf_evlist__mmap(evlist, UINT_MAX, false); | 1719 | err = perf_evlist__mmap(evlist, UINT_MAX, false); |
| 1671 | if (err < 0) { | 1720 | if (err < 0) { |
| @@ -1722,26 +1771,35 @@ again: | |||
| 1722 | handler = evsel->handler.func; | 1771 | handler = evsel->handler.func; |
| 1723 | handler(trace, evsel, &sample); | 1772 | handler(trace, evsel, &sample); |
| 1724 | 1773 | ||
| 1725 | if (done) | 1774 | if (interrupted) |
| 1726 | goto out_unmap_evlist; | 1775 | goto out_disable; |
| 1727 | } | 1776 | } |
| 1728 | } | 1777 | } |
| 1729 | 1778 | ||
| 1730 | if (trace->nr_events == before) { | 1779 | if (trace->nr_events == before) { |
| 1731 | if (done) | 1780 | int timeout = done ? 100 : -1; |
| 1732 | goto out_unmap_evlist; | ||
| 1733 | 1781 | ||
| 1734 | poll(evlist->pollfd, evlist->nr_fds, -1); | 1782 | if (poll(evlist->pollfd, evlist->nr_fds, timeout) > 0) |
| 1783 | goto again; | ||
| 1784 | } else { | ||
| 1785 | goto again; | ||
| 1735 | } | 1786 | } |
| 1736 | 1787 | ||
| 1737 | if (done) | 1788 | out_disable: |
| 1738 | perf_evlist__disable(evlist); | 1789 | perf_evlist__disable(evlist); |
| 1739 | 1790 | ||
| 1740 | goto again; | 1791 | if (!err) { |
| 1792 | if (trace->summary) | ||
| 1793 | trace__fprintf_thread_summary(trace, trace->output); | ||
| 1741 | 1794 | ||
| 1742 | out_unmap_evlist: | 1795 | if (trace->show_tool_stats) { |
| 1743 | if (!err && trace->summary) | 1796 | fprintf(trace->output, "Stats:\n " |
| 1744 | trace__fprintf_thread_summary(trace, trace->output); | 1797 | " vfs_getname : %" PRIu64 "\n" |
| 1798 | " proc_getname: %" PRIu64 "\n", | ||
| 1799 | trace->stats.vfs_getname, | ||
| 1800 | trace->stats.proc_getname); | ||
| 1801 | } | ||
| 1802 | } | ||
| 1745 | 1803 | ||
| 1746 | perf_evlist__munmap(evlist); | 1804 | perf_evlist__munmap(evlist); |
| 1747 | out_close_evlist: | 1805 | out_close_evlist: |
| @@ -1753,38 +1811,33 @@ out_delete_evlist: | |||
| 1753 | out: | 1811 | out: |
| 1754 | trace->live = false; | 1812 | trace->live = false; |
| 1755 | return err; | 1813 | return err; |
| 1814 | { | ||
| 1815 | char errbuf[BUFSIZ]; | ||
| 1816 | |||
| 1756 | out_error_tp: | 1817 | out_error_tp: |
| 1757 | switch(errno) { | 1818 | perf_evlist__strerror_tp(evlist, errno, errbuf, sizeof(errbuf)); |
| 1758 | case ENOENT: | 1819 | goto out_error; |
| 1759 | fputs("Error:\tUnable to find debugfs\n" | 1820 | |
| 1760 | "Hint:\tWas your kernel was compiled with debugfs support?\n" | 1821 | out_error_open: |
| 1761 | "Hint:\tIs the debugfs filesystem mounted?\n" | 1822 | perf_evlist__strerror_open(evlist, errno, errbuf, sizeof(errbuf)); |
| 1762 | "Hint:\tTry 'sudo mount -t debugfs nodev /sys/kernel/debug'\n", | 1823 | |
| 1763 | trace->output); | 1824 | out_error: |
| 1764 | break; | 1825 | fprintf(trace->output, "%s\n", errbuf); |
| 1765 | case EACCES: | ||
| 1766 | fprintf(trace->output, | ||
| 1767 | "Error:\tNo permissions to read %s/tracing/events/raw_syscalls\n" | ||
| 1768 | "Hint:\tTry 'sudo mount -o remount,mode=755 %s'\n", | ||
| 1769 | debugfs_mountpoint, debugfs_mountpoint); | ||
| 1770 | break; | ||
| 1771 | default: { | ||
| 1772 | char bf[256]; | ||
| 1773 | fprintf(trace->output, "Can't trace: %s\n", | ||
| 1774 | strerror_r(errno, bf, sizeof(bf))); | ||
| 1775 | } | ||
| 1776 | break; | ||
| 1777 | } | ||
| 1778 | goto out_delete_evlist; | 1826 | goto out_delete_evlist; |
| 1779 | } | 1827 | } |
| 1828 | } | ||
| 1780 | 1829 | ||
| 1781 | static int trace__replay(struct trace *trace) | 1830 | static int trace__replay(struct trace *trace) |
| 1782 | { | 1831 | { |
| 1783 | const struct perf_evsel_str_handler handlers[] = { | 1832 | const struct perf_evsel_str_handler handlers[] = { |
| 1784 | { "raw_syscalls:sys_enter", trace__sys_enter, }, | 1833 | { "raw_syscalls:sys_enter", trace__sys_enter, }, |
| 1785 | { "raw_syscalls:sys_exit", trace__sys_exit, }, | 1834 | { "raw_syscalls:sys_exit", trace__sys_exit, }, |
| 1835 | { "probe:vfs_getname", trace__vfs_getname, }, | ||
| 1836 | }; | ||
| 1837 | struct perf_data_file file = { | ||
| 1838 | .path = input_name, | ||
| 1839 | .mode = PERF_DATA_MODE_READ, | ||
| 1786 | }; | 1840 | }; |
| 1787 | |||
| 1788 | struct perf_session *session; | 1841 | struct perf_session *session; |
| 1789 | int err = -1; | 1842 | int err = -1; |
| 1790 | 1843 | ||
| @@ -1807,8 +1860,7 @@ static int trace__replay(struct trace *trace) | |||
| 1807 | if (symbol__init() < 0) | 1860 | if (symbol__init() < 0) |
| 1808 | return -1; | 1861 | return -1; |
| 1809 | 1862 | ||
| 1810 | session = perf_session__new(input_name, O_RDONLY, 0, false, | 1863 | session = perf_session__new(&file, false, &trace->tool); |
| 1811 | &trace->tool); | ||
| 1812 | if (session == NULL) | 1864 | if (session == NULL) |
| 1813 | return -ENOMEM; | 1865 | return -ENOMEM; |
| 1814 | 1866 | ||
| @@ -1992,7 +2044,10 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 1992 | NULL | 2044 | NULL |
| 1993 | }; | 2045 | }; |
| 1994 | struct trace trace = { | 2046 | struct trace trace = { |
| 1995 | .audit_machine = audit_detect_machine(), | 2047 | .audit = { |
| 2048 | .machine = audit_detect_machine(), | ||
| 2049 | .open_id = audit_name_to_syscall("open", trace.audit.machine), | ||
| 2050 | }, | ||
| 1996 | .syscalls = { | 2051 | .syscalls = { |
| 1997 | . max = -1, | 2052 | . max = -1, |
| 1998 | }, | 2053 | }, |
| @@ -2014,6 +2069,7 @@ int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused) | |||
| 2014 | const struct option trace_options[] = { | 2069 | const struct option trace_options[] = { |
| 2015 | OPT_BOOLEAN(0, "comm", &trace.show_comm, | 2070 | OPT_BOOLEAN(0, "comm", &trace.show_comm, |
| 2016 | "show the thread COMM next to its id"), | 2071 | "show the thread COMM next to its id"), |
| 2072 | OPT_BOOLEAN(0, "tool_stats", &trace.show_tool_stats, "show tool stats"), | ||
| 2017 | OPT_STRING('e', "expr", &ev_qualifier_str, "expr", | 2073 | OPT_STRING('e', "expr", &ev_qualifier_str, "expr", |
| 2018 | "list of events to trace"), | 2074 | "list of events to trace"), |
| 2019 | OPT_STRING('o', "output", &output_name, "file", "output file name"), | 2075 | OPT_STRING('o', "output", &output_name, "file", "output file name"), |
diff --git a/tools/perf/config/feature-checks/test-on-exit.c b/tools/perf/config/feature-checks/test-on-exit.c index 8f64ed3a58d9..8e88b16e6ded 100644 --- a/tools/perf/config/feature-checks/test-on-exit.c +++ b/tools/perf/config/feature-checks/test-on-exit.c | |||
| @@ -1,4 +1,5 @@ | |||
| 1 | #include <stdio.h> | 1 | #include <stdio.h> |
| 2 | #include <stdlib.h> | ||
| 2 | 3 | ||
| 3 | static void exit_fn(int status, void *__data) | 4 | static void exit_fn(int status, void *__data) |
| 4 | { | 5 | { |
diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 84502e88488b..f61c230beec4 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h | |||
| @@ -220,7 +220,6 @@ struct perf_record_opts { | |||
| 220 | bool no_delay; | 220 | bool no_delay; |
| 221 | bool no_inherit; | 221 | bool no_inherit; |
| 222 | bool no_samples; | 222 | bool no_samples; |
| 223 | bool pipe_output; | ||
| 224 | bool raw_samples; | 223 | bool raw_samples; |
| 225 | bool sample_address; | 224 | bool sample_address; |
| 226 | bool sample_weight; | 225 | bool sample_weight; |
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 482f68081cd8..e3970e3eaacf 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c | |||
| @@ -21,12 +21,6 @@ | |||
| 21 | 21 | ||
| 22 | __thread struct callchain_cursor callchain_cursor; | 22 | __thread struct callchain_cursor callchain_cursor; |
| 23 | 23 | ||
| 24 | #define chain_for_each_child(child, parent) \ | ||
| 25 | list_for_each_entry(child, &parent->children, siblings) | ||
| 26 | |||
| 27 | #define chain_for_each_child_safe(child, next, parent) \ | ||
| 28 | list_for_each_entry_safe(child, next, &parent->children, siblings) | ||
| 29 | |||
| 30 | static void | 24 | static void |
| 31 | rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, | 25 | rb_insert_callchain(struct rb_root *root, struct callchain_node *chain, |
| 32 | enum chain_mode mode) | 26 | enum chain_mode mode) |
| @@ -71,10 +65,16 @@ static void | |||
| 71 | __sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node, | 65 | __sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node, |
| 72 | u64 min_hit) | 66 | u64 min_hit) |
| 73 | { | 67 | { |
| 68 | struct rb_node *n; | ||
| 74 | struct callchain_node *child; | 69 | struct callchain_node *child; |
| 75 | 70 | ||
| 76 | chain_for_each_child(child, node) | 71 | n = rb_first(&node->rb_root_in); |
| 72 | while (n) { | ||
| 73 | child = rb_entry(n, struct callchain_node, rb_node_in); | ||
| 74 | n = rb_next(n); | ||
| 75 | |||
| 77 | __sort_chain_flat(rb_root, child, min_hit); | 76 | __sort_chain_flat(rb_root, child, min_hit); |
| 77 | } | ||
| 78 | 78 | ||
| 79 | if (node->hit && node->hit >= min_hit) | 79 | if (node->hit && node->hit >= min_hit) |
| 80 | rb_insert_callchain(rb_root, node, CHAIN_FLAT); | 80 | 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 | static void __sort_chain_graph_abs(struct callchain_node *node, | 94 | static void __sort_chain_graph_abs(struct callchain_node *node, |
| 95 | u64 min_hit) | 95 | u64 min_hit) |
| 96 | { | 96 | { |
| 97 | struct rb_node *n; | ||
| 97 | struct callchain_node *child; | 98 | struct callchain_node *child; |
| 98 | 99 | ||
| 99 | node->rb_root = RB_ROOT; | 100 | node->rb_root = RB_ROOT; |
| 101 | n = rb_first(&node->rb_root_in); | ||
| 102 | |||
| 103 | while (n) { | ||
| 104 | child = rb_entry(n, struct callchain_node, rb_node_in); | ||
| 105 | n = rb_next(n); | ||
| 100 | 106 | ||
| 101 | chain_for_each_child(child, node) { | ||
| 102 | __sort_chain_graph_abs(child, min_hit); | 107 | __sort_chain_graph_abs(child, min_hit); |
| 103 | if (callchain_cumul_hits(child) >= min_hit) | 108 | if (callchain_cumul_hits(child) >= min_hit) |
| 104 | rb_insert_callchain(&node->rb_root, child, | 109 | 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 | static void __sort_chain_graph_rel(struct callchain_node *node, | 122 | static void __sort_chain_graph_rel(struct callchain_node *node, |
| 118 | double min_percent) | 123 | double min_percent) |
| 119 | { | 124 | { |
| 125 | struct rb_node *n; | ||
| 120 | struct callchain_node *child; | 126 | struct callchain_node *child; |
| 121 | u64 min_hit; | 127 | u64 min_hit; |
| 122 | 128 | ||
| 123 | node->rb_root = RB_ROOT; | 129 | node->rb_root = RB_ROOT; |
| 124 | min_hit = ceil(node->children_hit * min_percent); | 130 | min_hit = ceil(node->children_hit * min_percent); |
| 125 | 131 | ||
| 126 | chain_for_each_child(child, node) { | 132 | n = rb_first(&node->rb_root_in); |
| 133 | while (n) { | ||
| 134 | child = rb_entry(n, struct callchain_node, rb_node_in); | ||
| 135 | n = rb_next(n); | ||
| 136 | |||
| 127 | __sort_chain_graph_rel(child, min_percent); | 137 | __sort_chain_graph_rel(child, min_percent); |
| 128 | if (callchain_cumul_hits(child) >= min_hit) | 138 | if (callchain_cumul_hits(child) >= min_hit) |
| 129 | rb_insert_callchain(&node->rb_root, child, | 139 | rb_insert_callchain(&node->rb_root, child, |
| @@ -173,19 +183,26 @@ create_child(struct callchain_node *parent, bool inherit_children) | |||
| 173 | return NULL; | 183 | return NULL; |
| 174 | } | 184 | } |
| 175 | new->parent = parent; | 185 | new->parent = parent; |
| 176 | INIT_LIST_HEAD(&new->children); | ||
| 177 | INIT_LIST_HEAD(&new->val); | 186 | INIT_LIST_HEAD(&new->val); |
| 178 | 187 | ||
| 179 | if (inherit_children) { | 188 | if (inherit_children) { |
| 180 | struct callchain_node *next; | 189 | struct rb_node *n; |
| 190 | struct callchain_node *child; | ||
| 191 | |||
| 192 | new->rb_root_in = parent->rb_root_in; | ||
| 193 | parent->rb_root_in = RB_ROOT; | ||
| 181 | 194 | ||
| 182 | list_splice(&parent->children, &new->children); | 195 | n = rb_first(&new->rb_root_in); |
| 183 | INIT_LIST_HEAD(&parent->children); | 196 | while (n) { |
| 197 | child = rb_entry(n, struct callchain_node, rb_node_in); | ||
| 198 | child->parent = new; | ||
| 199 | n = rb_next(n); | ||
| 200 | } | ||
| 184 | 201 | ||
| 185 | chain_for_each_child(next, new) | 202 | /* make it the first child */ |
| 186 | next->parent = new; | 203 | rb_link_node(&new->rb_node_in, NULL, &parent->rb_root_in.rb_node); |
| 204 | rb_insert_color(&new->rb_node_in, &parent->rb_root_in); | ||
| 187 | } | 205 | } |
| 188 | list_add_tail(&new->siblings, &parent->children); | ||
| 189 | 206 | ||
| 190 | return new; | 207 | return new; |
| 191 | } | 208 | } |
| @@ -223,7 +240,7 @@ fill_node(struct callchain_node *node, struct callchain_cursor *cursor) | |||
| 223 | } | 240 | } |
| 224 | } | 241 | } |
| 225 | 242 | ||
| 226 | static void | 243 | static struct callchain_node * |
| 227 | add_child(struct callchain_node *parent, | 244 | add_child(struct callchain_node *parent, |
| 228 | struct callchain_cursor *cursor, | 245 | struct callchain_cursor *cursor, |
| 229 | u64 period) | 246 | u64 period) |
| @@ -235,6 +252,19 @@ add_child(struct callchain_node *parent, | |||
| 235 | 252 | ||
| 236 | new->children_hit = 0; | 253 | new->children_hit = 0; |
| 237 | new->hit = period; | 254 | new->hit = period; |
| 255 | return new; | ||
| 256 | } | ||
| 257 | |||
| 258 | static s64 match_chain(struct callchain_cursor_node *node, | ||
| 259 | struct callchain_list *cnode) | ||
| 260 | { | ||
| 261 | struct symbol *sym = node->sym; | ||
| 262 | |||
| 263 | if (cnode->ms.sym && sym && | ||
| 264 | callchain_param.key == CCKEY_FUNCTION) | ||
| 265 | return cnode->ms.sym->start - sym->start; | ||
| 266 | else | ||
| 267 | return cnode->ip - node->ip; | ||
| 238 | } | 268 | } |
| 239 | 269 | ||
| 240 | /* | 270 | /* |
| @@ -272,9 +302,33 @@ split_add_child(struct callchain_node *parent, | |||
| 272 | 302 | ||
| 273 | /* create a new child for the new branch if any */ | 303 | /* create a new child for the new branch if any */ |
| 274 | if (idx_total < cursor->nr) { | 304 | if (idx_total < cursor->nr) { |
| 305 | struct callchain_node *first; | ||
| 306 | struct callchain_list *cnode; | ||
| 307 | struct callchain_cursor_node *node; | ||
| 308 | struct rb_node *p, **pp; | ||
| 309 | |||
| 275 | parent->hit = 0; | 310 | parent->hit = 0; |
| 276 | add_child(parent, cursor, period); | ||
| 277 | parent->children_hit += period; | 311 | parent->children_hit += period; |
| 312 | |||
| 313 | node = callchain_cursor_current(cursor); | ||
| 314 | new = add_child(parent, cursor, period); | ||
| 315 | |||
| 316 | /* | ||
| 317 | * This is second child since we moved parent's children | ||
| 318 | * to new (first) child above. | ||
| 319 | */ | ||
| 320 | p = parent->rb_root_in.rb_node; | ||
| 321 | first = rb_entry(p, struct callchain_node, rb_node_in); | ||
| 322 | cnode = list_first_entry(&first->val, struct callchain_list, | ||
| 323 | list); | ||
| 324 | |||
| 325 | if (match_chain(node, cnode) < 0) | ||
| 326 | pp = &p->rb_left; | ||
| 327 | else | ||
| 328 | pp = &p->rb_right; | ||
| 329 | |||
| 330 | rb_link_node(&new->rb_node_in, p, pp); | ||
| 331 | rb_insert_color(&new->rb_node_in, &parent->rb_root_in); | ||
| 278 | } else { | 332 | } else { |
| 279 | parent->hit = period; | 333 | parent->hit = period; |
| 280 | } | 334 | } |
| @@ -291,16 +345,40 @@ append_chain_children(struct callchain_node *root, | |||
| 291 | u64 period) | 345 | u64 period) |
| 292 | { | 346 | { |
| 293 | struct callchain_node *rnode; | 347 | struct callchain_node *rnode; |
| 348 | struct callchain_cursor_node *node; | ||
| 349 | struct rb_node **p = &root->rb_root_in.rb_node; | ||
| 350 | struct rb_node *parent = NULL; | ||
| 351 | |||
| 352 | node = callchain_cursor_current(cursor); | ||
| 353 | if (!node) | ||
| 354 | return; | ||
| 294 | 355 | ||
| 295 | /* lookup in childrens */ | 356 | /* lookup in childrens */ |
| 296 | chain_for_each_child(rnode, root) { | 357 | while (*p) { |
| 297 | unsigned int ret = append_chain(rnode, cursor, period); | 358 | s64 ret; |
| 359 | struct callchain_list *cnode; | ||
| 298 | 360 | ||
| 299 | if (!ret) | 361 | parent = *p; |
| 362 | rnode = rb_entry(parent, struct callchain_node, rb_node_in); | ||
| 363 | cnode = list_first_entry(&rnode->val, struct callchain_list, | ||
| 364 | list); | ||
| 365 | |||
| 366 | /* just check first entry */ | ||
| 367 | ret = match_chain(node, cnode); | ||
| 368 | if (ret == 0) { | ||
| 369 | append_chain(rnode, cursor, period); | ||
| 300 | goto inc_children_hit; | 370 | goto inc_children_hit; |
| 371 | } | ||
| 372 | |||
| 373 | if (ret < 0) | ||
| 374 | p = &parent->rb_left; | ||
| 375 | else | ||
| 376 | p = &parent->rb_right; | ||
| 301 | } | 377 | } |
| 302 | /* nothing in children, add to the current node */ | 378 | /* nothing in children, add to the current node */ |
| 303 | add_child(root, cursor, period); | 379 | rnode = add_child(root, cursor, period); |
| 380 | rb_link_node(&rnode->rb_node_in, parent, p); | ||
| 381 | rb_insert_color(&rnode->rb_node_in, &root->rb_root_in); | ||
| 304 | 382 | ||
| 305 | inc_children_hit: | 383 | inc_children_hit: |
| 306 | root->children_hit += period; | 384 | root->children_hit += period; |
| @@ -325,28 +403,20 @@ append_chain(struct callchain_node *root, | |||
| 325 | */ | 403 | */ |
| 326 | list_for_each_entry(cnode, &root->val, list) { | 404 | list_for_each_entry(cnode, &root->val, list) { |
| 327 | struct callchain_cursor_node *node; | 405 | struct callchain_cursor_node *node; |
| 328 | struct symbol *sym; | ||
| 329 | 406 | ||
| 330 | node = callchain_cursor_current(cursor); | 407 | node = callchain_cursor_current(cursor); |
| 331 | if (!node) | 408 | if (!node) |
| 332 | break; | 409 | break; |
| 333 | 410 | ||
| 334 | sym = node->sym; | 411 | if (match_chain(node, cnode) != 0) |
| 335 | |||
| 336 | if (cnode->ms.sym && sym && | ||
| 337 | callchain_param.key == CCKEY_FUNCTION) { | ||
| 338 | if (cnode->ms.sym->start != sym->start) | ||
| 339 | break; | ||
| 340 | } else if (cnode->ip != node->ip) | ||
| 341 | break; | 412 | break; |
| 342 | 413 | ||
| 343 | if (!found) | 414 | found = true; |
| 344 | found = true; | ||
| 345 | 415 | ||
| 346 | callchain_cursor_advance(cursor); | 416 | callchain_cursor_advance(cursor); |
| 347 | } | 417 | } |
| 348 | 418 | ||
| 349 | /* matches not, relay on the parent */ | 419 | /* matches not, relay no the parent */ |
| 350 | if (!found) { | 420 | if (!found) { |
| 351 | cursor->curr = curr_snap; | 421 | cursor->curr = curr_snap; |
| 352 | cursor->pos = start; | 422 | cursor->pos = start; |
| @@ -395,8 +465,9 @@ merge_chain_branch(struct callchain_cursor *cursor, | |||
| 395 | struct callchain_node *dst, struct callchain_node *src) | 465 | struct callchain_node *dst, struct callchain_node *src) |
| 396 | { | 466 | { |
| 397 | struct callchain_cursor_node **old_last = cursor->last; | 467 | struct callchain_cursor_node **old_last = cursor->last; |
| 398 | struct callchain_node *child, *next_child; | 468 | struct callchain_node *child; |
| 399 | struct callchain_list *list, *next_list; | 469 | struct callchain_list *list, *next_list; |
| 470 | struct rb_node *n; | ||
| 400 | int old_pos = cursor->nr; | 471 | int old_pos = cursor->nr; |
| 401 | int err = 0; | 472 | int err = 0; |
| 402 | 473 | ||
| @@ -412,12 +483,16 @@ merge_chain_branch(struct callchain_cursor *cursor, | |||
| 412 | append_chain_children(dst, cursor, src->hit); | 483 | append_chain_children(dst, cursor, src->hit); |
| 413 | } | 484 | } |
| 414 | 485 | ||
| 415 | chain_for_each_child_safe(child, next_child, src) { | 486 | n = rb_first(&src->rb_root_in); |
| 487 | while (n) { | ||
| 488 | child = container_of(n, struct callchain_node, rb_node_in); | ||
| 489 | n = rb_next(n); | ||
| 490 | rb_erase(&child->rb_node_in, &src->rb_root_in); | ||
| 491 | |||
| 416 | err = merge_chain_branch(cursor, dst, child); | 492 | err = merge_chain_branch(cursor, dst, child); |
| 417 | if (err) | 493 | if (err) |
| 418 | break; | 494 | break; |
| 419 | 495 | ||
| 420 | list_del(&child->siblings); | ||
| 421 | free(child); | 496 | free(child); |
| 422 | } | 497 | } |
| 423 | 498 | ||
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 2b585bc308cf..7bb36022377f 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h | |||
| @@ -21,11 +21,11 @@ enum chain_order { | |||
| 21 | 21 | ||
| 22 | struct callchain_node { | 22 | struct callchain_node { |
| 23 | struct callchain_node *parent; | 23 | struct callchain_node *parent; |
| 24 | struct list_head siblings; | ||
| 25 | struct list_head children; | ||
| 26 | struct list_head val; | 24 | struct list_head val; |
| 27 | struct rb_node rb_node; /* to sort nodes in an rbtree */ | 25 | struct rb_node rb_node_in; /* to insert nodes in an rbtree */ |
| 28 | struct rb_root rb_root; /* sorted tree of children */ | 26 | struct rb_node rb_node; /* to sort nodes in an output tree */ |
| 27 | struct rb_root rb_root_in; /* input tree of children */ | ||
| 28 | struct rb_root rb_root; /* sorted output tree of children */ | ||
| 29 | unsigned int val_nr; | 29 | unsigned int val_nr; |
| 30 | u64 hit; | 30 | u64 hit; |
| 31 | u64 children_hit; | 31 | u64 children_hit; |
| @@ -86,13 +86,12 @@ extern __thread struct callchain_cursor callchain_cursor; | |||
| 86 | 86 | ||
| 87 | static inline void callchain_init(struct callchain_root *root) | 87 | static inline void callchain_init(struct callchain_root *root) |
| 88 | { | 88 | { |
| 89 | INIT_LIST_HEAD(&root->node.siblings); | ||
| 90 | INIT_LIST_HEAD(&root->node.children); | ||
| 91 | INIT_LIST_HEAD(&root->node.val); | 89 | INIT_LIST_HEAD(&root->node.val); |
| 92 | 90 | ||
| 93 | root->node.parent = NULL; | 91 | root->node.parent = NULL; |
| 94 | root->node.hit = 0; | 92 | root->node.hit = 0; |
| 95 | root->node.children_hit = 0; | 93 | root->node.children_hit = 0; |
| 94 | root->node.rb_root_in = RB_ROOT; | ||
| 96 | root->max_depth = 0; | 95 | root->max_depth = 0; |
| 97 | } | 96 | } |
| 98 | 97 | ||
diff --git a/tools/perf/util/data.c b/tools/perf/util/data.c new file mode 100644 index 000000000000..7d09faf85cf1 --- /dev/null +++ b/tools/perf/util/data.c | |||
| @@ -0,0 +1,120 @@ | |||
| 1 | #include <linux/compiler.h> | ||
| 2 | #include <linux/kernel.h> | ||
| 3 | #include <sys/types.h> | ||
| 4 | #include <sys/stat.h> | ||
| 5 | #include <unistd.h> | ||
| 6 | #include <string.h> | ||
| 7 | |||
| 8 | #include "data.h" | ||
| 9 | #include "util.h" | ||
| 10 | |||
| 11 | static bool check_pipe(struct perf_data_file *file) | ||
| 12 | { | ||
| 13 | struct stat st; | ||
| 14 | bool is_pipe = false; | ||
| 15 | int fd = perf_data_file__is_read(file) ? | ||
| 16 | STDIN_FILENO : STDOUT_FILENO; | ||
| 17 | |||
| 18 | if (!file->path) { | ||
| 19 | if (!fstat(fd, &st) && S_ISFIFO(st.st_mode)) | ||
| 20 | is_pipe = true; | ||
| 21 | } else { | ||
| 22 | if (!strcmp(file->path, "-")) | ||
| 23 | is_pipe = true; | ||
| 24 | } | ||
| 25 | |||
| 26 | if (is_pipe) | ||
| 27 | file->fd = fd; | ||
| 28 | |||
| 29 | return file->is_pipe = is_pipe; | ||
| 30 | } | ||
| 31 | |||
| 32 | static int check_backup(struct perf_data_file *file) | ||
| 33 | { | ||
| 34 | struct stat st; | ||
| 35 | |||
| 36 | if (!stat(file->path, &st) && st.st_size) { | ||
| 37 | /* TODO check errors properly */ | ||
| 38 | char oldname[PATH_MAX]; | ||
| 39 | snprintf(oldname, sizeof(oldname), "%s.old", | ||
| 40 | file->path); | ||
| 41 | unlink(oldname); | ||
| 42 | rename(file->path, oldname); | ||
| 43 | } | ||
| 44 | |||
| 45 | return 0; | ||
| 46 | } | ||
| 47 | |||
| 48 | static int open_file_read(struct perf_data_file *file) | ||
| 49 | { | ||
| 50 | struct stat st; | ||
| 51 | int fd; | ||
| 52 | |||
| 53 | fd = open(file->path, O_RDONLY); | ||
| 54 | if (fd < 0) { | ||
| 55 | int err = errno; | ||
| 56 | |||
| 57 | pr_err("failed to open %s: %s", file->path, strerror(err)); | ||
| 58 | if (err == ENOENT && !strcmp(file->path, "perf.data")) | ||
| 59 | pr_err(" (try 'perf record' first)"); | ||
| 60 | pr_err("\n"); | ||
| 61 | return -err; | ||
| 62 | } | ||
| 63 | |||
| 64 | if (fstat(fd, &st) < 0) | ||
| 65 | goto out_close; | ||
| 66 | |||
| 67 | if (!file->force && st.st_uid && (st.st_uid != geteuid())) { | ||
| 68 | pr_err("file %s not owned by current user or root\n", | ||
| 69 | file->path); | ||
| 70 | goto out_close; | ||
| 71 | } | ||
| 72 | |||
| 73 | if (!st.st_size) { | ||
| 74 | pr_info("zero-sized file (%s), nothing to do!\n", | ||
| 75 | file->path); | ||
| 76 | goto out_close; | ||
| 77 | } | ||
| 78 | |||
| 79 | file->size = st.st_size; | ||
| 80 | return fd; | ||
| 81 | |||
| 82 | out_close: | ||
| 83 | close(fd); | ||
| 84 | return -1; | ||
| 85 | } | ||
| 86 | |||
| 87 | static int open_file_write(struct perf_data_file *file) | ||
| 88 | { | ||
| 89 | if (check_backup(file)) | ||
| 90 | return -1; | ||
| 91 | |||
| 92 | return open(file->path, O_CREAT|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR); | ||
| 93 | } | ||
| 94 | |||
| 95 | static int open_file(struct perf_data_file *file) | ||
| 96 | { | ||
| 97 | int fd; | ||
| 98 | |||
| 99 | fd = perf_data_file__is_read(file) ? | ||
| 100 | open_file_read(file) : open_file_write(file); | ||
| 101 | |||
| 102 | file->fd = fd; | ||
| 103 | return fd < 0 ? -1 : 0; | ||
| 104 | } | ||
| 105 | |||
| 106 | int perf_data_file__open(struct perf_data_file *file) | ||
| 107 | { | ||
| 108 | if (check_pipe(file)) | ||
| 109 | return 0; | ||
| 110 | |||
| 111 | if (!file->path) | ||
| 112 | file->path = "perf.data"; | ||
| 113 | |||
| 114 | return open_file(file); | ||
| 115 | } | ||
| 116 | |||
| 117 | void perf_data_file__close(struct perf_data_file *file) | ||
| 118 | { | ||
| 119 | close(file->fd); | ||
| 120 | } | ||
diff --git a/tools/perf/util/data.h b/tools/perf/util/data.h new file mode 100644 index 000000000000..8c2df80152a5 --- /dev/null +++ b/tools/perf/util/data.h | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | #ifndef __PERF_DATA_H | ||
| 2 | #define __PERF_DATA_H | ||
| 3 | |||
| 4 | #include <stdbool.h> | ||
| 5 | |||
| 6 | enum perf_data_mode { | ||
| 7 | PERF_DATA_MODE_WRITE, | ||
| 8 | PERF_DATA_MODE_READ, | ||
| 9 | }; | ||
| 10 | |||
| 11 | struct perf_data_file { | ||
| 12 | const char *path; | ||
| 13 | int fd; | ||
| 14 | bool is_pipe; | ||
| 15 | bool force; | ||
| 16 | unsigned long size; | ||
| 17 | enum perf_data_mode mode; | ||
| 18 | }; | ||
| 19 | |||
| 20 | static inline bool perf_data_file__is_read(struct perf_data_file *file) | ||
| 21 | { | ||
| 22 | return file->mode == PERF_DATA_MODE_READ; | ||
| 23 | } | ||
| 24 | |||
| 25 | static inline bool perf_data_file__is_write(struct perf_data_file *file) | ||
| 26 | { | ||
| 27 | return file->mode == PERF_DATA_MODE_WRITE; | ||
| 28 | } | ||
| 29 | |||
| 30 | static inline int perf_data_file__is_pipe(struct perf_data_file *file) | ||
| 31 | { | ||
| 32 | return file->is_pipe; | ||
| 33 | } | ||
| 34 | |||
| 35 | static inline int perf_data_file__fd(struct perf_data_file *file) | ||
| 36 | { | ||
| 37 | return file->fd; | ||
| 38 | } | ||
| 39 | |||
| 40 | static inline unsigned long perf_data_file__size(struct perf_data_file *file) | ||
| 41 | { | ||
| 42 | return file->size; | ||
| 43 | } | ||
| 44 | |||
| 45 | int perf_data_file__open(struct perf_data_file *file); | ||
| 46 | void perf_data_file__close(struct perf_data_file *file); | ||
| 47 | |||
| 48 | #endif /* __PERF_DATA_H */ | ||
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index cb9523f50a37..85c4c80bcac8 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
| @@ -608,9 +608,36 @@ static int __perf_evlist__mmap(struct perf_evlist *evlist, | |||
| 608 | return 0; | 608 | return 0; |
| 609 | } | 609 | } |
| 610 | 610 | ||
| 611 | static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, int mask) | 611 | static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx, |
| 612 | int prot, int mask, int cpu, int thread, | ||
| 613 | int *output) | ||
| 612 | { | 614 | { |
| 613 | struct perf_evsel *evsel; | 615 | struct perf_evsel *evsel; |
| 616 | |||
| 617 | list_for_each_entry(evsel, &evlist->entries, node) { | ||
| 618 | int fd = FD(evsel, cpu, thread); | ||
| 619 | |||
| 620 | if (*output == -1) { | ||
| 621 | *output = fd; | ||
| 622 | if (__perf_evlist__mmap(evlist, idx, prot, mask, | ||
| 623 | *output) < 0) | ||
| 624 | return -1; | ||
| 625 | } else { | ||
| 626 | if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, *output) != 0) | ||
| 627 | return -1; | ||
| 628 | } | ||
| 629 | |||
| 630 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && | ||
| 631 | perf_evlist__id_add_fd(evlist, evsel, cpu, thread, fd) < 0) | ||
| 632 | return -1; | ||
| 633 | } | ||
| 634 | |||
| 635 | return 0; | ||
| 636 | } | ||
| 637 | |||
| 638 | static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, | ||
| 639 | int mask) | ||
| 640 | { | ||
| 614 | int cpu, thread; | 641 | int cpu, thread; |
| 615 | int nr_cpus = cpu_map__nr(evlist->cpus); | 642 | int nr_cpus = cpu_map__nr(evlist->cpus); |
| 616 | int nr_threads = thread_map__nr(evlist->threads); | 643 | 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 | int output = -1; | 647 | int output = -1; |
| 621 | 648 | ||
| 622 | for (thread = 0; thread < nr_threads; thread++) { | 649 | for (thread = 0; thread < nr_threads; thread++) { |
| 623 | list_for_each_entry(evsel, &evlist->entries, node) { | 650 | if (perf_evlist__mmap_per_evsel(evlist, cpu, prot, mask, |
| 624 | int fd = FD(evsel, cpu, thread); | 651 | cpu, thread, &output)) |
| 625 | 652 | goto out_unmap; | |
| 626 | if (output == -1) { | ||
| 627 | output = fd; | ||
| 628 | if (__perf_evlist__mmap(evlist, cpu, | ||
| 629 | prot, mask, output) < 0) | ||
| 630 | goto out_unmap; | ||
| 631 | } else { | ||
| 632 | if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, output) != 0) | ||
| 633 | goto out_unmap; | ||
| 634 | } | ||
| 635 | |||
| 636 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && | ||
| 637 | perf_evlist__id_add_fd(evlist, evsel, cpu, thread, fd) < 0) | ||
| 638 | goto out_unmap; | ||
| 639 | } | ||
| 640 | } | 653 | } |
| 641 | } | 654 | } |
| 642 | 655 | ||
| @@ -648,9 +661,9 @@ out_unmap: | |||
| 648 | return -1; | 661 | return -1; |
| 649 | } | 662 | } |
| 650 | 663 | ||
| 651 | static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, int mask) | 664 | static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, |
| 665 | int mask) | ||
| 652 | { | 666 | { |
| 653 | struct perf_evsel *evsel; | ||
| 654 | int thread; | 667 | int thread; |
| 655 | int nr_threads = thread_map__nr(evlist->threads); | 668 | int nr_threads = thread_map__nr(evlist->threads); |
| 656 | 669 | ||
| @@ -658,23 +671,9 @@ static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, in | |||
| 658 | for (thread = 0; thread < nr_threads; thread++) { | 671 | for (thread = 0; thread < nr_threads; thread++) { |
| 659 | int output = -1; | 672 | int output = -1; |
| 660 | 673 | ||
| 661 | list_for_each_entry(evsel, &evlist->entries, node) { | 674 | if (perf_evlist__mmap_per_evsel(evlist, thread, prot, mask, 0, |
| 662 | int fd = FD(evsel, 0, thread); | 675 | thread, &output)) |
| 663 | 676 | goto out_unmap; | |
| 664 | if (output == -1) { | ||
| 665 | output = fd; | ||
| 666 | if (__perf_evlist__mmap(evlist, thread, | ||
| 667 | prot, mask, output) < 0) | ||
| 668 | goto out_unmap; | ||
| 669 | } else { | ||
| 670 | if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, output) != 0) | ||
| 671 | goto out_unmap; | ||
| 672 | } | ||
| 673 | |||
| 674 | if ((evsel->attr.read_format & PERF_FORMAT_ID) && | ||
| 675 | perf_evlist__id_add_fd(evlist, evsel, 0, thread, fd) < 0) | ||
| 676 | goto out_unmap; | ||
| 677 | } | ||
| 678 | } | 677 | } |
| 679 | 678 | ||
| 680 | return 0; | 679 | return 0; |
| @@ -738,20 +737,17 @@ int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, | |||
| 738 | return 0; | 737 | return 0; |
| 739 | } | 738 | } |
| 740 | 739 | ||
| 741 | /** perf_evlist__mmap - Create per cpu maps to receive events | 740 | /** |
| 742 | * | 741 | * perf_evlist__mmap - Create mmaps to receive events. |
| 743 | * @evlist - list of events | 742 | * @evlist: list of events |
| 744 | * @pages - map length in pages | 743 | * @pages: map length in pages |
| 745 | * @overwrite - overwrite older events? | 744 | * @overwrite: overwrite older events? |
| 746 | * | ||
| 747 | * If overwrite is false the user needs to signal event consuption using: | ||
| 748 | * | ||
| 749 | * struct perf_mmap *m = &evlist->mmap[cpu]; | ||
| 750 | * unsigned int head = perf_mmap__read_head(m); | ||
| 751 | * | 745 | * |
| 752 | * perf_mmap__write_tail(m, head) | 746 | * If @overwrite is %false the user needs to signal event consumption using |
| 747 | * perf_mmap__write_tail(). Using perf_evlist__mmap_read() does this | ||
| 748 | * automatically. | ||
| 753 | * | 749 | * |
| 754 | * Using perf_evlist__read_on_cpu does this automatically. | 750 | * Return: %0 on success, negative error code otherwise. |
| 755 | */ | 751 | */ |
| 756 | int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, | 752 | int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, |
| 757 | bool overwrite) | 753 | bool overwrite) |
| @@ -769,7 +765,7 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, | |||
| 769 | 765 | ||
| 770 | evlist->overwrite = overwrite; | 766 | evlist->overwrite = overwrite; |
| 771 | evlist->mmap_len = perf_evlist__mmap_size(pages); | 767 | evlist->mmap_len = perf_evlist__mmap_size(pages); |
| 772 | pr_debug("mmap size %luB\n", evlist->mmap_len); | 768 | pr_debug("mmap size %zuB\n", evlist->mmap_len); |
| 773 | mask = evlist->mmap_len - page_size - 1; | 769 | mask = evlist->mmap_len - page_size - 1; |
| 774 | 770 | ||
| 775 | list_for_each_entry(evsel, &evlist->entries, node) { | 771 | list_for_each_entry(evsel, &evlist->entries, node) { |
| @@ -1126,3 +1122,66 @@ size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp) | |||
| 1126 | 1122 | ||
| 1127 | return printed + fprintf(fp, "\n");; | 1123 | return printed + fprintf(fp, "\n");; |
| 1128 | } | 1124 | } |
| 1125 | |||
| 1126 | int perf_evlist__strerror_tp(struct perf_evlist *evlist __maybe_unused, | ||
| 1127 | int err, char *buf, size_t size) | ||
| 1128 | { | ||
| 1129 | char sbuf[128]; | ||
| 1130 | |||
| 1131 | switch (err) { | ||
| 1132 | case ENOENT: | ||
| 1133 | scnprintf(buf, size, "%s", | ||
| 1134 | "Error:\tUnable to find debugfs\n" | ||
| 1135 | "Hint:\tWas your kernel was compiled with debugfs support?\n" | ||
| 1136 | "Hint:\tIs the debugfs filesystem mounted?\n" | ||
| 1137 | "Hint:\tTry 'sudo mount -t debugfs nodev /sys/kernel/debug'"); | ||
| 1138 | break; | ||
| 1139 | case EACCES: | ||
| 1140 | scnprintf(buf, size, | ||
| 1141 | "Error:\tNo permissions to read %s/tracing/events/raw_syscalls\n" | ||
| 1142 | "Hint:\tTry 'sudo mount -o remount,mode=755 %s'\n", | ||
| 1143 | debugfs_mountpoint, debugfs_mountpoint); | ||
| 1144 | break; | ||
| 1145 | default: | ||
| 1146 | scnprintf(buf, size, "%s", strerror_r(err, sbuf, sizeof(sbuf))); | ||
| 1147 | break; | ||
| 1148 | } | ||
| 1149 | |||
| 1150 | return 0; | ||
| 1151 | } | ||
| 1152 | |||
| 1153 | int perf_evlist__strerror_open(struct perf_evlist *evlist __maybe_unused, | ||
| 1154 | int err, char *buf, size_t size) | ||
| 1155 | { | ||
| 1156 | int printed, value; | ||
| 1157 | char sbuf[128], *emsg = strerror_r(err, sbuf, sizeof(sbuf)); | ||
| 1158 | |||
| 1159 | switch (err) { | ||
| 1160 | case EACCES: | ||
| 1161 | case EPERM: | ||
| 1162 | printed = scnprintf(buf, size, | ||
| 1163 | "Error:\t%s.\n" | ||
| 1164 | "Hint:\tCheck /proc/sys/kernel/perf_event_paranoid setting.", emsg); | ||
| 1165 | |||
| 1166 | if (filename__read_int("/proc/sys/kernel/perf_event_paranoid", &value)) | ||
| 1167 | break; | ||
| 1168 | |||
| 1169 | printed += scnprintf(buf + printed, size - printed, "\nHint:\t"); | ||
| 1170 | |||
| 1171 | if (value >= 2) { | ||
| 1172 | printed += scnprintf(buf + printed, size - printed, | ||
| 1173 | "For your workloads it needs to be <= 1\nHint:\t"); | ||
| 1174 | } | ||
| 1175 | printed += scnprintf(buf + printed, size - printed, | ||
| 1176 | "For system wide tracing it needs to be set to -1"); | ||
| 1177 | |||
| 1178 | printed += scnprintf(buf + printed, size - printed, | ||
| 1179 | ".\nHint:\tThe current value is %d.", value); | ||
| 1180 | break; | ||
| 1181 | default: | ||
| 1182 | scnprintf(buf, size, "%s", emsg); | ||
| 1183 | break; | ||
| 1184 | } | ||
| 1185 | |||
| 1186 | return 0; | ||
| 1187 | } | ||
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 722618f84c53..7f8f1aeb9cfe 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h | |||
| @@ -168,6 +168,9 @@ static inline struct perf_evsel *perf_evlist__last(struct perf_evlist *evlist) | |||
| 168 | 168 | ||
| 169 | size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp); | 169 | size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp); |
| 170 | 170 | ||
| 171 | int perf_evlist__strerror_tp(struct perf_evlist *evlist, int err, char *buf, size_t size); | ||
| 172 | int perf_evlist__strerror_open(struct perf_evlist *evlist, int err, char *buf, size_t size); | ||
| 173 | |||
| 171 | static inline unsigned int perf_mmap__read_head(struct perf_mmap *mm) | 174 | static inline unsigned int perf_mmap__read_head(struct perf_mmap *mm) |
| 172 | { | 175 | { |
| 173 | struct perf_event_mmap_page *pc = mm->base; | 176 | struct perf_event_mmap_page *pc = mm->base; |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index bfebc1ea3c51..ec0cc1e21c62 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
| @@ -986,6 +986,7 @@ static size_t perf_event_attr__fprintf(struct perf_event_attr *attr, FILE *fp) | |||
| 986 | ret += PRINT_ATTR2(exclude_host, exclude_guest); | 986 | ret += PRINT_ATTR2(exclude_host, exclude_guest); |
| 987 | ret += PRINT_ATTR2N("excl.callchain_kern", exclude_callchain_kernel, | 987 | ret += PRINT_ATTR2N("excl.callchain_kern", exclude_callchain_kernel, |
| 988 | "excl.callchain_user", exclude_callchain_user); | 988 | "excl.callchain_user", exclude_callchain_user); |
| 989 | ret += PRINT_ATTR_U32(mmap2); | ||
| 989 | 990 | ||
| 990 | ret += PRINT_ATTR_U32(wakeup_events); | 991 | ret += PRINT_ATTR_U32(wakeup_events); |
| 991 | ret += PRINT_ATTR_U32(wakeup_watermark); | 992 | ret += PRINT_ATTR_U32(wakeup_watermark); |
| @@ -1217,6 +1218,7 @@ static int perf_evsel__parse_id_sample(const struct perf_evsel *evsel, | |||
| 1217 | 1218 | ||
| 1218 | sample->pid = u.val32[0]; | 1219 | sample->pid = u.val32[0]; |
| 1219 | sample->tid = u.val32[1]; | 1220 | sample->tid = u.val32[1]; |
| 1221 | array--; | ||
| 1220 | } | 1222 | } |
| 1221 | 1223 | ||
| 1222 | return 0; | 1224 | return 0; |
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index c3e5a3b817ab..26d9520a0c1b 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c | |||
| @@ -22,6 +22,7 @@ | |||
| 22 | #include "vdso.h" | 22 | #include "vdso.h" |
| 23 | #include "strbuf.h" | 23 | #include "strbuf.h" |
| 24 | #include "build-id.h" | 24 | #include "build-id.h" |
| 25 | #include "data.h" | ||
| 25 | 26 | ||
| 26 | static bool no_buildid_cache = false; | 27 | static bool no_buildid_cache = false; |
| 27 | 28 | ||
| @@ -2189,7 +2190,7 @@ int perf_header__fprintf_info(struct perf_session *session, FILE *fp, bool full) | |||
| 2189 | { | 2190 | { |
| 2190 | struct header_print_data hd; | 2191 | struct header_print_data hd; |
| 2191 | struct perf_header *header = &session->header; | 2192 | struct perf_header *header = &session->header; |
| 2192 | int fd = session->fd; | 2193 | int fd = perf_data_file__fd(session->file); |
| 2193 | hd.fp = fp; | 2194 | hd.fp = fp; |
| 2194 | hd.full = full; | 2195 | hd.full = full; |
| 2195 | 2196 | ||
| @@ -2650,7 +2651,8 @@ static int perf_header__read_pipe(struct perf_session *session) | |||
| 2650 | struct perf_header *header = &session->header; | 2651 | struct perf_header *header = &session->header; |
| 2651 | struct perf_pipe_file_header f_header; | 2652 | struct perf_pipe_file_header f_header; |
| 2652 | 2653 | ||
| 2653 | if (perf_file_header__read_pipe(&f_header, header, session->fd, | 2654 | if (perf_file_header__read_pipe(&f_header, header, |
| 2655 | perf_data_file__fd(session->file), | ||
| 2654 | session->repipe) < 0) { | 2656 | session->repipe) < 0) { |
| 2655 | pr_debug("incompatible file format\n"); | 2657 | pr_debug("incompatible file format\n"); |
| 2656 | return -EINVAL; | 2658 | return -EINVAL; |
| @@ -2751,18 +2753,19 @@ static int perf_evlist__prepare_tracepoint_events(struct perf_evlist *evlist, | |||
| 2751 | 2753 | ||
| 2752 | int perf_session__read_header(struct perf_session *session) | 2754 | int perf_session__read_header(struct perf_session *session) |
| 2753 | { | 2755 | { |
| 2756 | struct perf_data_file *file = session->file; | ||
| 2754 | struct perf_header *header = &session->header; | 2757 | struct perf_header *header = &session->header; |
| 2755 | struct perf_file_header f_header; | 2758 | struct perf_file_header f_header; |
| 2756 | struct perf_file_attr f_attr; | 2759 | struct perf_file_attr f_attr; |
| 2757 | u64 f_id; | 2760 | u64 f_id; |
| 2758 | int nr_attrs, nr_ids, i, j; | 2761 | int nr_attrs, nr_ids, i, j; |
| 2759 | int fd = session->fd; | 2762 | int fd = perf_data_file__fd(file); |
| 2760 | 2763 | ||
| 2761 | session->evlist = perf_evlist__new(); | 2764 | session->evlist = perf_evlist__new(); |
| 2762 | if (session->evlist == NULL) | 2765 | if (session->evlist == NULL) |
| 2763 | return -ENOMEM; | 2766 | return -ENOMEM; |
| 2764 | 2767 | ||
| 2765 | if (session->fd_pipe) | 2768 | if (perf_data_file__is_pipe(file)) |
| 2766 | return perf_header__read_pipe(session); | 2769 | return perf_header__read_pipe(session); |
| 2767 | 2770 | ||
| 2768 | if (perf_file_header__read(&f_header, header, fd) < 0) | 2771 | if (perf_file_header__read(&f_header, header, fd) < 0) |
| @@ -2777,7 +2780,7 @@ int perf_session__read_header(struct perf_session *session) | |||
| 2777 | if (f_header.data.size == 0) { | 2780 | if (f_header.data.size == 0) { |
| 2778 | pr_warning("WARNING: The %s file's data size field is 0 which is unexpected.\n" | 2781 | pr_warning("WARNING: The %s file's data size field is 0 which is unexpected.\n" |
| 2779 | "Was the 'perf record' command properly terminated?\n", | 2782 | "Was the 'perf record' command properly terminated?\n", |
| 2780 | session->filename); | 2783 | file->path); |
| 2781 | } | 2784 | } |
| 2782 | 2785 | ||
| 2783 | nr_attrs = f_header.attrs.size / f_header.attr_size; | 2786 | 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 | struct perf_session *session) | 2993 | struct perf_session *session) |
| 2991 | { | 2994 | { |
| 2992 | ssize_t size_read, padding, size = event->tracing_data.size; | 2995 | ssize_t size_read, padding, size = event->tracing_data.size; |
| 2993 | off_t offset = lseek(session->fd, 0, SEEK_CUR); | 2996 | int fd = perf_data_file__fd(session->file); |
| 2997 | off_t offset = lseek(fd, 0, SEEK_CUR); | ||
| 2994 | char buf[BUFSIZ]; | 2998 | char buf[BUFSIZ]; |
| 2995 | 2999 | ||
| 2996 | /* setup for reading amidst mmap */ | 3000 | /* setup for reading amidst mmap */ |
| 2997 | lseek(session->fd, offset + sizeof(struct tracing_data_event), | 3001 | lseek(fd, offset + sizeof(struct tracing_data_event), |
| 2998 | SEEK_SET); | 3002 | SEEK_SET); |
| 2999 | 3003 | ||
| 3000 | size_read = trace_report(session->fd, &session->pevent, | 3004 | size_read = trace_report(fd, &session->pevent, |
| 3001 | session->repipe); | 3005 | session->repipe); |
| 3002 | padding = PERF_ALIGN(size_read, sizeof(u64)) - size_read; | 3006 | padding = PERF_ALIGN(size_read, sizeof(u64)) - size_read; |
| 3003 | 3007 | ||
| 3004 | if (readn(session->fd, buf, padding) < 0) { | 3008 | if (readn(fd, buf, padding) < 0) { |
| 3005 | pr_err("%s: reading input file", __func__); | 3009 | pr_err("%s: reading input file", __func__); |
| 3006 | return -1; | 3010 | return -1; |
| 3007 | } | 3011 | } |
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 6b861aefd99a..ea93425cce95 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c | |||
| @@ -1253,10 +1253,12 @@ static int machine__resolve_callchain_sample(struct machine *machine, | |||
| 1253 | struct thread *thread, | 1253 | struct thread *thread, |
| 1254 | struct ip_callchain *chain, | 1254 | struct ip_callchain *chain, |
| 1255 | struct symbol **parent, | 1255 | struct symbol **parent, |
| 1256 | struct addr_location *root_al) | 1256 | struct addr_location *root_al, |
| 1257 | int max_stack) | ||
| 1257 | { | 1258 | { |
| 1258 | u8 cpumode = PERF_RECORD_MISC_USER; | 1259 | u8 cpumode = PERF_RECORD_MISC_USER; |
| 1259 | unsigned int i; | 1260 | int chain_nr = min(max_stack, (int)chain->nr); |
| 1261 | int i; | ||
| 1260 | int err; | 1262 | int err; |
| 1261 | 1263 | ||
| 1262 | callchain_cursor_reset(&callchain_cursor); | 1264 | callchain_cursor_reset(&callchain_cursor); |
| @@ -1266,7 +1268,7 @@ static int machine__resolve_callchain_sample(struct machine *machine, | |||
| 1266 | return 0; | 1268 | return 0; |
| 1267 | } | 1269 | } |
| 1268 | 1270 | ||
| 1269 | for (i = 0; i < chain->nr; i++) { | 1271 | for (i = 0; i < chain_nr; i++) { |
| 1270 | u64 ip; | 1272 | u64 ip; |
| 1271 | struct addr_location al; | 1273 | struct addr_location al; |
| 1272 | 1274 | ||
| @@ -1338,12 +1340,14 @@ int machine__resolve_callchain(struct machine *machine, | |||
| 1338 | struct thread *thread, | 1340 | struct thread *thread, |
| 1339 | struct perf_sample *sample, | 1341 | struct perf_sample *sample, |
| 1340 | struct symbol **parent, | 1342 | struct symbol **parent, |
| 1341 | struct addr_location *root_al) | 1343 | struct addr_location *root_al, |
| 1344 | int max_stack) | ||
| 1342 | { | 1345 | { |
| 1343 | int ret; | 1346 | int ret; |
| 1344 | 1347 | ||
| 1345 | ret = machine__resolve_callchain_sample(machine, thread, | 1348 | ret = machine__resolve_callchain_sample(machine, thread, |
| 1346 | sample->callchain, parent, root_al); | 1349 | sample->callchain, parent, |
| 1350 | root_al, max_stack); | ||
| 1347 | if (ret) | 1351 | if (ret) |
| 1348 | return ret; | 1352 | return ret; |
| 1349 | 1353 | ||
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index d44c09bdc45e..4c1f5d567f54 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h | |||
| @@ -92,7 +92,8 @@ int machine__resolve_callchain(struct machine *machine, | |||
| 92 | struct thread *thread, | 92 | struct thread *thread, |
| 93 | struct perf_sample *sample, | 93 | struct perf_sample *sample, |
| 94 | struct symbol **parent, | 94 | struct symbol **parent, |
| 95 | struct addr_location *root_al); | 95 | struct addr_location *root_al, |
| 96 | int max_stack); | ||
| 96 | 97 | ||
| 97 | /* | 98 | /* |
| 98 | * Default guest kernel is defined by parameter --guestkallsyms | 99 | * Default guest kernel is defined by parameter --guestkallsyms |
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 98125319b158..c90e55cf7e82 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
| @@ -998,8 +998,10 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob, | |||
| 998 | char evt_path[MAXPATHLEN]; | 998 | char evt_path[MAXPATHLEN]; |
| 999 | char dir_path[MAXPATHLEN]; | 999 | char dir_path[MAXPATHLEN]; |
| 1000 | 1000 | ||
| 1001 | if (debugfs_valid_mountpoint(tracing_events_path)) | 1001 | if (debugfs_valid_mountpoint(tracing_events_path)) { |
| 1002 | printf(" [ Tracepoints not available: %s ]\n", strerror(errno)); | ||
| 1002 | return; | 1003 | return; |
| 1004 | } | ||
| 1003 | 1005 | ||
| 1004 | sys_dir = opendir(tracing_events_path); | 1006 | sys_dir = opendir(tracing_events_path); |
| 1005 | if (!sys_dir) | 1007 | if (!sys_dir) |
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index a85e4ae5f3ac..c0c9795c4f02 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c | |||
| @@ -282,7 +282,7 @@ static void perl_process_tracepoint(union perf_event *perf_event __maybe_unused, | |||
| 282 | 282 | ||
| 283 | event = find_cache_event(evsel); | 283 | event = find_cache_event(evsel); |
| 284 | if (!event) | 284 | if (!event) |
| 285 | die("ug! no event found for type %" PRIu64, evsel->attr.config); | 285 | die("ug! no event found for type %" PRIu64, (u64)evsel->attr.config); |
| 286 | 286 | ||
| 287 | pid = raw_field_value(event, "common_pid", data); | 287 | pid = raw_field_value(event, "common_pid", data); |
| 288 | 288 | ||
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index d1e449534b33..854c5aa4db0d 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
| @@ -16,73 +16,34 @@ | |||
| 16 | #include "perf_regs.h" | 16 | #include "perf_regs.h" |
| 17 | #include "vdso.h" | 17 | #include "vdso.h" |
| 18 | 18 | ||
| 19 | static int perf_session__open(struct perf_session *self, bool force) | 19 | static int perf_session__open(struct perf_session *self) |
| 20 | { | 20 | { |
| 21 | struct stat input_stat; | 21 | struct perf_data_file *file = self->file; |
| 22 | |||
| 23 | if (!strcmp(self->filename, "-")) { | ||
| 24 | self->fd_pipe = true; | ||
| 25 | self->fd = STDIN_FILENO; | ||
| 26 | |||
| 27 | if (perf_session__read_header(self) < 0) | ||
| 28 | pr_err("incompatible file format (rerun with -v to learn more)"); | ||
| 29 | |||
| 30 | return 0; | ||
| 31 | } | ||
| 32 | |||
| 33 | self->fd = open(self->filename, O_RDONLY); | ||
| 34 | if (self->fd < 0) { | ||
| 35 | int err = errno; | ||
| 36 | |||
| 37 | pr_err("failed to open %s: %s", self->filename, strerror(err)); | ||
| 38 | if (err == ENOENT && !strcmp(self->filename, "perf.data")) | ||
| 39 | pr_err(" (try 'perf record' first)"); | ||
| 40 | pr_err("\n"); | ||
| 41 | return -errno; | ||
| 42 | } | ||
| 43 | |||
| 44 | if (fstat(self->fd, &input_stat) < 0) | ||
| 45 | goto out_close; | ||
| 46 | |||
| 47 | if (!force && input_stat.st_uid && (input_stat.st_uid != geteuid())) { | ||
| 48 | pr_err("file %s not owned by current user or root\n", | ||
| 49 | self->filename); | ||
| 50 | goto out_close; | ||
| 51 | } | ||
| 52 | |||
| 53 | if (!input_stat.st_size) { | ||
| 54 | pr_info("zero-sized file (%s), nothing to do!\n", | ||
| 55 | self->filename); | ||
| 56 | goto out_close; | ||
| 57 | } | ||
| 58 | 22 | ||
| 59 | if (perf_session__read_header(self) < 0) { | 23 | if (perf_session__read_header(self) < 0) { |
| 60 | pr_err("incompatible file format (rerun with -v to learn more)"); | 24 | pr_err("incompatible file format (rerun with -v to learn more)"); |
| 61 | goto out_close; | 25 | return -1; |
| 62 | } | 26 | } |
| 63 | 27 | ||
| 28 | if (perf_data_file__is_pipe(file)) | ||
| 29 | return 0; | ||
| 30 | |||
| 64 | if (!perf_evlist__valid_sample_type(self->evlist)) { | 31 | if (!perf_evlist__valid_sample_type(self->evlist)) { |
| 65 | pr_err("non matching sample_type"); | 32 | pr_err("non matching sample_type"); |
| 66 | goto out_close; | 33 | return -1; |
| 67 | } | 34 | } |
| 68 | 35 | ||
| 69 | if (!perf_evlist__valid_sample_id_all(self->evlist)) { | 36 | if (!perf_evlist__valid_sample_id_all(self->evlist)) { |
| 70 | pr_err("non matching sample_id_all"); | 37 | pr_err("non matching sample_id_all"); |
| 71 | goto out_close; | 38 | return -1; |
| 72 | } | 39 | } |
| 73 | 40 | ||
| 74 | if (!perf_evlist__valid_read_format(self->evlist)) { | 41 | if (!perf_evlist__valid_read_format(self->evlist)) { |
| 75 | pr_err("non matching read_format"); | 42 | pr_err("non matching read_format"); |
| 76 | goto out_close; | 43 | return -1; |
| 77 | } | 44 | } |
| 78 | 45 | ||
| 79 | self->size = input_stat.st_size; | ||
| 80 | return 0; | 46 | return 0; |
| 81 | |||
| 82 | out_close: | ||
| 83 | close(self->fd); | ||
| 84 | self->fd = -1; | ||
| 85 | return -1; | ||
| 86 | } | 47 | } |
| 87 | 48 | ||
| 88 | void perf_session__set_id_hdr_size(struct perf_session *session) | 49 | 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 | machines__destroy_kernel_maps(&self->machines); | 67 | machines__destroy_kernel_maps(&self->machines); |
| 107 | } | 68 | } |
| 108 | 69 | ||
| 109 | struct perf_session *perf_session__new(const char *filename, int mode, | 70 | struct perf_session *perf_session__new(struct perf_data_file *file, |
| 110 | bool force, bool repipe, | 71 | bool repipe, struct perf_tool *tool) |
| 111 | struct perf_tool *tool) | ||
| 112 | { | 72 | { |
| 113 | struct perf_session *self; | 73 | struct perf_session *self; |
| 114 | struct stat st; | ||
| 115 | size_t len; | ||
| 116 | |||
| 117 | if (!filename || !strlen(filename)) { | ||
| 118 | if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode)) | ||
| 119 | filename = "-"; | ||
| 120 | else | ||
| 121 | filename = "perf.data"; | ||
| 122 | } | ||
| 123 | 74 | ||
| 124 | len = strlen(filename); | 75 | self = zalloc(sizeof(*self)); |
| 125 | self = zalloc(sizeof(*self) + len); | 76 | if (!self) |
| 126 | |||
| 127 | if (self == NULL) | ||
| 128 | goto out; | 77 | goto out; |
| 129 | 78 | ||
| 130 | memcpy(self->filename, filename, len); | ||
| 131 | self->repipe = repipe; | 79 | self->repipe = repipe; |
| 132 | INIT_LIST_HEAD(&self->ordered_samples.samples); | 80 | INIT_LIST_HEAD(&self->ordered_samples.samples); |
| 133 | INIT_LIST_HEAD(&self->ordered_samples.sample_cache); | 81 | INIT_LIST_HEAD(&self->ordered_samples.sample_cache); |
| 134 | INIT_LIST_HEAD(&self->ordered_samples.to_free); | 82 | INIT_LIST_HEAD(&self->ordered_samples.to_free); |
| 135 | machines__init(&self->machines); | 83 | machines__init(&self->machines); |
| 136 | 84 | ||
| 137 | if (mode == O_RDONLY) { | 85 | if (file) { |
| 138 | if (perf_session__open(self, force) < 0) | 86 | if (perf_data_file__open(file)) |
| 139 | goto out_delete; | 87 | goto out_delete; |
| 140 | perf_session__set_id_hdr_size(self); | 88 | |
| 141 | } else if (mode == O_WRONLY) { | 89 | self->file = file; |
| 90 | |||
| 91 | if (perf_data_file__is_read(file)) { | ||
| 92 | if (perf_session__open(self) < 0) | ||
| 93 | goto out_close; | ||
| 94 | |||
| 95 | perf_session__set_id_hdr_size(self); | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | if (!file || perf_data_file__is_write(file)) { | ||
| 142 | /* | 100 | /* |
| 143 | * In O_RDONLY mode this will be performed when reading the | 101 | * In O_RDONLY mode this will be performed when reading the |
| 144 | * kernel MMAP event, in perf_event__process_mmap(). | 102 | * kernel MMAP event, in perf_event__process_mmap(). |
| @@ -153,10 +111,13 @@ struct perf_session *perf_session__new(const char *filename, int mode, | |||
| 153 | tool->ordered_samples = false; | 111 | tool->ordered_samples = false; |
| 154 | } | 112 | } |
| 155 | 113 | ||
| 156 | out: | ||
| 157 | return self; | 114 | return self; |
| 158 | out_delete: | 115 | |
| 116 | out_close: | ||
| 117 | perf_data_file__close(file); | ||
| 118 | out_delete: | ||
| 159 | perf_session__delete(self); | 119 | perf_session__delete(self); |
| 120 | out: | ||
| 160 | return NULL; | 121 | return NULL; |
| 161 | } | 122 | } |
| 162 | 123 | ||
| @@ -193,7 +154,8 @@ void perf_session__delete(struct perf_session *self) | |||
| 193 | perf_session__delete_threads(self); | 154 | perf_session__delete_threads(self); |
| 194 | perf_session_env__delete(&self->header.env); | 155 | perf_session_env__delete(&self->header.env); |
| 195 | machines__exit(&self->machines); | 156 | machines__exit(&self->machines); |
| 196 | close(self->fd); | 157 | if (self->file) |
| 158 | perf_data_file__close(self->file); | ||
| 197 | free(self); | 159 | free(self); |
| 198 | vdso__exit(); | 160 | vdso__exit(); |
| 199 | } | 161 | } |
| @@ -453,6 +415,9 @@ void perf_event__attr_swap(struct perf_event_attr *attr) | |||
| 453 | attr->bp_type = bswap_32(attr->bp_type); | 415 | attr->bp_type = bswap_32(attr->bp_type); |
| 454 | attr->bp_addr = bswap_64(attr->bp_addr); | 416 | attr->bp_addr = bswap_64(attr->bp_addr); |
| 455 | attr->bp_len = bswap_64(attr->bp_len); | 417 | attr->bp_len = bswap_64(attr->bp_len); |
| 418 | attr->branch_sample_type = bswap_64(attr->branch_sample_type); | ||
| 419 | attr->sample_regs_user = bswap_64(attr->sample_regs_user); | ||
| 420 | attr->sample_stack_user = bswap_32(attr->sample_stack_user); | ||
| 456 | 421 | ||
| 457 | swap_bitfield((u8 *) (&attr->read_format + 1), sizeof(u64)); | 422 | swap_bitfield((u8 *) (&attr->read_format + 1), sizeof(u64)); |
| 458 | } | 423 | } |
| @@ -1047,6 +1012,7 @@ static int perf_session_deliver_event(struct perf_session *session, | |||
| 1047 | static int perf_session__process_user_event(struct perf_session *session, union perf_event *event, | 1012 | static int perf_session__process_user_event(struct perf_session *session, union perf_event *event, |
| 1048 | struct perf_tool *tool, u64 file_offset) | 1013 | struct perf_tool *tool, u64 file_offset) |
| 1049 | { | 1014 | { |
| 1015 | int fd = perf_data_file__fd(session->file); | ||
| 1050 | int err; | 1016 | int err; |
| 1051 | 1017 | ||
| 1052 | dump_event(session, event, file_offset, NULL); | 1018 | dump_event(session, event, file_offset, NULL); |
| @@ -1060,7 +1026,7 @@ static int perf_session__process_user_event(struct perf_session *session, union | |||
| 1060 | return err; | 1026 | return err; |
| 1061 | case PERF_RECORD_HEADER_TRACING_DATA: | 1027 | case PERF_RECORD_HEADER_TRACING_DATA: |
| 1062 | /* setup for reading amidst mmap */ | 1028 | /* setup for reading amidst mmap */ |
| 1063 | lseek(session->fd, file_offset, SEEK_SET); | 1029 | lseek(fd, file_offset, SEEK_SET); |
| 1064 | return tool->tracing_data(tool, event, session); | 1030 | return tool->tracing_data(tool, event, session); |
| 1065 | case PERF_RECORD_HEADER_BUILD_ID: | 1031 | case PERF_RECORD_HEADER_BUILD_ID: |
| 1066 | return tool->build_id(tool, event, session); | 1032 | return tool->build_id(tool, event, session); |
| @@ -1186,6 +1152,7 @@ volatile int session_done; | |||
| 1186 | static int __perf_session__process_pipe_events(struct perf_session *self, | 1152 | static int __perf_session__process_pipe_events(struct perf_session *self, |
| 1187 | struct perf_tool *tool) | 1153 | struct perf_tool *tool) |
| 1188 | { | 1154 | { |
| 1155 | int fd = perf_data_file__fd(self->file); | ||
| 1189 | union perf_event *event; | 1156 | union perf_event *event; |
| 1190 | uint32_t size, cur_size = 0; | 1157 | uint32_t size, cur_size = 0; |
| 1191 | void *buf = NULL; | 1158 | void *buf = NULL; |
| @@ -1204,7 +1171,7 @@ static int __perf_session__process_pipe_events(struct perf_session *self, | |||
| 1204 | return -errno; | 1171 | return -errno; |
| 1205 | more: | 1172 | more: |
| 1206 | event = buf; | 1173 | event = buf; |
| 1207 | err = readn(self->fd, event, sizeof(struct perf_event_header)); | 1174 | err = readn(fd, event, sizeof(struct perf_event_header)); |
| 1208 | if (err <= 0) { | 1175 | if (err <= 0) { |
| 1209 | if (err == 0) | 1176 | if (err == 0) |
| 1210 | goto done; | 1177 | goto done; |
| @@ -1236,7 +1203,7 @@ more: | |||
| 1236 | p += sizeof(struct perf_event_header); | 1203 | p += sizeof(struct perf_event_header); |
| 1237 | 1204 | ||
| 1238 | if (size - sizeof(struct perf_event_header)) { | 1205 | if (size - sizeof(struct perf_event_header)) { |
| 1239 | err = readn(self->fd, p, size - sizeof(struct perf_event_header)); | 1206 | err = readn(fd, p, size - sizeof(struct perf_event_header)); |
| 1240 | if (err <= 0) { | 1207 | if (err <= 0) { |
| 1241 | if (err == 0) { | 1208 | if (err == 0) { |
| 1242 | pr_err("unexpected end of event stream\n"); | 1209 | pr_err("unexpected end of event stream\n"); |
| @@ -1263,7 +1230,9 @@ more: | |||
| 1263 | if (!session_done()) | 1230 | if (!session_done()) |
| 1264 | goto more; | 1231 | goto more; |
| 1265 | done: | 1232 | done: |
| 1266 | err = 0; | 1233 | /* do the final flush for ordered samples */ |
| 1234 | self->ordered_samples.next_flush = ULLONG_MAX; | ||
| 1235 | err = flush_sample_queue(self, tool); | ||
| 1267 | out_err: | 1236 | out_err: |
| 1268 | free(buf); | 1237 | free(buf); |
| 1269 | perf_session__warn_about_errors(self, tool); | 1238 | perf_session__warn_about_errors(self, tool); |
| @@ -1315,6 +1284,7 @@ int __perf_session__process_events(struct perf_session *session, | |||
| 1315 | u64 data_offset, u64 data_size, | 1284 | u64 data_offset, u64 data_size, |
| 1316 | u64 file_size, struct perf_tool *tool) | 1285 | u64 file_size, struct perf_tool *tool) |
| 1317 | { | 1286 | { |
| 1287 | int fd = perf_data_file__fd(session->file); | ||
| 1318 | u64 head, page_offset, file_offset, file_pos, progress_next; | 1288 | u64 head, page_offset, file_offset, file_pos, progress_next; |
| 1319 | int err, mmap_prot, mmap_flags, map_idx = 0; | 1289 | int err, mmap_prot, mmap_flags, map_idx = 0; |
| 1320 | size_t mmap_size; | 1290 | size_t mmap_size; |
| @@ -1347,7 +1317,7 @@ int __perf_session__process_events(struct perf_session *session, | |||
| 1347 | mmap_flags = MAP_PRIVATE; | 1317 | mmap_flags = MAP_PRIVATE; |
| 1348 | } | 1318 | } |
| 1349 | remap: | 1319 | remap: |
| 1350 | buf = mmap(NULL, mmap_size, mmap_prot, mmap_flags, session->fd, | 1320 | buf = mmap(NULL, mmap_size, mmap_prot, mmap_flags, fd, |
| 1351 | file_offset); | 1321 | file_offset); |
| 1352 | if (buf == MAP_FAILED) { | 1322 | if (buf == MAP_FAILED) { |
| 1353 | pr_err("failed to mmap file\n"); | 1323 | pr_err("failed to mmap file\n"); |
| @@ -1392,13 +1362,13 @@ more: | |||
| 1392 | "Processing events..."); | 1362 | "Processing events..."); |
| 1393 | } | 1363 | } |
| 1394 | 1364 | ||
| 1395 | err = 0; | ||
| 1396 | if (session_done()) | 1365 | if (session_done()) |
| 1397 | goto out_err; | 1366 | goto out; |
| 1398 | 1367 | ||
| 1399 | if (file_pos < file_size) | 1368 | if (file_pos < file_size) |
| 1400 | goto more; | 1369 | goto more; |
| 1401 | 1370 | ||
| 1371 | out: | ||
| 1402 | /* do the final flush for ordered samples */ | 1372 | /* do the final flush for ordered samples */ |
| 1403 | session->ordered_samples.next_flush = ULLONG_MAX; | 1373 | session->ordered_samples.next_flush = ULLONG_MAX; |
| 1404 | err = flush_sample_queue(session, tool); | 1374 | err = flush_sample_queue(session, tool); |
| @@ -1412,16 +1382,17 @@ out_err: | |||
| 1412 | int perf_session__process_events(struct perf_session *self, | 1382 | int perf_session__process_events(struct perf_session *self, |
| 1413 | struct perf_tool *tool) | 1383 | struct perf_tool *tool) |
| 1414 | { | 1384 | { |
| 1385 | u64 size = perf_data_file__size(self->file); | ||
| 1415 | int err; | 1386 | int err; |
| 1416 | 1387 | ||
| 1417 | if (perf_session__register_idle_thread(self) == NULL) | 1388 | if (perf_session__register_idle_thread(self) == NULL) |
| 1418 | return -ENOMEM; | 1389 | return -ENOMEM; |
| 1419 | 1390 | ||
| 1420 | if (!self->fd_pipe) | 1391 | if (!perf_data_file__is_pipe(self->file)) |
| 1421 | err = __perf_session__process_events(self, | 1392 | err = __perf_session__process_events(self, |
| 1422 | self->header.data_offset, | 1393 | self->header.data_offset, |
| 1423 | self->header.data_size, | 1394 | self->header.data_size, |
| 1424 | self->size, tool); | 1395 | size, tool); |
| 1425 | else | 1396 | else |
| 1426 | err = __perf_session__process_pipe_events(self, tool); | 1397 | err = __perf_session__process_pipe_events(self, tool); |
| 1427 | 1398 | ||
| @@ -1541,7 +1512,8 @@ void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event, | |||
| 1541 | if (symbol_conf.use_callchain && sample->callchain) { | 1512 | if (symbol_conf.use_callchain && sample->callchain) { |
| 1542 | 1513 | ||
| 1543 | if (machine__resolve_callchain(machine, evsel, al.thread, | 1514 | if (machine__resolve_callchain(machine, evsel, al.thread, |
| 1544 | sample, NULL, NULL) != 0) { | 1515 | sample, NULL, NULL, |
| 1516 | PERF_MAX_STACK_DEPTH) != 0) { | ||
| 1545 | if (verbose) | 1517 | if (verbose) |
| 1546 | error("Failed to resolve callchain. Skipping\n"); | 1518 | error("Failed to resolve callchain. Skipping\n"); |
| 1547 | return; | 1519 | return; |
| @@ -1645,13 +1617,14 @@ int perf_session__cpu_bitmap(struct perf_session *session, | |||
| 1645 | void perf_session__fprintf_info(struct perf_session *session, FILE *fp, | 1617 | void perf_session__fprintf_info(struct perf_session *session, FILE *fp, |
| 1646 | bool full) | 1618 | bool full) |
| 1647 | { | 1619 | { |
| 1620 | int fd = perf_data_file__fd(session->file); | ||
| 1648 | struct stat st; | 1621 | struct stat st; |
| 1649 | int ret; | 1622 | int ret; |
| 1650 | 1623 | ||
| 1651 | if (session == NULL || fp == NULL) | 1624 | if (session == NULL || fp == NULL) |
| 1652 | return; | 1625 | return; |
| 1653 | 1626 | ||
| 1654 | ret = fstat(session->fd, &st); | 1627 | ret = fstat(fd, &st); |
| 1655 | if (ret == -1) | 1628 | if (ret == -1) |
| 1656 | return; | 1629 | return; |
| 1657 | 1630 | ||
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 04bf7373a7e5..27c74d38b868 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include "machine.h" | 7 | #include "machine.h" |
| 8 | #include "symbol.h" | 8 | #include "symbol.h" |
| 9 | #include "thread.h" | 9 | #include "thread.h" |
| 10 | #include "data.h" | ||
| 10 | #include <linux/rbtree.h> | 11 | #include <linux/rbtree.h> |
| 11 | #include <linux/perf_event.h> | 12 | #include <linux/perf_event.h> |
| 12 | 13 | ||
| @@ -29,16 +30,13 @@ struct ordered_samples { | |||
| 29 | 30 | ||
| 30 | struct perf_session { | 31 | struct perf_session { |
| 31 | struct perf_header header; | 32 | struct perf_header header; |
| 32 | unsigned long size; | ||
| 33 | struct machines machines; | 33 | struct machines machines; |
| 34 | struct perf_evlist *evlist; | 34 | struct perf_evlist *evlist; |
| 35 | struct pevent *pevent; | 35 | struct pevent *pevent; |
| 36 | struct events_stats stats; | 36 | struct events_stats stats; |
| 37 | int fd; | ||
| 38 | bool fd_pipe; | ||
| 39 | bool repipe; | 37 | bool repipe; |
| 40 | struct ordered_samples ordered_samples; | 38 | struct ordered_samples ordered_samples; |
| 41 | char filename[1]; | 39 | struct perf_data_file *file; |
| 42 | }; | 40 | }; |
| 43 | 41 | ||
| 44 | #define PRINT_IP_OPT_IP (1<<0) | 42 | #define PRINT_IP_OPT_IP (1<<0) |
| @@ -49,9 +47,8 @@ struct perf_session { | |||
| 49 | 47 | ||
| 50 | struct perf_tool; | 48 | struct perf_tool; |
| 51 | 49 | ||
| 52 | struct perf_session *perf_session__new(const char *filename, int mode, | 50 | struct perf_session *perf_session__new(struct perf_data_file *file, |
| 53 | bool force, bool repipe, | 51 | bool repipe, struct perf_tool *tool); |
| 54 | struct perf_tool *tool); | ||
| 55 | void perf_session__delete(struct perf_session *session); | 52 | void perf_session__delete(struct perf_session *session); |
| 56 | 53 | ||
| 57 | void perf_event_header__bswap(struct perf_event_header *self); | 54 | void perf_event_header__bswap(struct perf_event_header *self); |
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 32c56377e008..1f9821db9e77 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c | |||
| @@ -182,9 +182,19 @@ static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r) | |||
| 182 | static int64_t | 182 | static int64_t |
| 183 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) | 183 | sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) |
| 184 | { | 184 | { |
| 185 | int64_t ret; | ||
| 186 | |||
| 185 | if (!left->ms.sym && !right->ms.sym) | 187 | if (!left->ms.sym && !right->ms.sym) |
| 186 | return right->level - left->level; | 188 | return right->level - left->level; |
| 187 | 189 | ||
| 190 | /* | ||
| 191 | * comparing symbol address alone is not enough since it's a | ||
| 192 | * relative address within a dso. | ||
| 193 | */ | ||
| 194 | ret = sort__dso_cmp(left, right); | ||
| 195 | if (ret != 0) | ||
| 196 | return ret; | ||
| 197 | |||
| 188 | return _sort__sym_cmp(left->ms.sym, right->ms.sym); | 198 | return _sort__sym_cmp(left->ms.sym, right->ms.sym); |
| 189 | } | 199 | } |
| 190 | 200 | ||
diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h index b554ffc462b6..88cfeaff600b 100644 --- a/tools/perf/util/top.h +++ b/tools/perf/util/top.h | |||
| @@ -24,6 +24,7 @@ struct perf_top { | |||
| 24 | u64 exact_samples; | 24 | u64 exact_samples; |
| 25 | u64 guest_us_samples, guest_kernel_samples; | 25 | u64 guest_us_samples, guest_kernel_samples; |
| 26 | int print_entries, count_filter, delay_secs; | 26 | int print_entries, count_filter, delay_secs; |
| 27 | int max_stack; | ||
| 27 | bool hide_kernel_symbols, hide_user_symbols, zero; | 28 | bool hide_kernel_symbols, hide_user_symbols, zero; |
| 28 | bool use_tui, use_stdio; | 29 | bool use_tui, use_stdio; |
| 29 | bool kptr_restrict_warned; | 30 | bool kptr_restrict_warned; |
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 8dc8cf39f4ed..c25e57b3acb2 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c | |||
| @@ -394,3 +394,20 @@ unsigned long parse_tag_value(const char *str, struct parse_tag *tags) | |||
| 394 | 394 | ||
| 395 | return (unsigned long) -1; | 395 | return (unsigned long) -1; |
| 396 | } | 396 | } |
| 397 | |||
| 398 | int filename__read_int(const char *filename, int *value) | ||
| 399 | { | ||
| 400 | char line[64]; | ||
| 401 | int fd = open(filename, O_RDONLY), err = -1; | ||
| 402 | |||
| 403 | if (fd < 0) | ||
| 404 | return -1; | ||
| 405 | |||
| 406 | if (read(fd, line, sizeof(line)) > 0) { | ||
| 407 | *value = atoi(line); | ||
| 408 | err = 0; | ||
| 409 | } | ||
| 410 | |||
| 411 | close(fd); | ||
| 412 | return err; | ||
| 413 | } | ||
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index 42dfba70fbfc..c8f362daba87 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
| @@ -305,4 +305,6 @@ struct dso; | |||
| 305 | 305 | ||
| 306 | char *get_srcline(struct dso *dso, unsigned long addr); | 306 | char *get_srcline(struct dso *dso, unsigned long addr); |
| 307 | void free_srcline(char *srcline); | 307 | void free_srcline(char *srcline); |
| 308 | |||
| 309 | int filename__read_int(const char *filename, int *value); | ||
| 308 | #endif /* GIT_COMPAT_UTIL_H */ | 310 | #endif /* GIT_COMPAT_UTIL_H */ |
