Commit dad38ca6 authored by Ingo Molnar's avatar Ingo Molnar

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

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: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents d1b26c70 d37ba880
...@@ -438,6 +438,11 @@ struct auxtrace_record *intel_bts_recording_init(int *err) ...@@ -438,6 +438,11 @@ struct auxtrace_record *intel_bts_recording_init(int *err)
if (!intel_bts_pmu) if (!intel_bts_pmu)
return NULL; return NULL;
if (setenv("JITDUMP_USE_ARCH_TIMESTAMP", "1", 1)) {
*err = -errno;
return NULL;
}
btsr = zalloc(sizeof(struct intel_bts_recording)); btsr = zalloc(sizeof(struct intel_bts_recording));
if (!btsr) { if (!btsr) {
*err = -ENOMEM; *err = -ENOMEM;
......
...@@ -1027,6 +1027,11 @@ struct auxtrace_record *intel_pt_recording_init(int *err) ...@@ -1027,6 +1027,11 @@ struct auxtrace_record *intel_pt_recording_init(int *err)
if (!intel_pt_pmu) if (!intel_pt_pmu)
return NULL; return NULL;
if (setenv("JITDUMP_USE_ARCH_TIMESTAMP", "1", 1)) {
*err = -errno;
return NULL;
}
ptr = zalloc(sizeof(struct intel_pt_recording)); ptr = zalloc(sizeof(struct intel_pt_recording));
if (!ptr) { if (!ptr) {
*err = -ENOMEM; *err = -ENOMEM;
......
...@@ -7,7 +7,6 @@ ...@@ -7,7 +7,6 @@
#include <linux/types.h> #include <linux/types.h>
#include "../../util/debug.h" #include "../../util/debug.h"
#include "../../util/tsc.h" #include "../../util/tsc.h"
#include "tsc.h"
int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc, int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc,
struct perf_tsc_conversion *tc) struct perf_tsc_conversion *tc)
...@@ -46,3 +45,34 @@ u64 rdtsc(void) ...@@ -46,3 +45,34 @@ u64 rdtsc(void)
return low | ((u64)high) << 32; return low | ((u64)high) << 32;
} }
int perf_event__synth_time_conv(const struct perf_event_mmap_page *pc,
struct perf_tool *tool,
perf_event__handler_t process,
struct machine *machine)
{
union perf_event event = {
.time_conv = {
.header = {
.type = PERF_RECORD_TIME_CONV,
.size = sizeof(struct time_conv_event),
},
},
};
struct perf_tsc_conversion tc;
int err;
err = perf_read_tsc_conversion(pc, &tc);
if (err == -EOPNOTSUPP)
return 0;
if (err)
return err;
pr_debug2("Synthesizing TSC conversion information\n");
event.time_conv.time_mult = tc.time_mult;
event.time_conv.time_shift = tc.time_shift;
event.time_conv.time_zero = tc.time_zero;
return process(tool, &event, NULL, machine);
}
#ifndef TOOLS_PERF_ARCH_X86_UTIL_TSC_H__
#define TOOLS_PERF_ARCH_X86_UTIL_TSC_H__
#include <linux/types.h>
struct perf_tsc_conversion {
u16 time_shift;
u32 time_mult;
u64 time_zero;
};
struct perf_event_mmap_page;
int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc,
struct perf_tsc_conversion *tc);
#endif /* TOOLS_PERF_ARCH_X86_UTIL_TSC_H__ */
...@@ -748,6 +748,7 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -748,6 +748,7 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
.auxtrace_info = perf_event__repipe_op2_synth, .auxtrace_info = perf_event__repipe_op2_synth,
.auxtrace = perf_event__repipe_auxtrace, .auxtrace = perf_event__repipe_auxtrace,
.auxtrace_error = perf_event__repipe_op2_synth, .auxtrace_error = perf_event__repipe_op2_synth,
.time_conv = perf_event__repipe_op2_synth,
.finished_round = perf_event__repipe_oe_synth, .finished_round = perf_event__repipe_oe_synth,
.build_id = perf_event__repipe_op2_synth, .build_id = perf_event__repipe_op2_synth,
.id_index = perf_event__repipe_op2_synth, .id_index = perf_event__repipe_op2_synth,
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include "util/data.h" #include "util/data.h"
#include "util/perf_regs.h" #include "util/perf_regs.h"
#include "util/auxtrace.h" #include "util/auxtrace.h"
#include "util/tsc.h"
#include "util/parse-branch-options.h" #include "util/parse-branch-options.h"
#include "util/parse-regs-options.h" #include "util/parse-regs-options.h"
#include "util/llvm-utils.h" #include "util/llvm-utils.h"
...@@ -512,6 +513,15 @@ static void workload_exec_failed_signal(int signo __maybe_unused, ...@@ -512,6 +513,15 @@ static void workload_exec_failed_signal(int signo __maybe_unused,
static void snapshot_sig_handler(int sig); static void snapshot_sig_handler(int sig);
int __weak
perf_event__synth_time_conv(const struct perf_event_mmap_page *pc __maybe_unused,
struct perf_tool *tool __maybe_unused,
perf_event__handler_t process __maybe_unused,
struct machine *machine __maybe_unused)
{
return 0;
}
static int record__synthesize(struct record *rec) static int record__synthesize(struct record *rec)
{ {
struct perf_session *session = rec->session; struct perf_session *session = rec->session;
...@@ -549,6 +559,11 @@ static int record__synthesize(struct record *rec) ...@@ -549,6 +559,11 @@ static int record__synthesize(struct record *rec)
} }
} }
err = perf_event__synth_time_conv(rec->evlist->mmap[0].base, tool,
process_synthesized_event, machine);
if (err)
goto out;
if (rec->opts.full_auxtrace) { if (rec->opts.full_auxtrace) {
err = perf_event__synthesize_auxtrace_info(rec->itr, tool, err = perf_event__synthesize_auxtrace_info(rec->itr, tool,
session, process_synthesized_event); session, process_synthesized_event);
......
...@@ -40,6 +40,11 @@ ...@@ -40,6 +40,11 @@
#include <sys/mman.h> #include <sys/mman.h>
#include <linux/futex.h> #include <linux/futex.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/seccomp.h>
#include <linux/filter.h>
#include <linux/audit.h>
#include <sys/ptrace.h>
#include <linux/random.h>
/* For older distros: */ /* For older distros: */
#ifndef MAP_STACK #ifndef MAP_STACK
...@@ -1001,6 +1006,69 @@ static const char *tioctls[] = { ...@@ -1001,6 +1006,69 @@ static const char *tioctls[] = {
static DEFINE_STRARRAY_OFFSET(tioctls, 0x5401); static DEFINE_STRARRAY_OFFSET(tioctls, 0x5401);
#endif /* defined(__i386__) || defined(__x86_64__) */ #endif /* defined(__i386__) || defined(__x86_64__) */
static size_t syscall_arg__scnprintf_seccomp_op(char *bf, size_t size, struct syscall_arg *arg)
{
int op = arg->val;
size_t printed = 0;
switch (op) {
#define P_SECCOMP_SET_MODE_OP(n) case SECCOMP_SET_MODE_##n: printed = scnprintf(bf, size, #n); break
P_SECCOMP_SET_MODE_OP(STRICT);
P_SECCOMP_SET_MODE_OP(FILTER);
#undef P_SECCOMP_SET_MODE_OP
default: printed = scnprintf(bf, size, "%#x", op); break;
}
return printed;
}
#define SCA_SECCOMP_OP syscall_arg__scnprintf_seccomp_op
static size_t syscall_arg__scnprintf_seccomp_flags(char *bf, size_t size,
struct syscall_arg *arg)
{
int printed = 0, flags = arg->val;
#define P_FLAG(n) \
if (flags & SECCOMP_FILTER_FLAG_##n) { \
printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \
flags &= ~SECCOMP_FILTER_FLAG_##n; \
}
P_FLAG(TSYNC);
#undef P_FLAG
if (flags)
printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", flags);
return printed;
}
#define SCA_SECCOMP_FLAGS syscall_arg__scnprintf_seccomp_flags
static size_t syscall_arg__scnprintf_getrandom_flags(char *bf, size_t size,
struct syscall_arg *arg)
{
int printed = 0, flags = arg->val;
#define P_FLAG(n) \
if (flags & GRND_##n) { \
printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \
flags &= ~GRND_##n; \
}
P_FLAG(RANDOM);
P_FLAG(NONBLOCK);
#undef P_FLAG
if (flags)
printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", flags);
return printed;
}
#define SCA_GETRANDOM_FLAGS syscall_arg__scnprintf_getrandom_flags
#define STRARRAY(arg, name, array) \ #define STRARRAY(arg, name, array) \
.arg_scnprintf = { [arg] = SCA_STRARRAY, }, \ .arg_scnprintf = { [arg] = SCA_STRARRAY, }, \
.arg_parm = { [arg] = &strarray__##array, } .arg_parm = { [arg] = &strarray__##array, }
...@@ -1093,6 +1161,8 @@ static struct syscall_fmt { ...@@ -1093,6 +1161,8 @@ static struct syscall_fmt {
{ .name = "getdents64", .errmsg = true, { .name = "getdents64", .errmsg = true,
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, }, .arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
{ .name = "getitimer", .errmsg = true, STRARRAY(0, which, itimers), }, { .name = "getitimer", .errmsg = true, STRARRAY(0, which, itimers), },
{ .name = "getrandom", .errmsg = true,
.arg_scnprintf = { [2] = SCA_GETRANDOM_FLAGS, /* flags */ }, },
{ .name = "getrlimit", .errmsg = true, STRARRAY(0, resource, rlimit_resources), }, { .name = "getrlimit", .errmsg = true, STRARRAY(0, resource, rlimit_resources), },
{ .name = "getxattr", .errmsg = true, { .name = "getxattr", .errmsg = true,
.arg_scnprintf = { [0] = SCA_FILENAME, /* pathname */ }, }, .arg_scnprintf = { [0] = SCA_FILENAME, /* pathname */ }, },
...@@ -1234,6 +1304,9 @@ static struct syscall_fmt { ...@@ -1234,6 +1304,9 @@ static struct syscall_fmt {
.arg_scnprintf = { [1] = SCA_SIGNUM, /* sig */ }, }, .arg_scnprintf = { [1] = SCA_SIGNUM, /* sig */ }, },
{ .name = "rt_tgsigqueueinfo", .errmsg = true, { .name = "rt_tgsigqueueinfo", .errmsg = true,
.arg_scnprintf = { [2] = SCA_SIGNUM, /* sig */ }, }, .arg_scnprintf = { [2] = SCA_SIGNUM, /* sig */ }, },
{ .name = "seccomp", .errmsg = true,
.arg_scnprintf = { [0] = SCA_SECCOMP_OP, /* op */
[1] = SCA_SECCOMP_FLAGS, /* flags */ }, },
{ .name = "select", .errmsg = true, .timeout = true, }, { .name = "select", .errmsg = true, .timeout = true, },
{ .name = "sendmmsg", .errmsg = true, { .name = "sendmmsg", .errmsg = true,
.arg_scnprintf = { [0] = SCA_FD, /* fd */ .arg_scnprintf = { [0] = SCA_FD, /* fd */
...@@ -1618,6 +1691,7 @@ static int trace__process_event(struct trace *trace, struct machine *machine, ...@@ -1618,6 +1691,7 @@ static int trace__process_event(struct trace *trace, struct machine *machine,
color_fprintf(trace->output, PERF_COLOR_RED, color_fprintf(trace->output, PERF_COLOR_RED,
"LOST %" PRIu64 " events!\n", event->lost.lost); "LOST %" PRIu64 " events!\n", event->lost.lost);
ret = machine__process_lost_event(machine, event, sample); ret = machine__process_lost_event(machine, event, sample);
break;
default: default:
ret = machine__process_event(machine, event, sample); ret = machine__process_event(machine, event, sample);
break; break;
...@@ -2326,6 +2400,23 @@ static bool skip_sample(struct trace *trace, struct perf_sample *sample) ...@@ -2326,6 +2400,23 @@ static bool skip_sample(struct trace *trace, struct perf_sample *sample)
return false; return false;
} }
static void trace__set_base_time(struct trace *trace,
struct perf_evsel *evsel,
struct perf_sample *sample)
{
/*
* BPF events were not setting PERF_SAMPLE_TIME, so be more robust
* and don't use sample->time unconditionally, we may end up having
* some other event in the future without PERF_SAMPLE_TIME for good
* reason, i.e. we may not be interested in its timestamps, just in
* it taking place, picking some piece of information when it
* appears in our event stream (vfs_getname comes to mind).
*/
if (trace->base_time == 0 && !trace->full_time &&
(evsel->attr.sample_type & PERF_SAMPLE_TIME))
trace->base_time = sample->time;
}
static int trace__process_sample(struct perf_tool *tool, static int trace__process_sample(struct perf_tool *tool,
union perf_event *event, union perf_event *event,
struct perf_sample *sample, struct perf_sample *sample,
...@@ -2340,8 +2431,7 @@ static int trace__process_sample(struct perf_tool *tool, ...@@ -2340,8 +2431,7 @@ static int trace__process_sample(struct perf_tool *tool,
if (skip_sample(trace, sample)) if (skip_sample(trace, sample))
return 0; return 0;
if (!trace->full_time && trace->base_time == 0) trace__set_base_time(trace, evsel, sample);
trace->base_time = sample->time;
if (handler) { if (handler) {
++trace->nr_events; ++trace->nr_events;
...@@ -2479,9 +2569,6 @@ static void trace__handle_event(struct trace *trace, union perf_event *event, st ...@@ -2479,9 +2569,6 @@ static void trace__handle_event(struct trace *trace, union perf_event *event, st
const u32 type = event->header.type; const u32 type = event->header.type;
struct perf_evsel *evsel; struct perf_evsel *evsel;
if (!trace->full_time && trace->base_time == 0)
trace->base_time = sample->time;
if (type != PERF_RECORD_SAMPLE) { if (type != PERF_RECORD_SAMPLE) {
trace__process_event(trace, trace->host, event, sample); trace__process_event(trace, trace->host, event, sample);
return; return;
...@@ -2493,6 +2580,8 @@ static void trace__handle_event(struct trace *trace, union perf_event *event, st ...@@ -2493,6 +2580,8 @@ static void trace__handle_event(struct trace *trace, union perf_event *event, st
return; return;
} }
trace__set_base_time(trace, evsel, sample);
if (evsel->attr.type == PERF_TYPE_TRACEPOINT && if (evsel->attr.type == PERF_TYPE_TRACEPOINT &&
sample->raw_data == NULL) { sample->raw_data == NULL) {
fprintf(trace->output, "%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n", fprintf(trace->output, "%s sample with no payload for tid: %d, cpu %d, raw_size=%d, skipping...\n",
......
...@@ -92,6 +92,22 @@ static int get_e_machine(struct jitheader *hdr) ...@@ -92,6 +92,22 @@ static int get_e_machine(struct jitheader *hdr)
return ret; return ret;
} }
static int use_arch_timestamp;
static inline uint64_t
get_arch_timestamp(void)
{
#if defined(__i386__) || defined(__x86_64__)
unsigned int low, high;
asm volatile("rdtsc" : "=a" (low), "=d" (high));
return low | ((uint64_t)high) << 32;
#else
return 0;
#endif
}
#define NSEC_PER_SEC 1000000000 #define NSEC_PER_SEC 1000000000
static int perf_clk_id = CLOCK_MONOTONIC; static int perf_clk_id = CLOCK_MONOTONIC;
...@@ -107,6 +123,9 @@ perf_get_timestamp(void) ...@@ -107,6 +123,9 @@ perf_get_timestamp(void)
struct timespec ts; struct timespec ts;
int ret; int ret;
if (use_arch_timestamp)
return get_arch_timestamp();
ret = clock_gettime(perf_clk_id, &ts); ret = clock_gettime(perf_clk_id, &ts);
if (ret) if (ret)
return 0; return 0;
...@@ -203,6 +222,17 @@ perf_close_marker_file(void) ...@@ -203,6 +222,17 @@ perf_close_marker_file(void)
munmap(marker_addr, pgsz); munmap(marker_addr, pgsz);
} }
static void
init_arch_timestamp(void)
{
char *str = getenv("JITDUMP_USE_ARCH_TIMESTAMP");
if (!str || !*str || !strcmp(str, "0"))
return;
use_arch_timestamp = 1;
}
void *jvmti_open(void) void *jvmti_open(void)
{ {
int pad_cnt; int pad_cnt;
...@@ -211,11 +241,17 @@ void *jvmti_open(void) ...@@ -211,11 +241,17 @@ void *jvmti_open(void)
int fd; int fd;
FILE *fp; FILE *fp;
init_arch_timestamp();
/* /*
* check if clockid is supported * check if clockid is supported
*/ */
if (!perf_get_timestamp()) if (!perf_get_timestamp()) {
warnx("jvmti: kernel does not support %d clock id", perf_clk_id); if (use_arch_timestamp)
warnx("jvmti: arch timestamp not supported");
else
warnx("jvmti: kernel does not support %d clock id", perf_clk_id);
}
memset(&header, 0, sizeof(header)); memset(&header, 0, sizeof(header));
...@@ -263,6 +299,9 @@ void *jvmti_open(void) ...@@ -263,6 +299,9 @@ void *jvmti_open(void)
header.timestamp = perf_get_timestamp(); header.timestamp = perf_get_timestamp();
if (use_arch_timestamp)
header.flags |= JITDUMP_FLAGS_ARCH_TIMESTAMP;
if (!fwrite(&header, sizeof(header), 1, fp)) { if (!fwrite(&header, sizeof(header), 1, fp)) {
warn("jvmti: cannot write dumpfile header"); warn("jvmti: cannot write dumpfile header");
goto error; goto error;
......
...@@ -69,8 +69,7 @@ libperf-y += stat-shadow.o ...@@ -69,8 +69,7 @@ libperf-y += stat-shadow.o
libperf-y += record.o libperf-y += record.o
libperf-y += srcline.o libperf-y += srcline.o
libperf-y += data.o libperf-y += data.o
libperf-$(CONFIG_X86) += tsc.o libperf-y += tsc.o
libperf-$(CONFIG_AUXTRACE) += tsc.o
libperf-y += cloexec.o libperf-y += cloexec.o
libperf-y += thread-stack.o libperf-y += thread-stack.o
libperf-$(CONFIG_AUXTRACE) += auxtrace.o libperf-$(CONFIG_AUXTRACE) += auxtrace.o
......
...@@ -45,6 +45,7 @@ static const char *perf_event__names[] = { ...@@ -45,6 +45,7 @@ static const char *perf_event__names[] = {
[PERF_RECORD_STAT] = "STAT", [PERF_RECORD_STAT] = "STAT",
[PERF_RECORD_STAT_ROUND] = "STAT_ROUND", [PERF_RECORD_STAT_ROUND] = "STAT_ROUND",
[PERF_RECORD_EVENT_UPDATE] = "EVENT_UPDATE", [PERF_RECORD_EVENT_UPDATE] = "EVENT_UPDATE",
[PERF_RECORD_TIME_CONV] = "TIME_CONV",
}; };
const char *perf_event__name(unsigned int id) const char *perf_event__name(unsigned int id)
......
...@@ -233,6 +233,7 @@ enum perf_user_event_type { /* above any possible kernel type */ ...@@ -233,6 +233,7 @@ enum perf_user_event_type { /* above any possible kernel type */
PERF_RECORD_STAT = 76, PERF_RECORD_STAT = 76,
PERF_RECORD_STAT_ROUND = 77, PERF_RECORD_STAT_ROUND = 77,
PERF_RECORD_EVENT_UPDATE = 78, PERF_RECORD_EVENT_UPDATE = 78,
PERF_RECORD_TIME_CONV = 79,
PERF_RECORD_HEADER_MAX PERF_RECORD_HEADER_MAX
}; };
...@@ -469,6 +470,13 @@ struct stat_round_event { ...@@ -469,6 +470,13 @@ struct stat_round_event {
u64 time; u64 time;
}; };
struct time_conv_event {
struct perf_event_header header;
u64 time_shift;
u64 time_mult;
u64 time_zero;
};
union perf_event { union perf_event {
struct perf_event_header header; struct perf_event_header header;
struct mmap_event mmap; struct mmap_event mmap;
...@@ -497,6 +505,7 @@ union perf_event { ...@@ -497,6 +505,7 @@ union perf_event {
struct stat_config_event stat_config; struct stat_config_event stat_config;
struct stat_event stat; struct stat_event stat;
struct stat_round_event stat_round; struct stat_round_event stat_round;
struct time_conv_event time_conv;
}; };
void perf_event__print_totals(void); void perf_event__print_totals(void);
......
...@@ -226,7 +226,8 @@ struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx) ...@@ -226,7 +226,8 @@ struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx)
perf_evsel__init(evsel, attr, idx); perf_evsel__init(evsel, attr, idx);
if (perf_evsel__is_bpf_output(evsel)) { if (perf_evsel__is_bpf_output(evsel)) {
evsel->attr.sample_type |= PERF_SAMPLE_RAW; evsel->attr.sample_type |= (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME |
PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD),
evsel->attr.sample_period = 1; evsel->attr.sample_period = 1;
} }
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include "strlist.h" #include "strlist.h"
#include <elf.h> #include <elf.h>
#include "tsc.h"
#include "session.h" #include "session.h"
#include "jit.h" #include "jit.h"
#include "jitdump.h" #include "jitdump.h"
...@@ -33,6 +34,7 @@ struct jit_buf_desc { ...@@ -33,6 +34,7 @@ struct jit_buf_desc {
size_t bufsize; size_t bufsize;
FILE *in; FILE *in;
bool needs_bswap; /* handles cross-endianess */ bool needs_bswap; /* handles cross-endianess */
bool use_arch_timestamp;
void *debug_data; void *debug_data;
size_t nr_debug_entries; size_t nr_debug_entries;
uint32_t code_load_count; uint32_t code_load_count;
...@@ -158,13 +160,16 @@ jit_open(struct jit_buf_desc *jd, const char *name) ...@@ -158,13 +160,16 @@ jit_open(struct jit_buf_desc *jd, const char *name)
header.flags = bswap_64(header.flags); header.flags = bswap_64(header.flags);
} }
jd->use_arch_timestamp = header.flags & JITDUMP_FLAGS_ARCH_TIMESTAMP;
if (verbose > 2) if (verbose > 2)
pr_debug("version=%u\nhdr.size=%u\nts=0x%llx\npid=%d\nelf_mach=%d\n", pr_debug("version=%u\nhdr.size=%u\nts=0x%llx\npid=%d\nelf_mach=%d\nuse_arch_timestamp=%d\n",
header.version, header.version,
header.total_size, header.total_size,
(unsigned long long)header.timestamp, (unsigned long long)header.timestamp,
header.pid, header.pid,
header.elf_mach); header.elf_mach,
jd->use_arch_timestamp);
if (header.flags & JITDUMP_FLAGS_RESERVED) { if (header.flags & JITDUMP_FLAGS_RESERVED) {
pr_err("jitdump file contains invalid or unsupported flags 0x%llx\n", 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,10 +177,15 @@ jit_open(struct jit_buf_desc *jd, const char *name)
goto error; goto error;
} }
if (jd->use_arch_timestamp && !jd->session->time_conv.time_mult) {
pr_err("jitdump file uses arch timestamps but there is no timestamp conversion\n");
goto error;
}
/* /*
* validate event is using the correct clockid * validate event is using the correct clockid
*/ */
if (jit_validate_events(jd->session)) { if (!jd->use_arch_timestamp && jit_validate_events(jd->session)) {
pr_err("error, jitted code must be sampled with perf record -k 1\n"); pr_err("error, jitted code must be sampled with perf record -k 1\n");
goto error; goto error;
} }
...@@ -329,6 +339,23 @@ jit_inject_event(struct jit_buf_desc *jd, union perf_event *event) ...@@ -329,6 +339,23 @@ jit_inject_event(struct jit_buf_desc *jd, union perf_event *event)
return 0; return 0;
} }
static uint64_t convert_timestamp(struct jit_buf_desc *jd, uint64_t timestamp)
{
struct perf_tsc_conversion tc;
if (!jd->use_arch_timestamp)
return timestamp;
tc.time_shift = jd->session->time_conv.time_shift;
tc.time_mult = jd->session->time_conv.time_mult;
tc.time_zero = jd->session->time_conv.time_zero;
if (!tc.time_mult)
return 0;
return tsc_to_perf_time(timestamp, &tc);
}
static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr) static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr)
{ {
struct perf_sample sample; struct perf_sample sample;
...@@ -410,7 +437,7 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr) ...@@ -410,7 +437,7 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr)
id->tid = tid; id->tid = tid;
} }
if (jd->sample_type & PERF_SAMPLE_TIME) if (jd->sample_type & PERF_SAMPLE_TIME)
id->time = jr->load.p.timestamp; id->time = convert_timestamp(jd, jr->load.p.timestamp);
/* /*
* create pseudo sample to induce dso hit increment * 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,7 +526,7 @@ static int jit_repipe_code_move(struct jit_buf_desc *jd, union jr_entry *jr)
id->tid = tid; id->tid = tid;
} }
if (jd->sample_type & PERF_SAMPLE_TIME) if (jd->sample_type & PERF_SAMPLE_TIME)
id->time = jr->load.p.timestamp; id->time = convert_timestamp(jd, jr->load.p.timestamp);
/* /*
* create pseudo sample to induce dso hit increment * create pseudo sample to induce dso hit increment
......
...@@ -23,9 +23,12 @@ ...@@ -23,9 +23,12 @@
#define JITHEADER_VERSION 1 #define JITHEADER_VERSION 1
enum jitdump_flags_bits { enum jitdump_flags_bits {
JITDUMP_FLAGS_ARCH_TIMESTAMP_BIT,
JITDUMP_FLAGS_MAX_BIT, JITDUMP_FLAGS_MAX_BIT,
}; };
#define JITDUMP_FLAGS_ARCH_TIMESTAMP (1ULL << JITDUMP_FLAGS_ARCH_TIMESTAMP_BIT)
#define JITDUMP_FLAGS_RESERVED (JITDUMP_FLAGS_MAX_BIT < 64 ? \ #define JITDUMP_FLAGS_RESERVED (JITDUMP_FLAGS_MAX_BIT < 64 ? \
(~((1ULL << JITDUMP_FLAGS_MAX_BIT) - 1)) : 0) (~((1ULL << JITDUMP_FLAGS_MAX_BIT) - 1)) : 0)
......
...@@ -602,14 +602,13 @@ static void pmu_format_value(unsigned long *format, __u64 value, __u64 *v, ...@@ -602,14 +602,13 @@ static void pmu_format_value(unsigned long *format, __u64 value, __u64 *v,
static __u64 pmu_format_max_value(const unsigned long *format) static __u64 pmu_format_max_value(const unsigned long *format)
{ {
int w; __u64 w = 0;
int fbit;
w = bitmap_weight(format, PERF_PMU_FORMAT_BITS); for_each_set_bit(fbit, format, PERF_PMU_FORMAT_BITS)
if (!w) w |= (1ULL << fbit);
return 0;
if (w < 64) return w;
return (1ULL << w) - 1;
return -1;
} }
/* /*
......
...@@ -409,6 +409,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool) ...@@ -409,6 +409,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool)
tool->stat = process_stat_stub; tool->stat = process_stat_stub;
if (tool->stat_round == NULL) if (tool->stat_round == NULL)
tool->stat_round = process_stat_round_stub; tool->stat_round = process_stat_round_stub;
if (tool->time_conv == NULL)
tool->time_conv = process_event_op2_stub;
} }
static void swap_sample_id_all(union perf_event *event, void *data) 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,6 +796,7 @@ static perf_event__swap_op perf_event__swap_ops[] = {
[PERF_RECORD_STAT] = perf_event__stat_swap, [PERF_RECORD_STAT] = perf_event__stat_swap,
[PERF_RECORD_STAT_ROUND] = perf_event__stat_round_swap, [PERF_RECORD_STAT_ROUND] = perf_event__stat_round_swap,
[PERF_RECORD_EVENT_UPDATE] = perf_event__event_update_swap, [PERF_RECORD_EVENT_UPDATE] = perf_event__event_update_swap,
[PERF_RECORD_TIME_CONV] = perf_event__all64_swap,
[PERF_RECORD_HEADER_MAX] = NULL, [PERF_RECORD_HEADER_MAX] = NULL,
}; };
...@@ -1341,6 +1344,9 @@ static s64 perf_session__process_user_event(struct perf_session *session, ...@@ -1341,6 +1344,9 @@ static s64 perf_session__process_user_event(struct perf_session *session,
return tool->stat(tool, event, session); return tool->stat(tool, event, session);
case PERF_RECORD_STAT_ROUND: case PERF_RECORD_STAT_ROUND:
return tool->stat_round(tool, event, session); return tool->stat_round(tool, event, session);
case PERF_RECORD_TIME_CONV:
session->time_conv = event->time_conv;
return tool->time_conv(tool, event, session);
default: default:
return -EINVAL; return -EINVAL;
} }
......
...@@ -26,6 +26,7 @@ struct perf_session { ...@@ -26,6 +26,7 @@ struct perf_session {
struct itrace_synth_opts *itrace_synth_opts; struct itrace_synth_opts *itrace_synth_opts;
struct list_head auxtrace_index; struct list_head auxtrace_index;
struct trace_event tevent; struct trace_event tevent;
struct time_conv_event time_conv;
bool repipe; bool repipe;
bool one_mmap; bool one_mmap;
void *one_mmap_addr; void *one_mmap_addr;
......
...@@ -57,6 +57,7 @@ struct perf_tool { ...@@ -57,6 +57,7 @@ struct perf_tool {
id_index, id_index,
auxtrace_info, auxtrace_info,
auxtrace_error, auxtrace_error,
time_conv,
thread_map, thread_map,
cpu_map, cpu_map,
stat_config, stat_config,
......
...@@ -3,10 +3,29 @@ ...@@ -3,10 +3,29 @@
#include <linux/types.h> #include <linux/types.h>
#include "../arch/x86/util/tsc.h" #include "event.h"
struct perf_tsc_conversion {
u16 time_shift;
u32 time_mult;
u64 time_zero;
};
struct perf_event_mmap_page;
int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc,
struct perf_tsc_conversion *tc);
u64 perf_time_to_tsc(u64 ns, struct perf_tsc_conversion *tc); u64 perf_time_to_tsc(u64 ns, struct perf_tsc_conversion *tc);
u64 tsc_to_perf_time(u64 cyc, struct perf_tsc_conversion *tc); u64 tsc_to_perf_time(u64 cyc, struct perf_tsc_conversion *tc);
u64 rdtsc(void); u64 rdtsc(void);
struct perf_event_mmap_page;
struct perf_tool;
struct machine;
int perf_event__synth_time_conv(const struct perf_event_mmap_page *pc,
struct perf_tool *tool,
perf_event__handler_t process,
struct machine *machine);
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment