From 44b3802122174ba499613bac3aab2e66e948ce1e Mon Sep 17 00:00:00 2001 From: Alexander Yarygin Date: Thu, 3 Jul 2014 18:29:04 +0400 Subject: perf kvm: Use defines of kvm events Currently perf-kvm uses string literals for kvm event names, but it works only for x86, because other architectures may have other names for those events. To reduce dependence on architecture, we add file with defines for: - kvm_entry and kvm_exit events, - exit reason field name in kvm_exit event, - length of exit reasons strings, - vcpu_id field name in kvm trace events, and replace literals in perf-kvm. Reviewed-by: Cornelia Huck Reviewed-by David Ahern Signed-off-by: Alexander Yarygin Acked-by: Christian Borntraeger Cc: Christian Borntraeger Cc: Cornelia Huck Cc: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1404397747-20939-2-git-send-email-yarygin@linux.vnet.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- arch/x86/include/uapi/asm/Kbuild | 1 + arch/x86/include/uapi/asm/kvm_perf.h | 16 ++++++++++++++++ tools/perf/MANIFEST | 1 + tools/perf/builtin-kvm.c | 34 ++++++++++++++++------------------ 4 files changed, 34 insertions(+), 18 deletions(-) create mode 100644 arch/x86/include/uapi/asm/kvm_perf.h diff --git a/arch/x86/include/uapi/asm/Kbuild b/arch/x86/include/uapi/asm/Kbuild index 09409c4..3dec769 100644 --- a/arch/x86/include/uapi/asm/Kbuild +++ b/arch/x86/include/uapi/asm/Kbuild @@ -22,6 +22,7 @@ header-y += ipcbuf.h header-y += ist.h header-y += kvm.h header-y += kvm_para.h +header-y += kvm_perf.h header-y += ldt.h header-y += mce.h header-y += mman.h diff --git a/arch/x86/include/uapi/asm/kvm_perf.h b/arch/x86/include/uapi/asm/kvm_perf.h new file mode 100644 index 0000000..3bb964f --- /dev/null +++ b/arch/x86/include/uapi/asm/kvm_perf.h @@ -0,0 +1,16 @@ +#ifndef _ASM_X86_KVM_PERF_H +#define _ASM_X86_KVM_PERF_H + +#include +#include +#include + +#define DECODE_STR_LEN 20 + +#define VCPU_ID "vcpu_id" + +#define KVM_ENTRY_TRACE "kvm:kvm_entry" +#define KVM_EXIT_TRACE "kvm:kvm_exit" +#define KVM_EXIT_REASON "exit_reason" + +#endif /* _ASM_X86_KVM_PERF_H */ diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST index 45da209..02b485d 100644 --- a/tools/perf/MANIFEST +++ b/tools/perf/MANIFEST @@ -37,3 +37,4 @@ arch/x86/include/asm/kvm_host.h arch/x86/include/uapi/asm/svm.h arch/x86/include/uapi/asm/vmx.h arch/x86/include/uapi/asm/kvm.h +arch/x86/include/uapi/asm/kvm_perf.h diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index 41dbeaf..6d73346 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c @@ -30,9 +30,7 @@ #include #ifdef HAVE_KVM_STAT_SUPPORT -#include -#include -#include +#include struct event_key { #define INVALID_KEY (~0ULL) @@ -75,7 +73,7 @@ struct kvm_events_ops { bool (*is_end_event)(struct perf_evsel *evsel, struct perf_sample *sample, struct event_key *key); void (*decode_key)(struct perf_kvm_stat *kvm, struct event_key *key, - char decode[20]); + char *decode); const char *name; }; @@ -126,12 +124,12 @@ static void exit_event_get_key(struct perf_evsel *evsel, struct event_key *key) { key->info = 0; - key->key = perf_evsel__intval(evsel, sample, "exit_reason"); + key->key = perf_evsel__intval(evsel, sample, KVM_EXIT_REASON); } static bool kvm_exit_event(struct perf_evsel *evsel) { - return !strcmp(evsel->name, "kvm:kvm_exit"); + return !strcmp(evsel->name, KVM_EXIT_TRACE); } static bool exit_event_begin(struct perf_evsel *evsel, @@ -147,7 +145,7 @@ static bool exit_event_begin(struct perf_evsel *evsel, static bool kvm_entry_event(struct perf_evsel *evsel) { - return !strcmp(evsel->name, "kvm:kvm_entry"); + return !strcmp(evsel->name, KVM_ENTRY_TRACE); } static bool exit_event_end(struct perf_evsel *evsel, @@ -182,12 +180,12 @@ static const char *get_exit_reason(struct perf_kvm_stat *kvm, static void exit_event_decode_key(struct perf_kvm_stat *kvm, struct event_key *key, - char decode[20]) + char *decode) { const char *exit_reason = get_exit_reason(kvm, kvm->exit_reasons, key->key); - scnprintf(decode, 20, "%s", exit_reason); + scnprintf(decode, DECODE_STR_LEN, "%s", exit_reason); } static struct kvm_events_ops exit_events = { @@ -249,9 +247,9 @@ static bool mmio_event_end(struct perf_evsel *evsel, struct perf_sample *sample, static void mmio_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused, struct event_key *key, - char decode[20]) + char *decode) { - scnprintf(decode, 20, "%#lx:%s", (unsigned long)key->key, + scnprintf(decode, DECODE_STR_LEN, "%#lx:%s", (unsigned long)key->key, key->info == KVM_TRACE_MMIO_WRITE ? "W" : "R"); } @@ -292,9 +290,9 @@ static bool ioport_event_end(struct perf_evsel *evsel, static void ioport_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused, struct event_key *key, - char decode[20]) + char *decode) { - scnprintf(decode, 20, "%#llx:%s", (unsigned long long)key->key, + scnprintf(decode, DECODE_STR_LEN, "%#llx:%s", (unsigned long long)key->key, key->info ? "POUT" : "PIN"); } @@ -524,7 +522,7 @@ static bool handle_end_event(struct perf_kvm_stat *kvm, time_diff = sample->time - time_begin; if (kvm->duration && time_diff > kvm->duration) { - char decode[32]; + char decode[DECODE_STR_LEN]; kvm->events_ops->decode_key(kvm, &event->key, decode); if (strcmp(decode, "HLT")) { @@ -552,7 +550,7 @@ struct vcpu_event_record *per_vcpu_record(struct thread *thread, return NULL; } - vcpu_record->vcpu_id = perf_evsel__intval(evsel, sample, "vcpu_id"); + vcpu_record->vcpu_id = perf_evsel__intval(evsel, sample, VCPU_ID); thread->priv = vcpu_record; } @@ -739,7 +737,7 @@ static void show_timeofday(void) static void print_result(struct perf_kvm_stat *kvm) { - char decode[20]; + char decode[DECODE_STR_LEN]; struct kvm_event *event; int vcpu = kvm->trace_vcpu; @@ -750,7 +748,7 @@ static void print_result(struct perf_kvm_stat *kvm) pr_info("\n\n"); print_vcpu_info(kvm); - pr_info("%20s ", kvm->events_ops->name); + pr_info("%*s ", DECODE_STR_LEN, kvm->events_ops->name); pr_info("%10s ", "Samples"); pr_info("%9s ", "Samples%"); @@ -769,7 +767,7 @@ static void print_result(struct perf_kvm_stat *kvm) min = get_event_min(event, vcpu); kvm->events_ops->decode_key(kvm, &event->key, decode); - pr_info("%20s ", decode); + pr_info("%*s ", DECODE_STR_LEN, decode); pr_info("%10llu ", (unsigned long long)ecount); pr_info("%8.2f%% ", (double)ecount / kvm->total_count * 100); pr_info("%8.2f%% ", (double)etime / kvm->total_time * 100); -- cgit v1.1 From 9daa81239e60c162153fb2a365b8492c9a9bf632 Mon Sep 17 00:00:00 2001 From: Alexander Yarygin Date: Thu, 3 Jul 2014 18:29:05 +0400 Subject: perf kvm: Move arch specific code into arch/ Parts of a 'perf kvm stat' code make sense only for x86. Let's move this code into the arch/x86/kvm-stat.c file and add util/kvm-stat.h for generic structure definitions. Add a global array 'kvm_reg_events_ops' for accessing the arch-specific 'kvm_events_ops' from generic code. Since the several global arrays (i.e. 'kvm_events_tp') have been moved to arch/*, we can not know their sizes and use them directly in builtin-kvm.c. This patch fixes that problem by adding trimming NULL element to each array and changing the behavior of their handlers in generic code. Reviewed-by: David Ahern Reviewed-by: Cornelia Huck Signed-off-by: Alexander Yarygin Acked-by: Christian Borntraeger Cc: Christian Borntraeger Cc: Cornelia Huck Cc: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1404397747-20939-3-git-send-email-yarygin@linux.vnet.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.perf | 1 + tools/perf/arch/x86/Makefile | 1 + tools/perf/arch/x86/util/kvm-stat.c | 151 ++++++++++++++++++ tools/perf/builtin-kvm.c | 297 +++++------------------------------- tools/perf/util/kvm-stat.h | 130 ++++++++++++++++ 5 files changed, 317 insertions(+), 263 deletions(-) create mode 100644 tools/perf/arch/x86/util/kvm-stat.c create mode 100644 tools/perf/util/kvm-stat.h diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 9670a16..90c4983 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -300,6 +300,7 @@ LIB_H += ui/progress.h LIB_H += ui/util.h LIB_H += ui/ui.h LIB_H += util/data.h +LIB_H += util/kvm-stat.h LIB_OBJS += $(OUTPUT)util/abspath.o LIB_OBJS += $(OUTPUT)util/alias.o diff --git a/tools/perf/arch/x86/Makefile b/tools/perf/arch/x86/Makefile index d393901..9b21881 100644 --- a/tools/perf/arch/x86/Makefile +++ b/tools/perf/arch/x86/Makefile @@ -16,3 +16,4 @@ LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/header.o LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/tsc.o LIB_H += arch/$(ARCH)/util/tsc.h HAVE_KVM_STAT_SUPPORT := 1 +LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/kvm-stat.o diff --git a/tools/perf/arch/x86/util/kvm-stat.c b/tools/perf/arch/x86/util/kvm-stat.c new file mode 100644 index 0000000..2f8d2c1 --- /dev/null +++ b/tools/perf/arch/x86/util/kvm-stat.c @@ -0,0 +1,151 @@ +#include "../../util/kvm-stat.h" +#include + +define_exit_reasons_table(vmx_exit_reasons, VMX_EXIT_REASONS); +define_exit_reasons_table(svm_exit_reasons, SVM_EXIT_REASONS); + +static struct kvm_events_ops exit_events = { + .is_begin_event = exit_event_begin, + .is_end_event = exit_event_end, + .decode_key = exit_event_decode_key, + .name = "VM-EXIT" +}; + +/* + * For the mmio events, we treat: + * the time of MMIO write: kvm_mmio(KVM_TRACE_MMIO_WRITE...) -> kvm_entry + * the time of MMIO read: kvm_exit -> kvm_mmio(KVM_TRACE_MMIO_READ...). + */ +static void mmio_event_get_key(struct perf_evsel *evsel, struct perf_sample *sample, + struct event_key *key) +{ + key->key = perf_evsel__intval(evsel, sample, "gpa"); + key->info = perf_evsel__intval(evsel, sample, "type"); +} + +#define KVM_TRACE_MMIO_READ_UNSATISFIED 0 +#define KVM_TRACE_MMIO_READ 1 +#define KVM_TRACE_MMIO_WRITE 2 + +static bool mmio_event_begin(struct perf_evsel *evsel, + struct perf_sample *sample, struct event_key *key) +{ + /* MMIO read begin event in kernel. */ + if (kvm_exit_event(evsel)) + return true; + + /* MMIO write begin event in kernel. */ + if (!strcmp(evsel->name, "kvm:kvm_mmio") && + perf_evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_WRITE) { + mmio_event_get_key(evsel, sample, key); + return true; + } + + return false; +} + +static bool mmio_event_end(struct perf_evsel *evsel, struct perf_sample *sample, + struct event_key *key) +{ + /* MMIO write end event in kernel. */ + if (kvm_entry_event(evsel)) + return true; + + /* MMIO read end event in kernel.*/ + if (!strcmp(evsel->name, "kvm:kvm_mmio") && + perf_evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_READ) { + mmio_event_get_key(evsel, sample, key); + return true; + } + + return false; +} + +static void mmio_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused, + struct event_key *key, + char *decode) +{ + scnprintf(decode, DECODE_STR_LEN, "%#lx:%s", + (unsigned long)key->key, + key->info == KVM_TRACE_MMIO_WRITE ? "W" : "R"); +} + +static struct kvm_events_ops mmio_events = { + .is_begin_event = mmio_event_begin, + .is_end_event = mmio_event_end, + .decode_key = mmio_event_decode_key, + .name = "MMIO Access" +}; + + /* The time of emulation pio access is from kvm_pio to kvm_entry. */ +static void ioport_event_get_key(struct perf_evsel *evsel, + struct perf_sample *sample, + struct event_key *key) +{ + key->key = perf_evsel__intval(evsel, sample, "port"); + key->info = perf_evsel__intval(evsel, sample, "rw"); +} + +static bool ioport_event_begin(struct perf_evsel *evsel, + struct perf_sample *sample, + struct event_key *key) +{ + if (!strcmp(evsel->name, "kvm:kvm_pio")) { + ioport_event_get_key(evsel, sample, key); + return true; + } + + return false; +} + +static bool ioport_event_end(struct perf_evsel *evsel, + struct perf_sample *sample __maybe_unused, + struct event_key *key __maybe_unused) +{ + return kvm_entry_event(evsel); +} + +static void ioport_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused, + struct event_key *key, + char *decode) +{ + scnprintf(decode, DECODE_STR_LEN, "%#llx:%s", + (unsigned long long)key->key, + key->info ? "POUT" : "PIN"); +} + +static struct kvm_events_ops ioport_events = { + .is_begin_event = ioport_event_begin, + .is_end_event = ioport_event_end, + .decode_key = ioport_event_decode_key, + .name = "IO Port Access" +}; + +const char * const kvm_events_tp[] = { + "kvm:kvm_entry", + "kvm:kvm_exit", + "kvm:kvm_mmio", + "kvm:kvm_pio", + NULL, +}; + +struct kvm_reg_events_ops kvm_reg_events_ops[] = { + { .name = "vmexit", .ops = &exit_events }, + { .name = "mmio", .ops = &mmio_events }, + { .name = "ioport", .ops = &ioport_events }, + { NULL, NULL }, +}; + +int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid) +{ + if (strstr(cpuid, "Intel")) { + kvm->exit_reasons = vmx_exit_reasons; + kvm->exit_reasons_isa = "VMX"; + } else if (strstr(cpuid, "AMD")) { + kvm->exit_reasons = svm_exit_reasons; + kvm->exit_reasons_isa = "SVM"; + } else + return -ENOTSUP; + + return 0; +} diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index 6d73346..75ee8c1 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c @@ -31,109 +31,23 @@ #ifdef HAVE_KVM_STAT_SUPPORT #include +#include "util/kvm-stat.h" -struct event_key { - #define INVALID_KEY (~0ULL) - u64 key; - int info; -}; - -struct kvm_event_stats { - u64 time; - struct stats stats; -}; - -struct kvm_event { - struct list_head hash_entry; - struct rb_node rb; - - struct event_key key; - - struct kvm_event_stats total; - - #define DEFAULT_VCPU_NUM 8 - int max_vcpu; - struct kvm_event_stats *vcpu; -}; - -typedef int (*key_cmp_fun)(struct kvm_event*, struct kvm_event*, int); - -struct kvm_event_key { - const char *name; - key_cmp_fun key; -}; - - -struct perf_kvm_stat; - -struct kvm_events_ops { - bool (*is_begin_event)(struct perf_evsel *evsel, - struct perf_sample *sample, - struct event_key *key); - bool (*is_end_event)(struct perf_evsel *evsel, - struct perf_sample *sample, struct event_key *key); - void (*decode_key)(struct perf_kvm_stat *kvm, struct event_key *key, - char *decode); - const char *name; -}; - -struct exit_reasons_table { - unsigned long exit_code; - const char *reason; -}; - -#define EVENTS_BITS 12 -#define EVENTS_CACHE_SIZE (1UL << EVENTS_BITS) - -struct perf_kvm_stat { - struct perf_tool tool; - struct record_opts opts; - struct perf_evlist *evlist; - struct perf_session *session; - - const char *file_name; - const char *report_event; - const char *sort_key; - int trace_vcpu; - - struct exit_reasons_table *exit_reasons; - const char *exit_reasons_isa; - - struct kvm_events_ops *events_ops; - key_cmp_fun compare; - struct list_head kvm_events_cache[EVENTS_CACHE_SIZE]; - - u64 total_time; - u64 total_count; - u64 lost_events; - u64 duration; - - const char *pid_str; - struct intlist *pid_list; - - struct rb_root result; - - int timerfd; - unsigned int display_time; - bool live; -}; - - -static void exit_event_get_key(struct perf_evsel *evsel, - struct perf_sample *sample, - struct event_key *key) +void exit_event_get_key(struct perf_evsel *evsel, + struct perf_sample *sample, + struct event_key *key) { key->info = 0; key->key = perf_evsel__intval(evsel, sample, KVM_EXIT_REASON); } -static bool kvm_exit_event(struct perf_evsel *evsel) +bool kvm_exit_event(struct perf_evsel *evsel) { return !strcmp(evsel->name, KVM_EXIT_TRACE); } -static bool exit_event_begin(struct perf_evsel *evsel, - struct perf_sample *sample, struct event_key *key) +bool exit_event_begin(struct perf_evsel *evsel, + struct perf_sample *sample, struct event_key *key) { if (kvm_exit_event(evsel)) { exit_event_get_key(evsel, sample, key); @@ -143,26 +57,18 @@ static bool exit_event_begin(struct perf_evsel *evsel, return false; } -static bool kvm_entry_event(struct perf_evsel *evsel) +bool kvm_entry_event(struct perf_evsel *evsel) { return !strcmp(evsel->name, KVM_ENTRY_TRACE); } -static bool exit_event_end(struct perf_evsel *evsel, - struct perf_sample *sample __maybe_unused, - struct event_key *key __maybe_unused) +bool exit_event_end(struct perf_evsel *evsel, + struct perf_sample *sample __maybe_unused, + struct event_key *key __maybe_unused) { return kvm_entry_event(evsel); } -#define define_exit_reasons_table(name, symbols) \ - static struct exit_reasons_table name[] = { \ - symbols, { -1, NULL } \ - } - -define_exit_reasons_table(vmx_exit_reasons, VMX_EXIT_REASONS); -define_exit_reasons_table(svm_exit_reasons, SVM_EXIT_REASONS); - static const char *get_exit_reason(struct perf_kvm_stat *kvm, struct exit_reasons_table *tbl, u64 exit_code) @@ -178,9 +84,9 @@ static const char *get_exit_reason(struct perf_kvm_stat *kvm, return "UNKNOWN"; } -static void exit_event_decode_key(struct perf_kvm_stat *kvm, - struct event_key *key, - char *decode) +void exit_event_decode_key(struct perf_kvm_stat *kvm, + struct event_key *key, + char *decode) { const char *exit_reason = get_exit_reason(kvm, kvm->exit_reasons, key->key); @@ -188,139 +94,20 @@ static void exit_event_decode_key(struct perf_kvm_stat *kvm, scnprintf(decode, DECODE_STR_LEN, "%s", exit_reason); } -static struct kvm_events_ops exit_events = { - .is_begin_event = exit_event_begin, - .is_end_event = exit_event_end, - .decode_key = exit_event_decode_key, - .name = "VM-EXIT" -}; - -/* - * For the mmio events, we treat: - * the time of MMIO write: kvm_mmio(KVM_TRACE_MMIO_WRITE...) -> kvm_entry - * the time of MMIO read: kvm_exit -> kvm_mmio(KVM_TRACE_MMIO_READ...). - */ -static void mmio_event_get_key(struct perf_evsel *evsel, struct perf_sample *sample, - struct event_key *key) -{ - key->key = perf_evsel__intval(evsel, sample, "gpa"); - key->info = perf_evsel__intval(evsel, sample, "type"); -} - -#define KVM_TRACE_MMIO_READ_UNSATISFIED 0 -#define KVM_TRACE_MMIO_READ 1 -#define KVM_TRACE_MMIO_WRITE 2 - -static bool mmio_event_begin(struct perf_evsel *evsel, - struct perf_sample *sample, struct event_key *key) -{ - /* MMIO read begin event in kernel. */ - if (kvm_exit_event(evsel)) - return true; - - /* MMIO write begin event in kernel. */ - if (!strcmp(evsel->name, "kvm:kvm_mmio") && - perf_evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_WRITE) { - mmio_event_get_key(evsel, sample, key); - return true; - } - - return false; -} - -static bool mmio_event_end(struct perf_evsel *evsel, struct perf_sample *sample, - struct event_key *key) -{ - /* MMIO write end event in kernel. */ - if (kvm_entry_event(evsel)) - return true; - - /* MMIO read end event in kernel.*/ - if (!strcmp(evsel->name, "kvm:kvm_mmio") && - perf_evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_READ) { - mmio_event_get_key(evsel, sample, key); - return true; - } - - return false; -} - -static void mmio_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused, - struct event_key *key, - char *decode) -{ - scnprintf(decode, DECODE_STR_LEN, "%#lx:%s", (unsigned long)key->key, - key->info == KVM_TRACE_MMIO_WRITE ? "W" : "R"); -} - -static struct kvm_events_ops mmio_events = { - .is_begin_event = mmio_event_begin, - .is_end_event = mmio_event_end, - .decode_key = mmio_event_decode_key, - .name = "MMIO Access" -}; - - /* The time of emulation pio access is from kvm_pio to kvm_entry. */ -static void ioport_event_get_key(struct perf_evsel *evsel, - struct perf_sample *sample, - struct event_key *key) +static bool register_kvm_events_ops(struct perf_kvm_stat *kvm) { - key->key = perf_evsel__intval(evsel, sample, "port"); - key->info = perf_evsel__intval(evsel, sample, "rw"); -} + struct kvm_reg_events_ops *events_ops = kvm_reg_events_ops; -static bool ioport_event_begin(struct perf_evsel *evsel, - struct perf_sample *sample, - struct event_key *key) -{ - if (!strcmp(evsel->name, "kvm:kvm_pio")) { - ioport_event_get_key(evsel, sample, key); - return true; + for (events_ops = kvm_reg_events_ops; events_ops->name; events_ops++) { + if (!strcmp(events_ops->name, kvm->report_event)) { + kvm->events_ops = events_ops->ops; + return true; + } } return false; } -static bool ioport_event_end(struct perf_evsel *evsel, - struct perf_sample *sample __maybe_unused, - struct event_key *key __maybe_unused) -{ - return kvm_entry_event(evsel); -} - -static void ioport_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused, - struct event_key *key, - char *decode) -{ - scnprintf(decode, DECODE_STR_LEN, "%#llx:%s", (unsigned long long)key->key, - key->info ? "POUT" : "PIN"); -} - -static struct kvm_events_ops ioport_events = { - .is_begin_event = ioport_event_begin, - .is_end_event = ioport_event_end, - .decode_key = ioport_event_decode_key, - .name = "IO Port Access" -}; - -static bool register_kvm_events_ops(struct perf_kvm_stat *kvm) -{ - bool ret = true; - - if (!strcmp(kvm->report_event, "vmexit")) - kvm->events_ops = &exit_events; - else if (!strcmp(kvm->report_event, "mmio")) - kvm->events_ops = &mmio_events; - else if (!strcmp(kvm->report_event, "ioport")) - kvm->events_ops = &ioport_events; - else { - pr_err("Unknown report event:%s\n", kvm->report_event); - ret = false; - } - - return ret; -} - struct vcpu_event_record { int vcpu_id; u64 start_time; @@ -833,20 +620,6 @@ static int process_sample_event(struct perf_tool *tool, return 0; } -static int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid) -{ - if (strstr(cpuid, "Intel")) { - kvm->exit_reasons = vmx_exit_reasons; - kvm->exit_reasons_isa = "VMX"; - } else if (strstr(cpuid, "AMD")) { - kvm->exit_reasons = svm_exit_reasons; - kvm->exit_reasons_isa = "SVM"; - } else - return -ENOTSUP; - - return 0; -} - static int cpu_isa_config(struct perf_kvm_stat *kvm) { char buf[64], *cpuid; @@ -1305,13 +1078,6 @@ exit: return ret; } -static const char * const kvm_events_tp[] = { - "kvm:kvm_entry", - "kvm:kvm_exit", - "kvm:kvm_mmio", - "kvm:kvm_pio", -}; - #define STRDUP_FAIL_EXIT(s) \ ({ char *_p; \ _p = strdup(s); \ @@ -1323,7 +1089,7 @@ static const char * const kvm_events_tp[] = { static int kvm_events_record(struct perf_kvm_stat *kvm, int argc, const char **argv) { - unsigned int rec_argc, i, j; + unsigned int rec_argc, i, j, events_tp_size; const char **rec_argv; const char * const record_args[] = { "record", @@ -1331,9 +1097,14 @@ kvm_events_record(struct perf_kvm_stat *kvm, int argc, const char **argv) "-m", "1024", "-c", "1", }; + const char * const *events_tp; + events_tp_size = 0; + + for (events_tp = kvm_events_tp; *events_tp; events_tp++) + events_tp_size++; rec_argc = ARRAY_SIZE(record_args) + argc + 2 + - 2 * ARRAY_SIZE(kvm_events_tp); + 2 * events_tp_size; rec_argv = calloc(rec_argc + 1, sizeof(char *)); if (rec_argv == NULL) @@ -1342,7 +1113,7 @@ kvm_events_record(struct perf_kvm_stat *kvm, int argc, const char **argv) for (i = 0; i < ARRAY_SIZE(record_args); i++) rec_argv[i] = STRDUP_FAIL_EXIT(record_args[i]); - for (j = 0; j < ARRAY_SIZE(kvm_events_tp); j++) { + for (j = 0; j < events_tp_size; j++) { rec_argv[i++] = "-e"; rec_argv[i++] = STRDUP_FAIL_EXIT(kvm_events_tp[j]); } @@ -1396,16 +1167,16 @@ static struct perf_evlist *kvm_live_event_list(void) { struct perf_evlist *evlist; char *tp, *name, *sys; - unsigned int j; int err = -1; + const char * const *events_tp; evlist = perf_evlist__new(); if (evlist == NULL) return NULL; - for (j = 0; j < ARRAY_SIZE(kvm_events_tp); j++) { + for (events_tp = kvm_events_tp; *events_tp; events_tp++) { - tp = strdup(kvm_events_tp[j]); + tp = strdup(*events_tp); if (tp == NULL) goto out; @@ -1414,7 +1185,7 @@ static struct perf_evlist *kvm_live_event_list(void) name = strchr(tp, ':'); if (name == NULL) { pr_err("Error parsing %s tracepoint: subsystem delimiter not found\n", - kvm_events_tp[j]); + *events_tp); free(tp); goto out; } @@ -1422,7 +1193,7 @@ static struct perf_evlist *kvm_live_event_list(void) name++; if (perf_evlist__add_newtp(evlist, sys, name, NULL)) { - pr_err("Failed to add %s tracepoint to the list\n", kvm_events_tp[j]); + pr_err("Failed to add %s tracepoint to the list\n", *events_tp); free(tp); goto out; } diff --git a/tools/perf/util/kvm-stat.h b/tools/perf/util/kvm-stat.h new file mode 100644 index 0000000..d0d9fb1 --- /dev/null +++ b/tools/perf/util/kvm-stat.h @@ -0,0 +1,130 @@ +#ifndef __PERF_KVM_STAT_H +#define __PERF_KVM_STAT_H + +#include "../perf.h" +#include "evsel.h" +#include "evlist.h" +#include "session.h" +#include "tool.h" +#include "stat.h" + +struct event_key { + #define INVALID_KEY (~0ULL) + u64 key; + int info; +}; + +struct kvm_event_stats { + u64 time; + struct stats stats; +}; + +struct kvm_event { + struct list_head hash_entry; + struct rb_node rb; + + struct event_key key; + + struct kvm_event_stats total; + + #define DEFAULT_VCPU_NUM 8 + int max_vcpu; + struct kvm_event_stats *vcpu; +}; + +typedef int (*key_cmp_fun)(struct kvm_event*, struct kvm_event*, int); + +struct kvm_event_key { + const char *name; + key_cmp_fun key; +}; + +struct perf_kvm_stat; + +struct kvm_events_ops { + bool (*is_begin_event)(struct perf_evsel *evsel, + struct perf_sample *sample, + struct event_key *key); + bool (*is_end_event)(struct perf_evsel *evsel, + struct perf_sample *sample, struct event_key *key); + void (*decode_key)(struct perf_kvm_stat *kvm, struct event_key *key, + char *decode); + const char *name; +}; + +struct exit_reasons_table { + unsigned long exit_code; + const char *reason; +}; + +#define EVENTS_BITS 12 +#define EVENTS_CACHE_SIZE (1UL << EVENTS_BITS) + +struct perf_kvm_stat { + struct perf_tool tool; + struct record_opts opts; + struct perf_evlist *evlist; + struct perf_session *session; + + const char *file_name; + const char *report_event; + const char *sort_key; + int trace_vcpu; + + struct exit_reasons_table *exit_reasons; + const char *exit_reasons_isa; + + struct kvm_events_ops *events_ops; + key_cmp_fun compare; + struct list_head kvm_events_cache[EVENTS_CACHE_SIZE]; + + u64 total_time; + u64 total_count; + u64 lost_events; + u64 duration; + + const char *pid_str; + struct intlist *pid_list; + + struct rb_root result; + + int timerfd; + unsigned int display_time; + bool live; +}; + +struct kvm_reg_events_ops { + const char *name; + struct kvm_events_ops *ops; +}; + +void exit_event_get_key(struct perf_evsel *evsel, + struct perf_sample *sample, + struct event_key *key); +bool exit_event_begin(struct perf_evsel *evsel, + struct perf_sample *sample, + struct event_key *key); +bool exit_event_end(struct perf_evsel *evsel, + struct perf_sample *sample, + struct event_key *key); +void exit_event_decode_key(struct perf_kvm_stat *kvm, + struct event_key *key, + char *decode); + +bool kvm_exit_event(struct perf_evsel *evsel); +bool kvm_entry_event(struct perf_evsel *evsel); + +#define define_exit_reasons_table(name, symbols) \ + static struct exit_reasons_table name[] = { \ + symbols, { -1, NULL } \ + } + +/* + * arch specific callbacks and data structures + */ +int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid); + +extern const char * const kvm_events_tp[]; +extern struct kvm_reg_events_ops kvm_reg_events_ops[]; + +#endif /* __PERF_KVM_STAT_H */ -- cgit v1.1 From 54c801ff71ba9c9ae41871e226b9d846ff9c6bab Mon Sep 17 00:00:00 2001 From: Alexander Yarygin Date: Thu, 3 Jul 2014 18:29:06 +0400 Subject: perf kvm: Add skip_event() for --duration option Current code skips output of the x86 specific HLT event in order to avoid flooding the output with enabled --duration option. The events to be skipped should be architecture dependent, though. Let's add an architecture specific array of events to be skipped and introduce a skip_event() function checking against that array. Reviewed-by: Christian Borntraeger Reviewed-by: Cornelia Huck Reviewed-by: David Ahern Signed-off-by: Alexander Yarygin Cc: Christian Borntraeger Cc: Cornelia Huck Cc: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1404397747-20939-4-git-send-email-yarygin@linux.vnet.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/x86/util/kvm-stat.c | 5 +++++ tools/perf/builtin-kvm.c | 13 ++++++++++++- tools/perf/util/kvm-stat.h | 1 + 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/tools/perf/arch/x86/util/kvm-stat.c b/tools/perf/arch/x86/util/kvm-stat.c index 2f8d2c1..14e4e66 100644 --- a/tools/perf/arch/x86/util/kvm-stat.c +++ b/tools/perf/arch/x86/util/kvm-stat.c @@ -136,6 +136,11 @@ struct kvm_reg_events_ops kvm_reg_events_ops[] = { { NULL, NULL }, }; +const char * const kvm_skip_events[] = { + "HLT", + NULL, +}; + int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid) { if (strstr(cpuid, "Intel")) { diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index 75ee8c1..fc2d63d 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c @@ -261,6 +261,17 @@ static bool update_kvm_event(struct kvm_event *event, int vcpu_id, return true; } +static bool skip_event(const char *event) +{ + const char * const *skip_events; + + for (skip_events = kvm_skip_events; *skip_events; skip_events++) + if (!strcmp(event, *skip_events)) + return true; + + return false; +} + static bool handle_end_event(struct perf_kvm_stat *kvm, struct vcpu_event_record *vcpu_record, struct event_key *key, @@ -312,7 +323,7 @@ static bool handle_end_event(struct perf_kvm_stat *kvm, char decode[DECODE_STR_LEN]; kvm->events_ops->decode_key(kvm, &event->key, decode); - if (strcmp(decode, "HLT")) { + if (!skip_event(decode)) { pr_info("%" PRIu64 " VM %d, vcpu %d: %s event took %" PRIu64 "usec\n", sample->time, sample->pid, vcpu_record->vcpu_id, decode, time_diff/1000); diff --git a/tools/perf/util/kvm-stat.h b/tools/perf/util/kvm-stat.h index d0d9fb1..ba937ca 100644 --- a/tools/perf/util/kvm-stat.h +++ b/tools/perf/util/kvm-stat.h @@ -126,5 +126,6 @@ int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid); extern const char * const kvm_events_tp[]; extern struct kvm_reg_events_ops kvm_reg_events_ops[]; +extern const char * const kvm_skip_events[]; #endif /* __PERF_KVM_STAT_H */ -- cgit v1.1 From 3be8e2a0a53c3179a44a933614f6a893da0b5c19 Mon Sep 17 00:00:00 2001 From: Alexander Yarygin Date: Thu, 3 Jul 2014 18:29:07 +0400 Subject: perf kvm: Add stat support on s390 On s390, the vmexit event has a tree-like structure: between exit_event_begin and exit_event_end several other events may happen and with each of them refining the previous ones. This patch adds a decoder for such events to the generic code and also the files and kvm-stat.c for s390. Commands 'perf kvm stat record', 'report' and 'live' are supported. Reviewed-by: David Ahern Signed-off-by: Alexander Yarygin Acked-by: Christian Borntraeger Cc: Christian Borntraeger Cc: Cornelia Huck Cc: David Ahern Cc: Ingo Molnar Cc: Jiri Olsa Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1404397747-20939-5-git-send-email-yarygin@linux.vnet.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- arch/s390/include/uapi/asm/Kbuild | 1 + arch/s390/include/uapi/asm/kvm_perf.h | 25 ++++++++ tools/perf/Documentation/perf-kvm.txt | 16 +++--- tools/perf/MANIFEST | 2 + tools/perf/arch/s390/Makefile | 2 + tools/perf/arch/s390/util/kvm-stat.c | 105 ++++++++++++++++++++++++++++++++++ tools/perf/builtin-kvm.c | 52 +++++++++++++++-- tools/perf/util/kvm-stat.h | 9 +++ 8 files changed, 201 insertions(+), 11 deletions(-) create mode 100644 arch/s390/include/uapi/asm/kvm_perf.h create mode 100644 tools/perf/arch/s390/util/kvm-stat.c diff --git a/arch/s390/include/uapi/asm/Kbuild b/arch/s390/include/uapi/asm/Kbuild index 6a9a9eb..0e2b54d 100644 --- a/arch/s390/include/uapi/asm/Kbuild +++ b/arch/s390/include/uapi/asm/Kbuild @@ -16,6 +16,7 @@ header-y += ioctls.h header-y += ipcbuf.h header-y += kvm.h header-y += kvm_para.h +header-y += kvm_perf.h header-y += kvm_virtio.h header-y += mman.h header-y += monwriter.h diff --git a/arch/s390/include/uapi/asm/kvm_perf.h b/arch/s390/include/uapi/asm/kvm_perf.h new file mode 100644 index 0000000..3972827 --- /dev/null +++ b/arch/s390/include/uapi/asm/kvm_perf.h @@ -0,0 +1,25 @@ +/* + * Definitions for perf-kvm on s390 + * + * Copyright 2014 IBM Corp. + * Author(s): Alexander Yarygin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License (version 2 only) + * as published by the Free Software Foundation. + */ + +#ifndef __LINUX_KVM_PERF_S390_H +#define __LINUX_KVM_PERF_S390_H + +#include + +#define DECODE_STR_LEN 40 + +#define VCPU_ID "id" + +#define KVM_ENTRY_TRACE "kvm:kvm_s390_sie_enter" +#define KVM_EXIT_TRACE "kvm:kvm_s390_sie_exit" +#define KVM_EXIT_REASON "icptcode" + +#endif diff --git a/tools/perf/Documentation/perf-kvm.txt b/tools/perf/Documentation/perf-kvm.txt index 52276a6..6e689dc 100644 --- a/tools/perf/Documentation/perf-kvm.txt +++ b/tools/perf/Documentation/perf-kvm.txt @@ -51,9 +51,9 @@ There are a couple of variants of perf kvm: 'perf kvm stat ' to run a command and gather performance counter statistics. Especially, perf 'kvm stat record/report' generates a statistical analysis - of KVM events. Currently, vmexit, mmio and ioport events are supported. - 'perf kvm stat record ' records kvm events and the events between - start and end . + of KVM events. Currently, vmexit, mmio (x86 only) and ioport (x86 only) + events are supported. 'perf kvm stat record ' records kvm events + and the events between start and end . And this command produces a file which contains tracing results of kvm events. @@ -103,8 +103,8 @@ STAT REPORT OPTIONS analyze events which occures on this vcpu. (default: all vcpus) --event=:: - event to be analyzed. Possible values: vmexit, mmio, ioport. - (default: vmexit) + event to be analyzed. Possible values: vmexit, mmio (x86 only), + ioport (x86 only). (default: vmexit) -k:: --key=:: Sorting key. Possible values: sample (default, sort by samples @@ -138,7 +138,8 @@ STAT LIVE OPTIONS --event=:: - event to be analyzed. Possible values: vmexit, mmio, ioport. + event to be analyzed. Possible values: vmexit, + mmio (x86 only), ioport (x86 only). (default: vmexit) -k:: @@ -147,7 +148,8 @@ STAT LIVE OPTIONS number), time (sort by average time). --duration=:: - Show events other than HLT that take longer than duration usecs. + Show events other than HLT (x86 only) or Wait state (s390 only) + that take longer than duration usecs. SEE ALSO -------- diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST index 02b485d..344c4d3 100644 --- a/tools/perf/MANIFEST +++ b/tools/perf/MANIFEST @@ -38,3 +38,5 @@ arch/x86/include/uapi/asm/svm.h arch/x86/include/uapi/asm/vmx.h arch/x86/include/uapi/asm/kvm.h arch/x86/include/uapi/asm/kvm_perf.h +arch/s390/include/uapi/asm/sie.h +arch/s390/include/uapi/asm/kvm_perf.h diff --git a/tools/perf/arch/s390/Makefile b/tools/perf/arch/s390/Makefile index 744e629..798ac73 100644 --- a/tools/perf/arch/s390/Makefile +++ b/tools/perf/arch/s390/Makefile @@ -3,3 +3,5 @@ PERF_HAVE_DWARF_REGS := 1 LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o endif LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/header.o +HAVE_KVM_STAT_SUPPORT := 1 +LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/kvm-stat.o diff --git a/tools/perf/arch/s390/util/kvm-stat.c b/tools/perf/arch/s390/util/kvm-stat.c new file mode 100644 index 0000000..a5dbc07 --- /dev/null +++ b/tools/perf/arch/s390/util/kvm-stat.c @@ -0,0 +1,105 @@ +/* + * Arch specific functions for perf kvm stat. + * + * Copyright 2014 IBM Corp. + * Author(s): Alexander Yarygin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License (version 2 only) + * as published by the Free Software Foundation. + */ + +#include "../../util/kvm-stat.h" +#include + +define_exit_reasons_table(sie_exit_reasons, sie_intercept_code); +define_exit_reasons_table(sie_icpt_insn_codes, icpt_insn_codes); +define_exit_reasons_table(sie_sigp_order_codes, sigp_order_codes); +define_exit_reasons_table(sie_diagnose_codes, diagnose_codes); +define_exit_reasons_table(sie_icpt_prog_codes, icpt_prog_codes); + +static void event_icpt_insn_get_key(struct perf_evsel *evsel, + struct perf_sample *sample, + struct event_key *key) +{ + unsigned long insn; + + insn = perf_evsel__intval(evsel, sample, "instruction"); + key->key = icpt_insn_decoder(insn); + key->exit_reasons = sie_icpt_insn_codes; +} + +static void event_sigp_get_key(struct perf_evsel *evsel, + struct perf_sample *sample, + struct event_key *key) +{ + key->key = perf_evsel__intval(evsel, sample, "order_code"); + key->exit_reasons = sie_sigp_order_codes; +} + +static void event_diag_get_key(struct perf_evsel *evsel, + struct perf_sample *sample, + struct event_key *key) +{ + key->key = perf_evsel__intval(evsel, sample, "code"); + key->exit_reasons = sie_diagnose_codes; +} + +static void event_icpt_prog_get_key(struct perf_evsel *evsel, + struct perf_sample *sample, + struct event_key *key) +{ + key->key = perf_evsel__intval(evsel, sample, "code"); + key->exit_reasons = sie_icpt_prog_codes; +} + +static struct child_event_ops child_events[] = { + { .name = "kvm:kvm_s390_intercept_instruction", + .get_key = event_icpt_insn_get_key }, + { .name = "kvm:kvm_s390_handle_sigp", + .get_key = event_sigp_get_key }, + { .name = "kvm:kvm_s390_handle_diag", + .get_key = event_diag_get_key }, + { .name = "kvm:kvm_s390_intercept_prog", + .get_key = event_icpt_prog_get_key }, + { NULL, NULL }, +}; + +static struct kvm_events_ops exit_events = { + .is_begin_event = exit_event_begin, + .is_end_event = exit_event_end, + .child_ops = child_events, + .decode_key = exit_event_decode_key, + .name = "VM-EXIT" +}; + +const char * const kvm_events_tp[] = { + "kvm:kvm_s390_sie_enter", + "kvm:kvm_s390_sie_exit", + "kvm:kvm_s390_intercept_instruction", + "kvm:kvm_s390_handle_sigp", + "kvm:kvm_s390_handle_diag", + "kvm:kvm_s390_intercept_prog", + NULL, +}; + +struct kvm_reg_events_ops kvm_reg_events_ops[] = { + { .name = "vmexit", .ops = &exit_events }, + { NULL, NULL }, +}; + +const char * const kvm_skip_events[] = { + "Wait state", + NULL, +}; + +int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid) +{ + if (strstr(cpuid, "IBM/S390")) { + kvm->exit_reasons = sie_exit_reasons; + kvm->exit_reasons_isa = "SIE"; + } else + return -ENOTSUP; + + return 0; +} diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index fc2d63d..43367eb 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c @@ -88,7 +88,7 @@ void exit_event_decode_key(struct perf_kvm_stat *kvm, struct event_key *key, char *decode) { - const char *exit_reason = get_exit_reason(kvm, kvm->exit_reasons, + const char *exit_reason = get_exit_reason(kvm, key->exit_reasons, key->key); scnprintf(decode, DECODE_STR_LEN, "%s", exit_reason); @@ -261,6 +261,43 @@ static bool update_kvm_event(struct kvm_event *event, int vcpu_id, return true; } +static bool is_child_event(struct perf_kvm_stat *kvm, + struct perf_evsel *evsel, + struct perf_sample *sample, + struct event_key *key) +{ + struct child_event_ops *child_ops; + + child_ops = kvm->events_ops->child_ops; + + if (!child_ops) + return false; + + for (; child_ops->name; child_ops++) { + if (!strcmp(evsel->name, child_ops->name)) { + child_ops->get_key(evsel, sample, key); + return true; + } + } + + return false; +} + +static bool handle_child_event(struct perf_kvm_stat *kvm, + struct vcpu_event_record *vcpu_record, + struct event_key *key, + struct perf_sample *sample __maybe_unused) +{ + struct kvm_event *event = NULL; + + if (key->key != INVALID_KEY) + event = find_create_kvm_event(kvm, key); + + vcpu_record->last_event = event; + + return true; +} + static bool skip_event(const char *event) { const char * const *skip_events; @@ -361,7 +398,8 @@ static bool handle_kvm_event(struct perf_kvm_stat *kvm, struct perf_sample *sample) { struct vcpu_event_record *vcpu_record; - struct event_key key = {.key = INVALID_KEY}; + struct event_key key = { .key = INVALID_KEY, + .exit_reasons = kvm->exit_reasons }; vcpu_record = per_vcpu_record(thread, evsel, sample); if (!vcpu_record) @@ -375,6 +413,9 @@ static bool handle_kvm_event(struct perf_kvm_stat *kvm, if (kvm->events_ops->is_begin_event(evsel, sample, &key)) return handle_begin_event(kvm, vcpu_record, &key, sample->time); + if (is_child_event(kvm, evsel, sample, &key)) + return handle_child_event(kvm, vcpu_record, &key, sample); + if (kvm->events_ops->is_end_event(evsel, sample, &key)) return handle_end_event(kvm, vcpu_record, &key, sample); @@ -1143,7 +1184,8 @@ kvm_events_report(struct perf_kvm_stat *kvm, int argc, const char **argv) { const struct option kvm_events_report_options[] = { OPT_STRING(0, "event", &kvm->report_event, "report event", - "event for reporting: vmexit, mmio, ioport"), + "event for reporting: vmexit, " + "mmio (x86 only), ioport (x86 only)"), OPT_INTEGER(0, "vcpu", &kvm->trace_vcpu, "vcpu id to report"), OPT_STRING('k', "key", &kvm->sort_key, "sort-key", @@ -1249,7 +1291,9 @@ static int kvm_events_live(struct perf_kvm_stat *kvm, "key for sorting: sample(sort by samples number)" " time (sort by avg time)"), OPT_U64(0, "duration", &kvm->duration, - "show events other than HALT that take longer than duration usecs"), + "show events other than" + " HLT (x86 only) or Wait state (s390 only)" + " that take longer than duration usecs"), OPT_END() }; const char * const live_usage[] = { diff --git a/tools/perf/util/kvm-stat.h b/tools/perf/util/kvm-stat.h index ba937ca..0b5a8cd 100644 --- a/tools/perf/util/kvm-stat.h +++ b/tools/perf/util/kvm-stat.h @@ -12,6 +12,7 @@ struct event_key { #define INVALID_KEY (~0ULL) u64 key; int info; + struct exit_reasons_table *exit_reasons; }; struct kvm_event_stats { @@ -41,12 +42,20 @@ struct kvm_event_key { struct perf_kvm_stat; +struct child_event_ops { + void (*get_key)(struct perf_evsel *evsel, + struct perf_sample *sample, + struct event_key *key); + const char *name; +}; + struct kvm_events_ops { bool (*is_begin_event)(struct perf_evsel *evsel, struct perf_sample *sample, struct event_key *key); bool (*is_end_event)(struct perf_evsel *evsel, struct perf_sample *sample, struct event_key *key); + struct child_event_ops *child_ops; void (*decode_key)(struct perf_kvm_stat *kvm, struct event_key *key, char *decode); const char *name; -- cgit v1.1 From 05f832e3a267d6e45d092595bdf9339d127ea137 Mon Sep 17 00:00:00 2001 From: Joseph Schuchart Date: Wed, 9 Jul 2014 16:16:31 +0200 Subject: perf script: Add missing calls to Py_DECREF for return values Signed-off-by: Joseph Schuchart Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Thomas Ilsche Link: http://lkml.kernel.org/r/53BD4EBF.5050407@tu-dresden.de Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/scripting-engines/trace-event-python.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index e55b65a..b6c1a69 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -50,10 +50,14 @@ static int zero_flag_atom; static PyObject *main_module, *main_dict; +static void handler_call_die(const char *handler_name) NORETURN; static void handler_call_die(const char *handler_name) { PyErr_Print(); Py_FatalError("problem in Python trace event handler"); + // Py_FatalError does not return + // but we have to make the compiler happy + abort(); } /* @@ -97,6 +101,7 @@ static void define_value(enum print_arg_type field_type, retval = PyObject_CallObject(handler, t); if (retval == NULL) handler_call_die(handler_name); + Py_DECREF(retval); } Py_DECREF(t); @@ -143,6 +148,7 @@ static void define_field(enum print_arg_type field_type, retval = PyObject_CallObject(handler, t); if (retval == NULL) handler_call_die(handler_name); + Py_DECREF(retval); } Py_DECREF(t); @@ -361,6 +367,7 @@ static void python_process_tracepoint(struct perf_sample *sample, retval = PyObject_CallObject(handler, t); if (retval == NULL) handler_call_die(handler_name); + Py_DECREF(retval); } else { handler = PyDict_GetItemString(main_dict, "trace_unhandled"); if (handler && PyCallable_Check(handler)) { @@ -368,6 +375,7 @@ static void python_process_tracepoint(struct perf_sample *sample, retval = PyObject_CallObject(handler, t); if (retval == NULL) handler_call_die("trace_unhandled"); + Py_DECREF(retval); } Py_DECREF(dict); } @@ -427,6 +435,7 @@ static void python_process_general_event(struct perf_sample *sample, retval = PyObject_CallObject(handler, t); if (retval == NULL) handler_call_die(handler_name); + Py_DECREF(retval); exit: Py_DECREF(dict); Py_DECREF(t); @@ -548,8 +557,7 @@ static int python_stop_script(void) retval = PyObject_CallObject(handler, NULL); if (retval == NULL) handler_call_die("trace_end"); - else - Py_DECREF(retval); + Py_DECREF(retval); out: Py_XDECREF(main_dict); Py_XDECREF(main_module); -- cgit v1.1 From 0f5f5bcd112292f14b75750dde7461463bb1c7bb Mon Sep 17 00:00:00 2001 From: Joseph Schuchart Date: Thu, 10 Jul 2014 13:50:51 +0200 Subject: perf script: Add callchain to generic and tracepoint events This provides valuable information for tracing performance problems. Since this change alters the interface for the python scripts, also adjust the script generation and the provided scripts. Signed-off-by: Joseph Schuchart Acked-by: Thomas Ilsche Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Thomas Ilsche Link: http://lkml.kernel.org/r/53BE7E1B.10503@tu-dresden.de Signed-off-by: Arnaldo Carvalho de Melo --- .../python/Perf-Trace-Util/lib/Perf/Trace/Core.py | 3 +- tools/perf/scripts/python/check-perf-trace.py | 4 +- .../perf/scripts/python/failed-syscalls-by-pid.py | 2 +- tools/perf/scripts/python/futex-contention.py | 4 +- tools/perf/scripts/python/net_dropmonitor.py | 2 +- tools/perf/scripts/python/netdev-times.py | 26 ++--- tools/perf/scripts/python/sched-migration.py | 41 ++++---- tools/perf/scripts/python/sctop.py | 2 +- tools/perf/scripts/python/syscall-counts-by-pid.py | 2 +- tools/perf/scripts/python/syscall-counts.py | 2 +- .../util/scripting-engines/trace-event-python.c | 106 ++++++++++++++++++++- 11 files changed, 146 insertions(+), 48 deletions(-) diff --git a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Core.py b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Core.py index de7211e..38dfb72 100644 --- a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Core.py +++ b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/Core.py @@ -107,12 +107,13 @@ def taskState(state): class EventHeaders: def __init__(self, common_cpu, common_secs, common_nsecs, - common_pid, common_comm): + common_pid, common_comm, common_callchain): self.cpu = common_cpu self.secs = common_secs self.nsecs = common_nsecs self.pid = common_pid self.comm = common_comm + self.callchain = common_callchain def ts(self): return (self.secs * (10 ** 9)) + self.nsecs diff --git a/tools/perf/scripts/python/check-perf-trace.py b/tools/perf/scripts/python/check-perf-trace.py index 4647a76..334599c 100644 --- a/tools/perf/scripts/python/check-perf-trace.py +++ b/tools/perf/scripts/python/check-perf-trace.py @@ -27,7 +27,7 @@ def trace_end(): def irq__softirq_entry(event_name, context, common_cpu, common_secs, common_nsecs, common_pid, common_comm, - vec): + common_callchain, vec): print_header(event_name, common_cpu, common_secs, common_nsecs, common_pid, common_comm) @@ -38,7 +38,7 @@ def irq__softirq_entry(event_name, context, common_cpu, def kmem__kmalloc(event_name, context, common_cpu, common_secs, common_nsecs, common_pid, common_comm, - call_site, ptr, bytes_req, bytes_alloc, + common_callchain, call_site, ptr, bytes_req, bytes_alloc, gfp_flags): print_header(event_name, common_cpu, common_secs, common_nsecs, common_pid, common_comm) diff --git a/tools/perf/scripts/python/failed-syscalls-by-pid.py b/tools/perf/scripts/python/failed-syscalls-by-pid.py index 266a836..cafeff3 100644 --- a/tools/perf/scripts/python/failed-syscalls-by-pid.py +++ b/tools/perf/scripts/python/failed-syscalls-by-pid.py @@ -39,7 +39,7 @@ def trace_end(): def raw_syscalls__sys_exit(event_name, context, common_cpu, common_secs, common_nsecs, common_pid, common_comm, - id, ret): + common_callchain, id, ret): if (for_comm and common_comm != for_comm) or \ (for_pid and common_pid != for_pid ): return diff --git a/tools/perf/scripts/python/futex-contention.py b/tools/perf/scripts/python/futex-contention.py index 11e70a3..0f5cf43 100644 --- a/tools/perf/scripts/python/futex-contention.py +++ b/tools/perf/scripts/python/futex-contention.py @@ -21,7 +21,7 @@ thread_blocktime = {} lock_waits = {} # long-lived stats on (tid,lock) blockage elapsed time process_names = {} # long-lived pid-to-execname mapping -def syscalls__sys_enter_futex(event, ctxt, cpu, s, ns, tid, comm, +def syscalls__sys_enter_futex(event, ctxt, cpu, s, ns, tid, comm, callchain, nr, uaddr, op, val, utime, uaddr2, val3): cmd = op & FUTEX_CMD_MASK if cmd != FUTEX_WAIT: @@ -31,7 +31,7 @@ def syscalls__sys_enter_futex(event, ctxt, cpu, s, ns, tid, comm, thread_thislock[tid] = uaddr thread_blocktime[tid] = nsecs(s, ns) -def syscalls__sys_exit_futex(event, ctxt, cpu, s, ns, tid, comm, +def syscalls__sys_exit_futex(event, ctxt, cpu, s, ns, tid, comm, callchain, nr, ret): if thread_blocktime.has_key(tid): elapsed = nsecs(s, ns) - thread_blocktime[tid] diff --git a/tools/perf/scripts/python/net_dropmonitor.py b/tools/perf/scripts/python/net_dropmonitor.py index b574059..0b6ce8c 100755 --- a/tools/perf/scripts/python/net_dropmonitor.py +++ b/tools/perf/scripts/python/net_dropmonitor.py @@ -66,7 +66,7 @@ def trace_end(): print_drop_table() # called from perf, when it finds a correspoinding event -def skb__kfree_skb(name, context, cpu, sec, nsec, pid, comm, +def skb__kfree_skb(name, context, cpu, sec, nsec, pid, comm, callchain, skbaddr, location, protocol): slocation = str(location) try: diff --git a/tools/perf/scripts/python/netdev-times.py b/tools/perf/scripts/python/netdev-times.py index 9aa0a32..4d21ef2 100644 --- a/tools/perf/scripts/python/netdev-times.py +++ b/tools/perf/scripts/python/netdev-times.py @@ -224,75 +224,75 @@ def trace_end(): (len(rx_skb_list), of_count_rx_skb_list) # called from perf, when it finds a correspoinding event -def irq__softirq_entry(name, context, cpu, sec, nsec, pid, comm, vec): +def irq__softirq_entry(name, context, cpu, sec, nsec, pid, comm, callchain, vec): if symbol_str("irq__softirq_entry", "vec", vec) != "NET_RX": return event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, vec) all_event_list.append(event_info) -def irq__softirq_exit(name, context, cpu, sec, nsec, pid, comm, vec): +def irq__softirq_exit(name, context, cpu, sec, nsec, pid, comm, callchain, vec): if symbol_str("irq__softirq_entry", "vec", vec) != "NET_RX": return event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, vec) all_event_list.append(event_info) -def irq__softirq_raise(name, context, cpu, sec, nsec, pid, comm, vec): +def irq__softirq_raise(name, context, cpu, sec, nsec, pid, comm, callchain, vec): if symbol_str("irq__softirq_entry", "vec", vec) != "NET_RX": return event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, vec) all_event_list.append(event_info) def irq__irq_handler_entry(name, context, cpu, sec, nsec, pid, comm, - irq, irq_name): + callchain, irq, irq_name): event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, irq, irq_name) all_event_list.append(event_info) -def irq__irq_handler_exit(name, context, cpu, sec, nsec, pid, comm, irq, ret): +def irq__irq_handler_exit(name, context, cpu, sec, nsec, pid, comm, callchain, irq, ret): event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, irq, ret) all_event_list.append(event_info) -def napi__napi_poll(name, context, cpu, sec, nsec, pid, comm, napi, dev_name): +def napi__napi_poll(name, context, cpu, sec, nsec, pid, comm, callchain, napi, dev_name): event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, napi, dev_name) all_event_list.append(event_info) -def net__netif_receive_skb(name, context, cpu, sec, nsec, pid, comm, skbaddr, +def net__netif_receive_skb(name, context, cpu, sec, nsec, pid, comm, callchain, skbaddr, skblen, dev_name): event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, skbaddr, skblen, dev_name) all_event_list.append(event_info) -def net__netif_rx(name, context, cpu, sec, nsec, pid, comm, skbaddr, +def net__netif_rx(name, context, cpu, sec, nsec, pid, comm, callchain, skbaddr, skblen, dev_name): event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, skbaddr, skblen, dev_name) all_event_list.append(event_info) -def net__net_dev_queue(name, context, cpu, sec, nsec, pid, comm, +def net__net_dev_queue(name, context, cpu, sec, nsec, pid, comm, callchain, skbaddr, skblen, dev_name): event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, skbaddr, skblen, dev_name) all_event_list.append(event_info) -def net__net_dev_xmit(name, context, cpu, sec, nsec, pid, comm, +def net__net_dev_xmit(name, context, cpu, sec, nsec, pid, comm, callchain, skbaddr, skblen, rc, dev_name): event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, skbaddr, skblen, rc ,dev_name) all_event_list.append(event_info) -def skb__kfree_skb(name, context, cpu, sec, nsec, pid, comm, +def skb__kfree_skb(name, context, cpu, sec, nsec, pid, comm, callchain, skbaddr, protocol, location): event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, skbaddr, protocol, location) all_event_list.append(event_info) -def skb__consume_skb(name, context, cpu, sec, nsec, pid, comm, skbaddr): +def skb__consume_skb(name, context, cpu, sec, nsec, pid, comm, callchain, skbaddr): event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, skbaddr) all_event_list.append(event_info) -def skb__skb_copy_datagram_iovec(name, context, cpu, sec, nsec, pid, comm, +def skb__skb_copy_datagram_iovec(name, context, cpu, sec, nsec, pid, comm, callchain, skbaddr, skblen): event_info = (name, context, cpu, nsecs(sec, nsec), pid, comm, skbaddr, skblen) diff --git a/tools/perf/scripts/python/sched-migration.py b/tools/perf/scripts/python/sched-migration.py index 74d55ec..de66cb3 100644 --- a/tools/perf/scripts/python/sched-migration.py +++ b/tools/perf/scripts/python/sched-migration.py @@ -369,93 +369,92 @@ def trace_end(): def sched__sched_stat_runtime(event_name, context, common_cpu, common_secs, common_nsecs, common_pid, common_comm, - comm, pid, runtime, vruntime): + common_callchain, comm, pid, runtime, vruntime): pass def sched__sched_stat_iowait(event_name, context, common_cpu, common_secs, common_nsecs, common_pid, common_comm, - comm, pid, delay): + common_callchain, comm, pid, delay): pass def sched__sched_stat_sleep(event_name, context, common_cpu, common_secs, common_nsecs, common_pid, common_comm, - comm, pid, delay): + common_callchain, comm, pid, delay): pass def sched__sched_stat_wait(event_name, context, common_cpu, common_secs, common_nsecs, common_pid, common_comm, - comm, pid, delay): + common_callchain, comm, pid, delay): pass def sched__sched_process_fork(event_name, context, common_cpu, common_secs, common_nsecs, common_pid, common_comm, - parent_comm, parent_pid, child_comm, child_pid): + common_callchain, parent_comm, parent_pid, child_comm, child_pid): pass def sched__sched_process_wait(event_name, context, common_cpu, common_secs, common_nsecs, common_pid, common_comm, - comm, pid, prio): + common_callchain, comm, pid, prio): pass def sched__sched_process_exit(event_name, context, common_cpu, common_secs, common_nsecs, common_pid, common_comm, - comm, pid, prio): + common_callchain, comm, pid, prio): pass def sched__sched_process_free(event_name, context, common_cpu, common_secs, common_nsecs, common_pid, common_comm, - comm, pid, prio): + common_callchain, comm, pid, prio): pass def sched__sched_migrate_task(event_name, context, common_cpu, common_secs, common_nsecs, common_pid, common_comm, - comm, pid, prio, orig_cpu, + common_callchain, comm, pid, prio, orig_cpu, dest_cpu): headers = EventHeaders(common_cpu, common_secs, common_nsecs, - common_pid, common_comm) + common_pid, common_comm, common_callchain) parser.migrate(headers, pid, prio, orig_cpu, dest_cpu) def sched__sched_switch(event_name, context, common_cpu, - common_secs, common_nsecs, common_pid, common_comm, + common_secs, common_nsecs, common_pid, common_comm, common_callchain, prev_comm, prev_pid, prev_prio, prev_state, next_comm, next_pid, next_prio): headers = EventHeaders(common_cpu, common_secs, common_nsecs, - common_pid, common_comm) + common_pid, common_comm, common_callchain) parser.sched_switch(headers, prev_comm, prev_pid, prev_prio, prev_state, next_comm, next_pid, next_prio) def sched__sched_wakeup_new(event_name, context, common_cpu, common_secs, common_nsecs, common_pid, common_comm, - comm, pid, prio, success, + common_callchain, comm, pid, prio, success, target_cpu): headers = EventHeaders(common_cpu, common_secs, common_nsecs, - common_pid, common_comm) + common_pid, common_comm, common_callchain) parser.wake_up(headers, comm, pid, success, target_cpu, 1) def sched__sched_wakeup(event_name, context, common_cpu, common_secs, common_nsecs, common_pid, common_comm, - comm, pid, prio, success, + common_callchain, comm, pid, prio, success, target_cpu): headers = EventHeaders(common_cpu, common_secs, common_nsecs, - common_pid, common_comm) + common_pid, common_comm, common_callchain) parser.wake_up(headers, comm, pid, success, target_cpu, 0) def sched__sched_wait_task(event_name, context, common_cpu, common_secs, common_nsecs, common_pid, common_comm, - comm, pid, prio): + common_callchain, comm, pid, prio): pass def sched__sched_kthread_stop_ret(event_name, context, common_cpu, common_secs, common_nsecs, common_pid, common_comm, - ret): + common_callchain, ret): pass def sched__sched_kthread_stop(event_name, context, common_cpu, common_secs, common_nsecs, common_pid, common_comm, - comm, pid): + common_callchain, comm, pid): pass -def trace_unhandled(event_name, context, common_cpu, common_secs, common_nsecs, - common_pid, common_comm): +def trace_unhandled(event_name, context, event_fields_dict): pass diff --git a/tools/perf/scripts/python/sctop.py b/tools/perf/scripts/python/sctop.py index c9f3058..61621b9 100644 --- a/tools/perf/scripts/python/sctop.py +++ b/tools/perf/scripts/python/sctop.py @@ -44,7 +44,7 @@ def trace_begin(): def raw_syscalls__sys_enter(event_name, context, common_cpu, common_secs, common_nsecs, common_pid, common_comm, - id, args): + common_callchain, id, args): if for_comm is not None: if common_comm != for_comm: return diff --git a/tools/perf/scripts/python/syscall-counts-by-pid.py b/tools/perf/scripts/python/syscall-counts-by-pid.py index cf2054c..daf314c 100644 --- a/tools/perf/scripts/python/syscall-counts-by-pid.py +++ b/tools/perf/scripts/python/syscall-counts-by-pid.py @@ -38,7 +38,7 @@ def trace_end(): def raw_syscalls__sys_enter(event_name, context, common_cpu, common_secs, common_nsecs, common_pid, common_comm, - id, args): + common_callchain, id, args): if (for_comm and common_comm != for_comm) or \ (for_pid and common_pid != for_pid ): diff --git a/tools/perf/scripts/python/syscall-counts.py b/tools/perf/scripts/python/syscall-counts.py index 92b2938..e66a773 100644 --- a/tools/perf/scripts/python/syscall-counts.py +++ b/tools/perf/scripts/python/syscall-counts.py @@ -35,7 +35,7 @@ def trace_end(): def raw_syscalls__sys_enter(event_name, context, common_cpu, common_secs, common_nsecs, common_pid, common_comm, - id, args): + common_callchain, id, args): if for_comm is not None: if common_comm != for_comm: return diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index b6c1a69..cf65404 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -32,6 +32,7 @@ #include "../event.h" #include "../thread.h" #include "../trace-event.h" +#include "../machine.h" PyMODINIT_FUNC initperf_trace_context(void); @@ -278,12 +279,90 @@ static PyObject *get_field_numeric_entry(struct event_format *event, return obj; } + +static PyObject *python_process_callchain(struct perf_sample *sample, + struct perf_evsel *evsel, + struct addr_location *al) +{ + PyObject *pylist; + + pylist = PyList_New(0); + if (!pylist) + Py_FatalError("couldn't create Python list"); + + if (!symbol_conf.use_callchain || !sample->callchain) + goto exit; + + if (machine__resolve_callchain(al->machine, evsel, al->thread, + sample, NULL, NULL, + PERF_MAX_STACK_DEPTH) != 0) { + pr_err("Failed to resolve callchain. Skipping\n"); + goto exit; + } + callchain_cursor_commit(&callchain_cursor); + + + while (1) { + PyObject *pyelem; + struct callchain_cursor_node *node; + node = callchain_cursor_current(&callchain_cursor); + if (!node) + break; + + pyelem = PyDict_New(); + if (!pyelem) + Py_FatalError("couldn't create Python dictionary"); + + + pydict_set_item_string_decref(pyelem, "ip", + PyLong_FromUnsignedLongLong(node->ip)); + + if (node->sym) { + PyObject *pysym = PyDict_New(); + if (!pysym) + Py_FatalError("couldn't create Python dictionary"); + pydict_set_item_string_decref(pysym, "start", + PyLong_FromUnsignedLongLong(node->sym->start)); + pydict_set_item_string_decref(pysym, "end", + PyLong_FromUnsignedLongLong(node->sym->end)); + pydict_set_item_string_decref(pysym, "binding", + PyInt_FromLong(node->sym->binding)); + pydict_set_item_string_decref(pysym, "name", + PyString_FromStringAndSize(node->sym->name, + node->sym->namelen)); + pydict_set_item_string_decref(pyelem, "sym", pysym); + } + + if (node->map) { + struct map *map = node->map; + const char *dsoname = "[unknown]"; + if (map && map->dso && (map->dso->name || map->dso->long_name)) { + if (symbol_conf.show_kernel_path && map->dso->long_name) + dsoname = map->dso->long_name; + else if (map->dso->name) + dsoname = map->dso->name; + } + pydict_set_item_string_decref(pyelem, "dso", + PyString_FromString(dsoname)); + } + + callchain_cursor_advance(&callchain_cursor); + PyList_Append(pylist, pyelem); + Py_DECREF(pyelem); + } + +exit: + return pylist; +} + + static void python_process_tracepoint(struct perf_sample *sample, struct perf_evsel *evsel, struct thread *thread, struct addr_location *al) { - PyObject *handler, *retval, *context, *t, *obj, *dict = NULL; + PyObject *handler, *retval, *context, *t, *obj, *callchain; + PyObject *dict = NULL; static char handler_name[256]; struct format_field *field; unsigned long s, ns; @@ -326,18 +405,23 @@ static void python_process_tracepoint(struct perf_sample *sample, PyTuple_SetItem(t, n++, PyString_FromString(handler_name)); PyTuple_SetItem(t, n++, context); + /* ip unwinding */ + callchain = python_process_callchain(sample, evsel, al); + if (handler) { PyTuple_SetItem(t, n++, PyInt_FromLong(cpu)); PyTuple_SetItem(t, n++, PyInt_FromLong(s)); PyTuple_SetItem(t, n++, PyInt_FromLong(ns)); PyTuple_SetItem(t, n++, PyInt_FromLong(pid)); PyTuple_SetItem(t, n++, PyString_FromString(comm)); + PyTuple_SetItem(t, n++, callchain); } else { pydict_set_item_string_decref(dict, "common_cpu", PyInt_FromLong(cpu)); pydict_set_item_string_decref(dict, "common_s", PyInt_FromLong(s)); pydict_set_item_string_decref(dict, "common_ns", PyInt_FromLong(ns)); pydict_set_item_string_decref(dict, "common_pid", PyInt_FromLong(pid)); pydict_set_item_string_decref(dict, "common_comm", PyString_FromString(comm)); + pydict_set_item_string_decref(dict, "common_callchain", callchain); } for (field = event->format.fields; field; field = field->next) { if (field->flags & FIELD_IS_STRING) { @@ -357,6 +441,7 @@ static void python_process_tracepoint(struct perf_sample *sample, pydict_set_item_string_decref(dict, field->name, obj); } + if (!handler) PyTuple_SetItem(t, n++, dict); @@ -388,7 +473,7 @@ static void python_process_general_event(struct perf_sample *sample, struct thread *thread, struct addr_location *al) { - PyObject *handler, *retval, *t, *dict; + PyObject *handler, *retval, *t, *dict, *callchain; static char handler_name[64]; unsigned n = 0; @@ -428,6 +513,10 @@ static void python_process_general_event(struct perf_sample *sample, PyString_FromString(al->sym->name)); } + /* ip unwinding */ + callchain = python_process_callchain(sample, evsel, al); + pydict_set_item_string_decref(dict, "callchain", callchain); + PyTuple_SetItem(t, n++, dict); if (_PyTuple_Resize(&t, n) == -1) Py_FatalError("error resizing Python tuple"); @@ -624,6 +713,7 @@ static int python_generate_script(struct pevent *pevent, const char *outfile) fprintf(ofp, "common_nsecs, "); fprintf(ofp, "common_pid, "); fprintf(ofp, "common_comm,\n\t"); + fprintf(ofp, "common_callchain, "); not_first = 0; count = 0; @@ -667,7 +757,7 @@ static int python_generate_script(struct pevent *pevent, const char *outfile) fprintf(ofp, "%%u"); } - fprintf(ofp, "\\n\" %% \\\n\t\t("); + fprintf(ofp, "\" %% \\\n\t\t("); not_first = 0; count = 0; @@ -703,7 +793,15 @@ static int python_generate_script(struct pevent *pevent, const char *outfile) fprintf(ofp, "%s", f->name); } - fprintf(ofp, "),\n\n"); + fprintf(ofp, ")\n\n"); + + fprintf(ofp, "\t\tfor node in common_callchain:"); + fprintf(ofp, "\n\t\t\tif 'sym' in node:"); + fprintf(ofp, "\n\t\t\t\tprint \"\\t[%%x] %%s\" %% (node['ip'], node['sym']['name'])"); + fprintf(ofp, "\n\t\t\telse:"); + fprintf(ofp, "\n\t\t\t\tprint \"\t[%%x]\" %% (node['ip'])\n\n"); + fprintf(ofp, "\t\tprint \"\\n\"\n\n"); + } fprintf(ofp, "def trace_unhandled(event_name, context, " -- cgit v1.1 From 57608cfd8827a74237d264a197722e2c99f72da4 Mon Sep 17 00:00:00 2001 From: Joseph Schuchart Date: Thu, 10 Jul 2014 13:50:56 +0200 Subject: perf script: Provide additional sample information on generic events To python scripts, including pid, tid, and cpu for which the event was recorded. At the moment, the pointer to the sample struct is passed to scripts, which seems to be of little use. The patch puts this information in dictionaries for easy access by Python scripts. Signed-off-by: Joseph Schuchart Acked-by: Thomas Ilsche Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Thomas Ilsche Link: http://lkml.kernel.org/r/53BE7E20.8080500@tu-dresden.de Signed-off-by: Arnaldo Carvalho de Melo --- .../util/scripting-engines/trace-event-python.c | 23 +++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index cf65404..b366b48 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -473,7 +473,7 @@ static void python_process_general_event(struct perf_sample *sample, struct thread *thread, struct addr_location *al) { - PyObject *handler, *retval, *t, *dict, *callchain; + PyObject *handler, *retval, *t, *dict, *callchain, *dict_sample; static char handler_name[64]; unsigned n = 0; @@ -489,6 +489,10 @@ static void python_process_general_event(struct perf_sample *sample, if (!dict) Py_FatalError("couldn't create Python dictionary"); + dict_sample = PyDict_New(); + if (!dict_sample) + Py_FatalError("couldn't create Python dictionary"); + snprintf(handler_name, sizeof(handler_name), "%s", "process_event"); handler = PyDict_GetItemString(main_dict, handler_name); @@ -498,8 +502,21 @@ static void python_process_general_event(struct perf_sample *sample, pydict_set_item_string_decref(dict, "ev_name", PyString_FromString(perf_evsel__name(evsel))); pydict_set_item_string_decref(dict, "attr", PyString_FromStringAndSize( (const char *)&evsel->attr, sizeof(evsel->attr))); - pydict_set_item_string_decref(dict, "sample", PyString_FromStringAndSize( - (const char *)sample, sizeof(*sample))); + + pydict_set_item_string_decref(dict_sample, "pid", + PyInt_FromLong(sample->pid)); + pydict_set_item_string_decref(dict_sample, "tid", + PyInt_FromLong(sample->tid)); + pydict_set_item_string_decref(dict_sample, "cpu", + PyInt_FromLong(sample->cpu)); + pydict_set_item_string_decref(dict_sample, "ip", + PyLong_FromUnsignedLongLong(sample->ip)); + pydict_set_item_string_decref(dict_sample, "time", + PyLong_FromUnsignedLongLong(sample->time)); + pydict_set_item_string_decref(dict_sample, "period", + PyLong_FromUnsignedLongLong(sample->period)); + pydict_set_item_string_decref(dict, "sample", dict_sample); + pydict_set_item_string_decref(dict, "raw_buf", PyString_FromStringAndSize( (const char *)sample->raw_data, sample->raw_size)); pydict_set_item_string_decref(dict, "comm", -- cgit v1.1 From 1fcb8768636d38cb6fdfeef83a5ee596c4bd9c56 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 14 Jul 2014 13:02:25 +0300 Subject: perf machine: Fix the value used for unknown pids The value used for unknown pids cannot be zero because that is used by the "idle" task. Use -1 instead. Also handle the unknown pid case when creating map groups. Note that, threads with an unknown pid should not occur because fork (or synthesized) events precede the thread's existence. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1405332185-4050-2-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-sched.c | 12 ++++++------ tools/perf/util/machine.c | 6 +++--- tools/perf/util/session.c | 5 +++-- tools/perf/util/thread.c | 2 +- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index c38d06c..b7f555a 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -935,8 +935,8 @@ static int latency_switch_event(struct perf_sched *sched, return -1; } - sched_out = machine__findnew_thread(machine, 0, prev_pid); - sched_in = machine__findnew_thread(machine, 0, next_pid); + sched_out = machine__findnew_thread(machine, -1, prev_pid); + sched_in = machine__findnew_thread(machine, -1, next_pid); out_events = thread_atoms_search(&sched->atom_root, sched_out, &sched->cmp_pid); if (!out_events) { @@ -979,7 +979,7 @@ static int latency_runtime_event(struct perf_sched *sched, { const u32 pid = perf_evsel__intval(evsel, sample, "pid"); const u64 runtime = perf_evsel__intval(evsel, sample, "runtime"); - struct thread *thread = machine__findnew_thread(machine, 0, pid); + struct thread *thread = machine__findnew_thread(machine, -1, pid); struct work_atoms *atoms = thread_atoms_search(&sched->atom_root, thread, &sched->cmp_pid); u64 timestamp = sample->time; int cpu = sample->cpu; @@ -1012,7 +1012,7 @@ static int latency_wakeup_event(struct perf_sched *sched, struct thread *wakee; u64 timestamp = sample->time; - wakee = machine__findnew_thread(machine, 0, pid); + wakee = machine__findnew_thread(machine, -1, pid); atoms = thread_atoms_search(&sched->atom_root, wakee, &sched->cmp_pid); if (!atoms) { if (thread_atoms_insert(sched, wakee)) @@ -1072,7 +1072,7 @@ static int latency_migrate_task_event(struct perf_sched *sched, if (sched->profile_cpu == -1) return 0; - migrant = machine__findnew_thread(machine, 0, pid); + migrant = machine__findnew_thread(machine, -1, pid); atoms = thread_atoms_search(&sched->atom_root, migrant, &sched->cmp_pid); if (!atoms) { if (thread_atoms_insert(sched, migrant)) @@ -1290,7 +1290,7 @@ static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel, return -1; } - sched_in = machine__findnew_thread(machine, 0, next_pid); + sched_in = machine__findnew_thread(machine, -1, next_pid); sched->curr_thread[this_cpu] = sched_in; diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index e9b943a..5b80877 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -34,7 +34,7 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid) return -ENOMEM; if (pid != HOST_KERNEL_ID) { - struct thread *thread = machine__findnew_thread(machine, 0, + struct thread *thread = machine__findnew_thread(machine, -1, pid); char comm[64]; @@ -286,7 +286,7 @@ static struct thread *__machine__findnew_thread(struct machine *machine, * the full rbtree: */ if (machine->last_match && machine->last_match->tid == tid) { - if (pid && pid != machine->last_match->pid_) + if (pid != -1 && pid != machine->last_match->pid_) machine->last_match->pid_ = pid; return machine->last_match; } @@ -297,7 +297,7 @@ static struct thread *__machine__findnew_thread(struct machine *machine, if (th->tid == tid) { machine->last_match = th; - if (pid && pid != th->pid_) + if (pid != -1 && pid != th->pid_) th->pid_ = pid; return th; } diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 64a186e..c2f4ca9 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1083,13 +1083,14 @@ void perf_event_header__bswap(struct perf_event_header *hdr) struct thread *perf_session__findnew(struct perf_session *session, pid_t pid) { - return machine__findnew_thread(&session->machines.host, 0, pid); + return machine__findnew_thread(&session->machines.host, -1, pid); } static struct thread *perf_session__register_idle_thread(struct perf_session *session) { - struct thread *thread = perf_session__findnew(session, 0); + struct thread *thread; + thread = machine__findnew_thread(&session->machines.host, 0, 0); if (thread == NULL || thread__set_comm(thread, "swapper", 0)) { pr_err("problem inserting idle task.\n"); thread = NULL; diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 2fde0d5..7a32f44 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -13,7 +13,7 @@ int thread__init_map_groups(struct thread *thread, struct machine *machine) struct thread *leader; pid_t pid = thread->pid_; - if (pid == thread->tid) { + if (pid == thread->tid || pid == -1) { thread->mg = map_groups__new(); } else { leader = machine__findnew_thread(machine, pid, pid); -- cgit v1.1 From 022c50d09c2c2bc31506ad16c4bcba7fb418ce34 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 14 Jul 2014 13:02:27 +0300 Subject: perf script: Display PERF_RECORD_MISC_COMM_EXEC flag Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1405332185-4050-4-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 9 ++++++++- tools/perf/util/evsel.c | 3 ++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index d0281bd..198c4cc 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -603,7 +603,14 @@ int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp) { - return fprintf(fp, ": %s:%d\n", event->comm.comm, event->comm.tid); + const char *s; + + if (event->header.misc & PERF_RECORD_MISC_COMM_EXEC) + s = " exec"; + else + s = ""; + + return fprintf(fp, "%s: %s:%d\n", s, event->comm.comm, event->comm.tid); } int perf_event__process_comm(struct perf_tool *tool __maybe_unused, diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 8606175..b760d32 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -960,6 +960,7 @@ static size_t perf_event_attr__fprintf(struct perf_event_attr *attr, FILE *fp) ret += PRINT_ATTR2(exclude_user, exclude_kernel); ret += PRINT_ATTR2(exclude_hv, exclude_idle); ret += PRINT_ATTR2(mmap, comm); + ret += PRINT_ATTR2(mmap2, comm_exec); ret += PRINT_ATTR2(freq, inherit_stat); ret += PRINT_ATTR2(enable_on_exec, task); ret += PRINT_ATTR2(watermark, precise_ip); @@ -967,7 +968,6 @@ static size_t perf_event_attr__fprintf(struct perf_event_attr *attr, FILE *fp) ret += PRINT_ATTR2(exclude_host, exclude_guest); ret += PRINT_ATTR2N("excl.callchain_kern", exclude_callchain_kernel, "excl.callchain_user", exclude_callchain_user); - ret += PRINT_ATTR_U32(mmap2); ret += PRINT_ATTR_U32(wakeup_events); ret += PRINT_ATTR_U32(wakeup_watermark); @@ -1940,6 +1940,7 @@ int perf_evsel__fprintf(struct perf_evsel *evsel, if_print(mmap); if_print(mmap2); if_print(comm); + if_print(comm_exec); if_print(freq); if_print(inherit_stat); if_print(enable_on_exec); -- cgit v1.1 From 39e09d40bea440d9cfe645b55aff251294318669 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 14 Jul 2014 13:02:28 +0300 Subject: perf record: Select comm_exec flag if supported The comm_exec flag on the attribute can later be found in the perf.data file allowing a tool to know in advance if the captured data has the flag. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1405332185-4050-5-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/record.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/record.c b/tools/perf/util/record.c index 049e0a0..1657231 100644 --- a/tools/perf/util/record.c +++ b/tools/perf/util/record.c @@ -69,15 +69,26 @@ static void perf_probe_sample_identifier(struct perf_evsel *evsel) evsel->attr.sample_type |= PERF_SAMPLE_IDENTIFIER; } +static void perf_probe_comm_exec(struct perf_evsel *evsel) +{ + evsel->attr.comm_exec = 1; +} + bool perf_can_sample_identifier(void) { return perf_probe_api(perf_probe_sample_identifier); } +static bool perf_can_comm_exec(void) +{ + return perf_probe_api(perf_probe_comm_exec); +} + void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts) { struct perf_evsel *evsel; bool use_sample_identifier = false; + bool use_comm_exec; /* * Set the evsel leader links before we configure attributes, @@ -89,8 +100,13 @@ void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts) if (evlist->cpus->map[0] < 0) opts->no_inherit = true; - evlist__for_each(evlist, evsel) + use_comm_exec = perf_can_comm_exec(); + + evlist__for_each(evlist, evsel) { perf_evsel__config(evsel, opts); + if (!evsel->idx && use_comm_exec) + evsel->attr.comm_exec = 1; + } if (evlist->nr_entries > 1) { struct perf_evsel *first = perf_evlist__first(evlist); -- cgit v1.1 From 1f2a7069b6e895487b1d9229f5f62799cc4ea0aa Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 14 Jul 2014 13:02:31 +0300 Subject: perf tools: Fix missing kernel map load thread__find_addr_map() falls back to trying the kernel maps if the address is negative and is not found in userspace maps. As commented in the code, the kernel maps must be "loaded" before use. This patch ensures that happens under the fallback condition also. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1405332185-4050-8-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/event.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 198c4cc..7e0e8ae 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -788,6 +788,7 @@ try_again: cpumode == PERF_RECORD_MISC_USER && machine && mg != &machine->kmaps) { mg = &machine->kmaps; + load_map = true; goto try_again; } } else { -- cgit v1.1 From a2f3b6bf0adadcb5f9383c60aa1355e0f9cba3da Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 14 Jul 2014 13:02:33 +0300 Subject: perf symbols: Fix missing GNU IFUNC symbols Symbols of type STT_GNU_IFUNC are functions so accept them as such. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1405332185-4050-10-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol-elf.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 6864661..dce5ccf 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -49,7 +49,8 @@ static inline uint8_t elf_sym__type(const GElf_Sym *sym) static inline int elf_sym__is_function(const GElf_Sym *sym) { - return elf_sym__type(sym) == STT_FUNC && + return (elf_sym__type(sym) == STT_FUNC || + elf_sym__type(sym) == STT_GNU_IFUNC) && sym->st_name != 0 && sym->st_shndx != SHN_UNDEF; } -- cgit v1.1 From e38b43c3f3fd8ebe6f558400d1647a923bc19d44 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 14 Jul 2014 13:02:34 +0300 Subject: perf inject: Fix build id injection Build Ids won't be injected unless the build id feature flag is set. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1405332185-4050-11-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-inject.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 16c7c11..cf6a605 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -389,6 +389,9 @@ static int __cmd_inject(struct perf_inject *inject) ret = perf_session__process_events(session, &inject->tool); if (!file_out->is_pipe) { + if (inject->build_ids) + perf_header__set_feat(&session->header, + HEADER_BUILD_ID); session->header.data_size = inject->bytes_written; perf_session__write_header(session, session->evlist, file_out->fd, true); } -- cgit v1.1 From 4d40b051b1ac41ecbc818deed27750b4c1697520 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 14 Jul 2014 13:02:35 +0300 Subject: perf callchain: Fix appending a callchain from a previous sample hist_entry__append_callchain() must check if the sample has a callcahin or it will append the callchain from a previous sample. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1405332185-4050-12-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/callchain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 48b6d3f..437ee09 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -626,7 +626,7 @@ int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *sample) { - if (!symbol_conf.use_callchain) + if (!symbol_conf.use_callchain || sample->callchain == NULL) return 0; return callchain_append(he->callchain, &callchain_cursor, sample->period); } -- cgit v1.1 From 5173fbb8a11b2857aeec9e5f4e9568d4e1b84dbd Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 14 Jul 2014 13:02:38 +0300 Subject: perf buildid-cache: Apply force option to copying kcore Currently a copy of kcore is not made if there is one already with the same modules at the same addresses. Change this to make a copy anyway if the force (-f) option is also used. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1405332185-4050-15-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-buildid-cache.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c index b22dbb1..2a2c78f 100644 --- a/tools/perf/builtin-buildid-cache.c +++ b/tools/perf/builtin-buildid-cache.c @@ -125,7 +125,8 @@ static int build_id_cache__kcore_existing(const char *from_dir, char *to_dir, return ret; } -static int build_id_cache__add_kcore(const char *filename, const char *debugdir) +static int build_id_cache__add_kcore(const char *filename, const char *debugdir, + bool force) { char dir[32], sbuildid[BUILD_ID_SIZE * 2 + 1]; char from_dir[PATH_MAX], to_dir[PATH_MAX]; @@ -144,7 +145,8 @@ static int build_id_cache__add_kcore(const char *filename, const char *debugdir) scnprintf(to_dir, sizeof(to_dir), "%s/[kernel.kcore]/%s", debugdir, sbuildid); - if (!build_id_cache__kcore_existing(from_dir, to_dir, sizeof(to_dir))) { + if (!force && + !build_id_cache__kcore_existing(from_dir, to_dir, sizeof(to_dir))) { pr_debug("same kcore found in %s\n", to_dir); return 0; } @@ -389,7 +391,7 @@ int cmd_buildid_cache(int argc, const char **argv, } if (kcore_filename && - build_id_cache__add_kcore(kcore_filename, debugdir)) + build_id_cache__add_kcore(kcore_filename, debugdir, force)) pr_warning("Couldn't add %s\n", kcore_filename); return ret; -- cgit v1.1 From c6d8f2a4a0c5e366330a6a2a94c06b652f4ca554 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 14 Jul 2014 13:02:41 +0300 Subject: perf symbols: Record whether a dso is 64-bit Add a flag to 'struct dso' to record if the dso is 64-bit or not. Update the flag when reading the ELF. This is needed for instruction decoding. For example, x86 instruction decoding depends on whether or not the 64-bit instruction set is used. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1405332185-4050-18-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/dso.c | 1 + tools/perf/util/dso.h | 1 + tools/perf/util/symbol-elf.c | 3 +++ tools/perf/util/symbol-minimal.c | 22 ++++++++++++++++++++++ tools/perf/util/symbol.c | 1 + tools/perf/util/symbol.h | 1 + 6 files changed, 29 insertions(+) diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 819f104..fc006fe 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -703,6 +703,7 @@ struct dso *dso__new(const char *name) dso->data.fd = -1; dso->symtab_type = DSO_BINARY_TYPE__NOT_FOUND; dso->binary_type = DSO_BINARY_TYPE__NOT_FOUND; + dso->is_64_bit = (sizeof(void *) == 8); dso->loaded = 0; dso->rel = 0; dso->sorted_by_name = 0; diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index ad553ba..c239e86 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h @@ -90,6 +90,7 @@ struct dso { u8 annotate_warned:1; u8 short_name_allocated:1; u8 long_name_allocated:1; + u8 is_64_bit:1; u8 sorted_by_name; u8 loaded; u8 rel; diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index dce5ccf..cef8f42 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -599,6 +599,8 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name, goto out_elf_end; } + ss->is_64_bit = (gelf_getclass(elf) == ELFCLASS64); + ss->symtab = elf_section_by_name(elf, &ehdr, &ss->symshdr, ".symtab", NULL); if (ss->symshdr.sh_type != SHT_SYMTAB) @@ -699,6 +701,7 @@ int dso__load_sym(struct dso *dso, struct map *map, bool remap_kernel = false, adjust_kernel_syms = false; dso->symtab_type = syms_ss->type; + dso->is_64_bit = syms_ss->is_64_bit; dso->rel = syms_ss->ehdr.e_type == ET_REL; /* diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c index bd15f49..101f55d 100644 --- a/tools/perf/util/symbol-minimal.c +++ b/tools/perf/util/symbol-minimal.c @@ -288,6 +288,23 @@ int dso__synthesize_plt_symbols(struct dso *dso __maybe_unused, return 0; } +static int fd__is_64_bit(int fd) +{ + u8 e_ident[EI_NIDENT]; + + if (lseek(fd, 0, SEEK_SET)) + return -1; + + if (readn(fd, e_ident, sizeof(e_ident)) != sizeof(e_ident)) + return -1; + + if (memcmp(e_ident, ELFMAG, SELFMAG) || + e_ident[EI_VERSION] != EV_CURRENT) + return -1; + + return e_ident[EI_CLASS] == ELFCLASS64; +} + int dso__load_sym(struct dso *dso, struct map *map __maybe_unused, struct symsrc *ss, struct symsrc *runtime_ss __maybe_unused, @@ -295,6 +312,11 @@ int dso__load_sym(struct dso *dso, struct map *map __maybe_unused, int kmodule __maybe_unused) { unsigned char *build_id[BUILD_ID_SIZE]; + int ret; + + ret = fd__is_64_bit(ss->fd); + if (ret >= 0) + dso->is_64_bit = ret; if (filename__read_build_id(ss->name, build_id, BUILD_ID_SIZE) > 0) { dso__set_build_id(dso, build_id); diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 2e6a2e2..ae2e446 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1065,6 +1065,7 @@ static int dso__load_kcore(struct dso *dso, struct map *map, &is_64_bit); if (err) goto out_err; + dso->is_64_bit = is_64_bit; if (list_empty(&md.maps)) { err = -EINVAL; diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index a81877b..436169d 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -216,6 +216,7 @@ struct symsrc { GElf_Shdr dynshdr; bool adjust_symbols; + bool is_64_bit; #endif }; -- cgit v1.1 From bdac0bcf779250e89b96d4a3f381ebaf02c2f4a9 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 14 Jul 2014 13:02:43 +0300 Subject: perf symbols: Do not attempt to read data from kallsyms Record kallsyms binary type so that tools will not attempt to read binary data from it. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1405332185-4050-20-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index ae2e446..156ae36 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -1664,6 +1664,7 @@ do_kallsyms: free(kallsyms_allocated_filename); if (err > 0 && !dso__is_kcore(dso)) { + dso->binary_type = DSO_BINARY_TYPE__KALLSYMS; dso__set_long_name(dso, "[kernel.kallsyms]", false); map__fixup_start(map); map__fixup_end(map); @@ -1711,6 +1712,7 @@ static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map, if (err > 0) pr_debug("Using %s for symbols\n", kallsyms_filename); if (err > 0 && !dso__is_kcore(dso)) { + dso->binary_type = DSO_BINARY_TYPE__GUEST_KALLSYMS; machine__mmap_name(machine, path, sizeof(path)); dso__set_long_name(dso, strdup(path), true); map__fixup_start(map); -- cgit v1.1 From 9c00a81b6aafc4ed375a43e7a54e6cf2d720c7c6 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 14 Jul 2014 13:02:50 +0300 Subject: perf symbols: Add ability to iterate over a dso's symbols Expose dso__first_symbol() and dso__next_symbol() to make it possible to iterate over a dso's symbols. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1405332185-4050-27-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/symbol.c | 17 ++++++++++++++++- tools/perf/util/symbol.h | 3 +++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 156ae36..eb06746 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -342,6 +342,16 @@ static struct symbol *symbols__first(struct rb_root *symbols) return NULL; } +static struct symbol *symbols__next(struct symbol *sym) +{ + struct rb_node *n = rb_next(&sym->rb_node); + + if (n) + return rb_entry(n, struct symbol, rb_node); + + return NULL; +} + struct symbol_name_rb_node { struct rb_node rb_node; struct symbol sym; @@ -412,11 +422,16 @@ struct symbol *dso__find_symbol(struct dso *dso, return symbols__find(&dso->symbols[type], addr); } -static struct symbol *dso__first_symbol(struct dso *dso, enum map_type type) +struct symbol *dso__first_symbol(struct dso *dso, enum map_type type) { return symbols__first(&dso->symbols[type]); } +struct symbol *dso__next_symbol(struct symbol *sym) +{ + return symbols__next(sym); +} + struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type, const char *name) { diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 436169d..ee2d3cc 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -240,6 +240,9 @@ struct symbol *dso__find_symbol(struct dso *dso, enum map_type type, struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type, const char *name); +struct symbol *dso__first_symbol(struct dso *dso, enum map_type type); +struct symbol *dso__next_symbol(struct symbol *sym); + int filename__read_build_id(const char *filename, void *bf, size_t size); int sysfs__read_build_id(const char *filename, void *bf, size_t size); int modules__parse(const char *filename, void *arg, -- cgit v1.1 From 919d86d3a3109d7d4f0d7347f34711ee2f8e6609 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 14 Jul 2014 13:02:51 +0300 Subject: perf session: Flag if the event stream is entirely in memory Flag if the event stream is a file that has been mmapped in one go. This is useful, for example, if a tool needs to keep an event for later reference. If the new flag is set, a pointer to the event can be retained, otherwise the event must be copied. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1405332185-4050-28-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 9 ++++++++- tools/perf/util/session.h | 3 +++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index c2f4ca9..eac14ce 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1297,8 +1297,10 @@ int __perf_session__process_events(struct perf_session *session, ui_progress__init(&prog, file_size, "Processing events..."); mmap_size = MMAP_SIZE; - if (mmap_size > file_size) + if (mmap_size > file_size) { mmap_size = file_size; + session->one_mmap = true; + } memset(mmaps, 0, sizeof(mmaps)); @@ -1320,6 +1322,10 @@ remap: mmaps[map_idx] = buf; map_idx = (map_idx + 1) & (ARRAY_SIZE(mmaps) - 1); file_pos = file_offset + head; + if (session->one_mmap) { + session->one_mmap_addr = buf; + session->one_mmap_offset = file_offset; + } more: event = fetch_mmaped_event(session, head, mmap_size, buf); @@ -1365,6 +1371,7 @@ out_err: ui_progress__finish(); perf_session__warn_about_errors(session, tool); perf_session_free_sample_buffers(session); + session->one_mmap = false; return err; } diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 3140f8a..0321013 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -36,6 +36,9 @@ struct perf_session { struct trace_event tevent; struct events_stats stats; bool repipe; + bool one_mmap; + void *one_mmap_addr; + u64 one_mmap_offset; struct ordered_samples ordered_samples; struct perf_data_file *file; }; -- cgit v1.1 From a8a8f3eb5de55aeaf007c18572668e8ec463547b Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 14 Jul 2014 13:02:52 +0300 Subject: perf evlist: Pass mmap parameters in a struct In preparation for adding more mmap parameters, pass existing parameters in a struct. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1405332185-4050-29-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 46 ++++++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index c51223a..814e954 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -606,12 +606,17 @@ static int perf_evlist__alloc_mmap(struct perf_evlist *evlist) return evlist->mmap != NULL ? 0 : -ENOMEM; } -static int __perf_evlist__mmap(struct perf_evlist *evlist, - int idx, int prot, int mask, int fd) +struct mmap_params { + int prot; + int mask; +}; + +static int __perf_evlist__mmap(struct perf_evlist *evlist, int idx, + struct mmap_params *mp, int fd) { evlist->mmap[idx].prev = 0; - evlist->mmap[idx].mask = mask; - evlist->mmap[idx].base = mmap(NULL, evlist->mmap_len, prot, + evlist->mmap[idx].mask = mp->mask; + evlist->mmap[idx].base = mmap(NULL, evlist->mmap_len, mp->prot, MAP_SHARED, fd, 0); if (evlist->mmap[idx].base == MAP_FAILED) { pr_debug2("failed to mmap perf event ring buffer, error %d\n", @@ -625,8 +630,8 @@ static int __perf_evlist__mmap(struct perf_evlist *evlist, } static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx, - int prot, int mask, int cpu, int thread, - int *output) + struct mmap_params *mp, int cpu, + int thread, int *output) { struct perf_evsel *evsel; @@ -635,8 +640,7 @@ static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx, if (*output == -1) { *output = fd; - if (__perf_evlist__mmap(evlist, idx, prot, mask, - *output) < 0) + if (__perf_evlist__mmap(evlist, idx, mp, *output) < 0) return -1; } else { if (ioctl(fd, PERF_EVENT_IOC_SET_OUTPUT, *output) != 0) @@ -651,8 +655,8 @@ static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx, return 0; } -static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, - int mask) +static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, + struct mmap_params *mp) { int cpu, thread; int nr_cpus = cpu_map__nr(evlist->cpus); @@ -663,8 +667,8 @@ static int perf_evlist__mmap_per_cpu(struct perf_evlist *evlist, int prot, int output = -1; for (thread = 0; thread < nr_threads; thread++) { - if (perf_evlist__mmap_per_evsel(evlist, cpu, prot, mask, - cpu, thread, &output)) + if (perf_evlist__mmap_per_evsel(evlist, cpu, mp, cpu, + thread, &output)) goto out_unmap; } } @@ -677,8 +681,8 @@ out_unmap: return -1; } -static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, - int mask) +static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, + struct mmap_params *mp) { int thread; int nr_threads = thread_map__nr(evlist->threads); @@ -687,8 +691,8 @@ static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, for (thread = 0; thread < nr_threads; thread++) { int output = -1; - if (perf_evlist__mmap_per_evsel(evlist, thread, prot, mask, 0, - thread, &output)) + if (perf_evlist__mmap_per_evsel(evlist, thread, mp, 0, thread, + &output)) goto out_unmap; } @@ -793,7 +797,9 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, struct perf_evsel *evsel; const struct cpu_map *cpus = evlist->cpus; const struct thread_map *threads = evlist->threads; - int prot = PROT_READ | (overwrite ? 0 : PROT_WRITE), mask; + struct mmap_params mp = { + .prot = PROT_READ | (overwrite ? 0 : PROT_WRITE), + }; if (evlist->mmap == NULL && perf_evlist__alloc_mmap(evlist) < 0) return -ENOMEM; @@ -804,7 +810,7 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, evlist->overwrite = overwrite; evlist->mmap_len = perf_evlist__mmap_size(pages); pr_debug("mmap size %zuB\n", evlist->mmap_len); - mask = evlist->mmap_len - page_size - 1; + mp.mask = evlist->mmap_len - page_size - 1; evlist__for_each(evlist, evsel) { if ((evsel->attr.read_format & PERF_FORMAT_ID) && @@ -814,9 +820,9 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, } if (cpu_map__empty(cpus)) - return perf_evlist__mmap_per_thread(evlist, prot, mask); + return perf_evlist__mmap_per_thread(evlist, &mp); - return perf_evlist__mmap_per_cpu(evlist, prot, mask); + return perf_evlist__mmap_per_cpu(evlist, &mp); } int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target) -- cgit v1.1 From f6d313699a9612a30fabe05bf2c9302c1408b5cf Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 14 Jul 2014 13:02:53 +0300 Subject: perf tools: Add feature test for __sync_val_compare_and_swap Add a feature test for __sync_val_compare_and_swap() and __sync_bool_compare_and_swap() Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1405332185-4050-30-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/config/Makefile | 6 ++++++ tools/perf/config/feature-checks/Makefile | 4 ++++ tools/perf/config/feature-checks/test-all.c | 5 +++++ .../config/feature-checks/test-sync-compare-and-swap.c | 14 ++++++++++++++ 4 files changed, 29 insertions(+) create mode 100644 tools/perf/config/feature-checks/test-sync-compare-and-swap.c diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index b7f42d5..1f67aa0 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -164,6 +164,7 @@ CORE_FEATURE_TESTS = \ backtrace \ dwarf \ fortify-source \ + sync-compare-and-swap \ glibc \ gtk2 \ gtk2-infobar \ @@ -199,6 +200,7 @@ LIB_FEATURE_TESTS = \ VF_FEATURE_TESTS = \ backtrace \ fortify-source \ + sync-compare-and-swap \ gtk2-infobar \ libelf-getphdrnum \ libelf-mmap \ @@ -272,6 +274,10 @@ CFLAGS += -I$(LIB_INCLUDE) CFLAGS += -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE +ifeq ($(feature-sync-compare-and-swap), 1) + CFLAGS += -DHAVE_SYNC_COMPARE_AND_SWAP_SUPPORT +endif + ifndef NO_BIONIC $(call feature_check,bionic) ifeq ($(feature-bionic), 1) diff --git a/tools/perf/config/feature-checks/Makefile b/tools/perf/config/feature-checks/Makefile index 64c84e5..6088f8d 100644 --- a/tools/perf/config/feature-checks/Makefile +++ b/tools/perf/config/feature-checks/Makefile @@ -5,6 +5,7 @@ FILES= \ test-bionic.bin \ test-dwarf.bin \ test-fortify-source.bin \ + test-sync-compare-and-swap.bin \ test-glibc.bin \ test-gtk2.bin \ test-gtk2-infobar.bin \ @@ -141,6 +142,9 @@ test-timerfd.bin: test-libdw-dwarf-unwind.bin: $(BUILD) +test-sync-compare-and-swap.bin: + $(BUILD) -Werror + -include *.d ############################### diff --git a/tools/perf/config/feature-checks/test-all.c b/tools/perf/config/feature-checks/test-all.c index fe5c1e5..a7d022e 100644 --- a/tools/perf/config/feature-checks/test-all.c +++ b/tools/perf/config/feature-checks/test-all.c @@ -89,6 +89,10 @@ # include "test-libdw-dwarf-unwind.c" #undef main +#define main main_test_sync_compare_and_swap +# include "test-sync-compare-and-swap.c" +#undef main + int main(int argc, char *argv[]) { main_test_libpython(); @@ -111,6 +115,7 @@ int main(int argc, char *argv[]) main_test_timerfd(); main_test_stackprotector_all(); main_test_libdw_dwarf_unwind(); + main_test_sync_compare_and_swap(argc, argv); return 0; } diff --git a/tools/perf/config/feature-checks/test-sync-compare-and-swap.c b/tools/perf/config/feature-checks/test-sync-compare-and-swap.c new file mode 100644 index 0000000..c34d4ca --- /dev/null +++ b/tools/perf/config/feature-checks/test-sync-compare-and-swap.c @@ -0,0 +1,14 @@ +#include + +volatile uint64_t x; + +int main(int argc, char *argv[]) +{ + uint64_t old, new = argc; + + argv = argv; + do { + old = __sync_val_compare_and_swap(&x, 0, 0); + } while (!__sync_bool_compare_and_swap(&x, old, new)); + return old == new; +} -- cgit v1.1 From ea8e08a16a1e6566be3f775c0bd351fa52ab6b9d Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 14 Jul 2014 13:02:54 +0300 Subject: perf tools: Add option macro OPT_CALLBACK_OPTARG Add an option macro that is the same as OPT_CALLBACK except that the argument is optional and it is possible to associate additional data with it. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1405332185-4050-31-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/parse-options.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h index d8dac8a..b59ba85 100644 --- a/tools/perf/util/parse-options.h +++ b/tools/perf/util/parse-options.h @@ -98,6 +98,7 @@ struct option { parse_opt_cb *callback; intptr_t defval; bool *set; + void *data; }; #define check_vtype(v, type) ( BUILD_BUG_ON_ZERO(!__builtin_types_compatible_p(typeof(v), type)) + v ) @@ -131,6 +132,10 @@ struct option { { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l),\ .value = (v), (a), .help = (h), .callback = (f), .defval = (intptr_t)d,\ .flags = PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NOARG} +#define OPT_CALLBACK_OPTARG(s, l, v, d, a, h, f) \ + { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), \ + .value = (v), (a), .help = (h), .callback = (f), \ + .flags = PARSE_OPT_OPTARG, .data = (d) } /* parse_options() will filter out the processed options and leave the * non-option argments in argv[]. -- cgit v1.1 From 6ff1ce763921f605aaf98c7a828b7df24d6923dc Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 14 Jul 2014 13:02:56 +0300 Subject: perf evsel: Add 'no_aux_samples' option Add an option to prevent additional samples being added to a selected event by perf_evsel__config(). This is needed when using the sched_switch tracepoint to follow object code execution. Since sched_switch will be used only for switch information, additional sampling is wasteful. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1405332185-4050-33-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 6 +++--- tools/perf/util/evsel.h | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index b760d32..40626e5 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -623,7 +623,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) attr->mmap_data = track; } - if (opts->call_graph_enabled) + if (opts->call_graph_enabled && !evsel->no_aux_samples) perf_evsel__config_callgraph(evsel, opts); if (target__has_cpu(&opts->target)) @@ -637,7 +637,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) target__has_cpu(&opts->target) || per_cpu)) perf_evsel__set_sample_bit(evsel, TIME); - if (opts->raw_samples) { + if (opts->raw_samples && !evsel->no_aux_samples) { perf_evsel__set_sample_bit(evsel, TIME); perf_evsel__set_sample_bit(evsel, RAW); perf_evsel__set_sample_bit(evsel, CPU); @@ -650,7 +650,7 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) attr->watermark = 0; attr->wakeup_events = 1; } - if (opts->branch_stack) { + if (opts->branch_stack && !evsel->no_aux_samples) { perf_evsel__set_sample_bit(evsel, BRANCH_STACK); attr->branch_sample_type = opts->branch_stack; } diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index a52e9a5..8dfec05 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -83,6 +83,7 @@ struct perf_evsel { int is_pos; bool supported; bool needs_swap; + bool no_aux_samples; /* parse modifier helper */ int exclude_GH; int nr_members; -- cgit v1.1 From 2afd2bcfc3a026d6e4c2184bf41ccd74eb05758b Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 14 Jul 2014 13:02:57 +0300 Subject: perf evsel: Add 'immediate' option Add an option to cause a selected event to be enabled immediately when configured by perf_evsel__config(). This is needed when using the sched_switch tracepoint to follow object code execution. By having sched_switch enabled immediately the first sched_switch event precedes the start of other tracing. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1405332185-4050-34-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 5 +++++ tools/perf/util/evsel.h | 1 + 2 files changed, 6 insertions(+) diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 40626e5..90f58cd 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -681,6 +681,11 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) if (target__none(&opts->target) && perf_evsel__is_group_leader(evsel) && !opts->initial_delay) attr->enable_on_exec = 1; + + if (evsel->immediate) { + attr->disabled = 0; + attr->enable_on_exec = 0; + } } int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 8dfec05..d7f93ce 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -84,6 +84,7 @@ struct perf_evsel { bool supported; bool needs_swap; bool no_aux_samples; + bool immediate; /* parse modifier helper */ int exclude_GH; int nr_members; -- cgit v1.1 From 29ce36121e6738012aaf00d983d25260627f2b0d Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 16 Jul 2014 11:07:13 +0300 Subject: perf machine: Fix map groups of threads with unknown pids Events like sched_switch do not provide a pid (tgid) which can result in threads with an unknown pid. If the pid is later discovered, join the map groups. Note the thread's map groups should be empty because they are populated by MMAP events which do provide the pid and tid. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1405498033-23817-1-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/machine.c | 57 ++++++++++++++++++++++++++++++++++++++++++----- tools/perf/util/map.c | 14 ++++++++++++ tools/perf/util/map.h | 1 + 3 files changed, 66 insertions(+), 6 deletions(-) diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 5b80877..5484fa4 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -272,6 +272,52 @@ void machines__set_id_hdr_size(struct machines *machines, u16 id_hdr_size) return; } +static void machine__update_thread_pid(struct machine *machine, + struct thread *th, pid_t pid) +{ + struct thread *leader; + + if (pid == th->pid_ || pid == -1 || th->pid_ != -1) + return; + + th->pid_ = pid; + + if (th->pid_ == th->tid) + return; + + leader = machine__findnew_thread(machine, th->pid_, th->pid_); + if (!leader) + goto out_err; + + if (!leader->mg) + leader->mg = map_groups__new(); + + if (!leader->mg) + goto out_err; + + if (th->mg == leader->mg) + return; + + if (th->mg) { + /* + * Maps are created from MMAP events which provide the pid and + * tid. Consequently there never should be any maps on a thread + * with an unknown pid. Just print an error if there are. + */ + if (!map_groups__empty(th->mg)) + pr_err("Discarding thread maps for %d:%d\n", + th->pid_, th->tid); + map_groups__delete(th->mg); + } + + th->mg = map_groups__get(leader->mg); + + return; + +out_err: + pr_err("Failed to join map groups for %d:%d\n", th->pid_, th->tid); +} + static struct thread *__machine__findnew_thread(struct machine *machine, pid_t pid, pid_t tid, bool create) @@ -285,10 +331,10 @@ static struct thread *__machine__findnew_thread(struct machine *machine, * so most of the time we dont have to look up * the full rbtree: */ - if (machine->last_match && machine->last_match->tid == tid) { - if (pid != -1 && pid != machine->last_match->pid_) - machine->last_match->pid_ = pid; - return machine->last_match; + th = machine->last_match; + if (th && th->tid == tid) { + machine__update_thread_pid(machine, th, pid); + return th; } while (*p != NULL) { @@ -297,8 +343,7 @@ static struct thread *__machine__findnew_thread(struct machine *machine, if (th->tid == tid) { machine->last_match = th; - if (pid != -1 && pid != th->pid_) - th->pid_ = pid; + machine__update_thread_pid(machine, th, pid); return th; } diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 25c571f..7af1480 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -454,6 +454,20 @@ void map_groups__exit(struct map_groups *mg) } } +bool map_groups__empty(struct map_groups *mg) +{ + int i; + + for (i = 0; i < MAP__NR_TYPES; ++i) { + if (maps__first(&mg->maps[i])) + return false; + if (!list_empty(&mg->removed_maps[i])) + return false; + } + + return true; +} + struct map_groups *map_groups__new(void) { struct map_groups *mg = malloc(sizeof(*mg)); diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 7758c72..5806a90 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -66,6 +66,7 @@ struct map_groups { struct map_groups *map_groups__new(void); void map_groups__delete(struct map_groups *mg); +bool map_groups__empty(struct map_groups *mg); static inline struct map_groups *map_groups__get(struct map_groups *mg) { -- cgit v1.1 From 9608b84e4dd95341b88cad646b114811f5bccbba Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 16 Jul 2014 10:19:43 +0300 Subject: perf thread: Allow deletion of a thread with no map groups It needs to be possible to call thread__delete() on a thread with no map groups. This is needed for a subsequent patch which deletes a thread on the error path before map groups have been attached. Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1405495184-20441-2-git-send-email-adrian.hunter@intel.com Signed-off-by: Adrian Hunter Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/thread.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 7a32f44..b9c36ef 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -60,8 +60,10 @@ void thread__delete(struct thread *thread) { struct comm *comm, *tmp; - map_groups__put(thread->mg); - thread->mg = NULL; + if (thread->mg) { + map_groups__put(thread->mg); + thread->mg = NULL; + } list_for_each_entry_safe(comm, tmp, &thread->comm_list, list) { list_del(&comm->list); comm__free(comm); -- cgit v1.1 From 418029b7324f8b90ac1dfbc8a44555d6905be761 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Wed, 16 Jul 2014 10:19:44 +0300 Subject: perf machine: Fix leak of 'struct thread' on error path __machine__findnew_thread() creates a 'struct thread' but does not free it on the error path. Fix it. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1405495184-20441-3-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/machine.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 5484fa4..93c8b6f 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -370,8 +370,10 @@ static struct thread *__machine__findnew_thread(struct machine *machine, * within thread__init_map_groups to find the thread * leader and that would screwed the rb tree. */ - if (thread__init_map_groups(th, machine)) + if (thread__init_map_groups(th, machine)) { + thread__delete(th); return NULL; + } } return th; -- cgit v1.1 From acebd408bef17169fbf79079b96f0264b535916c Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Mon, 14 Jul 2014 23:46:47 +0200 Subject: perf tools: Remove verbose from functions prototypes And use verbose as an global object in following functions: __map_groups__fprintf_maps __map_groups__fprintf_removed_maps map_groups__fprintf_maps map_groups__fprintf Also making map_groups__fprintf_maps static. Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1405374411-29012-2-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/ui/stdio/hist.c | 2 +- tools/perf/util/map.c | 24 ++++++++++++------------ tools/perf/util/map.h | 9 ++++----- tools/perf/util/thread.c | 4 ++-- 4 files changed, 19 insertions(+), 20 deletions(-) diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c index 90122ab..40af0ac 100644 --- a/tools/perf/ui/stdio/hist.c +++ b/tools/perf/ui/stdio/hist.c @@ -479,7 +479,7 @@ print_entries: if (h->ms.map == NULL && verbose > 1) { __map_groups__fprintf_maps(h->thread->mg, - MAP__FUNCTION, verbose, fp); + MAP__FUNCTION, fp); fprintf(fp, "%.10s end\n", graph_dotted_line); } } diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index 7af1480..845f627 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -12,6 +12,7 @@ #include "vdso.h" #include "build-id.h" #include "util.h" +#include "debug.h" #include const char *map_type__name[MAP__NR_TYPES] = { @@ -568,8 +569,8 @@ int map_groups__find_ams(struct addr_map_symbol *ams, symbol_filter_t filter) return ams->sym ? 0 : -1; } -size_t __map_groups__fprintf_maps(struct map_groups *mg, - enum map_type type, int verbose, FILE *fp) +size_t __map_groups__fprintf_maps(struct map_groups *mg, enum map_type type, + FILE *fp) { size_t printed = fprintf(fp, "%s:\n", map_type__name[type]); struct rb_node *nd; @@ -587,17 +588,16 @@ size_t __map_groups__fprintf_maps(struct map_groups *mg, return printed; } -size_t map_groups__fprintf_maps(struct map_groups *mg, int verbose, FILE *fp) +static size_t map_groups__fprintf_maps(struct map_groups *mg, FILE *fp) { size_t printed = 0, i; for (i = 0; i < MAP__NR_TYPES; ++i) - printed += __map_groups__fprintf_maps(mg, i, verbose, fp); + printed += __map_groups__fprintf_maps(mg, i, fp); return printed; } static size_t __map_groups__fprintf_removed_maps(struct map_groups *mg, - enum map_type type, - int verbose, FILE *fp) + enum map_type type, FILE *fp) { struct map *pos; size_t printed = 0; @@ -614,23 +614,23 @@ static size_t __map_groups__fprintf_removed_maps(struct map_groups *mg, } static size_t map_groups__fprintf_removed_maps(struct map_groups *mg, - int verbose, FILE *fp) + FILE *fp) { size_t printed = 0, i; for (i = 0; i < MAP__NR_TYPES; ++i) - printed += __map_groups__fprintf_removed_maps(mg, i, verbose, fp); + printed += __map_groups__fprintf_removed_maps(mg, i, fp); return printed; } -size_t map_groups__fprintf(struct map_groups *mg, int verbose, FILE *fp) +size_t map_groups__fprintf(struct map_groups *mg, FILE *fp) { - size_t printed = map_groups__fprintf_maps(mg, verbose, fp); + size_t printed = map_groups__fprintf_maps(mg, fp); printed += fprintf(fp, "Removed maps:\n"); - return printed + map_groups__fprintf_removed_maps(mg, verbose, fp); + return printed + map_groups__fprintf_removed_maps(mg, fp); } int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map, - int verbose, FILE *fp) + FILE *fp) { struct rb_root *root = &mg->maps[map->type]; struct rb_node *next = rb_first(root); diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 5806a90..22d13a2 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -142,8 +142,8 @@ void map__fixup_end(struct map *map); void map__reloc_vmlinux(struct map *map); -size_t __map_groups__fprintf_maps(struct map_groups *mg, - enum map_type type, int verbose, FILE *fp); +size_t __map_groups__fprintf_maps(struct map_groups *mg, enum map_type type, + FILE *fp); void maps__insert(struct rb_root *maps, struct map *map); void maps__remove(struct rb_root *maps, struct map *map); struct map *maps__find(struct rb_root *maps, u64 addr); @@ -153,8 +153,7 @@ void map_groups__init(struct map_groups *mg); void map_groups__exit(struct map_groups *mg); int map_groups__clone(struct map_groups *mg, struct map_groups *parent, enum map_type type); -size_t map_groups__fprintf(struct map_groups *mg, int verbose, FILE *fp); -size_t map_groups__fprintf_maps(struct map_groups *mg, int verbose, FILE *fp); +size_t map_groups__fprintf(struct map_groups *mg, FILE *fp); int maps__set_kallsyms_ref_reloc_sym(struct map **maps, const char *symbol_name, u64 addr); @@ -211,7 +210,7 @@ struct symbol *map_groups__find_function_by_name(struct map_groups *mg, } int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map, - int verbose, FILE *fp); + FILE *fp); struct map *map_groups__find_by_name(struct map_groups *mg, enum map_type type, const char *name); diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index b9c36ef..9692c06 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -129,12 +129,12 @@ int thread__comm_len(struct thread *thread) size_t thread__fprintf(struct thread *thread, FILE *fp) { return fprintf(fp, "Thread %d %s\n", thread->tid, thread__comm_str(thread)) + - map_groups__fprintf(thread->mg, verbose, fp); + map_groups__fprintf(thread->mg, fp); } void thread__insert_map(struct thread *thread, struct map *map) { - map_groups__fixup_overlappings(thread->mg, map, verbose, stderr); + map_groups__fixup_overlappings(thread->mg, map, stderr); map_groups__insert(thread->mg, map); } -- cgit v1.1 From 84f5d36f486609277801e827241396334185d11c Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Mon, 14 Jul 2014 23:46:48 +0200 Subject: perf tools: Move pr_* debug macros into debug object Moving pr_* debug macros to have it with in same object as debug variables, becase we will change them to use verbose variable in next patch. Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1405374411-29012-3-git-send-email-jolsa@kernel.org [ Add missing debug.h include in python scripting glue and in the libdw unwind lib ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/x86/tests/dwarf-unwind.c | 1 + tools/perf/arch/x86/util/unwind-libunwind.c | 1 + tools/perf/builtin-evlist.c | 1 + tools/perf/builtin-help.c | 1 + tools/perf/builtin-timechart.c | 1 + tools/perf/tests/dso-data.c | 1 + tools/perf/tests/evsel-roundtrip-name.c | 1 + tools/perf/tests/evsel-tp-sched.c | 1 + tools/perf/tests/open-syscall-tp-fields.c | 1 + tools/perf/tests/parse-events.c | 1 + tools/perf/tests/parse-no-sample-id-all.c | 1 + tools/perf/tests/sample-parsing.c | 1 + tools/perf/tests/thread-mg-share.c | 1 + tools/perf/util/data.c | 1 + tools/perf/util/debug.h | 20 ++++++++++++++++++++ tools/perf/util/include/linux/kernel.h | 21 --------------------- tools/perf/util/pstack.c | 1 + .../perf/util/scripting-engines/trace-event-perl.c | 1 + .../util/scripting-engines/trace-event-python.c | 1 + tools/perf/util/trace-event-info.c | 1 + tools/perf/util/trace-event-read.c | 1 + tools/perf/util/unwind-libdw.c | 1 + tools/perf/util/unwind-libunwind.c | 1 + tools/perf/util/util.c | 1 + tools/perf/util/vdso.c | 1 + 25 files changed, 43 insertions(+), 21 deletions(-) diff --git a/tools/perf/arch/x86/tests/dwarf-unwind.c b/tools/perf/arch/x86/tests/dwarf-unwind.c index 9f89f89..d8bbf7a 100644 --- a/tools/perf/arch/x86/tests/dwarf-unwind.c +++ b/tools/perf/arch/x86/tests/dwarf-unwind.c @@ -3,6 +3,7 @@ #include "thread.h" #include "map.h" #include "event.h" +#include "debug.h" #include "tests/tests.h" #define STACK_SIZE 8192 diff --git a/tools/perf/arch/x86/util/unwind-libunwind.c b/tools/perf/arch/x86/util/unwind-libunwind.c index 3261f68..db25e93 100644 --- a/tools/perf/arch/x86/util/unwind-libunwind.c +++ b/tools/perf/arch/x86/util/unwind-libunwind.c @@ -3,6 +3,7 @@ #include #include "perf_regs.h" #include "../../util/unwind.h" +#include "../../util/debug.h" #ifdef HAVE_ARCH_X86_64_SUPPORT int libunwind__arch_reg_id(int regnum) diff --git a/tools/perf/builtin-evlist.c b/tools/perf/builtin-evlist.c index c99e0de..66e12f5 100644 --- a/tools/perf/builtin-evlist.c +++ b/tools/perf/builtin-evlist.c @@ -15,6 +15,7 @@ #include "util/parse-options.h" #include "util/session.h" #include "util/data.h" +#include "util/debug.h" static int __cmd_evlist(const char *file_name, struct perf_attr_details *details) { diff --git a/tools/perf/builtin-help.c b/tools/perf/builtin-help.c index 178b88a..0384d93 100644 --- a/tools/perf/builtin-help.c +++ b/tools/perf/builtin-help.c @@ -11,6 +11,7 @@ #include "util/parse-options.h" #include "util/run-command.h" #include "util/help.h" +#include "util/debug.h" static struct man_viewer_list { struct man_viewer_list *next; diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 04c9c53..2f1a522 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -37,6 +37,7 @@ #include "util/svghelper.h" #include "util/tool.h" #include "util/data.h" +#include "util/debug.h" #define SUPPORT_OLD_POWER_EVENTS 1 #define PWR_EVENT_EXIT -1 diff --git a/tools/perf/tests/dso-data.c b/tools/perf/tests/dso-data.c index 630808c..caaf37f 100644 --- a/tools/perf/tests/dso-data.c +++ b/tools/perf/tests/dso-data.c @@ -10,6 +10,7 @@ #include "machine.h" #include "symbol.h" #include "tests.h" +#include "debug.h" static char *test_file(int size) { diff --git a/tools/perf/tests/evsel-roundtrip-name.c b/tools/perf/tests/evsel-roundtrip-name.c index 465cdbc..b8d8341 100644 --- a/tools/perf/tests/evsel-roundtrip-name.c +++ b/tools/perf/tests/evsel-roundtrip-name.c @@ -2,6 +2,7 @@ #include "evsel.h" #include "parse-events.h" #include "tests.h" +#include "debug.h" static int perf_evsel__roundtrip_cache_name_test(void) { diff --git a/tools/perf/tests/evsel-tp-sched.c b/tools/perf/tests/evsel-tp-sched.c index 35d7fdb..5216242 100644 --- a/tools/perf/tests/evsel-tp-sched.c +++ b/tools/perf/tests/evsel-tp-sched.c @@ -1,6 +1,7 @@ #include #include "evsel.h" #include "tests.h" +#include "debug.h" static int perf_evsel__test_field(struct perf_evsel *evsel, const char *name, int size, bool should_be_signed) diff --git a/tools/perf/tests/open-syscall-tp-fields.c b/tools/perf/tests/open-syscall-tp-fields.c index c505ef2..0785b64 100644 --- a/tools/perf/tests/open-syscall-tp-fields.c +++ b/tools/perf/tests/open-syscall-tp-fields.c @@ -3,6 +3,7 @@ #include "evsel.h" #include "thread_map.h" #include "tests.h" +#include "debug.h" int test__syscall_open_tp_fields(void) { diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index deba669..5941927 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -5,6 +5,7 @@ #include #include #include "tests.h" +#include "debug.h" #include #define PERF_TP_SAMPLE_TYPE (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | \ diff --git a/tools/perf/tests/parse-no-sample-id-all.c b/tools/perf/tests/parse-no-sample-id-all.c index 905019f..2c63ea6 100644 --- a/tools/perf/tests/parse-no-sample-id-all.c +++ b/tools/perf/tests/parse-no-sample-id-all.c @@ -7,6 +7,7 @@ #include "evlist.h" #include "header.h" #include "util.h" +#include "debug.h" static int process_event(struct perf_evlist **pevlist, union perf_event *event) { diff --git a/tools/perf/tests/sample-parsing.c b/tools/perf/tests/sample-parsing.c index 7ae8d17..ca292f9 100644 --- a/tools/perf/tests/sample-parsing.c +++ b/tools/perf/tests/sample-parsing.c @@ -4,6 +4,7 @@ #include "util.h" #include "event.h" #include "evsel.h" +#include "debug.h" #include "tests.h" diff --git a/tools/perf/tests/thread-mg-share.c b/tools/perf/tests/thread-mg-share.c index 2b2e0db..b028499 100644 --- a/tools/perf/tests/thread-mg-share.c +++ b/tools/perf/tests/thread-mg-share.c @@ -2,6 +2,7 @@ #include "machine.h" #include "thread.h" #include "map.h" +#include "debug.h" int test__thread_mg_share(void) { diff --git a/tools/perf/util/data.c b/tools/perf/util/data.c index ee370a7f..29d720c 100644 --- a/tools/perf/util/data.c +++ b/tools/perf/util/data.c @@ -7,6 +7,7 @@ #include "data.h" #include "util.h" +#include "debug.h" static bool check_pipe(struct perf_data_file *file) { diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index 443694c..8a8ceb3 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h @@ -11,6 +11,24 @@ extern int verbose; extern bool quiet, dump_trace; +#ifndef pr_fmt +#define pr_fmt(fmt) fmt +#endif + +#define pr_err(fmt, ...) \ + eprintf(0, pr_fmt(fmt), ##__VA_ARGS__) +#define pr_warning(fmt, ...) \ + eprintf(0, pr_fmt(fmt), ##__VA_ARGS__) +#define pr_info(fmt, ...) \ + eprintf(0, pr_fmt(fmt), ##__VA_ARGS__) +#define pr_debug(fmt, ...) \ + eprintf(1, pr_fmt(fmt), ##__VA_ARGS__) +#define pr_debugN(n, fmt, ...) \ + eprintf(n, pr_fmt(fmt), ##__VA_ARGS__) +#define pr_debug2(fmt, ...) pr_debugN(2, pr_fmt(fmt), ##__VA_ARGS__) +#define pr_debug3(fmt, ...) pr_debugN(3, pr_fmt(fmt), ##__VA_ARGS__) +#define pr_debug4(fmt, ...) pr_debugN(4, pr_fmt(fmt), ##__VA_ARGS__) + int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2))); void trace_event(union perf_event *event); @@ -19,4 +37,6 @@ int ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2))); void pr_stat(const char *fmt, ...); +int eprintf(int level, const char *fmt, ...) __attribute__((format(printf, 2, 3))); + #endif /* __PERF_DEBUG_H */ diff --git a/tools/perf/util/include/linux/kernel.h b/tools/perf/util/include/linux/kernel.h index 9844c31..09e8e7a 100644 --- a/tools/perf/util/include/linux/kernel.h +++ b/tools/perf/util/include/linux/kernel.h @@ -94,27 +94,6 @@ static inline int scnprintf(char * buf, size_t size, const char * fmt, ...) return (i >= ssize) ? (ssize - 1) : i; } -int eprintf(int level, - const char *fmt, ...) __attribute__((format(printf, 2, 3))); - -#ifndef pr_fmt -#define pr_fmt(fmt) fmt -#endif - -#define pr_err(fmt, ...) \ - eprintf(0, pr_fmt(fmt), ##__VA_ARGS__) -#define pr_warning(fmt, ...) \ - eprintf(0, pr_fmt(fmt), ##__VA_ARGS__) -#define pr_info(fmt, ...) \ - eprintf(0, pr_fmt(fmt), ##__VA_ARGS__) -#define pr_debug(fmt, ...) \ - eprintf(1, pr_fmt(fmt), ##__VA_ARGS__) -#define pr_debugN(n, fmt, ...) \ - eprintf(n, pr_fmt(fmt), ##__VA_ARGS__) -#define pr_debug2(fmt, ...) pr_debugN(2, pr_fmt(fmt), ##__VA_ARGS__) -#define pr_debug3(fmt, ...) pr_debugN(3, pr_fmt(fmt), ##__VA_ARGS__) -#define pr_debug4(fmt, ...) pr_debugN(4, pr_fmt(fmt), ##__VA_ARGS__) - /* * This looks more complex than it should be. But we need to * get the type for the ~ right in round_down (it needs to be diff --git a/tools/perf/util/pstack.c b/tools/perf/util/pstack.c index daa17ae..a126e6c 100644 --- a/tools/perf/util/pstack.c +++ b/tools/perf/util/pstack.c @@ -6,6 +6,7 @@ #include "util.h" #include "pstack.h" +#include "debug.h" #include #include diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index af7da56..b2dba9c 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c @@ -34,6 +34,7 @@ #include "../event.h" #include "../trace-event.h" #include "../evsel.h" +#include "../debug.h" void boot_Perf__Trace__Context(pTHX_ CV *cv); void boot_DynaLoader(pTHX_ CV *cv); diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index b366b48..cbce254 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -27,6 +27,7 @@ #include #include "../../perf.h" +#include "../debug.h" #include "../evsel.h" #include "../util.h" #include "../event.h" diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index c3bba88..eb72716 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c @@ -40,6 +40,7 @@ #include "trace-event.h" #include #include "evsel.h" +#include "debug.h" #define VERSION "0.5" diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index e113e18..ea3fd7f 100644 --- a/tools/perf/util/trace-event-read.c +++ b/tools/perf/util/trace-event-read.c @@ -36,6 +36,7 @@ #include "../perf.h" #include "util.h" #include "trace-event.h" +#include "debug.h" static int input_fd; diff --git a/tools/perf/util/unwind-libdw.c b/tools/perf/util/unwind-libdw.c index 5ec80a5..7419768 100644 --- a/tools/perf/util/unwind-libdw.c +++ b/tools/perf/util/unwind-libdw.c @@ -3,6 +3,7 @@ #include #include #include +#include "debug.h" #include "unwind.h" #include "unwind-libdw.h" #include "machine.h" diff --git a/tools/perf/util/unwind-libunwind.c b/tools/perf/util/unwind-libunwind.c index 25578b9..92b56db 100644 --- a/tools/perf/util/unwind-libunwind.c +++ b/tools/perf/util/unwind-libunwind.c @@ -30,6 +30,7 @@ #include "unwind.h" #include "symbol.h" #include "util.h" +#include "debug.h" extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as, diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index e4132ae..e52e746 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -1,5 +1,6 @@ #include "../perf.h" #include "util.h" +#include "debug.h" #include #include #ifdef HAVE_BACKTRACE_SUPPORT diff --git a/tools/perf/util/vdso.c b/tools/perf/util/vdso.c index 0ddb3b8..2905824 100644 --- a/tools/perf/util/vdso.c +++ b/tools/perf/util/vdso.c @@ -12,6 +12,7 @@ #include "util.h" #include "symbol.h" #include "linux/string.h" +#include "debug.h" static bool vdso_found; static char vdso_file[] = "/tmp/perf-vdso.so-XXXXXX"; -- cgit v1.1 From c95688aac7723c17b2badc23233706b2f02e58ed Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Mon, 14 Jul 2014 23:46:49 +0200 Subject: perf tools: Factor eprintf to allow different debug variables This way we can easily reuse current debug functions for another debug variables other than verbose. Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1405374411-29012-4-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/debug.c | 12 ++++++------ tools/perf/util/debug.h | 12 ++++++------ tools/perf/util/python.c | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index 299b555..c208d6f 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c @@ -16,11 +16,11 @@ int verbose; bool dump_trace = false, quiet = false; -static int _eprintf(int level, const char *fmt, va_list args) +static int _eprintf(int level, int var, const char *fmt, va_list args) { int ret = 0; - if (verbose >= level) { + if (var >= level) { if (use_browser >= 1) ui_helpline__vshow(fmt, args); else @@ -30,13 +30,13 @@ static int _eprintf(int level, const char *fmt, va_list args) return ret; } -int eprintf(int level, const char *fmt, ...) +int eprintf(int level, int var, const char *fmt, ...) { va_list args; int ret; va_start(args, fmt); - ret = _eprintf(level, fmt, args); + ret = _eprintf(level, var, fmt, args); va_end(args); return ret; @@ -51,9 +51,9 @@ void pr_stat(const char *fmt, ...) va_list args; va_start(args, fmt); - _eprintf(1, fmt, args); + _eprintf(1, verbose, fmt, args); va_end(args); - eprintf(1, "\n"); + eprintf(1, verbose, "\n"); } int dump_printf(const char *fmt, ...) diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index 8a8ceb3..1cb8081 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h @@ -16,15 +16,15 @@ extern bool quiet, dump_trace; #endif #define pr_err(fmt, ...) \ - eprintf(0, pr_fmt(fmt), ##__VA_ARGS__) + eprintf(0, verbose, pr_fmt(fmt), ##__VA_ARGS__) #define pr_warning(fmt, ...) \ - eprintf(0, pr_fmt(fmt), ##__VA_ARGS__) + eprintf(0, verbose, pr_fmt(fmt), ##__VA_ARGS__) #define pr_info(fmt, ...) \ - eprintf(0, pr_fmt(fmt), ##__VA_ARGS__) + eprintf(0, verbose, pr_fmt(fmt), ##__VA_ARGS__) #define pr_debug(fmt, ...) \ - eprintf(1, pr_fmt(fmt), ##__VA_ARGS__) + eprintf(1, verbose, pr_fmt(fmt), ##__VA_ARGS__) #define pr_debugN(n, fmt, ...) \ - eprintf(n, pr_fmt(fmt), ##__VA_ARGS__) + eprintf(n, verbose, pr_fmt(fmt), ##__VA_ARGS__) #define pr_debug2(fmt, ...) pr_debugN(2, pr_fmt(fmt), ##__VA_ARGS__) #define pr_debug3(fmt, ...) pr_debugN(3, pr_fmt(fmt), ##__VA_ARGS__) #define pr_debug4(fmt, ...) pr_debugN(4, pr_fmt(fmt), ##__VA_ARGS__) @@ -37,6 +37,6 @@ int ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2))); void pr_stat(const char *fmt, ...); -int eprintf(int level, const char *fmt, ...) __attribute__((format(printf, 2, 3))); +int eprintf(int level, int var, const char *fmt, ...) __attribute__((format(printf, 3, 4))); #endif /* __PERF_DEBUG_H */ diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 122669c..12aa9b0 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -14,12 +14,12 @@ */ int verbose; -int eprintf(int level, const char *fmt, ...) +int eprintf(int level, int var, const char *fmt, ...) { va_list args; int ret = 0; - if (verbose >= level) { + if (var >= level) { va_start(args, fmt); ret = vfprintf(stderr, fmt, args); va_end(args); -- cgit v1.1 From bbb2cea7e8dd496b41558df1a0ec9205497b7ebf Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 17 Jul 2014 12:55:00 +0200 Subject: perf tools: Add --debug optionto set debug variable Adding --debug option as a way to setup debug variables. Starting with support for verbose, more will come. It's possible to use it now with report command: $ perf --debug verbose ... $ perf --debug verbose=2 ... I'll need this support to add separated debug variable for ordered events change in order to separate debug output out of standard verbose stream. Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Jiri Olsa Cc: Namhyung Kim Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20140717105500.GG516@krava.redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf.txt | 10 ++++++++- tools/perf/perf.c | 13 +++++++++++- tools/perf/util/debug.c | 44 +++++++++++++++++++++++++++++++++++++++ tools/perf/util/debug.h | 2 ++ 4 files changed, 67 insertions(+), 2 deletions(-) diff --git a/tools/perf/Documentation/perf.txt b/tools/perf/Documentation/perf.txt index 0eeb247..d240bb2 100644 --- a/tools/perf/Documentation/perf.txt +++ b/tools/perf/Documentation/perf.txt @@ -8,7 +8,15 @@ perf - Performance analysis tools for Linux SYNOPSIS -------- [verse] -'perf' [--version] [--help] COMMAND [ARGS] +'perf' [--version] [--help] [OPTIONS] COMMAND [ARGS] + +OPTIONS +------- +--debug:: + Setup debug variable (just verbose for now) in value + range (0, 10). Use like: + --debug verbose # sets verbose = 1 + --debug verbose=2 # sets verbose = 2 DESCRIPTION ----------- diff --git a/tools/perf/perf.c b/tools/perf/perf.c index 95c58fc..eed3fb2 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -13,11 +13,12 @@ #include "util/quote.h" #include "util/run-command.h" #include "util/parse-events.h" +#include "util/debug.h" #include #include const char perf_usage_string[] = - "perf [--version] [--help] COMMAND [ARGS]"; + "perf [--version] [--debug variable[=VALUE]] [--help] COMMAND [ARGS]"; const char perf_more_info_string[] = "See 'perf help COMMAND' for more information on a specific command."; @@ -212,6 +213,16 @@ static int handle_options(const char ***argv, int *argc, int *envchanged) printf("%s ", p->cmd); } exit(0); + } else if (!strcmp(cmd, "--debug")) { + if (*argc < 2) { + fprintf(stderr, "No variable specified for --debug.\n"); + usage(perf_usage_string); + } + if (perf_debug_option((*argv)[1])) + usage(perf_usage_string); + + (*argv)++; + (*argc)--; } else { fprintf(stderr, "Unknown option: %s\n", cmd); usage(perf_usage_string); diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index c208d6f..71d4193 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c @@ -105,3 +105,47 @@ void trace_event(union perf_event *event) } printf(".\n"); } + +static struct debug_variable { + const char *name; + int *ptr; +} debug_variables[] = { + { .name = "verbose", .ptr = &verbose }, + { .name = NULL, } +}; + +int perf_debug_option(const char *str) +{ + struct debug_variable *var = &debug_variables[0]; + char *vstr, *s = strdup(str); + int v = 1; + + vstr = strchr(s, '='); + if (vstr) + *vstr++ = 0; + + while (var->name) { + if (!strcmp(s, var->name)) + break; + var++; + } + + if (!var->name) { + pr_err("Unknown debug variable name '%s'\n", s); + free(s); + return -1; + } + + if (vstr) { + v = atoi(vstr); + /* + * Allow only values in range (0, 10), + * otherwise set 0. + */ + v = (v < 0) || (v > 10) ? 0 : v; + } + + *var->ptr = v; + free(s); + return 0; +} diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index 1cb8081..89fb6b0 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h @@ -39,4 +39,6 @@ void pr_stat(const char *fmt, ...); int eprintf(int level, int var, const char *fmt, ...) __attribute__((format(printf, 3, 4))); +int perf_debug_option(const char *str); + #endif /* __PERF_DEBUG_H */ -- cgit v1.1 From ff527bccd469067a64f4ae9747b9045914667d34 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Mon, 14 Jul 2014 23:46:51 +0200 Subject: perf tools: Remove needless getopt.h includes We don't use getopt.h interfaces. Signed-off-by: Jiri Olsa Cc: Corey Ashford Cc: David Ahern Cc: Frederic Weisbecker Cc: Ingo Molnar Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1405374411-29012-6-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/probe-finder.c | 1 - tools/perf/util/trace-event-read.c | 1 - 2 files changed, 2 deletions(-) diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index 98e3047..dca9145 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index ea3fd7f..54d9e9b 100644 --- a/tools/perf/util/trace-event-read.c +++ b/tools/perf/util/trace-event-read.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include -- cgit v1.1 From 0b437860818dc717f6a9e8a5089223a8414f5fff Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 14 Jul 2014 13:03:03 +0300 Subject: perf tools: Allow TSC conversion on any arch It is possible to record a perf.data file on one architecture and process it on another. Consequently, TSC conversion functions need to be moved out of the arch directory. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Namhyung Kim Cc: Paul Mackerras Cc: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1405332185-4050-40-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.perf | 2 ++ tools/perf/arch/x86/util/tsc.c | 22 +--------------------- tools/perf/arch/x86/util/tsc.h | 3 --- tools/perf/tests/perf-time-to-tsc.c | 3 +-- tools/perf/util/tsc.c | 25 +++++++++++++++++++++++++ tools/perf/util/tsc.h | 11 +++++++++++ 6 files changed, 40 insertions(+), 26 deletions(-) create mode 100644 tools/perf/util/tsc.c create mode 100644 tools/perf/util/tsc.h diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 90c4983..3308b22 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -295,6 +295,7 @@ LIB_H += util/intlist.h LIB_H += util/perf_regs.h LIB_H += util/unwind.h LIB_H += util/vdso.h +LIB_H += util/tsc.h LIB_H += ui/helpline.h LIB_H += ui/progress.h LIB_H += ui/util.h @@ -374,6 +375,7 @@ LIB_OBJS += $(OUTPUT)util/stat.o LIB_OBJS += $(OUTPUT)util/record.o LIB_OBJS += $(OUTPUT)util/srcline.o LIB_OBJS += $(OUTPUT)util/data.o +LIB_OBJS += $(OUTPUT)util/tsc.o LIB_OBJS += $(OUTPUT)ui/setup.o LIB_OBJS += $(OUTPUT)ui/helpline.o diff --git a/tools/perf/arch/x86/util/tsc.c b/tools/perf/arch/x86/util/tsc.c index 40021fa..3655f24 100644 --- a/tools/perf/arch/x86/util/tsc.c +++ b/tools/perf/arch/x86/util/tsc.c @@ -6,29 +6,9 @@ #include "../../perf.h" #include #include "../../util/debug.h" +#include "../../util/tsc.h" #include "tsc.h" -u64 perf_time_to_tsc(u64 ns, struct perf_tsc_conversion *tc) -{ - u64 t, quot, rem; - - t = ns - tc->time_zero; - quot = t / tc->time_mult; - rem = t % tc->time_mult; - return (quot << tc->time_shift) + - (rem << tc->time_shift) / tc->time_mult; -} - -u64 tsc_to_perf_time(u64 cyc, struct perf_tsc_conversion *tc) -{ - u64 quot, rem; - - quot = cyc >> tc->time_shift; - rem = cyc & ((1 << tc->time_shift) - 1); - return tc->time_zero + quot * tc->time_mult + - ((rem * tc->time_mult) >> tc->time_shift); -} - int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc, struct perf_tsc_conversion *tc) { diff --git a/tools/perf/arch/x86/util/tsc.h b/tools/perf/arch/x86/util/tsc.h index 2affe03..2edc4d3 100644 --- a/tools/perf/arch/x86/util/tsc.h +++ b/tools/perf/arch/x86/util/tsc.h @@ -14,7 +14,4 @@ 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); - #endif /* TOOLS_PERF_ARCH_X86_UTIL_TSC_H__ */ diff --git a/tools/perf/tests/perf-time-to-tsc.c b/tools/perf/tests/perf-time-to-tsc.c index 3b7cd4d..0372f6e 100644 --- a/tools/perf/tests/perf-time-to-tsc.c +++ b/tools/perf/tests/perf-time-to-tsc.c @@ -8,10 +8,9 @@ #include "evsel.h" #include "thread_map.h" #include "cpumap.h" +#include "tsc.h" #include "tests.h" -#include "../arch/x86/util/tsc.h" - #define CHECK__(x) { \ while ((x) < 0) { \ pr_debug(#x " failed!\n"); \ diff --git a/tools/perf/util/tsc.c b/tools/perf/util/tsc.c new file mode 100644 index 0000000..ef4749836 --- /dev/null +++ b/tools/perf/util/tsc.c @@ -0,0 +1,25 @@ +#include +#include + +#include "tsc.h" + +u64 perf_time_to_tsc(u64 ns, struct perf_tsc_conversion *tc) +{ + u64 t, quot, rem; + + t = ns - tc->time_zero; + quot = t / tc->time_mult; + rem = t % tc->time_mult; + return (quot << tc->time_shift) + + (rem << tc->time_shift) / tc->time_mult; +} + +u64 tsc_to_perf_time(u64 cyc, struct perf_tsc_conversion *tc) +{ + u64 quot, rem; + + quot = cyc >> tc->time_shift; + rem = cyc & ((1 << tc->time_shift) - 1); + return tc->time_zero + quot * tc->time_mult + + ((rem * tc->time_mult) >> tc->time_shift); +} diff --git a/tools/perf/util/tsc.h b/tools/perf/util/tsc.h new file mode 100644 index 0000000..4eca848 --- /dev/null +++ b/tools/perf/util/tsc.h @@ -0,0 +1,11 @@ +#ifndef __PERF_TSC_H +#define __PERF_TSC_H + +#include + +#include "../arch/x86/util/tsc.h" + +u64 perf_time_to_tsc(u64 ns, struct perf_tsc_conversion *tc); +u64 tsc_to_perf_time(u64 cyc, struct perf_tsc_conversion *tc); + +#endif -- cgit v1.1