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)
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);
}
#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
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