diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-03-10 18:22:03 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-03-10 18:22:03 -0400 |
commit | 12ad143e1b803e541e48b8ba40f550250259ecdd (patch) | |
tree | 5202b407df21e5abaeb294d1ecddcf0a2eca7f8b /tools/perf/builtin-diff.c | |
parent | 262d6a9a63a387c8dfa9eb4f7713e159c941e52c (diff) | |
parent | b339da480315505aa28a723a983217ebcff95c86 (diff) |
Merge branch 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull perf updates from Thomas Gleixner:
"Perf updates and fixes:
Kernel:
- Handle events which have the bpf_event attribute set as side band
events as they carry information about BPF programs.
- Add missing switch-case fall-through comments
Libraries:
- Fix leaks and double frees in error code paths.
- Prevent buffer overflows in libtraceevent
Tools:
- Improvements in handling Intel BT/PTS
- Add BTF ELF markers to perf trace BPF programs to improve output
- Support --time, --cpu, --pid and --tid filters for perf diff
- Calculate the column width in perf annotate as the hardcoded 6
characters for the instruction are not sufficient
- Small fixes all over the place"
* 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (38 commits)
perf/core: Mark expected switch fall-through
perf/x86/intel/uncore: Fix client IMC events return huge result
perf/ring_buffer: Use high order allocations for AUX buffers optimistically
perf data: Force perf_data__open|close zero data->file.path
perf session: Fix double free in perf_data__close
perf evsel: Probe for precise_ip with simple attr
perf tools: Read and store caps/max_precise in perf_pmu
perf hist: Fix memory leak of srcline
perf hist: Add error path into hist_entry__init
perf c2c: Fix c2c report for empty numa node
perf script python: Add Python3 support to intel-pt-events.py
perf script python: Add Python3 support to event_analyzing_sample.py
perf script python: add Python3 support to check-perf-trace.py
perf script python: Add Python3 support to futex-contention.py
perf script python: Remove mixed indentation
perf diff: Support --pid/--tid filter options
perf diff: Support --cpu filter option
perf diff: Support --time filter option
perf thread: Generalize function to copy from thread addr space from intel-bts code
perf annotate: Calculate the max instruction name, align column to that
...
Diffstat (limited to 'tools/perf/builtin-diff.c')
-rw-r--r-- | tools/perf/builtin-diff.c | 168 |
1 files changed, 154 insertions, 14 deletions
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 58fe0e88215c..6e7920793729 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c | |||
@@ -19,12 +19,21 @@ | |||
19 | #include "util/util.h" | 19 | #include "util/util.h" |
20 | #include "util/data.h" | 20 | #include "util/data.h" |
21 | #include "util/config.h" | 21 | #include "util/config.h" |
22 | #include "util/time-utils.h" | ||
22 | 23 | ||
23 | #include <errno.h> | 24 | #include <errno.h> |
24 | #include <inttypes.h> | 25 | #include <inttypes.h> |
25 | #include <stdlib.h> | 26 | #include <stdlib.h> |
26 | #include <math.h> | 27 | #include <math.h> |
27 | 28 | ||
29 | struct perf_diff { | ||
30 | struct perf_tool tool; | ||
31 | const char *time_str; | ||
32 | struct perf_time_interval *ptime_range; | ||
33 | int range_size; | ||
34 | int range_num; | ||
35 | }; | ||
36 | |||
28 | /* Diff command specific HPP columns. */ | 37 | /* Diff command specific HPP columns. */ |
29 | enum { | 38 | enum { |
30 | PERF_HPP_DIFF__BASELINE, | 39 | PERF_HPP_DIFF__BASELINE, |
@@ -74,6 +83,9 @@ static unsigned int sort_compute = 1; | |||
74 | static s64 compute_wdiff_w1; | 83 | static s64 compute_wdiff_w1; |
75 | static s64 compute_wdiff_w2; | 84 | static s64 compute_wdiff_w2; |
76 | 85 | ||
86 | static const char *cpu_list; | ||
87 | static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); | ||
88 | |||
77 | enum { | 89 | enum { |
78 | COMPUTE_DELTA, | 90 | COMPUTE_DELTA, |
79 | COMPUTE_RATIO, | 91 | COMPUTE_RATIO, |
@@ -323,22 +335,33 @@ static int formula_fprintf(struct hist_entry *he, struct hist_entry *pair, | |||
323 | return -1; | 335 | return -1; |
324 | } | 336 | } |
325 | 337 | ||
326 | static int diff__process_sample_event(struct perf_tool *tool __maybe_unused, | 338 | static int diff__process_sample_event(struct perf_tool *tool, |
327 | union perf_event *event, | 339 | union perf_event *event, |
328 | struct perf_sample *sample, | 340 | struct perf_sample *sample, |
329 | struct perf_evsel *evsel, | 341 | struct perf_evsel *evsel, |
330 | struct machine *machine) | 342 | struct machine *machine) |
331 | { | 343 | { |
344 | struct perf_diff *pdiff = container_of(tool, struct perf_diff, tool); | ||
332 | struct addr_location al; | 345 | struct addr_location al; |
333 | struct hists *hists = evsel__hists(evsel); | 346 | struct hists *hists = evsel__hists(evsel); |
334 | int ret = -1; | 347 | int ret = -1; |
335 | 348 | ||
349 | if (perf_time__ranges_skip_sample(pdiff->ptime_range, pdiff->range_num, | ||
350 | sample->time)) { | ||
351 | return 0; | ||
352 | } | ||
353 | |||
336 | if (machine__resolve(machine, &al, sample) < 0) { | 354 | if (machine__resolve(machine, &al, sample) < 0) { |
337 | pr_warning("problem processing %d event, skipping it.\n", | 355 | pr_warning("problem processing %d event, skipping it.\n", |
338 | event->header.type); | 356 | event->header.type); |
339 | return -1; | 357 | return -1; |
340 | } | 358 | } |
341 | 359 | ||
360 | if (cpu_list && !test_bit(sample->cpu, cpu_bitmap)) { | ||
361 | ret = 0; | ||
362 | goto out_put; | ||
363 | } | ||
364 | |||
342 | if (!hists__add_entry(hists, &al, NULL, NULL, NULL, sample, true)) { | 365 | if (!hists__add_entry(hists, &al, NULL, NULL, NULL, sample, true)) { |
343 | pr_warning("problem incrementing symbol period, skipping event\n"); | 366 | pr_warning("problem incrementing symbol period, skipping event\n"); |
344 | goto out_put; | 367 | goto out_put; |
@@ -359,17 +382,19 @@ out_put: | |||
359 | return ret; | 382 | return ret; |
360 | } | 383 | } |
361 | 384 | ||
362 | static struct perf_tool tool = { | 385 | static struct perf_diff pdiff = { |
363 | .sample = diff__process_sample_event, | 386 | .tool = { |
364 | .mmap = perf_event__process_mmap, | 387 | .sample = diff__process_sample_event, |
365 | .mmap2 = perf_event__process_mmap2, | 388 | .mmap = perf_event__process_mmap, |
366 | .comm = perf_event__process_comm, | 389 | .mmap2 = perf_event__process_mmap2, |
367 | .exit = perf_event__process_exit, | 390 | .comm = perf_event__process_comm, |
368 | .fork = perf_event__process_fork, | 391 | .exit = perf_event__process_exit, |
369 | .lost = perf_event__process_lost, | 392 | .fork = perf_event__process_fork, |
370 | .namespaces = perf_event__process_namespaces, | 393 | .lost = perf_event__process_lost, |
371 | .ordered_events = true, | 394 | .namespaces = perf_event__process_namespaces, |
372 | .ordering_requires_timestamps = true, | 395 | .ordered_events = true, |
396 | .ordering_requires_timestamps = true, | ||
397 | }, | ||
373 | }; | 398 | }; |
374 | 399 | ||
375 | static struct perf_evsel *evsel_match(struct perf_evsel *evsel, | 400 | static struct perf_evsel *evsel_match(struct perf_evsel *evsel, |
@@ -771,19 +796,117 @@ static void data__free(struct data__file *d) | |||
771 | } | 796 | } |
772 | } | 797 | } |
773 | 798 | ||
799 | static int abstime_str_dup(char **pstr) | ||
800 | { | ||
801 | char *str = NULL; | ||
802 | |||
803 | if (pdiff.time_str && strchr(pdiff.time_str, ':')) { | ||
804 | str = strdup(pdiff.time_str); | ||
805 | if (!str) | ||
806 | return -ENOMEM; | ||
807 | } | ||
808 | |||
809 | *pstr = str; | ||
810 | return 0; | ||
811 | } | ||
812 | |||
813 | static int parse_absolute_time(struct data__file *d, char **pstr) | ||
814 | { | ||
815 | char *p = *pstr; | ||
816 | int ret; | ||
817 | |||
818 | /* | ||
819 | * Absolute timestamp for one file has the format: a.b,c.d | ||
820 | * For multiple files, the format is: a.b,c.d:a.b,c.d | ||
821 | */ | ||
822 | p = strchr(*pstr, ':'); | ||
823 | if (p) { | ||
824 | if (p == *pstr) { | ||
825 | pr_err("Invalid time string\n"); | ||
826 | return -EINVAL; | ||
827 | } | ||
828 | |||
829 | *p = 0; | ||
830 | p++; | ||
831 | if (*p == 0) { | ||
832 | pr_err("Invalid time string\n"); | ||
833 | return -EINVAL; | ||
834 | } | ||
835 | } | ||
836 | |||
837 | ret = perf_time__parse_for_ranges(*pstr, d->session, | ||
838 | &pdiff.ptime_range, | ||
839 | &pdiff.range_size, | ||
840 | &pdiff.range_num); | ||
841 | if (ret < 0) | ||
842 | return ret; | ||
843 | |||
844 | if (!p || *p == 0) | ||
845 | *pstr = NULL; | ||
846 | else | ||
847 | *pstr = p; | ||
848 | |||
849 | return ret; | ||
850 | } | ||
851 | |||
852 | static int parse_percent_time(struct data__file *d) | ||
853 | { | ||
854 | int ret; | ||
855 | |||
856 | ret = perf_time__parse_for_ranges(pdiff.time_str, d->session, | ||
857 | &pdiff.ptime_range, | ||
858 | &pdiff.range_size, | ||
859 | &pdiff.range_num); | ||
860 | return ret; | ||
861 | } | ||
862 | |||
863 | static int parse_time_str(struct data__file *d, char *abstime_ostr, | ||
864 | char **pabstime_tmp) | ||
865 | { | ||
866 | int ret = 0; | ||
867 | |||
868 | if (abstime_ostr) | ||
869 | ret = parse_absolute_time(d, pabstime_tmp); | ||
870 | else if (pdiff.time_str) | ||
871 | ret = parse_percent_time(d); | ||
872 | |||
873 | return ret; | ||
874 | } | ||
875 | |||
774 | static int __cmd_diff(void) | 876 | static int __cmd_diff(void) |
775 | { | 877 | { |
776 | struct data__file *d; | 878 | struct data__file *d; |
777 | int ret = -EINVAL, i; | 879 | int ret, i; |
880 | char *abstime_ostr, *abstime_tmp; | ||
881 | |||
882 | ret = abstime_str_dup(&abstime_ostr); | ||
883 | if (ret) | ||
884 | return ret; | ||
885 | |||
886 | abstime_tmp = abstime_ostr; | ||
887 | ret = -EINVAL; | ||
778 | 888 | ||
779 | data__for_each_file(i, d) { | 889 | data__for_each_file(i, d) { |
780 | d->session = perf_session__new(&d->data, false, &tool); | 890 | d->session = perf_session__new(&d->data, false, &pdiff.tool); |
781 | if (!d->session) { | 891 | if (!d->session) { |
782 | pr_err("Failed to open %s\n", d->data.path); | 892 | pr_err("Failed to open %s\n", d->data.path); |
783 | ret = -1; | 893 | ret = -1; |
784 | goto out_delete; | 894 | goto out_delete; |
785 | } | 895 | } |
786 | 896 | ||
897 | if (pdiff.time_str) { | ||
898 | ret = parse_time_str(d, abstime_ostr, &abstime_tmp); | ||
899 | if (ret < 0) | ||
900 | goto out_delete; | ||
901 | } | ||
902 | |||
903 | if (cpu_list) { | ||
904 | ret = perf_session__cpu_bitmap(d->session, cpu_list, | ||
905 | cpu_bitmap); | ||
906 | if (ret < 0) | ||
907 | goto out_delete; | ||
908 | } | ||
909 | |||
787 | ret = perf_session__process_events(d->session); | 910 | ret = perf_session__process_events(d->session); |
788 | if (ret) { | 911 | if (ret) { |
789 | pr_err("Failed to process %s\n", d->data.path); | 912 | pr_err("Failed to process %s\n", d->data.path); |
@@ -791,6 +914,9 @@ static int __cmd_diff(void) | |||
791 | } | 914 | } |
792 | 915 | ||
793 | perf_evlist__collapse_resort(d->session->evlist); | 916 | perf_evlist__collapse_resort(d->session->evlist); |
917 | |||
918 | if (pdiff.ptime_range) | ||
919 | zfree(&pdiff.ptime_range); | ||
794 | } | 920 | } |
795 | 921 | ||
796 | data_process(); | 922 | data_process(); |
@@ -802,6 +928,13 @@ static int __cmd_diff(void) | |||
802 | } | 928 | } |
803 | 929 | ||
804 | free(data__files); | 930 | free(data__files); |
931 | |||
932 | if (pdiff.ptime_range) | ||
933 | zfree(&pdiff.ptime_range); | ||
934 | |||
935 | if (abstime_ostr) | ||
936 | free(abstime_ostr); | ||
937 | |||
805 | return ret; | 938 | return ret; |
806 | } | 939 | } |
807 | 940 | ||
@@ -849,6 +982,13 @@ static const struct option options[] = { | |||
849 | OPT_UINTEGER('o', "order", &sort_compute, "Specify compute sorting."), | 982 | OPT_UINTEGER('o', "order", &sort_compute, "Specify compute sorting."), |
850 | OPT_CALLBACK(0, "percentage", NULL, "relative|absolute", | 983 | OPT_CALLBACK(0, "percentage", NULL, "relative|absolute", |
851 | "How to display percentage of filtered entries", parse_filter_percentage), | 984 | "How to display percentage of filtered entries", parse_filter_percentage), |
985 | OPT_STRING(0, "time", &pdiff.time_str, "str", | ||
986 | "Time span (time percent or absolute timestamp)"), | ||
987 | OPT_STRING(0, "cpu", &cpu_list, "cpu", "list of cpus to profile"), | ||
988 | OPT_STRING(0, "pid", &symbol_conf.pid_list_str, "pid[,pid...]", | ||
989 | "only consider symbols in these pids"), | ||
990 | OPT_STRING(0, "tid", &symbol_conf.tid_list_str, "tid[,tid...]", | ||
991 | "only consider symbols in these tids"), | ||
852 | OPT_END() | 992 | OPT_END() |
853 | }; | 993 | }; |
854 | 994 | ||