diff options
author | Ingo Molnar <mingo@kernel.org> | 2016-04-06 02:46:23 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2016-04-06 02:46:23 -0400 |
commit | dad38ca64a252144b4ccdfe9730a3fe2b7c61957 (patch) | |
tree | 5eccdc35d45e9d05a088512a68418fc4fa0612aa | |
parent | d1b26c70246bc72922ae61d9f972d5c2588409e7 (diff) | |
parent | d37ba880598654fda10b312331377cdca3edd574 (diff) |
Merge tag 'perf-core-for-mingo-20160401' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core
Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:
User visible changes:
- Do not use events that don't have timestamps when setting 'perf trace's
base timestamp, fixing up the timestamp column for syscalls (Arnaldo Carvalho de Melo)
- Make the 'bpf-output' sample_type be the same as tracepoint's, fixing up
'perf trace's timestamp column for bpf events (Wang Nan)
- Fix PMU term format max value calculation (Kan Liang)
- Pretty print 'seccomp', 'getrandom' syscalls in 'perf trace' (Arnaldo Carvalho de Melo)
Infrastructure changes:
- Add support for using TSC as an ARCH timestamp when synthesizing
JIT records (Adrian Hunter)
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r-- | tools/perf/arch/x86/util/intel-bts.c | 5 | ||||
-rw-r--r-- | tools/perf/arch/x86/util/intel-pt.c | 5 | ||||
-rw-r--r-- | tools/perf/arch/x86/util/tsc.c | 32 | ||||
-rw-r--r-- | tools/perf/arch/x86/util/tsc.h | 17 | ||||
-rw-r--r-- | tools/perf/builtin-inject.c | 1 | ||||
-rw-r--r-- | tools/perf/builtin-record.c | 15 | ||||
-rw-r--r-- | tools/perf/builtin-trace.c | 99 | ||||
-rw-r--r-- | tools/perf/jvmti/jvmti_agent.c | 43 | ||||
-rw-r--r-- | tools/perf/util/Build | 3 | ||||
-rw-r--r-- | tools/perf/util/event.c | 1 | ||||
-rw-r--r-- | tools/perf/util/event.h | 9 | ||||
-rw-r--r-- | tools/perf/util/evsel.c | 3 | ||||
-rw-r--r-- | tools/perf/util/jitdump.c | 37 | ||||
-rw-r--r-- | tools/perf/util/jitdump.h | 3 | ||||
-rw-r--r-- | tools/perf/util/pmu.c | 13 | ||||
-rw-r--r-- | tools/perf/util/session.c | 6 | ||||
-rw-r--r-- | tools/perf/util/session.h | 1 | ||||
-rw-r--r-- | tools/perf/util/tool.h | 1 | ||||
-rw-r--r-- | tools/perf/util/tsc.h | 21 |
19 files changed, 274 insertions, 41 deletions
diff --git a/tools/perf/arch/x86/util/intel-bts.c b/tools/perf/arch/x86/util/intel-bts.c index d66f9ad4df2e..7dc30637cf66 100644 --- a/tools/perf/arch/x86/util/intel-bts.c +++ b/tools/perf/arch/x86/util/intel-bts.c | |||
@@ -438,6 +438,11 @@ struct auxtrace_record *intel_bts_recording_init(int *err) | |||
438 | if (!intel_bts_pmu) | 438 | if (!intel_bts_pmu) |
439 | return NULL; | 439 | return NULL; |
440 | 440 | ||
441 | if (setenv("JITDUMP_USE_ARCH_TIMESTAMP", "1", 1)) { | ||
442 | *err = -errno; | ||
443 | return NULL; | ||
444 | } | ||
445 | |||
441 | btsr = zalloc(sizeof(struct intel_bts_recording)); | 446 | btsr = zalloc(sizeof(struct intel_bts_recording)); |
442 | if (!btsr) { | 447 | if (!btsr) { |
443 | *err = -ENOMEM; | 448 | *err = -ENOMEM; |
diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c index a3395179c9ee..a07b9605e93b 100644 --- a/tools/perf/arch/x86/util/intel-pt.c +++ b/tools/perf/arch/x86/util/intel-pt.c | |||
@@ -1027,6 +1027,11 @@ struct auxtrace_record *intel_pt_recording_init(int *err) | |||
1027 | if (!intel_pt_pmu) | 1027 | if (!intel_pt_pmu) |
1028 | return NULL; | 1028 | return NULL; |
1029 | 1029 | ||
1030 | if (setenv("JITDUMP_USE_ARCH_TIMESTAMP", "1", 1)) { | ||
1031 | *err = -errno; | ||
1032 | return NULL; | ||
1033 | } | ||
1034 | |||
1030 | ptr = zalloc(sizeof(struct intel_pt_recording)); | 1035 | ptr = zalloc(sizeof(struct intel_pt_recording)); |
1031 | if (!ptr) { | 1036 | if (!ptr) { |
1032 | *err = -ENOMEM; | 1037 | *err = -ENOMEM; |
diff --git a/tools/perf/arch/x86/util/tsc.c b/tools/perf/arch/x86/util/tsc.c index fd2868490d00..357f1b13b5ae 100644 --- a/tools/perf/arch/x86/util/tsc.c +++ b/tools/perf/arch/x86/util/tsc.c | |||
@@ -7,7 +7,6 @@ | |||
7 | #include <linux/types.h> | 7 | #include <linux/types.h> |
8 | #include "../../util/debug.h" | 8 | #include "../../util/debug.h" |
9 | #include "../../util/tsc.h" | 9 | #include "../../util/tsc.h" |
10 | #include "tsc.h" | ||
11 | 10 | ||
12 | int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc, | 11 | int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc, |
13 | struct perf_tsc_conversion *tc) | 12 | struct perf_tsc_conversion *tc) |
@@ -46,3 +45,34 @@ u64 rdtsc(void) | |||
46 | 45 | ||
47 | return low | ((u64)high) << 32; | 46 | return low | ((u64)high) << 32; |
48 | } | 47 | } |
48 | |||
49 | int perf_event__synth_time_conv(const struct perf_event_mmap_page *pc, | ||
50 | struct perf_tool *tool, | ||
51 | perf_event__handler_t process, | ||
52 | struct machine *machine) | ||
53 | { | ||
54 | union perf_event event = { | ||
55 | .time_conv = { | ||
56 | .header = { | ||
57 | .type = PERF_RECORD_TIME_CONV, | ||
58 | .size = sizeof(struct time_conv_event), | ||
59 | }, | ||
60 | }, | ||
61 | }; | ||
62 | struct perf_tsc_conversion tc; | ||
63 | int err; | ||
64 | |||
65 | err = perf_read_tsc_conversion(pc, &tc); | ||
66 | if (err == -EOPNOTSUPP) | ||
67 | return 0; | ||
68 | if (err) | ||
69 | return err; | ||
70 | |||
71 | pr_debug2("Synthesizing TSC conversion information\n"); | ||
72 | |||
73 | event.time_conv.time_mult = tc.time_mult; | ||
74 | event.time_conv.time_shift = tc.time_shift; | ||
75 | event.time_conv.time_zero = tc.time_zero; | ||
76 | |||
77 | return process(tool, &event, NULL, machine); | ||
78 | } | ||
diff --git a/tools/perf/arch/x86/util/tsc.h b/tools/perf/arch/x86/util/tsc.h deleted file mode 100644 index 2edc4d31065c..000000000000 --- a/tools/perf/arch/x86/util/tsc.h +++ /dev/null | |||
@@ -1,17 +0,0 @@ | |||
1 | #ifndef TOOLS_PERF_ARCH_X86_UTIL_TSC_H__ | ||
2 | #define TOOLS_PERF_ARCH_X86_UTIL_TSC_H__ | ||
3 | |||
4 | #include <linux/types.h> | ||
5 | |||
6 | struct perf_tsc_conversion { | ||
7 | u16 time_shift; | ||
8 | u32 time_mult; | ||
9 | u64 time_zero; | ||
10 | }; | ||
11 | |||
12 | struct perf_event_mmap_page; | ||
13 | |||
14 | int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc, | ||
15 | struct perf_tsc_conversion *tc); | ||
16 | |||
17 | #endif /* TOOLS_PERF_ARCH_X86_UTIL_TSC_H__ */ | ||
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index d1a2d104f2bc..e5afa8fe1bf1 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c | |||
@@ -748,6 +748,7 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) | |||
748 | .auxtrace_info = perf_event__repipe_op2_synth, | 748 | .auxtrace_info = perf_event__repipe_op2_synth, |
749 | .auxtrace = perf_event__repipe_auxtrace, | 749 | .auxtrace = perf_event__repipe_auxtrace, |
750 | .auxtrace_error = perf_event__repipe_op2_synth, | 750 | .auxtrace_error = perf_event__repipe_op2_synth, |
751 | .time_conv = perf_event__repipe_op2_synth, | ||
751 | .finished_round = perf_event__repipe_oe_synth, | 752 | .finished_round = perf_event__repipe_oe_synth, |
752 | .build_id = perf_event__repipe_op2_synth, | 753 | .build_id = perf_event__repipe_op2_synth, |
753 | .id_index = perf_event__repipe_op2_synth, | 754 | .id_index = perf_event__repipe_op2_synth, |
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 515510ecc76a..410035c6e300 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include "util/data.h" | 29 | #include "util/data.h" |
30 | #include "util/perf_regs.h" | 30 | #include "util/perf_regs.h" |
31 | #include "util/auxtrace.h" | 31 | #include "util/auxtrace.h" |
32 | #include "util/tsc.h" | ||
32 | #include "util/parse-branch-options.h" | 33 | #include "util/parse-branch-options.h" |
33 | #include "util/parse-regs-options.h" | 34 | #include "util/parse-regs-options.h" |
34 | #include "util/llvm-utils.h" | 35 | #include "util/llvm-utils.h" |
@@ -512,6 +513,15 @@ static void workload_exec_failed_signal(int signo __maybe_unused, | |||
512 | 513 | ||
513 | static void snapshot_sig_handler(int sig); | 514 | static void snapshot_sig_handler(int sig); |
514 | 515 | ||
516 | int __weak | ||
517 | perf_event__synth_time_conv(const struct perf_event_mmap_page *pc __maybe_unused, | ||
518 | struct perf_tool *tool __maybe_unused, | ||
519 | perf_event__handler_t process __maybe_unused, | ||
520 | struct machine *machine __maybe_unused) | ||
521 | { | ||
522 | return 0; | ||
523 | } | ||
524 | |||
515 | static int record__synthesize(struct record *rec) | 525 | static int record__synthesize(struct record *rec) |
516 | { | 526 | { |
517 | struct perf_session *session = rec->session; | 527 | struct perf_session *session = rec->session; |
@@ -549,6 +559,11 @@ static int record__synthesize(struct record *rec) | |||
549 | } | 559 | } |
550 | } | 560 | } |
551 | 561 | ||
562 | err = perf_event__synth_time_conv(rec->evlist->mmap[0].base, tool, | ||
563 | process_synthesized_event, machine); | ||
564 | if (err) | ||
565 | goto out; | ||
566 | |||
552 | if (rec->opts.full_auxtrace) { | 567 | if (rec->opts.full_auxtrace) { |
553 | err = perf_event__synthesize_auxtrace_info(rec->itr, tool, | 568 | err = perf_event__synthesize_auxtrace_info(rec->itr, tool, |
554 | session, process_synthesized_event); | 569 | session, process_synthesized_event); |
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 93ac724fb635..d309f4535a45 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c | |||
@@ -40,6 +40,11 @@ | |||
40 | #include <sys/mman.h> | 40 | #include <sys/mman.h> |
41 | #include <linux/futex.h> | 41 | #include <linux/futex.h> |
42 | #include <linux/err.h> | 42 | #include <linux/err.h> |
43 | #include <linux/seccomp.h> | ||
44 | #include <linux/filter.h> | ||
45 | #include <linux/audit.h> | ||
46 | #include <sys/ptrace.h> | ||
47 | #include <linux/random.h> | ||
43 | 48 | ||
44 | /* For older distros: */ | 49 | /* For older distros: */ |
45 | #ifndef MAP_STACK | 50 | #ifndef MAP_STACK |
@@ -1001,6 +1006,69 @@ static const char *tioctls[] = { | |||
1001 | static DEFINE_STRARRAY_OFFSET(tioctls, 0x5401); | 1006 | static DEFINE_STRARRAY_OFFSET(tioctls, 0x5401); |
1002 | #endif /* defined(__i386__) || defined(__x86_64__) */ | 1007 | #endif /* defined(__i386__) || defined(__x86_64__) */ |
1003 | 1008 | ||
1009 | static size_t syscall_arg__scnprintf_seccomp_op(char *bf, size_t size, struct syscall_arg *arg) | ||
1010 | { | ||
1011 | int op = arg->val; | ||
1012 | size_t printed = 0; | ||
1013 | |||
1014 | switch (op) { | ||
1015 | #define P_SECCOMP_SET_MODE_OP(n) case SECCOMP_SET_MODE_##n: printed = scnprintf(bf, size, #n); break | ||
1016 | P_SECCOMP_SET_MODE_OP(STRICT); | ||
1017 | P_SECCOMP_SET_MODE_OP(FILTER); | ||
1018 | #undef P_SECCOMP_SET_MODE_OP | ||
1019 | default: printed = scnprintf(bf, size, "%#x", op); break; | ||
1020 | } | ||
1021 | |||
1022 | return printed; | ||
1023 | } | ||
1024 | |||
1025 | #define SCA_SECCOMP_OP syscall_arg__scnprintf_seccomp_op | ||
1026 | |||
1027 | static size_t syscall_arg__scnprintf_seccomp_flags(char *bf, size_t size, | ||
1028 | struct syscall_arg *arg) | ||
1029 | { | ||
1030 | int printed = 0, flags = arg->val; | ||
1031 | |||
1032 | #define P_FLAG(n) \ | ||
1033 | if (flags & SECCOMP_FILTER_FLAG_##n) { \ | ||
1034 | printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \ | ||
1035 | flags &= ~SECCOMP_FILTER_FLAG_##n; \ | ||
1036 | } | ||
1037 | |||
1038 | P_FLAG(TSYNC); | ||
1039 | #undef P_FLAG | ||
1040 | |||
1041 | if (flags) | ||
1042 | printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", flags); | ||
1043 | |||
1044 | return printed; | ||
1045 | } | ||
1046 | |||
1047 | #define SCA_SECCOMP_FLAGS syscall_arg__scnprintf_seccomp_flags | ||
1048 | |||
1049 | static size_t syscall_arg__scnprintf_getrandom_flags(char *bf, size_t size, | ||
1050 | struct syscall_arg *arg) | ||
1051 | { | ||
1052 | int printed = 0, flags = arg->val; | ||
1053 | |||
1054 | #define P_FLAG(n) \ | ||
1055 | if (flags & GRND_##n) { \ | ||
1056 | printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \ | ||
1057 | flags &= ~GRND_##n; \ | ||
1058 | } | ||
1059 | |||
1060 | P_FLAG(RANDOM); | ||
1061 | P_FLAG(NONBLOCK); | ||
1062 | #undef P_FLAG | ||
1063 | |||
1064 | if (flags) | ||
1065 | printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", flags); | ||
1066 | |||
1067 | return printed; | ||
1068 | } | ||
1069 | |||
1070 | #define SCA_GETRANDOM_FLAGS syscall_arg__scnprintf_getrandom_flags | ||
1071 | |||
1004 | #define STRARRAY(arg, name, array) \ | 1072 | #define STRARRAY(arg, name, array) \ |
1005 | .arg_scnprintf = { [arg] = SCA_STRARRAY, }, \ | 1073 | .arg_scnprintf = { [arg] = SCA_STRARRAY, }, \ |
1006 | .arg_parm = { [arg] = &strarray__##array, } | 1074 | .arg_parm = { [arg] = &strarray__##array, } |
@@ -1093,6 +1161,8 @@ static struct syscall_fmt { | |||
1093 | { .name = "getdents64", .errmsg = true, | 1161 | { .name = "getdents64", .errmsg = true, |
1094 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, | 1162 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, |
1095 | { .name = "getitimer", .errmsg = true, STRARRAY(0, which, itimers), }, | 1163 | { .name = "getitimer", .errmsg = true, STRARRAY(0, which, itimers), }, |
1164 | { .name = "getrandom", .errmsg = true, | ||
1165 | .arg_scnprintf = { [2] = SCA_GETRANDOM_FLAGS, /* flags */ }, }, | ||
1096 | { .name = "getrlimit", .errmsg = true, STRARRAY(0, resource, rlimit_resources), }, | 1166 | { .name = "getrlimit", .errmsg = true, STRARRAY(0, resource, rlimit_resources), }, |
1097 | { .name = "getxattr", .errmsg = true, | 1167 | { .name = "getxattr", .errmsg = true, |
1098 | .arg_scnprintf = { [0] = SCA_FILENAME, /* pathname */ }, }, | 1168 | .arg_scnprintf = { [0] = SCA_FILENAME, /* pathname */ }, }, |
@@ -1234,6 +1304,9 @@ static struct syscall_fmt { | |||
1234 | .arg_scnprintf = { [1] = SCA_SIGNUM, /* sig */ }, }, | 1304 | .arg_scnprintf = { [1] = SCA_SIGNUM, /* sig */ }, }, |
1235 | { .name = "rt_tgsigqueueinfo", .errmsg = true, | 1305 | { .name = "rt_tgsigqueueinfo", .errmsg = true, |
1236 | .arg_scnprintf = { [2] = SCA_SIGNUM, /* sig */ }, }, | 1306 | .arg_scnprintf = { [2] = SCA_SIGNUM, /* sig */ }, }, |
1307 | { .name = "seccomp", .errmsg = true, | ||
1308 | .arg_scnprintf = { [0] = SCA_SECCOMP_OP, /* op */ | ||
1309 | [1] = SCA_SECCOMP_FLAGS, /* flags */ }, }, | ||
1237 | { .name = "select", .errmsg = true, .timeout = true, }, | 1310 | { .name = "select", .errmsg = true, .timeout = true, }, |
1238 | { .name = "sendmmsg", .errmsg = true, | 1311 | { .name = "sendmmsg", .errmsg = true, |
1239 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ | 1312 | .arg_scnprintf = { [0] = SCA_FD, /* fd */ |
@@ -1618,6 +1691,7 @@ static int trace__process_event(struct trace *trace, struct machine *machine, | |||
1618 | color_fprintf(trace->output, PERF_COLOR_RED, | 1691 | color_fprintf(trace->output, PERF_COLOR_RED, |
1619 | "LOST %" PRIu64 " events!\n", event->lost.lost); | 1692 | "LOST %" PRIu64 " events!\n", event->lost.lost); |
1620 | ret = machine__process_lost_event(machine, event, sample); | 1693 | ret = machine__process_lost_event(machine, event, sample); |
1694 | break; | ||
1621 | default: | 1695 | default: |
1622 | ret = machine__process_event(machine, event, sample); | 1696 | ret = machine__process_event(machine, event, sample); |
1623 | break; | 1697 | break; |
@@ -2326,6 +2400,23 @@ static bool skip_sample(struct trace *trace, struct perf_sample *sample) | |||
2326 | return false; | 2400 | return false; |
2327 | } | 2401 | } |
2328 | 2402 | ||
2403 | static void trace__set_base_time(struct trace *trace, | ||
2404 | struct perf_evsel *evsel, | ||
2405 | struct perf_sample *sample) | ||
2406 | { | ||
2407 | /* | ||
2408 | * BPF events were not setting PERF_SAMPLE_TIME, so be more robust | ||
2409 | * and don't use sample->time unconditionally, we may end up having | ||
2410 | * some other event in the future without PERF_SAMPLE_TIME for good | ||
2411 | * reason, i.e. we may not be interested in its timestamps, just in | ||
2412 | * it taking place, picking some piece of information when it | ||
2413 | * appears in our event stream (vfs_getname comes to mind). | ||
2414 | */ | ||
2415 | if (trace->base_time == 0 && !trace->full_time && | ||
2416 | (evsel->attr.sample_type & PERF_SAMPLE_TIME)) | ||
2417 | trace->base_time = sample->time; | ||
2418 | } | ||
2419 | |||
2329 | static int trace__process_sample(struct perf_tool *tool, | 2420 | static int trace__process_sample(struct perf_tool *tool, |
2330 | union perf_event *event, | 2421 | union perf_event *event, |
2331 | struct perf_sample *sample, | 2422 | struct perf_sample *sample, |
@@ -2340,8 +2431,7 @@ static int trace__process_sample(struct perf_tool *tool, | |||
2340 | if (skip_sample(trace, sample)) | 2431 | if (skip_sample(trace, sample)) |
2341 | return 0; | 2432 | return 0; |
2342 | 2433 | ||
2343 | if (!trace->full_time && trace->base_time == 0) | 2434 | trace__set_base_time(trace, evsel, sample); |
2344 | trace->base_time = sample->time; | ||
2345 | 2435 | ||
2346 | if (handler) { | 2436 | if (handler) { |
2347 | ++trace->nr_events; | 2437 | ++trace->nr_events; |
@@ -2479,9 +2569,6 @@ static void trace__handle_event(struct trace *trace, union perf_event *event, st | |||
2479 | const u32 type = event->header.type; | 2569 | const u32 type = event->header.type; |
2480 | struct perf_evsel *evsel; | 2570 | struct perf_evsel *evsel; |
2481 | 2571 | ||
2482 | if (!trace->full_time && trace->base_time == 0) | ||
2483 | trace->base_time = sample->time; | ||
2484 | |||
2485 | if (type != PERF_RECORD_SAMPLE) { | 2572 | if (type != PERF_RECORD_SAMPLE) { |
2486 | trace__process_event(trace, trace->host, event, sample); | 2573 | trace__process_event(trace, trace->host, event, sample); |
2487 | return; | 2574 | return; |
@@ -2493,6 +2580,8 @@ static void trace__handle_event(struct trace *trace, union perf_event *event, st | |||
2493 | return; | 2580 | return; |
2494 | } | 2581 | } |
2495 | 2582 | ||
2583 | trace__set_base_time(trace, evsel, sample); | ||
2584 | |||
2496 | if (evsel->attr.type == PERF_TYPE_TRACEPOINT && | 2585 | if (evsel->attr.type == PERF_TYPE_TRACEPOINT && |
2497 | sample->raw_data == NULL) { | 2586 | sample->raw_data == NULL) { |
2498 | fprintf(trace->output, "%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n", | 2587 | fprintf(trace->output, "%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n", |
diff --git a/tools/perf/jvmti/jvmti_agent.c b/tools/perf/jvmti/jvmti_agent.c index 6461e02ab940..3573f315f955 100644 --- a/tools/perf/jvmti/jvmti_agent.c +++ b/tools/perf/jvmti/jvmti_agent.c | |||
@@ -92,6 +92,22 @@ error: | |||
92 | return ret; | 92 | return ret; |
93 | } | 93 | } |
94 | 94 | ||
95 | static int use_arch_timestamp; | ||
96 | |||
97 | static inline uint64_t | ||
98 | get_arch_timestamp(void) | ||
99 | { | ||
100 | #if defined(__i386__) || defined(__x86_64__) | ||
101 | unsigned int low, high; | ||
102 | |||
103 | asm volatile("rdtsc" : "=a" (low), "=d" (high)); | ||
104 | |||
105 | return low | ((uint64_t)high) << 32; | ||
106 | #else | ||
107 | return 0; | ||
108 | #endif | ||
109 | } | ||
110 | |||
95 | #define NSEC_PER_SEC 1000000000 | 111 | #define NSEC_PER_SEC 1000000000 |
96 | static int perf_clk_id = CLOCK_MONOTONIC; | 112 | static int perf_clk_id = CLOCK_MONOTONIC; |
97 | 113 | ||
@@ -107,6 +123,9 @@ perf_get_timestamp(void) | |||
107 | struct timespec ts; | 123 | struct timespec ts; |
108 | int ret; | 124 | int ret; |
109 | 125 | ||
126 | if (use_arch_timestamp) | ||
127 | return get_arch_timestamp(); | ||
128 | |||
110 | ret = clock_gettime(perf_clk_id, &ts); | 129 | ret = clock_gettime(perf_clk_id, &ts); |
111 | if (ret) | 130 | if (ret) |
112 | return 0; | 131 | return 0; |
@@ -203,6 +222,17 @@ perf_close_marker_file(void) | |||
203 | munmap(marker_addr, pgsz); | 222 | munmap(marker_addr, pgsz); |
204 | } | 223 | } |
205 | 224 | ||
225 | static void | ||
226 | init_arch_timestamp(void) | ||
227 | { | ||
228 | char *str = getenv("JITDUMP_USE_ARCH_TIMESTAMP"); | ||
229 | |||
230 | if (!str || !*str || !strcmp(str, "0")) | ||
231 | return; | ||
232 | |||
233 | use_arch_timestamp = 1; | ||
234 | } | ||
235 | |||
206 | void *jvmti_open(void) | 236 | void *jvmti_open(void) |
207 | { | 237 | { |
208 | int pad_cnt; | 238 | int pad_cnt; |
@@ -211,11 +241,17 @@ void *jvmti_open(void) | |||
211 | int fd; | 241 | int fd; |
212 | FILE *fp; | 242 | FILE *fp; |
213 | 243 | ||
244 | init_arch_timestamp(); | ||
245 | |||
214 | /* | 246 | /* |
215 | * check if clockid is supported | 247 | * check if clockid is supported |
216 | */ | 248 | */ |
217 | if (!perf_get_timestamp()) | 249 | if (!perf_get_timestamp()) { |
218 | warnx("jvmti: kernel does not support %d clock id", perf_clk_id); | 250 | if (use_arch_timestamp) |
251 | warnx("jvmti: arch timestamp not supported"); | ||
252 | else | ||
253 | warnx("jvmti: kernel does not support %d clock id", perf_clk_id); | ||
254 | } | ||
219 | 255 | ||
220 | memset(&header, 0, sizeof(header)); | 256 | memset(&header, 0, sizeof(header)); |
221 | 257 | ||
@@ -263,6 +299,9 @@ void *jvmti_open(void) | |||
263 | 299 | ||
264 | header.timestamp = perf_get_timestamp(); | 300 | header.timestamp = perf_get_timestamp(); |
265 | 301 | ||
302 | if (use_arch_timestamp) | ||
303 | header.flags |= JITDUMP_FLAGS_ARCH_TIMESTAMP; | ||
304 | |||
266 | if (!fwrite(&header, sizeof(header), 1, fp)) { | 305 | if (!fwrite(&header, sizeof(header), 1, fp)) { |
267 | warn("jvmti: cannot write dumpfile header"); | 306 | warn("jvmti: cannot write dumpfile header"); |
268 | goto error; | 307 | goto error; |
diff --git a/tools/perf/util/Build b/tools/perf/util/Build index da48fd843438..85ceff357769 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build | |||
@@ -69,8 +69,7 @@ libperf-y += stat-shadow.o | |||
69 | libperf-y += record.o | 69 | libperf-y += record.o |
70 | libperf-y += srcline.o | 70 | libperf-y += srcline.o |
71 | libperf-y += data.o | 71 | libperf-y += data.o |
72 | libperf-$(CONFIG_X86) += tsc.o | 72 | libperf-y += tsc.o |
73 | libperf-$(CONFIG_AUXTRACE) += tsc.o | ||
74 | libperf-y += cloexec.o | 73 | libperf-y += cloexec.o |
75 | libperf-y += thread-stack.o | 74 | libperf-y += thread-stack.o |
76 | libperf-$(CONFIG_AUXTRACE) += auxtrace.o | 75 | libperf-$(CONFIG_AUXTRACE) += auxtrace.o |
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index dad55d04ffdd..b68959037688 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c | |||
@@ -45,6 +45,7 @@ static const char *perf_event__names[] = { | |||
45 | [PERF_RECORD_STAT] = "STAT", | 45 | [PERF_RECORD_STAT] = "STAT", |
46 | [PERF_RECORD_STAT_ROUND] = "STAT_ROUND", | 46 | [PERF_RECORD_STAT_ROUND] = "STAT_ROUND", |
47 | [PERF_RECORD_EVENT_UPDATE] = "EVENT_UPDATE", | 47 | [PERF_RECORD_EVENT_UPDATE] = "EVENT_UPDATE", |
48 | [PERF_RECORD_TIME_CONV] = "TIME_CONV", | ||
48 | }; | 49 | }; |
49 | 50 | ||
50 | const char *perf_event__name(unsigned int id) | 51 | const char *perf_event__name(unsigned int id) |
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 6bb1c928350d..8d363d5e65a2 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h | |||
@@ -233,6 +233,7 @@ enum perf_user_event_type { /* above any possible kernel type */ | |||
233 | PERF_RECORD_STAT = 76, | 233 | PERF_RECORD_STAT = 76, |
234 | PERF_RECORD_STAT_ROUND = 77, | 234 | PERF_RECORD_STAT_ROUND = 77, |
235 | PERF_RECORD_EVENT_UPDATE = 78, | 235 | PERF_RECORD_EVENT_UPDATE = 78, |
236 | PERF_RECORD_TIME_CONV = 79, | ||
236 | PERF_RECORD_HEADER_MAX | 237 | PERF_RECORD_HEADER_MAX |
237 | }; | 238 | }; |
238 | 239 | ||
@@ -469,6 +470,13 @@ struct stat_round_event { | |||
469 | u64 time; | 470 | u64 time; |
470 | }; | 471 | }; |
471 | 472 | ||
473 | struct time_conv_event { | ||
474 | struct perf_event_header header; | ||
475 | u64 time_shift; | ||
476 | u64 time_mult; | ||
477 | u64 time_zero; | ||
478 | }; | ||
479 | |||
472 | union perf_event { | 480 | union perf_event { |
473 | struct perf_event_header header; | 481 | struct perf_event_header header; |
474 | struct mmap_event mmap; | 482 | struct mmap_event mmap; |
@@ -497,6 +505,7 @@ union perf_event { | |||
497 | struct stat_config_event stat_config; | 505 | struct stat_config_event stat_config; |
498 | struct stat_event stat; | 506 | struct stat_event stat; |
499 | struct stat_round_event stat_round; | 507 | struct stat_round_event stat_round; |
508 | struct time_conv_event time_conv; | ||
500 | }; | 509 | }; |
501 | 510 | ||
502 | void perf_event__print_totals(void); | 511 | void perf_event__print_totals(void); |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 738ce226002b..3fd7c2c72f4a 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -226,7 +226,8 @@ struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx) | |||
226 | perf_evsel__init(evsel, attr, idx); | 226 | perf_evsel__init(evsel, attr, idx); |
227 | 227 | ||
228 | if (perf_evsel__is_bpf_output(evsel)) { | 228 | if (perf_evsel__is_bpf_output(evsel)) { |
229 | evsel->attr.sample_type |= PERF_SAMPLE_RAW; | 229 | evsel->attr.sample_type |= (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | |
230 | PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD), | ||
230 | evsel->attr.sample_period = 1; | 231 | evsel->attr.sample_period = 1; |
231 | } | 232 | } |
232 | 233 | ||
diff --git a/tools/perf/util/jitdump.c b/tools/perf/util/jitdump.c index ad0c0bb1fbc7..52fcef3074fe 100644 --- a/tools/perf/util/jitdump.c +++ b/tools/perf/util/jitdump.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include "strlist.h" | 17 | #include "strlist.h" |
18 | #include <elf.h> | 18 | #include <elf.h> |
19 | 19 | ||
20 | #include "tsc.h" | ||
20 | #include "session.h" | 21 | #include "session.h" |
21 | #include "jit.h" | 22 | #include "jit.h" |
22 | #include "jitdump.h" | 23 | #include "jitdump.h" |
@@ -33,6 +34,7 @@ struct jit_buf_desc { | |||
33 | size_t bufsize; | 34 | size_t bufsize; |
34 | FILE *in; | 35 | FILE *in; |
35 | bool needs_bswap; /* handles cross-endianess */ | 36 | bool needs_bswap; /* handles cross-endianess */ |
37 | bool use_arch_timestamp; | ||
36 | void *debug_data; | 38 | void *debug_data; |
37 | size_t nr_debug_entries; | 39 | size_t nr_debug_entries; |
38 | uint32_t code_load_count; | 40 | uint32_t code_load_count; |
@@ -158,13 +160,16 @@ jit_open(struct jit_buf_desc *jd, const char *name) | |||
158 | header.flags = bswap_64(header.flags); | 160 | header.flags = bswap_64(header.flags); |
159 | } | 161 | } |
160 | 162 | ||
163 | jd->use_arch_timestamp = header.flags & JITDUMP_FLAGS_ARCH_TIMESTAMP; | ||
164 | |||
161 | if (verbose > 2) | 165 | if (verbose > 2) |
162 | pr_debug("version=%u\nhdr.size=%u\nts=0x%llx\npid=%d\nelf_mach=%d\n", | 166 | pr_debug("version=%u\nhdr.size=%u\nts=0x%llx\npid=%d\nelf_mach=%d\nuse_arch_timestamp=%d\n", |
163 | header.version, | 167 | header.version, |
164 | header.total_size, | 168 | header.total_size, |
165 | (unsigned long long)header.timestamp, | 169 | (unsigned long long)header.timestamp, |
166 | header.pid, | 170 | header.pid, |
167 | header.elf_mach); | 171 | header.elf_mach, |
172 | jd->use_arch_timestamp); | ||
168 | 173 | ||
169 | if (header.flags & JITDUMP_FLAGS_RESERVED) { | 174 | if (header.flags & JITDUMP_FLAGS_RESERVED) { |
170 | pr_err("jitdump file contains invalid or unsupported flags 0x%llx\n", | 175 | pr_err("jitdump file contains invalid or unsupported flags 0x%llx\n", |
@@ -172,10 +177,15 @@ jit_open(struct jit_buf_desc *jd, const char *name) | |||
172 | goto error; | 177 | goto error; |
173 | } | 178 | } |
174 | 179 | ||
180 | if (jd->use_arch_timestamp && !jd->session->time_conv.time_mult) { | ||
181 | pr_err("jitdump file uses arch timestamps but there is no timestamp conversion\n"); | ||
182 | goto error; | ||
183 | } | ||
184 | |||
175 | /* | 185 | /* |
176 | * validate event is using the correct clockid | 186 | * validate event is using the correct clockid |
177 | */ | 187 | */ |
178 | if (jit_validate_events(jd->session)) { | 188 | if (!jd->use_arch_timestamp && jit_validate_events(jd->session)) { |
179 | pr_err("error, jitted code must be sampled with perf record -k 1\n"); | 189 | pr_err("error, jitted code must be sampled with perf record -k 1\n"); |
180 | goto error; | 190 | goto error; |
181 | } | 191 | } |
@@ -329,6 +339,23 @@ jit_inject_event(struct jit_buf_desc *jd, union perf_event *event) | |||
329 | return 0; | 339 | return 0; |
330 | } | 340 | } |
331 | 341 | ||
342 | static uint64_t convert_timestamp(struct jit_buf_desc *jd, uint64_t timestamp) | ||
343 | { | ||
344 | struct perf_tsc_conversion tc; | ||
345 | |||
346 | if (!jd->use_arch_timestamp) | ||
347 | return timestamp; | ||
348 | |||
349 | tc.time_shift = jd->session->time_conv.time_shift; | ||
350 | tc.time_mult = jd->session->time_conv.time_mult; | ||
351 | tc.time_zero = jd->session->time_conv.time_zero; | ||
352 | |||
353 | if (!tc.time_mult) | ||
354 | return 0; | ||
355 | |||
356 | return tsc_to_perf_time(timestamp, &tc); | ||
357 | } | ||
358 | |||
332 | static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr) | 359 | static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr) |
333 | { | 360 | { |
334 | struct perf_sample sample; | 361 | struct perf_sample sample; |
@@ -410,7 +437,7 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr) | |||
410 | id->tid = tid; | 437 | id->tid = tid; |
411 | } | 438 | } |
412 | if (jd->sample_type & PERF_SAMPLE_TIME) | 439 | if (jd->sample_type & PERF_SAMPLE_TIME) |
413 | id->time = jr->load.p.timestamp; | 440 | id->time = convert_timestamp(jd, jr->load.p.timestamp); |
414 | 441 | ||
415 | /* | 442 | /* |
416 | * create pseudo sample to induce dso hit increment | 443 | * create pseudo sample to induce dso hit increment |
@@ -499,7 +526,7 @@ static int jit_repipe_code_move(struct jit_buf_desc *jd, union jr_entry *jr) | |||
499 | id->tid = tid; | 526 | id->tid = tid; |
500 | } | 527 | } |
501 | if (jd->sample_type & PERF_SAMPLE_TIME) | 528 | if (jd->sample_type & PERF_SAMPLE_TIME) |
502 | id->time = jr->load.p.timestamp; | 529 | id->time = convert_timestamp(jd, jr->load.p.timestamp); |
503 | 530 | ||
504 | /* | 531 | /* |
505 | * create pseudo sample to induce dso hit increment | 532 | * create pseudo sample to induce dso hit increment |
diff --git a/tools/perf/util/jitdump.h b/tools/perf/util/jitdump.h index b66c1f503d9e..bcacd20d0c1c 100644 --- a/tools/perf/util/jitdump.h +++ b/tools/perf/util/jitdump.h | |||
@@ -23,9 +23,12 @@ | |||
23 | #define JITHEADER_VERSION 1 | 23 | #define JITHEADER_VERSION 1 |
24 | 24 | ||
25 | enum jitdump_flags_bits { | 25 | enum jitdump_flags_bits { |
26 | JITDUMP_FLAGS_ARCH_TIMESTAMP_BIT, | ||
26 | JITDUMP_FLAGS_MAX_BIT, | 27 | JITDUMP_FLAGS_MAX_BIT, |
27 | }; | 28 | }; |
28 | 29 | ||
30 | #define JITDUMP_FLAGS_ARCH_TIMESTAMP (1ULL << JITDUMP_FLAGS_ARCH_TIMESTAMP_BIT) | ||
31 | |||
29 | #define JITDUMP_FLAGS_RESERVED (JITDUMP_FLAGS_MAX_BIT < 64 ? \ | 32 | #define JITDUMP_FLAGS_RESERVED (JITDUMP_FLAGS_MAX_BIT < 64 ? \ |
30 | (~((1ULL << JITDUMP_FLAGS_MAX_BIT) - 1)) : 0) | 33 | (~((1ULL << JITDUMP_FLAGS_MAX_BIT) - 1)) : 0) |
31 | 34 | ||
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index adef23b1352e..bf34468a99cb 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c | |||
@@ -602,14 +602,13 @@ static void pmu_format_value(unsigned long *format, __u64 value, __u64 *v, | |||
602 | 602 | ||
603 | static __u64 pmu_format_max_value(const unsigned long *format) | 603 | static __u64 pmu_format_max_value(const unsigned long *format) |
604 | { | 604 | { |
605 | int w; | 605 | __u64 w = 0; |
606 | int fbit; | ||
606 | 607 | ||
607 | w = bitmap_weight(format, PERF_PMU_FORMAT_BITS); | 608 | for_each_set_bit(fbit, format, PERF_PMU_FORMAT_BITS) |
608 | if (!w) | 609 | w |= (1ULL << fbit); |
609 | return 0; | 610 | |
610 | if (w < 64) | 611 | return w; |
611 | return (1ULL << w) - 1; | ||
612 | return -1; | ||
613 | } | 612 | } |
614 | 613 | ||
615 | /* | 614 | /* |
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 4abd85c6346d..ef370557fb9a 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c | |||
@@ -409,6 +409,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool) | |||
409 | tool->stat = process_stat_stub; | 409 | tool->stat = process_stat_stub; |
410 | if (tool->stat_round == NULL) | 410 | if (tool->stat_round == NULL) |
411 | tool->stat_round = process_stat_round_stub; | 411 | tool->stat_round = process_stat_round_stub; |
412 | if (tool->time_conv == NULL) | ||
413 | tool->time_conv = process_event_op2_stub; | ||
412 | } | 414 | } |
413 | 415 | ||
414 | static void swap_sample_id_all(union perf_event *event, void *data) | 416 | static void swap_sample_id_all(union perf_event *event, void *data) |
@@ -794,6 +796,7 @@ static perf_event__swap_op perf_event__swap_ops[] = { | |||
794 | [PERF_RECORD_STAT] = perf_event__stat_swap, | 796 | [PERF_RECORD_STAT] = perf_event__stat_swap, |
795 | [PERF_RECORD_STAT_ROUND] = perf_event__stat_round_swap, | 797 | [PERF_RECORD_STAT_ROUND] = perf_event__stat_round_swap, |
796 | [PERF_RECORD_EVENT_UPDATE] = perf_event__event_update_swap, | 798 | [PERF_RECORD_EVENT_UPDATE] = perf_event__event_update_swap, |
799 | [PERF_RECORD_TIME_CONV] = perf_event__all64_swap, | ||
797 | [PERF_RECORD_HEADER_MAX] = NULL, | 800 | [PERF_RECORD_HEADER_MAX] = NULL, |
798 | }; | 801 | }; |
799 | 802 | ||
@@ -1341,6 +1344,9 @@ static s64 perf_session__process_user_event(struct perf_session *session, | |||
1341 | return tool->stat(tool, event, session); | 1344 | return tool->stat(tool, event, session); |
1342 | case PERF_RECORD_STAT_ROUND: | 1345 | case PERF_RECORD_STAT_ROUND: |
1343 | return tool->stat_round(tool, event, session); | 1346 | return tool->stat_round(tool, event, session); |
1347 | case PERF_RECORD_TIME_CONV: | ||
1348 | session->time_conv = event->time_conv; | ||
1349 | return tool->time_conv(tool, event, session); | ||
1344 | default: | 1350 | default: |
1345 | return -EINVAL; | 1351 | return -EINVAL; |
1346 | } | 1352 | } |
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 5f792e35d4c1..f96fc9e8c52e 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h | |||
@@ -26,6 +26,7 @@ struct perf_session { | |||
26 | struct itrace_synth_opts *itrace_synth_opts; | 26 | struct itrace_synth_opts *itrace_synth_opts; |
27 | struct list_head auxtrace_index; | 27 | struct list_head auxtrace_index; |
28 | struct trace_event tevent; | 28 | struct trace_event tevent; |
29 | struct time_conv_event time_conv; | ||
29 | bool repipe; | 30 | bool repipe; |
30 | bool one_mmap; | 31 | bool one_mmap; |
31 | void *one_mmap_addr; | 32 | void *one_mmap_addr; |
diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index 55de4cffcd4e..ac2590a3de2d 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h | |||
@@ -57,6 +57,7 @@ struct perf_tool { | |||
57 | id_index, | 57 | id_index, |
58 | auxtrace_info, | 58 | auxtrace_info, |
59 | auxtrace_error, | 59 | auxtrace_error, |
60 | time_conv, | ||
60 | thread_map, | 61 | thread_map, |
61 | cpu_map, | 62 | cpu_map, |
62 | stat_config, | 63 | stat_config, |
diff --git a/tools/perf/util/tsc.h b/tools/perf/util/tsc.h index a8b78f1b3243..d5b11e2b85e0 100644 --- a/tools/perf/util/tsc.h +++ b/tools/perf/util/tsc.h | |||
@@ -3,10 +3,29 @@ | |||
3 | 3 | ||
4 | #include <linux/types.h> | 4 | #include <linux/types.h> |
5 | 5 | ||
6 | #include "../arch/x86/util/tsc.h" | 6 | #include "event.h" |
7 | |||
8 | struct perf_tsc_conversion { | ||
9 | u16 time_shift; | ||
10 | u32 time_mult; | ||
11 | u64 time_zero; | ||
12 | }; | ||
13 | struct perf_event_mmap_page; | ||
14 | |||
15 | int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc, | ||
16 | struct perf_tsc_conversion *tc); | ||
7 | 17 | ||
8 | u64 perf_time_to_tsc(u64 ns, struct perf_tsc_conversion *tc); | 18 | u64 perf_time_to_tsc(u64 ns, struct perf_tsc_conversion *tc); |
9 | u64 tsc_to_perf_time(u64 cyc, struct perf_tsc_conversion *tc); | 19 | u64 tsc_to_perf_time(u64 cyc, struct perf_tsc_conversion *tc); |
10 | u64 rdtsc(void); | 20 | u64 rdtsc(void); |
11 | 21 | ||
22 | struct perf_event_mmap_page; | ||
23 | struct perf_tool; | ||
24 | struct machine; | ||
25 | |||
26 | int perf_event__synth_time_conv(const struct perf_event_mmap_page *pc, | ||
27 | struct perf_tool *tool, | ||
28 | perf_event__handler_t process, | ||
29 | struct machine *machine); | ||
30 | |||
12 | #endif | 31 | #endif |