forked from luck/tmp_suning_uos_patched
perf/core improvements and fixes:
User visible: - 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: - 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> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJW/u45AAoJENZQFvNTUqpAwIYP/0uYRezvvvt9zrnc6nInm5kn eCsV5olA9hsXP4d6g9I3V5OG+HwcBvmnDjS6OgjGqVG0Ek8qWj7sUQEMDR7fBHjq F7Qi6zBUhyrlQaUjfQ3QC+mCuP5ZYOriZ5Age64LoOzFbxwSl0kZAro1MD9Y3nWh xq9D++4Oe6wRrpgrA+o5DTVohU55PPaExRfWIE1zE4x1bwl7YlfYsIhmG6Pdk8zo u7Q5GQegGAeYIhNsAYbT6cDavRJkK04Z4Oc0xX5mk8kXDN3nxexht0pSgTMu9VnX K5BTgad3+sQeMpCSJVLMsoQ473NWsG4iGgy+htfTFGTkHA3pLSvQpXuvL1/cEZGn lcLrlFA2cZRa9ZuLUKc6GiQmVHNzofxmkEohHbGCcFG68IqqunK+NfWKIn2C7XKh Taye8vb+gx4xi0QJ8nU6UCSbKRr1rCRmekRwlsK7K+E9/ghr+ZhL1ZfbE1oe/7MQ 9Lq44Hn3KEx4RcAmU9vRc+MPdY4r9dCZxXiC1O/dYu4hgiOk2vLN7dPuROuyZqtu EfYvCUcLSuiU48TKAopmrWBcU6WzOilYeBV03Yr4KB4O3BbosUFWWIhehihceikM iWcA0JVaQ7Ni25ZNzA6DYjNMRLtJuJOtdCvNI1jrLhkkEbzx7Iinr+QchyJuGJ3e 5tKnaO0+TmyZYCqHH9RM =Jb69 -----END PGP SIGNATURE----- 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>
This commit is contained in:
commit
dad38ca64a
|
@ -438,6 +438,11 @@ struct auxtrace_record *intel_bts_recording_init(int *err)
|
|||
if (!intel_bts_pmu)
|
||||
return NULL;
|
||||
|
||||
if (setenv("JITDUMP_USE_ARCH_TIMESTAMP", "1", 1)) {
|
||||
*err = -errno;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
btsr = zalloc(sizeof(struct intel_bts_recording));
|
||||
if (!btsr) {
|
||||
*err = -ENOMEM;
|
||||
|
|
|
@ -1027,6 +1027,11 @@ struct auxtrace_record *intel_pt_recording_init(int *err)
|
|||
if (!intel_pt_pmu)
|
||||
return NULL;
|
||||
|
||||
if (setenv("JITDUMP_USE_ARCH_TIMESTAMP", "1", 1)) {
|
||||
*err = -errno;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ptr = zalloc(sizeof(struct intel_pt_recording));
|
||||
if (!ptr) {
|
||||
*err = -ENOMEM;
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include <linux/types.h>
|
||||
#include "../../util/debug.h"
|
||||
#include "../../util/tsc.h"
|
||||
#include "tsc.h"
|
||||
|
||||
int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc,
|
||||
struct perf_tsc_conversion *tc)
|
||||
|
@ -46,3 +45,34 @@ u64 rdtsc(void)
|
|||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
#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)
|
|||
.auxtrace_info = perf_event__repipe_op2_synth,
|
||||
.auxtrace = perf_event__repipe_auxtrace,
|
||||
.auxtrace_error = perf_event__repipe_op2_synth,
|
||||
.time_conv = perf_event__repipe_op2_synth,
|
||||
.finished_round = perf_event__repipe_oe_synth,
|
||||
.build_id = perf_event__repipe_op2_synth,
|
||||
.id_index = perf_event__repipe_op2_synth,
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "util/data.h"
|
||||
#include "util/perf_regs.h"
|
||||
#include "util/auxtrace.h"
|
||||
#include "util/tsc.h"
|
||||
#include "util/parse-branch-options.h"
|
||||
#include "util/parse-regs-options.h"
|
||||
#include "util/llvm-utils.h"
|
||||
|
@ -512,6 +513,15 @@ static void workload_exec_failed_signal(int signo __maybe_unused,
|
|||
|
||||
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)
|
||||
{
|
||||
struct perf_session *session = rec->session;
|
||||
|
@ -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) {
|
||||
err = perf_event__synthesize_auxtrace_info(rec->itr, tool,
|
||||
session, process_synthesized_event);
|
||||
|
|
|
@ -40,6 +40,11 @@
|
|||
#include <sys/mman.h>
|
||||
#include <linux/futex.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: */
|
||||
#ifndef MAP_STACK
|
||||
|
@ -1001,6 +1006,69 @@ static const char *tioctls[] = {
|
|||
static DEFINE_STRARRAY_OFFSET(tioctls, 0x5401);
|
||||
#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) \
|
||||
.arg_scnprintf = { [arg] = SCA_STRARRAY, }, \
|
||||
.arg_parm = { [arg] = &strarray__##array, }
|
||||
|
@ -1093,6 +1161,8 @@ static struct syscall_fmt {
|
|||
{ .name = "getdents64", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */ }, },
|
||||
{ .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 = "getxattr", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FILENAME, /* pathname */ }, },
|
||||
|
@ -1234,6 +1304,9 @@ static struct syscall_fmt {
|
|||
.arg_scnprintf = { [1] = SCA_SIGNUM, /* sig */ }, },
|
||||
{ .name = "rt_tgsigqueueinfo", .errmsg = true,
|
||||
.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 = "sendmmsg", .errmsg = true,
|
||||
.arg_scnprintf = { [0] = SCA_FD, /* fd */
|
||||
|
@ -1618,6 +1691,7 @@ static int trace__process_event(struct trace *trace, struct machine *machine,
|
|||
color_fprintf(trace->output, PERF_COLOR_RED,
|
||||
"LOST %" PRIu64 " events!\n", event->lost.lost);
|
||||
ret = machine__process_lost_event(machine, event, sample);
|
||||
break;
|
||||
default:
|
||||
ret = machine__process_event(machine, event, sample);
|
||||
break;
|
||||
|
@ -2326,6 +2400,23 @@ static bool skip_sample(struct trace *trace, struct perf_sample *sample)
|
|||
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,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
|
@ -2340,8 +2431,7 @@ static int trace__process_sample(struct perf_tool *tool,
|
|||
if (skip_sample(trace, sample))
|
||||
return 0;
|
||||
|
||||
if (!trace->full_time && trace->base_time == 0)
|
||||
trace->base_time = sample->time;
|
||||
trace__set_base_time(trace, evsel, sample);
|
||||
|
||||
if (handler) {
|
||||
++trace->nr_events;
|
||||
|
@ -2479,9 +2569,6 @@ static void trace__handle_event(struct trace *trace, union perf_event *event, st
|
|||
const u32 type = event->header.type;
|
||||
struct perf_evsel *evsel;
|
||||
|
||||
if (!trace->full_time && trace->base_time == 0)
|
||||
trace->base_time = sample->time;
|
||||
|
||||
if (type != PERF_RECORD_SAMPLE) {
|
||||
trace__process_event(trace, trace->host, event, sample);
|
||||
return;
|
||||
|
@ -2493,6 +2580,8 @@ static void trace__handle_event(struct trace *trace, union perf_event *event, st
|
|||
return;
|
||||
}
|
||||
|
||||
trace__set_base_time(trace, evsel, sample);
|
||||
|
||||
if (evsel->attr.type == PERF_TYPE_TRACEPOINT &&
|
||||
sample->raw_data == NULL) {
|
||||
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)
|
|||
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
|
||||
static int perf_clk_id = CLOCK_MONOTONIC;
|
||||
|
||||
|
@ -107,6 +123,9 @@ perf_get_timestamp(void)
|
|||
struct timespec ts;
|
||||
int ret;
|
||||
|
||||
if (use_arch_timestamp)
|
||||
return get_arch_timestamp();
|
||||
|
||||
ret = clock_gettime(perf_clk_id, &ts);
|
||||
if (ret)
|
||||
return 0;
|
||||
|
@ -203,6 +222,17 @@ perf_close_marker_file(void)
|
|||
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)
|
||||
{
|
||||
int pad_cnt;
|
||||
|
@ -211,11 +241,17 @@ void *jvmti_open(void)
|
|||
int fd;
|
||||
FILE *fp;
|
||||
|
||||
init_arch_timestamp();
|
||||
|
||||
/*
|
||||
* check if clockid is supported
|
||||
*/
|
||||
if (!perf_get_timestamp())
|
||||
warnx("jvmti: kernel does not support %d clock id", perf_clk_id);
|
||||
if (!perf_get_timestamp()) {
|
||||
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));
|
||||
|
||||
|
@ -263,6 +299,9 @@ void *jvmti_open(void)
|
|||
|
||||
header.timestamp = perf_get_timestamp();
|
||||
|
||||
if (use_arch_timestamp)
|
||||
header.flags |= JITDUMP_FLAGS_ARCH_TIMESTAMP;
|
||||
|
||||
if (!fwrite(&header, sizeof(header), 1, fp)) {
|
||||
warn("jvmti: cannot write dumpfile header");
|
||||
goto error;
|
||||
|
|
|
@ -69,8 +69,7 @@ libperf-y += stat-shadow.o
|
|||
libperf-y += record.o
|
||||
libperf-y += srcline.o
|
||||
libperf-y += data.o
|
||||
libperf-$(CONFIG_X86) += tsc.o
|
||||
libperf-$(CONFIG_AUXTRACE) += tsc.o
|
||||
libperf-y += tsc.o
|
||||
libperf-y += cloexec.o
|
||||
libperf-y += thread-stack.o
|
||||
libperf-$(CONFIG_AUXTRACE) += auxtrace.o
|
||||
|
|
|
@ -45,6 +45,7 @@ static const char *perf_event__names[] = {
|
|||
[PERF_RECORD_STAT] = "STAT",
|
||||
[PERF_RECORD_STAT_ROUND] = "STAT_ROUND",
|
||||
[PERF_RECORD_EVENT_UPDATE] = "EVENT_UPDATE",
|
||||
[PERF_RECORD_TIME_CONV] = "TIME_CONV",
|
||||
};
|
||||
|
||||
const char *perf_event__name(unsigned int id)
|
||||
|
|
|
@ -233,6 +233,7 @@ enum perf_user_event_type { /* above any possible kernel type */
|
|||
PERF_RECORD_STAT = 76,
|
||||
PERF_RECORD_STAT_ROUND = 77,
|
||||
PERF_RECORD_EVENT_UPDATE = 78,
|
||||
PERF_RECORD_TIME_CONV = 79,
|
||||
PERF_RECORD_HEADER_MAX
|
||||
};
|
||||
|
||||
|
@ -469,6 +470,13 @@ struct stat_round_event {
|
|||
u64 time;
|
||||
};
|
||||
|
||||
struct time_conv_event {
|
||||
struct perf_event_header header;
|
||||
u64 time_shift;
|
||||
u64 time_mult;
|
||||
u64 time_zero;
|
||||
};
|
||||
|
||||
union perf_event {
|
||||
struct perf_event_header header;
|
||||
struct mmap_event mmap;
|
||||
|
@ -497,6 +505,7 @@ union perf_event {
|
|||
struct stat_config_event stat_config;
|
||||
struct stat_event stat;
|
||||
struct stat_round_event stat_round;
|
||||
struct time_conv_event time_conv;
|
||||
};
|
||||
|
||||
void perf_event__print_totals(void);
|
||||
|
|
|
@ -226,7 +226,8 @@ struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx)
|
|||
perf_evsel__init(evsel, attr, idx);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "strlist.h"
|
||||
#include <elf.h>
|
||||
|
||||
#include "tsc.h"
|
||||
#include "session.h"
|
||||
#include "jit.h"
|
||||
#include "jitdump.h"
|
||||
|
@ -33,6 +34,7 @@ struct jit_buf_desc {
|
|||
size_t bufsize;
|
||||
FILE *in;
|
||||
bool needs_bswap; /* handles cross-endianess */
|
||||
bool use_arch_timestamp;
|
||||
void *debug_data;
|
||||
size_t nr_debug_entries;
|
||||
uint32_t code_load_count;
|
||||
|
@ -158,13 +160,16 @@ jit_open(struct jit_buf_desc *jd, const char *name)
|
|||
header.flags = bswap_64(header.flags);
|
||||
}
|
||||
|
||||
jd->use_arch_timestamp = header.flags & JITDUMP_FLAGS_ARCH_TIMESTAMP;
|
||||
|
||||
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.total_size,
|
||||
(unsigned long long)header.timestamp,
|
||||
header.pid,
|
||||
header.elf_mach);
|
||||
header.elf_mach,
|
||||
jd->use_arch_timestamp);
|
||||
|
||||
if (header.flags & JITDUMP_FLAGS_RESERVED) {
|
||||
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)
|
|||
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
|
||||
*/
|
||||
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");
|
||||
goto error;
|
||||
}
|
||||
|
@ -329,6 +339,23 @@ jit_inject_event(struct jit_buf_desc *jd, union perf_event *event)
|
|||
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)
|
||||
{
|
||||
struct perf_sample sample;
|
||||
|
@ -410,7 +437,7 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr)
|
|||
id->tid = tid;
|
||||
}
|
||||
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
|
||||
|
@ -499,7 +526,7 @@ static int jit_repipe_code_move(struct jit_buf_desc *jd, union jr_entry *jr)
|
|||
id->tid = tid;
|
||||
}
|
||||
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
|
||||
|
|
|
@ -23,9 +23,12 @@
|
|||
#define JITHEADER_VERSION 1
|
||||
|
||||
enum jitdump_flags_bits {
|
||||
JITDUMP_FLAGS_ARCH_TIMESTAMP_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 ? \
|
||||
(~((1ULL << JITDUMP_FLAGS_MAX_BIT) - 1)) : 0)
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
int w;
|
||||
__u64 w = 0;
|
||||
int fbit;
|
||||
|
||||
w = bitmap_weight(format, PERF_PMU_FORMAT_BITS);
|
||||
if (!w)
|
||||
return 0;
|
||||
if (w < 64)
|
||||
return (1ULL << w) - 1;
|
||||
return -1;
|
||||
for_each_set_bit(fbit, format, PERF_PMU_FORMAT_BITS)
|
||||
w |= (1ULL << fbit);
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -409,6 +409,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool)
|
|||
tool->stat = process_stat_stub;
|
||||
if (tool->stat_round == NULL)
|
||||
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)
|
||||
|
@ -794,6 +796,7 @@ static perf_event__swap_op perf_event__swap_ops[] = {
|
|||
[PERF_RECORD_STAT] = perf_event__stat_swap,
|
||||
[PERF_RECORD_STAT_ROUND] = perf_event__stat_round_swap,
|
||||
[PERF_RECORD_EVENT_UPDATE] = perf_event__event_update_swap,
|
||||
[PERF_RECORD_TIME_CONV] = perf_event__all64_swap,
|
||||
[PERF_RECORD_HEADER_MAX] = NULL,
|
||||
};
|
||||
|
||||
|
@ -1341,6 +1344,9 @@ static s64 perf_session__process_user_event(struct perf_session *session,
|
|||
return tool->stat(tool, event, session);
|
||||
case PERF_RECORD_STAT_ROUND:
|
||||
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:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ struct perf_session {
|
|||
struct itrace_synth_opts *itrace_synth_opts;
|
||||
struct list_head auxtrace_index;
|
||||
struct trace_event tevent;
|
||||
struct time_conv_event time_conv;
|
||||
bool repipe;
|
||||
bool one_mmap;
|
||||
void *one_mmap_addr;
|
||||
|
|
|
@ -57,6 +57,7 @@ struct perf_tool {
|
|||
id_index,
|
||||
auxtrace_info,
|
||||
auxtrace_error,
|
||||
time_conv,
|
||||
thread_map,
|
||||
cpu_map,
|
||||
stat_config,
|
||||
|
|
|
@ -3,10 +3,29 @@
|
|||
|
||||
#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 tsc_to_perf_time(u64 cyc, struct perf_tsc_conversion *tc);
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue
Block a user