perf/core improvements and fixes:
User visible: o Support S/390 in 'perf kvm stat' (Alexander Yarygin) Developer Stuff: o Various fixes and prep work related to supporting Intel PT (Adrian Hunter) o Introduce multiple debug variables control (Jiri Olsa) o Add callchain and additional sample information for python scripts (Joseph Schuchart) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJTx/TLAAoJENZQFvNTUqpAlacQALJ1cHV+rGEYmJ2O+HUqGqXv PHK2yxMlGziIr/YeG2cWg4eiEQvARCg9cHeUJlYl2xLR65dDD8RGsLybiU7mwnWL WLi8X0gM3FJfrG3TjJ+3Tn3S2T5LzhXU7LnlekMsbhcqdQKS0H47mw+XpiiuNtDw tuRRtb+O16wVsGC4iB0t4Ank59uuyKcUCqzg4H5MLcEkmaW3upn4SmGaX04Lvv/I jqp3xnaYnvRoH86wypmuyF1dJ5nEGa5HTGEoa/xC2xjugCqGRELXt6QyCrP2O8RE RUAy71HrDkBwAt2nroxjN/fO8uvMQWfgHVLTlfkViO8ju9oZJaQi1uQLPoCk4c8x TWY3peCmRFJKV9RblvNtLGH/HVznhIu6AQDe6HgDzq5vv7Pktw8a2FYgN7TU5YbS aRBeqJkMLmlGgkaPe2WpJRzK2AxbbUocQ9ilhrw15dCLS6h7IS6C2LwsuOxuYE5Z aGVHkB627e7aEFl6ifOe4PgvPxr5FFNeyY4RmtNeQlTdONN78uFYDBKX35V73kX2 OYnIsnb6Bh4G4mLtGVworhi7LzqPRHy3E4oRSn60aVW/ObGurAfrtWmr3IG1wd2r z3S2FJpF+6xEKyubaFWIHoAIPrI5RAHtvIJrdsUTsR7G6a8R42M+o1fvr4+NdAQx JPTLL4oe3up+VwqWVoAk =Z5AO -----END PGP SIGNATURE----- Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo: User visible changes: o Support S/390 in 'perf kvm stat' (Alexander Yarygin) Developer Stuff: o Various fixes and prep work related to supporting Intel PT (Adrian Hunter) o Introduce multiple debug variables control (Jiri Olsa) o Add callchain and additional sample information for python scripts (Joseph Schuchart) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
ec6dbcb7ad
@ -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
|
||||
|
25
arch/s390/include/uapi/asm/kvm_perf.h
Normal file
25
arch/s390/include/uapi/asm/kvm_perf.h
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Definitions for perf-kvm on s390
|
||||
*
|
||||
* Copyright 2014 IBM Corp.
|
||||
* Author(s): Alexander Yarygin <yarygin@linux.vnet.ibm.com>
|
||||
*
|
||||
* 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 <asm/sie.h>
|
||||
|
||||
#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
|
@ -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
|
||||
|
16
arch/x86/include/uapi/asm/kvm_perf.h
Normal file
16
arch/x86/include/uapi/asm/kvm_perf.h
Normal file
@ -0,0 +1,16 @@
|
||||
#ifndef _ASM_X86_KVM_PERF_H
|
||||
#define _ASM_X86_KVM_PERF_H
|
||||
|
||||
#include <asm/svm.h>
|
||||
#include <asm/vmx.h>
|
||||
#include <asm/kvm.h>
|
||||
|
||||
#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 */
|
@ -51,9 +51,9 @@ There are a couple of variants of perf kvm:
|
||||
'perf kvm stat <command>' 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 <command>' records kvm events and the events between
|
||||
start and end <command>.
|
||||
of KVM events. Currently, vmexit, mmio (x86 only) and ioport (x86 only)
|
||||
events are supported. 'perf kvm stat record <command>' records kvm events
|
||||
and the events between start and end <command>.
|
||||
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=<value>::
|
||||
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=<value>::
|
||||
Sorting key. Possible values: sample (default, sort by samples
|
||||
@ -138,7 +138,8 @@ STAT LIVE OPTIONS
|
||||
|
||||
|
||||
--event=<value>::
|
||||
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=<value>::
|
||||
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
|
||||
--------
|
||||
|
@ -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
|
||||
-----------
|
||||
|
@ -37,3 +37,6 @@ 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
|
||||
arch/s390/include/uapi/asm/sie.h
|
||||
arch/s390/include/uapi/asm/kvm_perf.h
|
||||
|
@ -295,11 +295,13 @@ 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
|
||||
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
|
||||
@ -373,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
|
||||
|
@ -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
|
||||
|
105
tools/perf/arch/s390/util/kvm-stat.c
Normal file
105
tools/perf/arch/s390/util/kvm-stat.c
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Arch specific functions for perf kvm stat.
|
||||
*
|
||||
* Copyright 2014 IBM Corp.
|
||||
* Author(s): Alexander Yarygin <yarygin@linux.vnet.ibm.com>
|
||||
*
|
||||
* 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 <asm/kvm_perf.h>
|
||||
|
||||
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;
|
||||
}
|
@ -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
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "thread.h"
|
||||
#include "map.h"
|
||||
#include "event.h"
|
||||
#include "debug.h"
|
||||
#include "tests/tests.h"
|
||||
|
||||
#define STACK_SIZE 8192
|
||||
|
156
tools/perf/arch/x86/util/kvm-stat.c
Normal file
156
tools/perf/arch/x86/util/kvm-stat.c
Normal file
@ -0,0 +1,156 @@
|
||||
#include "../../util/kvm-stat.h"
|
||||
#include <asm/kvm_perf.h>
|
||||
|
||||
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 },
|
||||
};
|
||||
|
||||
const char * const kvm_skip_events[] = {
|
||||
"HLT",
|
||||
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;
|
||||
}
|
@ -6,29 +6,9 @@
|
||||
#include "../../perf.h"
|
||||
#include <linux/types.h>
|
||||
#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)
|
||||
{
|
||||
|
@ -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__ */
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <libunwind.h>
|
||||
#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)
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -30,112 +30,24 @@
|
||||
#include <math.h>
|
||||
|
||||
#ifdef HAVE_KVM_STAT_SUPPORT
|
||||
#include <asm/svm.h>
|
||||
#include <asm/vmx.h>
|
||||
#include <asm/kvm.h>
|
||||
#include <asm/kvm_perf.h>
|
||||
#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[20]);
|
||||
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, "exit_reason");
|
||||
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:kvm_exit");
|
||||
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);
|
||||
@ -145,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:kvm_entry");
|
||||
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)
|
||||
@ -180,147 +84,28 @@ 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[20])
|
||||
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, 20, "%s", exit_reason);
|
||||
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[20])
|
||||
{
|
||||
scnprintf(decode, 20, "%#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[20])
|
||||
{
|
||||
scnprintf(decode, 20, "%#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;
|
||||
struct kvm_reg_events_ops *events_ops = kvm_reg_events_ops;
|
||||
|
||||
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;
|
||||
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 ret;
|
||||
return false;
|
||||
}
|
||||
|
||||
struct vcpu_event_record {
|
||||
@ -476,6 +261,54 @@ 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;
|
||||
|
||||
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,
|
||||
@ -524,10 +357,10 @@ 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")) {
|
||||
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);
|
||||
@ -552,7 +385,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;
|
||||
}
|
||||
|
||||
@ -565,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)
|
||||
@ -579,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);
|
||||
|
||||
@ -739,7 +576,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 +587,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 +606,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);
|
||||
@ -835,20 +672,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;
|
||||
@ -1307,13 +1130,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); \
|
||||
@ -1325,7 +1141,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",
|
||||
@ -1333,9 +1149,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)
|
||||
@ -1344,7 +1165,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]);
|
||||
}
|
||||
@ -1363,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",
|
||||
@ -1398,16 +1220,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;
|
||||
|
||||
@ -1416,7 +1238,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;
|
||||
}
|
||||
@ -1424,7 +1246,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;
|
||||
}
|
||||
@ -1469,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[] = {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
###############################
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -0,0 +1,14 @@
|
||||
#include <stdint.h>
|
||||
|
||||
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;
|
||||
}
|
@ -13,11 +13,12 @@
|
||||
#include "util/quote.h"
|
||||
#include "util/run-command.h"
|
||||
#include "util/parse-events.h"
|
||||
#include "util/debug.h"
|
||||
#include <api/fs/debugfs.h>
|
||||
#include <pthread.h>
|
||||
|
||||
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);
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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]
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 ):
|
||||
|
@ -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
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "machine.h"
|
||||
#include "symbol.h"
|
||||
#include "tests.h"
|
||||
#include "debug.h"
|
||||
|
||||
static char *test_file(int size)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include <traceevent/event-parse.h>
|
||||
#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)
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "evsel.h"
|
||||
#include "thread_map.h"
|
||||
#include "tests.h"
|
||||
#include "debug.h"
|
||||
|
||||
int test__syscall_open_tp_fields(void)
|
||||
{
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <api/fs/fs.h>
|
||||
#include <api/fs/debugfs.h>
|
||||
#include "tests.h"
|
||||
#include "debug.h"
|
||||
#include <linux/hw_breakpoint.h>
|
||||
|
||||
#define PERF_TP_SAMPLE_TYPE (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | \
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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"); \
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "util.h"
|
||||
#include "event.h"
|
||||
#include "evsel.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include "tests.h"
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "machine.h"
|
||||
#include "thread.h"
|
||||
#include "map.h"
|
||||
#include "debug.h"
|
||||
|
||||
int test__thread_mg_share(void)
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include "data.h"
|
||||
#include "util.h"
|
||||
#include "debug.h"
|
||||
|
||||
static bool check_pipe(struct perf_data_file *file)
|
||||
{
|
||||
|
@ -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, ...)
|
||||
@ -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;
|
||||
}
|
||||
|
@ -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, verbose, pr_fmt(fmt), ##__VA_ARGS__)
|
||||
#define pr_warning(fmt, ...) \
|
||||
eprintf(0, verbose, pr_fmt(fmt), ##__VA_ARGS__)
|
||||
#define pr_info(fmt, ...) \
|
||||
eprintf(0, verbose, pr_fmt(fmt), ##__VA_ARGS__)
|
||||
#define pr_debug(fmt, ...) \
|
||||
eprintf(1, verbose, pr_fmt(fmt), ##__VA_ARGS__)
|
||||
#define pr_debugN(n, fmt, ...) \
|
||||
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__)
|
||||
|
||||
int dump_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
|
||||
void trace_event(union perf_event *event);
|
||||
|
||||
@ -19,4 +37,8 @@ int ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2)));
|
||||
|
||||
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 */
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
@ -781,6 +788,7 @@ try_again:
|
||||
cpumode == PERF_RECORD_MISC_USER &&
|
||||
machine && mg != &machine->kmaps) {
|
||||
mg = &machine->kmaps;
|
||||
load_map = true;
|
||||
goto try_again;
|
||||
}
|
||||
} else {
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
@ -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)
|
||||
@ -960,6 +965,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 +973,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 +1945,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);
|
||||
|
@ -83,6 +83,8 @@ struct perf_evsel {
|
||||
int is_pos;
|
||||
bool supported;
|
||||
bool needs_swap;
|
||||
bool no_aux_samples;
|
||||
bool immediate;
|
||||
/* parse modifier helper */
|
||||
int exclude_GH;
|
||||
int nr_members;
|
||||
|
@ -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
|
||||
|
140
tools/perf/util/kvm-stat.h
Normal file
140
tools/perf/util/kvm-stat.h
Normal file
@ -0,0 +1,140 @@
|
||||
#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 exit_reasons_table *exit_reasons;
|
||||
};
|
||||
|
||||
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 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;
|
||||
};
|
||||
|
||||
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[];
|
||||
extern const char * const kvm_skip_events[];
|
||||
|
||||
#endif /* __PERF_KVM_STAT_H */
|
@ -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];
|
||||
|
||||
@ -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 && 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 && pid != th->pid_)
|
||||
th->pid_ = pid;
|
||||
machine__update_thread_pid(machine, th, pid);
|
||||
return th;
|
||||
}
|
||||
|
||||
@ -325,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;
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "vdso.h"
|
||||
#include "build-id.h"
|
||||
#include "util.h"
|
||||
#include "debug.h"
|
||||
#include <linux/string.h>
|
||||
|
||||
const char *map_type__name[MAP__NR_TYPES] = {
|
||||
@ -454,6 +455,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));
|
||||
@ -554,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;
|
||||
@ -573,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;
|
||||
@ -600,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);
|
||||
|
@ -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)
|
||||
{
|
||||
@ -141,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);
|
||||
@ -152,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);
|
||||
@ -210,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);
|
||||
|
@ -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[].
|
||||
|
@ -26,7 +26,6 @@
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "util.h"
|
||||
#include "pstack.h"
|
||||
#include "debug.h"
|
||||
#include <linux/kernel.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -27,11 +27,13 @@
|
||||
#include <errno.h>
|
||||
|
||||
#include "../../perf.h"
|
||||
#include "../debug.h"
|
||||
#include "../evsel.h"
|
||||
#include "../util.h"
|
||||
#include "../event.h"
|
||||
#include "../thread.h"
|
||||
#include "../trace-event.h"
|
||||
#include "../machine.h"
|
||||
|
||||
PyMODINIT_FUNC initperf_trace_context(void);
|
||||
|
||||
@ -50,10 +52,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 +103,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 +150,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);
|
||||
@ -272,12 +280,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;
|
||||
@ -320,18 +406,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) {
|
||||
@ -351,6 +442,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);
|
||||
|
||||
@ -361,6 +453,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 +461,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);
|
||||
}
|
||||
@ -380,7 +474,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, *dict_sample;
|
||||
static char handler_name[64];
|
||||
unsigned n = 0;
|
||||
|
||||
@ -396,6 +490,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);
|
||||
@ -405,8 +503,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",
|
||||
@ -420,6 +531,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");
|
||||
@ -427,6 +542,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 +664,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);
|
||||
@ -616,6 +731,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;
|
||||
@ -659,7 +775,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;
|
||||
@ -695,7 +811,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, "
|
||||
|
@ -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;
|
||||
@ -1296,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));
|
||||
|
||||
@ -1319,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);
|
||||
@ -1364,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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
@ -598,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)
|
||||
@ -698,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;
|
||||
|
||||
/*
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
@ -1065,6 +1080,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;
|
||||
@ -1663,6 +1679,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);
|
||||
@ -1710,6 +1727,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);
|
||||
|
@ -216,6 +216,7 @@ struct symsrc {
|
||||
GElf_Shdr dynshdr;
|
||||
|
||||
bool adjust_symbols;
|
||||
bool is_64_bit;
|
||||
#endif
|
||||
};
|
||||
|
||||
@ -239,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,
|
||||
|
@ -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);
|
||||
@ -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);
|
||||
@ -127,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);
|
||||
}
|
||||
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include "trace-event.h"
|
||||
#include <api/fs/debugfs.h>
|
||||
#include "evsel.h"
|
||||
#include "debug.h"
|
||||
|
||||
#define VERSION "0.5"
|
||||
|
||||
|
@ -22,7 +22,6 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
@ -36,6 +35,7 @@
|
||||
#include "../perf.h"
|
||||
#include "util.h"
|
||||
#include "trace-event.h"
|
||||
#include "debug.h"
|
||||
|
||||
static int input_fd;
|
||||
|
||||
|
25
tools/perf/util/tsc.c
Normal file
25
tools/perf/util/tsc.c
Normal file
@ -0,0 +1,25 @@
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/types.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);
|
||||
}
|
11
tools/perf/util/tsc.h
Normal file
11
tools/perf/util/tsc.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef __PERF_TSC_H
|
||||
#define __PERF_TSC_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#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
|
@ -3,6 +3,7 @@
|
||||
#include <elfutils/libdwfl.h>
|
||||
#include <inttypes.h>
|
||||
#include <errno.h>
|
||||
#include "debug.h"
|
||||
#include "unwind.h"
|
||||
#include "unwind-libdw.h"
|
||||
#include "machine.h"
|
||||
|
@ -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,
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "../perf.h"
|
||||
#include "util.h"
|
||||
#include "debug.h"
|
||||
#include <api/fs/fs.h>
|
||||
#include <sys/mman.h>
|
||||
#ifdef HAVE_BACKTRACE_SUPPORT
|
||||
|
@ -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";
|
||||
|
Loading…
x
Reference in New Issue
Block a user