mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-24 15:40:56 +07:00
Merge remote-tracking branch 'tip/perf/core' into kvm-updates/3.3
* tip/perf/core: (66 commits) perf, x86: Expose perf capability to other modules perf, x86: Implement arch event mask as quirk x86, perf: Disable non available architectural events jump_label: Provide jump_label_key initializers jump_label, x86: Fix section mismatch perf, core: Rate limit perf_sched_events jump_label patching perf: Fix enable_on_exec for sibling events perf: Remove superfluous arguments perf, x86: Prefer fixed-purpose counters when scheduling perf, x86: Fix event scheduler for constraints with overlapping counters perf, x86: Implement event scheduler helper functions perf: Avoid a useless pmu_disable() in the perf-tick x86/tools: Add decoded instruction dump mode x86: Update instruction decoder to support new AVX formats x86/tools: Fix insn_sanity message outputs x86/tools: Fix instruction decoder message output x86: Fix instruction decoder to handle grouped AVX instructions x86/tools: Fix Makefile to build all test tools perf test: Soft errors shouldn't stop the "Validate PERF_RECORD_" test perf test: Validate PERF_RECORD_ events and perf_sample fields ... Signed-off-by: Avi Kivity <avi@redhat.com> * commit 'b3d9468a8bd218a695e3a0ff112cd4efd27b670a': (66 commits) perf, x86: Expose perf capability to other modules perf, x86: Implement arch event mask as quirk x86, perf: Disable non available architectural events jump_label: Provide jump_label_key initializers jump_label, x86: Fix section mismatch perf, core: Rate limit perf_sched_events jump_label patching perf: Fix enable_on_exec for sibling events perf: Remove superfluous arguments perf, x86: Prefer fixed-purpose counters when scheduling perf, x86: Fix event scheduler for constraints with overlapping counters perf, x86: Implement event scheduler helper functions perf: Avoid a useless pmu_disable() in the perf-tick x86/tools: Add decoded instruction dump mode x86: Update instruction decoder to support new AVX formats x86/tools: Fix insn_sanity message outputs x86/tools: Fix instruction decoder message output x86: Fix instruction decoder to handle grouped AVX instructions x86/tools: Fix Makefile to build all test tools perf test: Soft errors shouldn't stop the "Validate PERF_RECORD_" test perf test: Validate PERF_RECORD_ events and perf_sample fields ...
This commit is contained in:
commit
9e31905f29
@ -1882,6 +1882,9 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
|
||||
arch_perfmon: [X86] Force use of architectural
|
||||
perfmon on Intel CPUs instead of the
|
||||
CPU specific event set.
|
||||
timer: [X86] Force use of architectural NMI
|
||||
timer mode (see also oprofile.timer
|
||||
for generic hr timer mode)
|
||||
|
||||
oops=panic Always panic on oopses. Default is to just kill the
|
||||
process, but there is a small probability of
|
||||
|
@ -191,8 +191,6 @@ And for string fields they are:
|
||||
|
||||
Currently, only exact string matches are supported.
|
||||
|
||||
Currently, the maximum number of predicates in a filter is 16.
|
||||
|
||||
5.2 Setting filters
|
||||
-------------------
|
||||
|
||||
|
@ -30,6 +30,10 @@ config OPROFILE_EVENT_MULTIPLEX
|
||||
config HAVE_OPROFILE
|
||||
bool
|
||||
|
||||
config OPROFILE_NMI_TIMER
|
||||
def_bool y
|
||||
depends on PERF_EVENTS && HAVE_PERF_EVENTS_NMI
|
||||
|
||||
config KPROBES
|
||||
bool "Kprobes"
|
||||
depends on MODULES
|
||||
|
@ -137,6 +137,13 @@ static inline int insn_is_avx(struct insn *insn)
|
||||
return (insn->vex_prefix.value != 0);
|
||||
}
|
||||
|
||||
/* Ensure this instruction is decoded completely */
|
||||
static inline int insn_complete(struct insn *insn)
|
||||
{
|
||||
return insn->opcode.got && insn->modrm.got && insn->sib.got &&
|
||||
insn->displacement.got && insn->immediate.got;
|
||||
}
|
||||
|
||||
static inline insn_byte_t insn_vex_m_bits(struct insn *insn)
|
||||
{
|
||||
if (insn->vex_prefix.nbytes == 2) /* 2 bytes VEX */
|
||||
|
@ -57,6 +57,7 @@
|
||||
(1 << (ARCH_PERFMON_UNHALTED_CORE_CYCLES_INDEX))
|
||||
|
||||
#define ARCH_PERFMON_BRANCH_MISSES_RETIRED 6
|
||||
#define ARCH_PERFMON_EVENTS_COUNT 7
|
||||
|
||||
/*
|
||||
* Intel "Architectural Performance Monitoring" CPUID
|
||||
@ -72,6 +73,19 @@ union cpuid10_eax {
|
||||
unsigned int full;
|
||||
};
|
||||
|
||||
union cpuid10_ebx {
|
||||
struct {
|
||||
unsigned int no_unhalted_core_cycles:1;
|
||||
unsigned int no_instructions_retired:1;
|
||||
unsigned int no_unhalted_reference_cycles:1;
|
||||
unsigned int no_llc_reference:1;
|
||||
unsigned int no_llc_misses:1;
|
||||
unsigned int no_branch_instruction_retired:1;
|
||||
unsigned int no_branch_misses_retired:1;
|
||||
} split;
|
||||
unsigned int full;
|
||||
};
|
||||
|
||||
union cpuid10_edx {
|
||||
struct {
|
||||
unsigned int num_counters_fixed:5;
|
||||
@ -81,6 +95,15 @@ union cpuid10_edx {
|
||||
unsigned int full;
|
||||
};
|
||||
|
||||
struct x86_pmu_capability {
|
||||
int version;
|
||||
int num_counters_gp;
|
||||
int num_counters_fixed;
|
||||
int bit_width_gp;
|
||||
int bit_width_fixed;
|
||||
unsigned int events_mask;
|
||||
int events_mask_len;
|
||||
};
|
||||
|
||||
/*
|
||||
* Fixed-purpose performance events:
|
||||
@ -202,6 +225,7 @@ struct perf_guest_switch_msr {
|
||||
};
|
||||
|
||||
extern struct perf_guest_switch_msr *perf_guest_get_msrs(int *nr);
|
||||
extern void perf_get_x86_pmu_capability(struct x86_pmu_capability *cap);
|
||||
#else
|
||||
static inline perf_guest_switch_msr *perf_guest_get_msrs(int *nr)
|
||||
{
|
||||
@ -209,6 +233,11 @@ static inline perf_guest_switch_msr *perf_guest_get_msrs(int *nr)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void perf_get_x86_pmu_capability(struct x86_pmu_capability *cap)
|
||||
{
|
||||
memset(cap, 0, sizeof(*cap));
|
||||
}
|
||||
|
||||
static inline void perf_events_lapic_init(void) { }
|
||||
#endif
|
||||
|
||||
|
@ -484,18 +484,195 @@ static inline int is_x86_event(struct perf_event *event)
|
||||
return event->pmu == &pmu;
|
||||
}
|
||||
|
||||
/*
|
||||
* Event scheduler state:
|
||||
*
|
||||
* Assign events iterating over all events and counters, beginning
|
||||
* with events with least weights first. Keep the current iterator
|
||||
* state in struct sched_state.
|
||||
*/
|
||||
struct sched_state {
|
||||
int weight;
|
||||
int event; /* event index */
|
||||
int counter; /* counter index */
|
||||
int unassigned; /* number of events to be assigned left */
|
||||
unsigned long used[BITS_TO_LONGS(X86_PMC_IDX_MAX)];
|
||||
};
|
||||
|
||||
/* Total max is X86_PMC_IDX_MAX, but we are O(n!) limited */
|
||||
#define SCHED_STATES_MAX 2
|
||||
|
||||
struct perf_sched {
|
||||
int max_weight;
|
||||
int max_events;
|
||||
struct event_constraint **constraints;
|
||||
struct sched_state state;
|
||||
int saved_states;
|
||||
struct sched_state saved[SCHED_STATES_MAX];
|
||||
};
|
||||
|
||||
/*
|
||||
* Initialize interator that runs through all events and counters.
|
||||
*/
|
||||
static void perf_sched_init(struct perf_sched *sched, struct event_constraint **c,
|
||||
int num, int wmin, int wmax)
|
||||
{
|
||||
int idx;
|
||||
|
||||
memset(sched, 0, sizeof(*sched));
|
||||
sched->max_events = num;
|
||||
sched->max_weight = wmax;
|
||||
sched->constraints = c;
|
||||
|
||||
for (idx = 0; idx < num; idx++) {
|
||||
if (c[idx]->weight == wmin)
|
||||
break;
|
||||
}
|
||||
|
||||
sched->state.event = idx; /* start with min weight */
|
||||
sched->state.weight = wmin;
|
||||
sched->state.unassigned = num;
|
||||
}
|
||||
|
||||
static void perf_sched_save_state(struct perf_sched *sched)
|
||||
{
|
||||
if (WARN_ON_ONCE(sched->saved_states >= SCHED_STATES_MAX))
|
||||
return;
|
||||
|
||||
sched->saved[sched->saved_states] = sched->state;
|
||||
sched->saved_states++;
|
||||
}
|
||||
|
||||
static bool perf_sched_restore_state(struct perf_sched *sched)
|
||||
{
|
||||
if (!sched->saved_states)
|
||||
return false;
|
||||
|
||||
sched->saved_states--;
|
||||
sched->state = sched->saved[sched->saved_states];
|
||||
|
||||
/* continue with next counter: */
|
||||
clear_bit(sched->state.counter++, sched->state.used);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Select a counter for the current event to schedule. Return true on
|
||||
* success.
|
||||
*/
|
||||
static bool __perf_sched_find_counter(struct perf_sched *sched)
|
||||
{
|
||||
struct event_constraint *c;
|
||||
int idx;
|
||||
|
||||
if (!sched->state.unassigned)
|
||||
return false;
|
||||
|
||||
if (sched->state.event >= sched->max_events)
|
||||
return false;
|
||||
|
||||
c = sched->constraints[sched->state.event];
|
||||
|
||||
/* Prefer fixed purpose counters */
|
||||
if (x86_pmu.num_counters_fixed) {
|
||||
idx = X86_PMC_IDX_FIXED;
|
||||
for_each_set_bit_cont(idx, c->idxmsk, X86_PMC_IDX_MAX) {
|
||||
if (!__test_and_set_bit(idx, sched->state.used))
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
/* Grab the first unused counter starting with idx */
|
||||
idx = sched->state.counter;
|
||||
for_each_set_bit_cont(idx, c->idxmsk, X86_PMC_IDX_FIXED) {
|
||||
if (!__test_and_set_bit(idx, sched->state.used))
|
||||
goto done;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
done:
|
||||
sched->state.counter = idx;
|
||||
|
||||
if (c->overlap)
|
||||
perf_sched_save_state(sched);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool perf_sched_find_counter(struct perf_sched *sched)
|
||||
{
|
||||
while (!__perf_sched_find_counter(sched)) {
|
||||
if (!perf_sched_restore_state(sched))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Go through all unassigned events and find the next one to schedule.
|
||||
* Take events with the least weight first. Return true on success.
|
||||
*/
|
||||
static bool perf_sched_next_event(struct perf_sched *sched)
|
||||
{
|
||||
struct event_constraint *c;
|
||||
|
||||
if (!sched->state.unassigned || !--sched->state.unassigned)
|
||||
return false;
|
||||
|
||||
do {
|
||||
/* next event */
|
||||
sched->state.event++;
|
||||
if (sched->state.event >= sched->max_events) {
|
||||
/* next weight */
|
||||
sched->state.event = 0;
|
||||
sched->state.weight++;
|
||||
if (sched->state.weight > sched->max_weight)
|
||||
return false;
|
||||
}
|
||||
c = sched->constraints[sched->state.event];
|
||||
} while (c->weight != sched->state.weight);
|
||||
|
||||
sched->state.counter = 0; /* start with first counter */
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Assign a counter for each event.
|
||||
*/
|
||||
static int perf_assign_events(struct event_constraint **constraints, int n,
|
||||
int wmin, int wmax, int *assign)
|
||||
{
|
||||
struct perf_sched sched;
|
||||
|
||||
perf_sched_init(&sched, constraints, n, wmin, wmax);
|
||||
|
||||
do {
|
||||
if (!perf_sched_find_counter(&sched))
|
||||
break; /* failed */
|
||||
if (assign)
|
||||
assign[sched.state.event] = sched.state.counter;
|
||||
} while (perf_sched_next_event(&sched));
|
||||
|
||||
return sched.state.unassigned;
|
||||
}
|
||||
|
||||
int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign)
|
||||
{
|
||||
struct event_constraint *c, *constraints[X86_PMC_IDX_MAX];
|
||||
unsigned long used_mask[BITS_TO_LONGS(X86_PMC_IDX_MAX)];
|
||||
int i, j, w, wmax, num = 0;
|
||||
int i, wmin, wmax, num = 0;
|
||||
struct hw_perf_event *hwc;
|
||||
|
||||
bitmap_zero(used_mask, X86_PMC_IDX_MAX);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
for (i = 0, wmin = X86_PMC_IDX_MAX, wmax = 0; i < n; i++) {
|
||||
c = x86_pmu.get_event_constraints(cpuc, cpuc->event_list[i]);
|
||||
constraints[i] = c;
|
||||
wmin = min(wmin, c->weight);
|
||||
wmax = max(wmax, c->weight);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -521,59 +698,11 @@ int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign)
|
||||
if (assign)
|
||||
assign[i] = hwc->idx;
|
||||
}
|
||||
if (i == n)
|
||||
goto done;
|
||||
|
||||
/*
|
||||
* begin slow path
|
||||
*/
|
||||
/* slow path */
|
||||
if (i != n)
|
||||
num = perf_assign_events(constraints, n, wmin, wmax, assign);
|
||||
|
||||
bitmap_zero(used_mask, X86_PMC_IDX_MAX);
|
||||
|
||||
/*
|
||||
* weight = number of possible counters
|
||||
*
|
||||
* 1 = most constrained, only works on one counter
|
||||
* wmax = least constrained, works on any counter
|
||||
*
|
||||
* assign events to counters starting with most
|
||||
* constrained events.
|
||||
*/
|
||||
wmax = x86_pmu.num_counters;
|
||||
|
||||
/*
|
||||
* when fixed event counters are present,
|
||||
* wmax is incremented by 1 to account
|
||||
* for one more choice
|
||||
*/
|
||||
if (x86_pmu.num_counters_fixed)
|
||||
wmax++;
|
||||
|
||||
for (w = 1, num = n; num && w <= wmax; w++) {
|
||||
/* for each event */
|
||||
for (i = 0; num && i < n; i++) {
|
||||
c = constraints[i];
|
||||
hwc = &cpuc->event_list[i]->hw;
|
||||
|
||||
if (c->weight != w)
|
||||
continue;
|
||||
|
||||
for_each_set_bit(j, c->idxmsk, X86_PMC_IDX_MAX) {
|
||||
if (!test_bit(j, used_mask))
|
||||
break;
|
||||
}
|
||||
|
||||
if (j == X86_PMC_IDX_MAX)
|
||||
break;
|
||||
|
||||
__set_bit(j, used_mask);
|
||||
|
||||
if (assign)
|
||||
assign[i] = j;
|
||||
num--;
|
||||
}
|
||||
}
|
||||
done:
|
||||
/*
|
||||
* scheduling failed or is just a simulation,
|
||||
* free resources if necessary
|
||||
@ -1119,6 +1248,7 @@ static void __init pmu_check_apic(void)
|
||||
|
||||
static int __init init_hw_perf_events(void)
|
||||
{
|
||||
struct x86_pmu_quirk *quirk;
|
||||
struct event_constraint *c;
|
||||
int err;
|
||||
|
||||
@ -1147,8 +1277,8 @@ static int __init init_hw_perf_events(void)
|
||||
|
||||
pr_cont("%s PMU driver.\n", x86_pmu.name);
|
||||
|
||||
if (x86_pmu.quirks)
|
||||
x86_pmu.quirks();
|
||||
for (quirk = x86_pmu.quirks; quirk; quirk = quirk->next)
|
||||
quirk->func();
|
||||
|
||||
if (x86_pmu.num_counters > X86_PMC_MAX_GENERIC) {
|
||||
WARN(1, KERN_ERR "hw perf events %d > max(%d), clipping!",
|
||||
@ -1171,7 +1301,7 @@ static int __init init_hw_perf_events(void)
|
||||
|
||||
unconstrained = (struct event_constraint)
|
||||
__EVENT_CONSTRAINT(0, (1ULL << x86_pmu.num_counters) - 1,
|
||||
0, x86_pmu.num_counters);
|
||||
0, x86_pmu.num_counters, 0);
|
||||
|
||||
if (x86_pmu.event_constraints) {
|
||||
for_each_event_constraint(c, x86_pmu.event_constraints) {
|
||||
@ -1566,3 +1696,15 @@ unsigned long perf_misc_flags(struct pt_regs *regs)
|
||||
|
||||
return misc;
|
||||
}
|
||||
|
||||
void perf_get_x86_pmu_capability(struct x86_pmu_capability *cap)
|
||||
{
|
||||
cap->version = x86_pmu.version;
|
||||
cap->num_counters_gp = x86_pmu.num_counters;
|
||||
cap->num_counters_fixed = x86_pmu.num_counters_fixed;
|
||||
cap->bit_width_gp = x86_pmu.cntval_bits;
|
||||
cap->bit_width_fixed = x86_pmu.cntval_bits;
|
||||
cap->events_mask = (unsigned int)x86_pmu.events_maskl;
|
||||
cap->events_mask_len = x86_pmu.events_mask_len;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(perf_get_x86_pmu_capability);
|
||||
|
@ -45,6 +45,7 @@ struct event_constraint {
|
||||
u64 code;
|
||||
u64 cmask;
|
||||
int weight;
|
||||
int overlap;
|
||||
};
|
||||
|
||||
struct amd_nb {
|
||||
@ -151,15 +152,40 @@ struct cpu_hw_events {
|
||||
void *kfree_on_online;
|
||||
};
|
||||
|
||||
#define __EVENT_CONSTRAINT(c, n, m, w) {\
|
||||
#define __EVENT_CONSTRAINT(c, n, m, w, o) {\
|
||||
{ .idxmsk64 = (n) }, \
|
||||
.code = (c), \
|
||||
.cmask = (m), \
|
||||
.weight = (w), \
|
||||
.overlap = (o), \
|
||||
}
|
||||
|
||||
#define EVENT_CONSTRAINT(c, n, m) \
|
||||
__EVENT_CONSTRAINT(c, n, m, HWEIGHT(n))
|
||||
__EVENT_CONSTRAINT(c, n, m, HWEIGHT(n), 0)
|
||||
|
||||
/*
|
||||
* The overlap flag marks event constraints with overlapping counter
|
||||
* masks. This is the case if the counter mask of such an event is not
|
||||
* a subset of any other counter mask of a constraint with an equal or
|
||||
* higher weight, e.g.:
|
||||
*
|
||||
* c_overlaps = EVENT_CONSTRAINT_OVERLAP(0, 0x09, 0);
|
||||
* c_another1 = EVENT_CONSTRAINT(0, 0x07, 0);
|
||||
* c_another2 = EVENT_CONSTRAINT(0, 0x38, 0);
|
||||
*
|
||||
* The event scheduler may not select the correct counter in the first
|
||||
* cycle because it needs to know which subsequent events will be
|
||||
* scheduled. It may fail to schedule the events then. So we set the
|
||||
* overlap flag for such constraints to give the scheduler a hint which
|
||||
* events to select for counter rescheduling.
|
||||
*
|
||||
* Care must be taken as the rescheduling algorithm is O(n!) which
|
||||
* will increase scheduling cycles for an over-commited system
|
||||
* dramatically. The number of such EVENT_CONSTRAINT_OVERLAP() macros
|
||||
* and its counter masks must be kept at a minimum.
|
||||
*/
|
||||
#define EVENT_CONSTRAINT_OVERLAP(c, n, m) \
|
||||
__EVENT_CONSTRAINT(c, n, m, HWEIGHT(n), 1)
|
||||
|
||||
/*
|
||||
* Constraint on the Event code.
|
||||
@ -235,6 +261,11 @@ union perf_capabilities {
|
||||
u64 capabilities;
|
||||
};
|
||||
|
||||
struct x86_pmu_quirk {
|
||||
struct x86_pmu_quirk *next;
|
||||
void (*func)(void);
|
||||
};
|
||||
|
||||
/*
|
||||
* struct x86_pmu - generic x86 pmu
|
||||
*/
|
||||
@ -259,6 +290,11 @@ struct x86_pmu {
|
||||
int num_counters_fixed;
|
||||
int cntval_bits;
|
||||
u64 cntval_mask;
|
||||
union {
|
||||
unsigned long events_maskl;
|
||||
unsigned long events_mask[BITS_TO_LONGS(ARCH_PERFMON_EVENTS_COUNT)];
|
||||
};
|
||||
int events_mask_len;
|
||||
int apic;
|
||||
u64 max_period;
|
||||
struct event_constraint *
|
||||
@ -268,7 +304,7 @@ struct x86_pmu {
|
||||
void (*put_event_constraints)(struct cpu_hw_events *cpuc,
|
||||
struct perf_event *event);
|
||||
struct event_constraint *event_constraints;
|
||||
void (*quirks)(void);
|
||||
struct x86_pmu_quirk *quirks;
|
||||
int perfctr_second_write;
|
||||
|
||||
int (*cpu_prepare)(int cpu);
|
||||
@ -309,6 +345,15 @@ struct x86_pmu {
|
||||
struct perf_guest_switch_msr *(*guest_get_msrs)(int *nr);
|
||||
};
|
||||
|
||||
#define x86_add_quirk(func_) \
|
||||
do { \
|
||||
static struct x86_pmu_quirk __quirk __initdata = { \
|
||||
.func = func_, \
|
||||
}; \
|
||||
__quirk.next = x86_pmu.quirks; \
|
||||
x86_pmu.quirks = &__quirk; \
|
||||
} while (0)
|
||||
|
||||
#define ERF_NO_HT_SHARING 1
|
||||
#define ERF_HAS_RSP_1 2
|
||||
|
||||
|
@ -492,7 +492,7 @@ static __initconst const struct x86_pmu amd_pmu = {
|
||||
static struct event_constraint amd_f15_PMC0 = EVENT_CONSTRAINT(0, 0x01, 0);
|
||||
static struct event_constraint amd_f15_PMC20 = EVENT_CONSTRAINT(0, 0x07, 0);
|
||||
static struct event_constraint amd_f15_PMC3 = EVENT_CONSTRAINT(0, 0x08, 0);
|
||||
static struct event_constraint amd_f15_PMC30 = EVENT_CONSTRAINT(0, 0x09, 0);
|
||||
static struct event_constraint amd_f15_PMC30 = EVENT_CONSTRAINT_OVERLAP(0, 0x09, 0);
|
||||
static struct event_constraint amd_f15_PMC50 = EVENT_CONSTRAINT(0, 0x3F, 0);
|
||||
static struct event_constraint amd_f15_PMC53 = EVENT_CONSTRAINT(0, 0x38, 0);
|
||||
|
||||
|
@ -1519,7 +1519,7 @@ static __initconst const struct x86_pmu intel_pmu = {
|
||||
.guest_get_msrs = intel_guest_get_msrs,
|
||||
};
|
||||
|
||||
static void intel_clovertown_quirks(void)
|
||||
static __init void intel_clovertown_quirk(void)
|
||||
{
|
||||
/*
|
||||
* PEBS is unreliable due to:
|
||||
@ -1545,19 +1545,60 @@ static void intel_clovertown_quirks(void)
|
||||
x86_pmu.pebs_constraints = NULL;
|
||||
}
|
||||
|
||||
static void intel_sandybridge_quirks(void)
|
||||
static __init void intel_sandybridge_quirk(void)
|
||||
{
|
||||
printk(KERN_WARNING "PEBS disabled due to CPU errata.\n");
|
||||
x86_pmu.pebs = 0;
|
||||
x86_pmu.pebs_constraints = NULL;
|
||||
}
|
||||
|
||||
static const struct { int id; char *name; } intel_arch_events_map[] __initconst = {
|
||||
{ PERF_COUNT_HW_CPU_CYCLES, "cpu cycles" },
|
||||
{ PERF_COUNT_HW_INSTRUCTIONS, "instructions" },
|
||||
{ PERF_COUNT_HW_BUS_CYCLES, "bus cycles" },
|
||||
{ PERF_COUNT_HW_CACHE_REFERENCES, "cache references" },
|
||||
{ PERF_COUNT_HW_CACHE_MISSES, "cache misses" },
|
||||
{ PERF_COUNT_HW_BRANCH_INSTRUCTIONS, "branch instructions" },
|
||||
{ PERF_COUNT_HW_BRANCH_MISSES, "branch misses" },
|
||||
};
|
||||
|
||||
static __init void intel_arch_events_quirk(void)
|
||||
{
|
||||
int bit;
|
||||
|
||||
/* disable event that reported as not presend by cpuid */
|
||||
for_each_set_bit(bit, x86_pmu.events_mask, ARRAY_SIZE(intel_arch_events_map)) {
|
||||
intel_perfmon_event_map[intel_arch_events_map[bit].id] = 0;
|
||||
printk(KERN_WARNING "CPUID marked event: \'%s\' unavailable\n",
|
||||
intel_arch_events_map[bit].name);
|
||||
}
|
||||
}
|
||||
|
||||
static __init void intel_nehalem_quirk(void)
|
||||
{
|
||||
union cpuid10_ebx ebx;
|
||||
|
||||
ebx.full = x86_pmu.events_maskl;
|
||||
if (ebx.split.no_branch_misses_retired) {
|
||||
/*
|
||||
* Erratum AAJ80 detected, we work it around by using
|
||||
* the BR_MISP_EXEC.ANY event. This will over-count
|
||||
* branch-misses, but it's still much better than the
|
||||
* architectural event which is often completely bogus:
|
||||
*/
|
||||
intel_perfmon_event_map[PERF_COUNT_HW_BRANCH_MISSES] = 0x7f89;
|
||||
ebx.split.no_branch_misses_retired = 0;
|
||||
x86_pmu.events_maskl = ebx.full;
|
||||
printk(KERN_INFO "CPU erratum AAJ80 worked around\n");
|
||||
}
|
||||
}
|
||||
|
||||
__init int intel_pmu_init(void)
|
||||
{
|
||||
union cpuid10_edx edx;
|
||||
union cpuid10_eax eax;
|
||||
union cpuid10_ebx ebx;
|
||||
unsigned int unused;
|
||||
unsigned int ebx;
|
||||
int version;
|
||||
|
||||
if (!cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) {
|
||||
@ -1574,8 +1615,8 @@ __init int intel_pmu_init(void)
|
||||
* Check whether the Architectural PerfMon supports
|
||||
* Branch Misses Retired hw_event or not.
|
||||
*/
|
||||
cpuid(10, &eax.full, &ebx, &unused, &edx.full);
|
||||
if (eax.split.mask_length <= ARCH_PERFMON_BRANCH_MISSES_RETIRED)
|
||||
cpuid(10, &eax.full, &ebx.full, &unused, &edx.full);
|
||||
if (eax.split.mask_length < ARCH_PERFMON_EVENTS_COUNT)
|
||||
return -ENODEV;
|
||||
|
||||
version = eax.split.version_id;
|
||||
@ -1589,6 +1630,9 @@ __init int intel_pmu_init(void)
|
||||
x86_pmu.cntval_bits = eax.split.bit_width;
|
||||
x86_pmu.cntval_mask = (1ULL << eax.split.bit_width) - 1;
|
||||
|
||||
x86_pmu.events_maskl = ebx.full;
|
||||
x86_pmu.events_mask_len = eax.split.mask_length;
|
||||
|
||||
/*
|
||||
* Quirk: v2 perfmon does not report fixed-purpose events, so
|
||||
* assume at least 3 events:
|
||||
@ -1608,6 +1652,8 @@ __init int intel_pmu_init(void)
|
||||
|
||||
intel_ds_init();
|
||||
|
||||
x86_add_quirk(intel_arch_events_quirk); /* Install first, so it runs last */
|
||||
|
||||
/*
|
||||
* Install the hw-cache-events table:
|
||||
*/
|
||||
@ -1617,7 +1663,7 @@ __init int intel_pmu_init(void)
|
||||
break;
|
||||
|
||||
case 15: /* original 65 nm celeron/pentium/core2/xeon, "Merom"/"Conroe" */
|
||||
x86_pmu.quirks = intel_clovertown_quirks;
|
||||
x86_add_quirk(intel_clovertown_quirk);
|
||||
case 22: /* single-core 65 nm celeron/core2solo "Merom-L"/"Conroe-L" */
|
||||
case 23: /* current 45 nm celeron/core2/xeon "Penryn"/"Wolfdale" */
|
||||
case 29: /* six-core 45 nm xeon "Dunnington" */
|
||||
@ -1651,17 +1697,8 @@ __init int intel_pmu_init(void)
|
||||
/* UOPS_EXECUTED.CORE_ACTIVE_CYCLES,c=1,i=1 */
|
||||
intel_perfmon_event_map[PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = 0x1803fb1;
|
||||
|
||||
if (ebx & 0x40) {
|
||||
/*
|
||||
* Erratum AAJ80 detected, we work it around by using
|
||||
* the BR_MISP_EXEC.ANY event. This will over-count
|
||||
* branch-misses, but it's still much better than the
|
||||
* architectural event which is often completely bogus:
|
||||
*/
|
||||
intel_perfmon_event_map[PERF_COUNT_HW_BRANCH_MISSES] = 0x7f89;
|
||||
x86_add_quirk(intel_nehalem_quirk);
|
||||
|
||||
pr_cont("erratum AAJ80 worked around, ");
|
||||
}
|
||||
pr_cont("Nehalem events, ");
|
||||
break;
|
||||
|
||||
@ -1701,7 +1738,7 @@ __init int intel_pmu_init(void)
|
||||
break;
|
||||
|
||||
case 42: /* SandyBridge */
|
||||
x86_pmu.quirks = intel_sandybridge_quirks;
|
||||
x86_add_quirk(intel_sandybridge_quirk);
|
||||
case 45: /* SandyBridge, "Romely-EP" */
|
||||
memcpy(hw_cache_event_ids, snb_hw_cache_event_ids,
|
||||
sizeof(hw_cache_event_ids));
|
||||
@ -1738,5 +1775,6 @@ __init int intel_pmu_init(void)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ void arch_jump_label_transform(struct jump_entry *entry,
|
||||
put_online_cpus();
|
||||
}
|
||||
|
||||
void arch_jump_label_transform_static(struct jump_entry *entry,
|
||||
__init_or_module void arch_jump_label_transform_static(struct jump_entry *entry,
|
||||
enum jump_label_type type)
|
||||
{
|
||||
__jump_label_transform(entry, type, text_poke_early);
|
||||
|
@ -82,9 +82,16 @@ insn_attr_t inat_get_avx_attribute(insn_byte_t opcode, insn_byte_t vex_m,
|
||||
const insn_attr_t *table;
|
||||
if (vex_m > X86_VEX_M_MAX || vex_p > INAT_LSTPFX_MAX)
|
||||
return 0;
|
||||
table = inat_avx_tables[vex_m][vex_p];
|
||||
/* At first, this checks the master table */
|
||||
table = inat_avx_tables[vex_m][0];
|
||||
if (!table)
|
||||
return 0;
|
||||
if (!inat_is_group(table[opcode]) && vex_p) {
|
||||
/* If this is not a group, get attribute directly */
|
||||
table = inat_avx_tables[vex_m][vex_p];
|
||||
if (!table)
|
||||
return 0;
|
||||
}
|
||||
return table[opcode];
|
||||
}
|
||||
|
||||
|
@ -202,7 +202,7 @@ void insn_get_opcode(struct insn *insn)
|
||||
m = insn_vex_m_bits(insn);
|
||||
p = insn_vex_p_bits(insn);
|
||||
insn->attr = inat_get_avx_attribute(op, m, p);
|
||||
if (!inat_accept_vex(insn->attr))
|
||||
if (!inat_accept_vex(insn->attr) && !inat_is_group(insn->attr))
|
||||
insn->attr = 0; /* This instruction is bad */
|
||||
goto end; /* VEX has only 1 byte for opcode */
|
||||
}
|
||||
@ -249,6 +249,8 @@ void insn_get_modrm(struct insn *insn)
|
||||
pfx = insn_last_prefix(insn);
|
||||
insn->attr = inat_get_group_attribute(mod, pfx,
|
||||
insn->attr);
|
||||
if (insn_is_avx(insn) && !inat_accept_vex(insn->attr))
|
||||
insn->attr = 0; /* This is bad */
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,11 @@
|
||||
# x86 Opcode Maps
|
||||
#
|
||||
# This is (mostly) based on following documentations.
|
||||
# - Intel(R) 64 and IA-32 Architectures Software Developer's Manual Vol.2
|
||||
# (#325383-040US, October 2011)
|
||||
# - Intel(R) Advanced Vector Extensions Programming Reference
|
||||
# (#319433-011,JUNE 2011).
|
||||
#
|
||||
#<Opcode maps>
|
||||
# Table: table-name
|
||||
# Referrer: escaped-name
|
||||
@ -15,10 +21,13 @@
|
||||
# EndTable
|
||||
#
|
||||
# AVX Superscripts
|
||||
# (VEX): this opcode can accept VEX prefix.
|
||||
# (oVEX): this opcode requires VEX prefix.
|
||||
# (o128): this opcode only supports 128bit VEX.
|
||||
# (o256): this opcode only supports 256bit VEX.
|
||||
# (v): this opcode requires VEX prefix.
|
||||
# (v1): this opcode only supports 128bit VEX.
|
||||
#
|
||||
# Last Prefix Superscripts
|
||||
# - (66): the last prefix is 0x66
|
||||
# - (F3): the last prefix is 0xF3
|
||||
# - (F2): the last prefix is 0xF2
|
||||
#
|
||||
|
||||
Table: one byte opcode
|
||||
@ -199,8 +208,8 @@ a0: MOV AL,Ob
|
||||
a1: MOV rAX,Ov
|
||||
a2: MOV Ob,AL
|
||||
a3: MOV Ov,rAX
|
||||
a4: MOVS/B Xb,Yb
|
||||
a5: MOVS/W/D/Q Xv,Yv
|
||||
a4: MOVS/B Yb,Xb
|
||||
a5: MOVS/W/D/Q Yv,Xv
|
||||
a6: CMPS/B Xb,Yb
|
||||
a7: CMPS/W/D Xv,Yv
|
||||
a8: TEST AL,Ib
|
||||
@ -233,8 +242,8 @@ c0: Grp2 Eb,Ib (1A)
|
||||
c1: Grp2 Ev,Ib (1A)
|
||||
c2: RETN Iw (f64)
|
||||
c3: RETN
|
||||
c4: LES Gz,Mp (i64) | 3bytes-VEX (Prefix)
|
||||
c5: LDS Gz,Mp (i64) | 2bytes-VEX (Prefix)
|
||||
c4: LES Gz,Mp (i64) | VEX+2byte (Prefix)
|
||||
c5: LDS Gz,Mp (i64) | VEX+1byte (Prefix)
|
||||
c6: Grp11 Eb,Ib (1A)
|
||||
c7: Grp11 Ev,Iz (1A)
|
||||
c8: ENTER Iw,Ib
|
||||
@ -320,14 +329,19 @@ AVXcode: 1
|
||||
# 3DNow! uses the last imm byte as opcode extension.
|
||||
0f: 3DNow! Pq,Qq,Ib
|
||||
# 0x0f 0x10-0x1f
|
||||
10: movups Vps,Wps (VEX) | movss Vss,Wss (F3),(VEX),(o128) | movupd Vpd,Wpd (66),(VEX) | movsd Vsd,Wsd (F2),(VEX),(o128)
|
||||
11: movups Wps,Vps (VEX) | movss Wss,Vss (F3),(VEX),(o128) | movupd Wpd,Vpd (66),(VEX) | movsd Wsd,Vsd (F2),(VEX),(o128)
|
||||
12: movlps Vq,Mq (VEX),(o128) | movlpd Vq,Mq (66),(VEX),(o128) | movhlps Vq,Uq (VEX),(o128) | movddup Vq,Wq (F2),(VEX) | movsldup Vq,Wq (F3),(VEX)
|
||||
13: mpvlps Mq,Vq (VEX),(o128) | movlpd Mq,Vq (66),(VEX),(o128)
|
||||
14: unpcklps Vps,Wq (VEX) | unpcklpd Vpd,Wq (66),(VEX)
|
||||
15: unpckhps Vps,Wq (VEX) | unpckhpd Vpd,Wq (66),(VEX)
|
||||
16: movhps Vq,Mq (VEX),(o128) | movhpd Vq,Mq (66),(VEX),(o128) | movlsps Vq,Uq (VEX),(o128) | movshdup Vq,Wq (F3),(VEX)
|
||||
17: movhps Mq,Vq (VEX),(o128) | movhpd Mq,Vq (66),(VEX),(o128)
|
||||
# NOTE: According to Intel SDM opcode map, vmovups and vmovupd has no operands
|
||||
# but it actually has operands. And also, vmovss and vmovsd only accept 128bit.
|
||||
# MOVSS/MOVSD has too many forms(3) on SDM. This map just shows a typical form.
|
||||
# Many AVX instructions lack v1 superscript, according to Intel AVX-Prgramming
|
||||
# Reference A.1
|
||||
10: vmovups Vps,Wps | vmovupd Vpd,Wpd (66) | vmovss Vx,Hx,Wss (F3),(v1) | vmovsd Vx,Hx,Wsd (F2),(v1)
|
||||
11: vmovups Wps,Vps | vmovupd Wpd,Vpd (66) | vmovss Wss,Hx,Vss (F3),(v1) | vmovsd Wsd,Hx,Vsd (F2),(v1)
|
||||
12: vmovlps Vq,Hq,Mq (v1) | vmovhlps Vq,Hq,Uq (v1) | vmovlpd Vq,Hq,Mq (66),(v1) | vmovsldup Vx,Wx (F3) | vmovddup Vx,Wx (F2)
|
||||
13: vmovlps Mq,Vq (v1) | vmovlpd Mq,Vq (66),(v1)
|
||||
14: vunpcklps Vx,Hx,Wx | vunpcklpd Vx,Hx,Wx (66)
|
||||
15: vunpckhps Vx,Hx,Wx | vunpckhpd Vx,Hx,Wx (66)
|
||||
16: vmovhps Vdq,Hq,Mq (v1) | vmovlhps Vdq,Hq,Uq (v1) | vmovhpd Vdq,Hq,Mq (66),(v1) | vmovshdup Vx,Wx (F3)
|
||||
17: vmovhps Mq,Vq (v1) | vmovhpd Mq,Vq (66),(v1)
|
||||
18: Grp16 (1A)
|
||||
19:
|
||||
1a:
|
||||
@ -345,14 +359,14 @@ AVXcode: 1
|
||||
25:
|
||||
26:
|
||||
27:
|
||||
28: movaps Vps,Wps (VEX) | movapd Vpd,Wpd (66),(VEX)
|
||||
29: movaps Wps,Vps (VEX) | movapd Wpd,Vpd (66),(VEX)
|
||||
2a: cvtpi2ps Vps,Qpi | cvtsi2ss Vss,Ed/q (F3),(VEX),(o128) | cvtpi2pd Vpd,Qpi (66) | cvtsi2sd Vsd,Ed/q (F2),(VEX),(o128)
|
||||
2b: movntps Mps,Vps (VEX) | movntpd Mpd,Vpd (66),(VEX)
|
||||
2c: cvttps2pi Ppi,Wps | cvttss2si Gd/q,Wss (F3),(VEX),(o128) | cvttpd2pi Ppi,Wpd (66) | cvttsd2si Gd/q,Wsd (F2),(VEX),(o128)
|
||||
2d: cvtps2pi Ppi,Wps | cvtss2si Gd/q,Wss (F3),(VEX),(o128) | cvtpd2pi Qpi,Wpd (66) | cvtsd2si Gd/q,Wsd (F2),(VEX),(o128)
|
||||
2e: ucomiss Vss,Wss (VEX),(o128) | ucomisd Vsd,Wsd (66),(VEX),(o128)
|
||||
2f: comiss Vss,Wss (VEX),(o128) | comisd Vsd,Wsd (66),(VEX),(o128)
|
||||
28: vmovaps Vps,Wps | vmovapd Vpd,Wpd (66)
|
||||
29: vmovaps Wps,Vps | vmovapd Wpd,Vpd (66)
|
||||
2a: cvtpi2ps Vps,Qpi | cvtpi2pd Vpd,Qpi (66) | vcvtsi2ss Vss,Hss,Ey (F3),(v1) | vcvtsi2sd Vsd,Hsd,Ey (F2),(v1)
|
||||
2b: vmovntps Mps,Vps | vmovntpd Mpd,Vpd (66)
|
||||
2c: cvttps2pi Ppi,Wps | cvttpd2pi Ppi,Wpd (66) | vcvttss2si Gy,Wss (F3),(v1) | vcvttsd2si Gy,Wsd (F2),(v1)
|
||||
2d: cvtps2pi Ppi,Wps | cvtpd2pi Qpi,Wpd (66) | vcvtss2si Gy,Wss (F3),(v1) | vcvtsd2si Gy,Wsd (F2),(v1)
|
||||
2e: vucomiss Vss,Wss (v1) | vucomisd Vsd,Wsd (66),(v1)
|
||||
2f: vcomiss Vss,Wss (v1) | vcomisd Vsd,Wsd (66),(v1)
|
||||
# 0x0f 0x30-0x3f
|
||||
30: WRMSR
|
||||
31: RDTSC
|
||||
@ -388,65 +402,66 @@ AVXcode: 1
|
||||
4e: CMOVLE/NG Gv,Ev
|
||||
4f: CMOVNLE/G Gv,Ev
|
||||
# 0x0f 0x50-0x5f
|
||||
50: movmskps Gd/q,Ups (VEX) | movmskpd Gd/q,Upd (66),(VEX)
|
||||
51: sqrtps Vps,Wps (VEX) | sqrtss Vss,Wss (F3),(VEX),(o128) | sqrtpd Vpd,Wpd (66),(VEX) | sqrtsd Vsd,Wsd (F2),(VEX),(o128)
|
||||
52: rsqrtps Vps,Wps (VEX) | rsqrtss Vss,Wss (F3),(VEX),(o128)
|
||||
53: rcpps Vps,Wps (VEX) | rcpss Vss,Wss (F3),(VEX),(o128)
|
||||
54: andps Vps,Wps (VEX) | andpd Vpd,Wpd (66),(VEX)
|
||||
55: andnps Vps,Wps (VEX) | andnpd Vpd,Wpd (66),(VEX)
|
||||
56: orps Vps,Wps (VEX) | orpd Vpd,Wpd (66),(VEX)
|
||||
57: xorps Vps,Wps (VEX) | xorpd Vpd,Wpd (66),(VEX)
|
||||
58: addps Vps,Wps (VEX) | addss Vss,Wss (F3),(VEX),(o128) | addpd Vpd,Wpd (66),(VEX) | addsd Vsd,Wsd (F2),(VEX),(o128)
|
||||
59: mulps Vps,Wps (VEX) | mulss Vss,Wss (F3),(VEX),(o128) | mulpd Vpd,Wpd (66),(VEX) | mulsd Vsd,Wsd (F2),(VEX),(o128)
|
||||
5a: cvtps2pd Vpd,Wps (VEX) | cvtss2sd Vsd,Wss (F3),(VEX),(o128) | cvtpd2ps Vps,Wpd (66),(VEX) | cvtsd2ss Vsd,Wsd (F2),(VEX),(o128)
|
||||
5b: cvtdq2ps Vps,Wdq (VEX) | cvtps2dq Vdq,Wps (66),(VEX) | cvttps2dq Vdq,Wps (F3),(VEX)
|
||||
5c: subps Vps,Wps (VEX) | subss Vss,Wss (F3),(VEX),(o128) | subpd Vpd,Wpd (66),(VEX) | subsd Vsd,Wsd (F2),(VEX),(o128)
|
||||
5d: minps Vps,Wps (VEX) | minss Vss,Wss (F3),(VEX),(o128) | minpd Vpd,Wpd (66),(VEX) | minsd Vsd,Wsd (F2),(VEX),(o128)
|
||||
5e: divps Vps,Wps (VEX) | divss Vss,Wss (F3),(VEX),(o128) | divpd Vpd,Wpd (66),(VEX) | divsd Vsd,Wsd (F2),(VEX),(o128)
|
||||
5f: maxps Vps,Wps (VEX) | maxss Vss,Wss (F3),(VEX),(o128) | maxpd Vpd,Wpd (66),(VEX) | maxsd Vsd,Wsd (F2),(VEX),(o128)
|
||||
50: vmovmskps Gy,Ups | vmovmskpd Gy,Upd (66)
|
||||
51: vsqrtps Vps,Wps | vsqrtpd Vpd,Wpd (66) | vsqrtss Vss,Hss,Wss (F3),(v1) | vsqrtsd Vsd,Hsd,Wsd (F2),(v1)
|
||||
52: vrsqrtps Vps,Wps | vrsqrtss Vss,Hss,Wss (F3),(v1)
|
||||
53: vrcpps Vps,Wps | vrcpss Vss,Hss,Wss (F3),(v1)
|
||||
54: vandps Vps,Hps,Wps | vandpd Vpd,Hpd,Wpd (66)
|
||||
55: vandnps Vps,Hps,Wps | vandnpd Vpd,Hpd,Wpd (66)
|
||||
56: vorps Vps,Hps,Wps | vorpd Vpd,Hpd,Wpd (66)
|
||||
57: vxorps Vps,Hps,Wps | vxorpd Vpd,Hpd,Wpd (66)
|
||||
58: vaddps Vps,Hps,Wps | vaddpd Vpd,Hpd,Wpd (66) | vaddss Vss,Hss,Wss (F3),(v1) | vaddsd Vsd,Hsd,Wsd (F2),(v1)
|
||||
59: vmulps Vps,Hps,Wps | vmulpd Vpd,Hpd,Wpd (66) | vmulss Vss,Hss,Wss (F3),(v1) | vmulsd Vsd,Hsd,Wsd (F2),(v1)
|
||||
5a: vcvtps2pd Vpd,Wps | vcvtpd2ps Vps,Wpd (66) | vcvtss2sd Vsd,Hx,Wss (F3),(v1) | vcvtsd2ss Vss,Hx,Wsd (F2),(v1)
|
||||
5b: vcvtdq2ps Vps,Wdq | vcvtps2dq Vdq,Wps (66) | vcvttps2dq Vdq,Wps (F3)
|
||||
5c: vsubps Vps,Hps,Wps | vsubpd Vpd,Hpd,Wpd (66) | vsubss Vss,Hss,Wss (F3),(v1) | vsubsd Vsd,Hsd,Wsd (F2),(v1)
|
||||
5d: vminps Vps,Hps,Wps | vminpd Vpd,Hpd,Wpd (66) | vminss Vss,Hss,Wss (F3),(v1) | vminsd Vsd,Hsd,Wsd (F2),(v1)
|
||||
5e: vdivps Vps,Hps,Wps | vdivpd Vpd,Hpd,Wpd (66) | vdivss Vss,Hss,Wss (F3),(v1) | vdivsd Vsd,Hsd,Wsd (F2),(v1)
|
||||
5f: vmaxps Vps,Hps,Wps | vmaxpd Vpd,Hpd,Wpd (66) | vmaxss Vss,Hss,Wss (F3),(v1) | vmaxsd Vsd,Hsd,Wsd (F2),(v1)
|
||||
# 0x0f 0x60-0x6f
|
||||
60: punpcklbw Pq,Qd | punpcklbw Vdq,Wdq (66),(VEX),(o128)
|
||||
61: punpcklwd Pq,Qd | punpcklwd Vdq,Wdq (66),(VEX),(o128)
|
||||
62: punpckldq Pq,Qd | punpckldq Vdq,Wdq (66),(VEX),(o128)
|
||||
63: packsswb Pq,Qq | packsswb Vdq,Wdq (66),(VEX),(o128)
|
||||
64: pcmpgtb Pq,Qq | pcmpgtb Vdq,Wdq (66),(VEX),(o128)
|
||||
65: pcmpgtw Pq,Qq | pcmpgtw Vdq,Wdq (66),(VEX),(o128)
|
||||
66: pcmpgtd Pq,Qq | pcmpgtd Vdq,Wdq (66),(VEX),(o128)
|
||||
67: packuswb Pq,Qq | packuswb Vdq,Wdq (66),(VEX),(o128)
|
||||
68: punpckhbw Pq,Qd | punpckhbw Vdq,Wdq (66),(VEX),(o128)
|
||||
69: punpckhwd Pq,Qd | punpckhwd Vdq,Wdq (66),(VEX),(o128)
|
||||
6a: punpckhdq Pq,Qd | punpckhdq Vdq,Wdq (66),(VEX),(o128)
|
||||
6b: packssdw Pq,Qd | packssdw Vdq,Wdq (66),(VEX),(o128)
|
||||
6c: punpcklqdq Vdq,Wdq (66),(VEX),(o128)
|
||||
6d: punpckhqdq Vdq,Wdq (66),(VEX),(o128)
|
||||
6e: movd/q/ Pd,Ed/q | movd/q Vdq,Ed/q (66),(VEX),(o128)
|
||||
6f: movq Pq,Qq | movdqa Vdq,Wdq (66),(VEX) | movdqu Vdq,Wdq (F3),(VEX)
|
||||
60: punpcklbw Pq,Qd | vpunpcklbw Vx,Hx,Wx (66),(v1)
|
||||
61: punpcklwd Pq,Qd | vpunpcklwd Vx,Hx,Wx (66),(v1)
|
||||
62: punpckldq Pq,Qd | vpunpckldq Vx,Hx,Wx (66),(v1)
|
||||
63: packsswb Pq,Qq | vpacksswb Vx,Hx,Wx (66),(v1)
|
||||
64: pcmpgtb Pq,Qq | vpcmpgtb Vx,Hx,Wx (66),(v1)
|
||||
65: pcmpgtw Pq,Qq | vpcmpgtw Vx,Hx,Wx (66),(v1)
|
||||
66: pcmpgtd Pq,Qq | vpcmpgtd Vx,Hx,Wx (66),(v1)
|
||||
67: packuswb Pq,Qq | vpackuswb Vx,Hx,Wx (66),(v1)
|
||||
68: punpckhbw Pq,Qd | vpunpckhbw Vx,Hx,Wx (66),(v1)
|
||||
69: punpckhwd Pq,Qd | vpunpckhwd Vx,Hx,Wx (66),(v1)
|
||||
6a: punpckhdq Pq,Qd | vpunpckhdq Vx,Hx,Wx (66),(v1)
|
||||
6b: packssdw Pq,Qd | vpackssdw Vx,Hx,Wx (66),(v1)
|
||||
6c: vpunpcklqdq Vx,Hx,Wx (66),(v1)
|
||||
6d: vpunpckhqdq Vx,Hx,Wx (66),(v1)
|
||||
6e: movd/q Pd,Ey | vmovd/q Vy,Ey (66),(v1)
|
||||
6f: movq Pq,Qq | vmovdqa Vx,Wx (66) | vmovdqu Vx,Wx (F3)
|
||||
# 0x0f 0x70-0x7f
|
||||
70: pshufw Pq,Qq,Ib | pshufd Vdq,Wdq,Ib (66),(VEX),(o128) | pshufhw Vdq,Wdq,Ib (F3),(VEX),(o128) | pshuflw VdqWdq,Ib (F2),(VEX),(o128)
|
||||
70: pshufw Pq,Qq,Ib | vpshufd Vx,Wx,Ib (66),(v1) | vpshufhw Vx,Wx,Ib (F3),(v1) | vpshuflw Vx,Wx,Ib (F2),(v1)
|
||||
71: Grp12 (1A)
|
||||
72: Grp13 (1A)
|
||||
73: Grp14 (1A)
|
||||
74: pcmpeqb Pq,Qq | pcmpeqb Vdq,Wdq (66),(VEX),(o128)
|
||||
75: pcmpeqw Pq,Qq | pcmpeqw Vdq,Wdq (66),(VEX),(o128)
|
||||
76: pcmpeqd Pq,Qq | pcmpeqd Vdq,Wdq (66),(VEX),(o128)
|
||||
77: emms/vzeroupper/vzeroall (VEX)
|
||||
78: VMREAD Ed/q,Gd/q
|
||||
79: VMWRITE Gd/q,Ed/q
|
||||
74: pcmpeqb Pq,Qq | vpcmpeqb Vx,Hx,Wx (66),(v1)
|
||||
75: pcmpeqw Pq,Qq | vpcmpeqw Vx,Hx,Wx (66),(v1)
|
||||
76: pcmpeqd Pq,Qq | vpcmpeqd Vx,Hx,Wx (66),(v1)
|
||||
# Note: Remove (v), because vzeroall and vzeroupper becomes emms without VEX.
|
||||
77: emms | vzeroupper | vzeroall
|
||||
78: VMREAD Ey,Gy
|
||||
79: VMWRITE Gy,Ey
|
||||
7a:
|
||||
7b:
|
||||
7c: haddps Vps,Wps (F2),(VEX) | haddpd Vpd,Wpd (66),(VEX)
|
||||
7d: hsubps Vps,Wps (F2),(VEX) | hsubpd Vpd,Wpd (66),(VEX)
|
||||
7e: movd/q Ed/q,Pd | movd/q Ed/q,Vdq (66),(VEX),(o128) | movq Vq,Wq (F3),(VEX),(o128)
|
||||
7f: movq Qq,Pq | movdqa Wdq,Vdq (66),(VEX) | movdqu Wdq,Vdq (F3),(VEX)
|
||||
7c: vhaddpd Vpd,Hpd,Wpd (66) | vhaddps Vps,Hps,Wps (F2)
|
||||
7d: vhsubpd Vpd,Hpd,Wpd (66) | vhsubps Vps,Hps,Wps (F2)
|
||||
7e: movd/q Ey,Pd | vmovd/q Ey,Vy (66),(v1) | vmovq Vq,Wq (F3),(v1)
|
||||
7f: movq Qq,Pq | vmovdqa Wx,Vx (66) | vmovdqu Wx,Vx (F3)
|
||||
# 0x0f 0x80-0x8f
|
||||
80: JO Jz (f64)
|
||||
81: JNO Jz (f64)
|
||||
82: JB/JNAE/JC Jz (f64)
|
||||
83: JNB/JAE/JNC Jz (f64)
|
||||
84: JZ/JE Jz (f64)
|
||||
85: JNZ/JNE Jz (f64)
|
||||
82: JB/JC/JNAE Jz (f64)
|
||||
83: JAE/JNB/JNC Jz (f64)
|
||||
84: JE/JZ Jz (f64)
|
||||
85: JNE/JNZ Jz (f64)
|
||||
86: JBE/JNA Jz (f64)
|
||||
87: JNBE/JA Jz (f64)
|
||||
87: JA/JNBE Jz (f64)
|
||||
88: JS Jz (f64)
|
||||
89: JNS Jz (f64)
|
||||
8a: JP/JPE Jz (f64)
|
||||
@ -502,18 +517,18 @@ b8: JMPE | POPCNT Gv,Ev (F3)
|
||||
b9: Grp10 (1A)
|
||||
ba: Grp8 Ev,Ib (1A)
|
||||
bb: BTC Ev,Gv
|
||||
bc: BSF Gv,Ev
|
||||
bd: BSR Gv,Ev
|
||||
bc: BSF Gv,Ev | TZCNT Gv,Ev (F3)
|
||||
bd: BSR Gv,Ev | LZCNT Gv,Ev (F3)
|
||||
be: MOVSX Gv,Eb
|
||||
bf: MOVSX Gv,Ew
|
||||
# 0x0f 0xc0-0xcf
|
||||
c0: XADD Eb,Gb
|
||||
c1: XADD Ev,Gv
|
||||
c2: cmpps Vps,Wps,Ib (VEX) | cmpss Vss,Wss,Ib (F3),(VEX),(o128) | cmppd Vpd,Wpd,Ib (66),(VEX) | cmpsd Vsd,Wsd,Ib (F2),(VEX)
|
||||
c3: movnti Md/q,Gd/q
|
||||
c4: pinsrw Pq,Rd/q/Mw,Ib | pinsrw Vdq,Rd/q/Mw,Ib (66),(VEX),(o128)
|
||||
c5: pextrw Gd,Nq,Ib | pextrw Gd,Udq,Ib (66),(VEX),(o128)
|
||||
c6: shufps Vps,Wps,Ib (VEX) | shufpd Vpd,Wpd,Ib (66),(VEX)
|
||||
c2: vcmpps Vps,Hps,Wps,Ib | vcmppd Vpd,Hpd,Wpd,Ib (66) | vcmpss Vss,Hss,Wss,Ib (F3),(v1) | vcmpsd Vsd,Hsd,Wsd,Ib (F2),(v1)
|
||||
c3: movnti My,Gy
|
||||
c4: pinsrw Pq,Ry/Mw,Ib | vpinsrw Vdq,Hdq,Ry/Mw,Ib (66),(v1)
|
||||
c5: pextrw Gd,Nq,Ib | vpextrw Gd,Udq,Ib (66),(v1)
|
||||
c6: vshufps Vps,Hps,Wps,Ib | vshufpd Vpd,Hpd,Wpd,Ib (66)
|
||||
c7: Grp9 (1A)
|
||||
c8: BSWAP RAX/EAX/R8/R8D
|
||||
c9: BSWAP RCX/ECX/R9/R9D
|
||||
@ -524,55 +539,55 @@ cd: BSWAP RBP/EBP/R13/R13D
|
||||
ce: BSWAP RSI/ESI/R14/R14D
|
||||
cf: BSWAP RDI/EDI/R15/R15D
|
||||
# 0x0f 0xd0-0xdf
|
||||
d0: addsubps Vps,Wps (F2),(VEX) | addsubpd Vpd,Wpd (66),(VEX)
|
||||
d1: psrlw Pq,Qq | psrlw Vdq,Wdq (66),(VEX),(o128)
|
||||
d2: psrld Pq,Qq | psrld Vdq,Wdq (66),(VEX),(o128)
|
||||
d3: psrlq Pq,Qq | psrlq Vdq,Wdq (66),(VEX),(o128)
|
||||
d4: paddq Pq,Qq | paddq Vdq,Wdq (66),(VEX),(o128)
|
||||
d5: pmullw Pq,Qq | pmullw Vdq,Wdq (66),(VEX),(o128)
|
||||
d6: movq Wq,Vq (66),(VEX),(o128) | movq2dq Vdq,Nq (F3) | movdq2q Pq,Uq (F2)
|
||||
d7: pmovmskb Gd,Nq | pmovmskb Gd,Udq (66),(VEX),(o128)
|
||||
d8: psubusb Pq,Qq | psubusb Vdq,Wdq (66),(VEX),(o128)
|
||||
d9: psubusw Pq,Qq | psubusw Vdq,Wdq (66),(VEX),(o128)
|
||||
da: pminub Pq,Qq | pminub Vdq,Wdq (66),(VEX),(o128)
|
||||
db: pand Pq,Qq | pand Vdq,Wdq (66),(VEX),(o128)
|
||||
dc: paddusb Pq,Qq | paddusb Vdq,Wdq (66),(VEX),(o128)
|
||||
dd: paddusw Pq,Qq | paddusw Vdq,Wdq (66),(VEX),(o128)
|
||||
de: pmaxub Pq,Qq | pmaxub Vdq,Wdq (66),(VEX),(o128)
|
||||
df: pandn Pq,Qq | pandn Vdq,Wdq (66),(VEX),(o128)
|
||||
d0: vaddsubpd Vpd,Hpd,Wpd (66) | vaddsubps Vps,Hps,Wps (F2)
|
||||
d1: psrlw Pq,Qq | vpsrlw Vx,Hx,Wx (66),(v1)
|
||||
d2: psrld Pq,Qq | vpsrld Vx,Hx,Wx (66),(v1)
|
||||
d3: psrlq Pq,Qq | vpsrlq Vx,Hx,Wx (66),(v1)
|
||||
d4: paddq Pq,Qq | vpaddq Vx,Hx,Wx (66),(v1)
|
||||
d5: pmullw Pq,Qq | vpmullw Vx,Hx,Wx (66),(v1)
|
||||
d6: vmovq Wq,Vq (66),(v1) | movq2dq Vdq,Nq (F3) | movdq2q Pq,Uq (F2)
|
||||
d7: pmovmskb Gd,Nq | vpmovmskb Gd,Ux (66),(v1)
|
||||
d8: psubusb Pq,Qq | vpsubusb Vx,Hx,Wx (66),(v1)
|
||||
d9: psubusw Pq,Qq | vpsubusw Vx,Hx,Wx (66),(v1)
|
||||
da: pminub Pq,Qq | vpminub Vx,Hx,Wx (66),(v1)
|
||||
db: pand Pq,Qq | vpand Vx,Hx,Wx (66),(v1)
|
||||
dc: paddusb Pq,Qq | vpaddusb Vx,Hx,Wx (66),(v1)
|
||||
dd: paddusw Pq,Qq | vpaddusw Vx,Hx,Wx (66),(v1)
|
||||
de: pmaxub Pq,Qq | vpmaxub Vx,Hx,Wx (66),(v1)
|
||||
df: pandn Pq,Qq | vpandn Vx,Hx,Wx (66),(v1)
|
||||
# 0x0f 0xe0-0xef
|
||||
e0: pavgb Pq,Qq | pavgb Vdq,Wdq (66),(VEX),(o128)
|
||||
e1: psraw Pq,Qq | psraw Vdq,Wdq (66),(VEX),(o128)
|
||||
e2: psrad Pq,Qq | psrad Vdq,Wdq (66),(VEX),(o128)
|
||||
e3: pavgw Pq,Qq | pavgw Vdq,Wdq (66),(VEX),(o128)
|
||||
e4: pmulhuw Pq,Qq | pmulhuw Vdq,Wdq (66),(VEX),(o128)
|
||||
e5: pmulhw Pq,Qq | pmulhw Vdq,Wdq (66),(VEX),(o128)
|
||||
e6: cvtpd2dq Vdq,Wpd (F2),(VEX) | cvttpd2dq Vdq,Wpd (66),(VEX) | cvtdq2pd Vpd,Wdq (F3),(VEX)
|
||||
e7: movntq Mq,Pq | movntdq Mdq,Vdq (66),(VEX)
|
||||
e8: psubsb Pq,Qq | psubsb Vdq,Wdq (66),(VEX),(o128)
|
||||
e9: psubsw Pq,Qq | psubsw Vdq,Wdq (66),(VEX),(o128)
|
||||
ea: pminsw Pq,Qq | pminsw Vdq,Wdq (66),(VEX),(o128)
|
||||
eb: por Pq,Qq | por Vdq,Wdq (66),(VEX),(o128)
|
||||
ec: paddsb Pq,Qq | paddsb Vdq,Wdq (66),(VEX),(o128)
|
||||
ed: paddsw Pq,Qq | paddsw Vdq,Wdq (66),(VEX),(o128)
|
||||
ee: pmaxsw Pq,Qq | pmaxsw Vdq,Wdq (66),(VEX),(o128)
|
||||
ef: pxor Pq,Qq | pxor Vdq,Wdq (66),(VEX),(o128)
|
||||
e0: pavgb Pq,Qq | vpavgb Vx,Hx,Wx (66),(v1)
|
||||
e1: psraw Pq,Qq | vpsraw Vx,Hx,Wx (66),(v1)
|
||||
e2: psrad Pq,Qq | vpsrad Vx,Hx,Wx (66),(v1)
|
||||
e3: pavgw Pq,Qq | vpavgw Vx,Hx,Wx (66),(v1)
|
||||
e4: pmulhuw Pq,Qq | vpmulhuw Vx,Hx,Wx (66),(v1)
|
||||
e5: pmulhw Pq,Qq | vpmulhw Vx,Hx,Wx (66),(v1)
|
||||
e6: vcvttpd2dq Vx,Wpd (66) | vcvtdq2pd Vx,Wdq (F3) | vcvtpd2dq Vx,Wpd (F2)
|
||||
e7: movntq Mq,Pq | vmovntdq Mx,Vx (66)
|
||||
e8: psubsb Pq,Qq | vpsubsb Vx,Hx,Wx (66),(v1)
|
||||
e9: psubsw Pq,Qq | vpsubsw Vx,Hx,Wx (66),(v1)
|
||||
ea: pminsw Pq,Qq | vpminsw Vx,Hx,Wx (66),(v1)
|
||||
eb: por Pq,Qq | vpor Vx,Hx,Wx (66),(v1)
|
||||
ec: paddsb Pq,Qq | vpaddsb Vx,Hx,Wx (66),(v1)
|
||||
ed: paddsw Pq,Qq | vpaddsw Vx,Hx,Wx (66),(v1)
|
||||
ee: pmaxsw Pq,Qq | vpmaxsw Vx,Hx,Wx (66),(v1)
|
||||
ef: pxor Pq,Qq | vpxor Vx,Hx,Wx (66),(v1)
|
||||
# 0x0f 0xf0-0xff
|
||||
f0: lddqu Vdq,Mdq (F2),(VEX)
|
||||
f1: psllw Pq,Qq | psllw Vdq,Wdq (66),(VEX),(o128)
|
||||
f2: pslld Pq,Qq | pslld Vdq,Wdq (66),(VEX),(o128)
|
||||
f3: psllq Pq,Qq | psllq Vdq,Wdq (66),(VEX),(o128)
|
||||
f4: pmuludq Pq,Qq | pmuludq Vdq,Wdq (66),(VEX),(o128)
|
||||
f5: pmaddwd Pq,Qq | pmaddwd Vdq,Wdq (66),(VEX),(o128)
|
||||
f6: psadbw Pq,Qq | psadbw Vdq,Wdq (66),(VEX),(o128)
|
||||
f7: maskmovq Pq,Nq | maskmovdqu Vdq,Udq (66),(VEX),(o128)
|
||||
f8: psubb Pq,Qq | psubb Vdq,Wdq (66),(VEX),(o128)
|
||||
f9: psubw Pq,Qq | psubw Vdq,Wdq (66),(VEX),(o128)
|
||||
fa: psubd Pq,Qq | psubd Vdq,Wdq (66),(VEX),(o128)
|
||||
fb: psubq Pq,Qq | psubq Vdq,Wdq (66),(VEX),(o128)
|
||||
fc: paddb Pq,Qq | paddb Vdq,Wdq (66),(VEX),(o128)
|
||||
fd: paddw Pq,Qq | paddw Vdq,Wdq (66),(VEX),(o128)
|
||||
fe: paddd Pq,Qq | paddd Vdq,Wdq (66),(VEX),(o128)
|
||||
f0: vlddqu Vx,Mx (F2)
|
||||
f1: psllw Pq,Qq | vpsllw Vx,Hx,Wx (66),(v1)
|
||||
f2: pslld Pq,Qq | vpslld Vx,Hx,Wx (66),(v1)
|
||||
f3: psllq Pq,Qq | vpsllq Vx,Hx,Wx (66),(v1)
|
||||
f4: pmuludq Pq,Qq | vpmuludq Vx,Hx,Wx (66),(v1)
|
||||
f5: pmaddwd Pq,Qq | vpmaddwd Vx,Hx,Wx (66),(v1)
|
||||
f6: psadbw Pq,Qq | vpsadbw Vx,Hx,Wx (66),(v1)
|
||||
f7: maskmovq Pq,Nq | vmaskmovdqu Vx,Ux (66),(v1)
|
||||
f8: psubb Pq,Qq | vpsubb Vx,Hx,Wx (66),(v1)
|
||||
f9: psubw Pq,Qq | vpsubw Vx,Hx,Wx (66),(v1)
|
||||
fa: psubd Pq,Qq | vpsubd Vx,Hx,Wx (66),(v1)
|
||||
fb: psubq Pq,Qq | vpsubq Vx,Hx,Wx (66),(v1)
|
||||
fc: paddb Pq,Qq | vpaddb Vx,Hx,Wx (66),(v1)
|
||||
fd: paddw Pq,Qq | vpaddw Vx,Hx,Wx (66),(v1)
|
||||
fe: paddd Pq,Qq | vpaddd Vx,Hx,Wx (66),(v1)
|
||||
ff:
|
||||
EndTable
|
||||
|
||||
@ -580,155 +595,193 @@ Table: 3-byte opcode 1 (0x0f 0x38)
|
||||
Referrer: 3-byte escape 1
|
||||
AVXcode: 2
|
||||
# 0x0f 0x38 0x00-0x0f
|
||||
00: pshufb Pq,Qq | pshufb Vdq,Wdq (66),(VEX),(o128)
|
||||
01: phaddw Pq,Qq | phaddw Vdq,Wdq (66),(VEX),(o128)
|
||||
02: phaddd Pq,Qq | phaddd Vdq,Wdq (66),(VEX),(o128)
|
||||
03: phaddsw Pq,Qq | phaddsw Vdq,Wdq (66),(VEX),(o128)
|
||||
04: pmaddubsw Pq,Qq | pmaddubsw Vdq,Wdq (66),(VEX),(o128)
|
||||
05: phsubw Pq,Qq | phsubw Vdq,Wdq (66),(VEX),(o128)
|
||||
06: phsubd Pq,Qq | phsubd Vdq,Wdq (66),(VEX),(o128)
|
||||
07: phsubsw Pq,Qq | phsubsw Vdq,Wdq (66),(VEX),(o128)
|
||||
08: psignb Pq,Qq | psignb Vdq,Wdq (66),(VEX),(o128)
|
||||
09: psignw Pq,Qq | psignw Vdq,Wdq (66),(VEX),(o128)
|
||||
0a: psignd Pq,Qq | psignd Vdq,Wdq (66),(VEX),(o128)
|
||||
0b: pmulhrsw Pq,Qq | pmulhrsw Vdq,Wdq (66),(VEX),(o128)
|
||||
0c: Vpermilps /r (66),(oVEX)
|
||||
0d: Vpermilpd /r (66),(oVEX)
|
||||
0e: vtestps /r (66),(oVEX)
|
||||
0f: vtestpd /r (66),(oVEX)
|
||||
00: pshufb Pq,Qq | vpshufb Vx,Hx,Wx (66),(v1)
|
||||
01: phaddw Pq,Qq | vphaddw Vx,Hx,Wx (66),(v1)
|
||||
02: phaddd Pq,Qq | vphaddd Vx,Hx,Wx (66),(v1)
|
||||
03: phaddsw Pq,Qq | vphaddsw Vx,Hx,Wx (66),(v1)
|
||||
04: pmaddubsw Pq,Qq | vpmaddubsw Vx,Hx,Wx (66),(v1)
|
||||
05: phsubw Pq,Qq | vphsubw Vx,Hx,Wx (66),(v1)
|
||||
06: phsubd Pq,Qq | vphsubd Vx,Hx,Wx (66),(v1)
|
||||
07: phsubsw Pq,Qq | vphsubsw Vx,Hx,Wx (66),(v1)
|
||||
08: psignb Pq,Qq | vpsignb Vx,Hx,Wx (66),(v1)
|
||||
09: psignw Pq,Qq | vpsignw Vx,Hx,Wx (66),(v1)
|
||||
0a: psignd Pq,Qq | vpsignd Vx,Hx,Wx (66),(v1)
|
||||
0b: pmulhrsw Pq,Qq | vpmulhrsw Vx,Hx,Wx (66),(v1)
|
||||
0c: vpermilps Vx,Hx,Wx (66),(v)
|
||||
0d: vpermilpd Vx,Hx,Wx (66),(v)
|
||||
0e: vtestps Vx,Wx (66),(v)
|
||||
0f: vtestpd Vx,Wx (66),(v)
|
||||
# 0x0f 0x38 0x10-0x1f
|
||||
10: pblendvb Vdq,Wdq (66)
|
||||
11:
|
||||
12:
|
||||
13:
|
||||
13: vcvtph2ps Vx,Wx,Ib (66),(v)
|
||||
14: blendvps Vdq,Wdq (66)
|
||||
15: blendvpd Vdq,Wdq (66)
|
||||
16:
|
||||
17: ptest Vdq,Wdq (66),(VEX)
|
||||
18: vbroadcastss /r (66),(oVEX)
|
||||
19: vbroadcastsd /r (66),(oVEX),(o256)
|
||||
1a: vbroadcastf128 /r (66),(oVEX),(o256)
|
||||
16: vpermps Vqq,Hqq,Wqq (66),(v)
|
||||
17: vptest Vx,Wx (66)
|
||||
18: vbroadcastss Vx,Wd (66),(v)
|
||||
19: vbroadcastsd Vqq,Wq (66),(v)
|
||||
1a: vbroadcastf128 Vqq,Mdq (66),(v)
|
||||
1b:
|
||||
1c: pabsb Pq,Qq | pabsb Vdq,Wdq (66),(VEX),(o128)
|
||||
1d: pabsw Pq,Qq | pabsw Vdq,Wdq (66),(VEX),(o128)
|
||||
1e: pabsd Pq,Qq | pabsd Vdq,Wdq (66),(VEX),(o128)
|
||||
1c: pabsb Pq,Qq | vpabsb Vx,Wx (66),(v1)
|
||||
1d: pabsw Pq,Qq | vpabsw Vx,Wx (66),(v1)
|
||||
1e: pabsd Pq,Qq | vpabsd Vx,Wx (66),(v1)
|
||||
1f:
|
||||
# 0x0f 0x38 0x20-0x2f
|
||||
20: pmovsxbw Vdq,Udq/Mq (66),(VEX),(o128)
|
||||
21: pmovsxbd Vdq,Udq/Md (66),(VEX),(o128)
|
||||
22: pmovsxbq Vdq,Udq/Mw (66),(VEX),(o128)
|
||||
23: pmovsxwd Vdq,Udq/Mq (66),(VEX),(o128)
|
||||
24: pmovsxwq Vdq,Udq/Md (66),(VEX),(o128)
|
||||
25: pmovsxdq Vdq,Udq/Mq (66),(VEX),(o128)
|
||||
20: vpmovsxbw Vx,Ux/Mq (66),(v1)
|
||||
21: vpmovsxbd Vx,Ux/Md (66),(v1)
|
||||
22: vpmovsxbq Vx,Ux/Mw (66),(v1)
|
||||
23: vpmovsxwd Vx,Ux/Mq (66),(v1)
|
||||
24: vpmovsxwq Vx,Ux/Md (66),(v1)
|
||||
25: vpmovsxdq Vx,Ux/Mq (66),(v1)
|
||||
26:
|
||||
27:
|
||||
28: pmuldq Vdq,Wdq (66),(VEX),(o128)
|
||||
29: pcmpeqq Vdq,Wdq (66),(VEX),(o128)
|
||||
2a: movntdqa Vdq,Mdq (66),(VEX),(o128)
|
||||
2b: packusdw Vdq,Wdq (66),(VEX),(o128)
|
||||
2c: vmaskmovps(ld) /r (66),(oVEX)
|
||||
2d: vmaskmovpd(ld) /r (66),(oVEX)
|
||||
2e: vmaskmovps(st) /r (66),(oVEX)
|
||||
2f: vmaskmovpd(st) /r (66),(oVEX)
|
||||
28: vpmuldq Vx,Hx,Wx (66),(v1)
|
||||
29: vpcmpeqq Vx,Hx,Wx (66),(v1)
|
||||
2a: vmovntdqa Vx,Mx (66),(v1)
|
||||
2b: vpackusdw Vx,Hx,Wx (66),(v1)
|
||||
2c: vmaskmovps Vx,Hx,Mx (66),(v)
|
||||
2d: vmaskmovpd Vx,Hx,Mx (66),(v)
|
||||
2e: vmaskmovps Mx,Hx,Vx (66),(v)
|
||||
2f: vmaskmovpd Mx,Hx,Vx (66),(v)
|
||||
# 0x0f 0x38 0x30-0x3f
|
||||
30: pmovzxbw Vdq,Udq/Mq (66),(VEX),(o128)
|
||||
31: pmovzxbd Vdq,Udq/Md (66),(VEX),(o128)
|
||||
32: pmovzxbq Vdq,Udq/Mw (66),(VEX),(o128)
|
||||
33: pmovzxwd Vdq,Udq/Mq (66),(VEX),(o128)
|
||||
34: pmovzxwq Vdq,Udq/Md (66),(VEX),(o128)
|
||||
35: pmovzxdq Vdq,Udq/Mq (66),(VEX),(o128)
|
||||
36:
|
||||
37: pcmpgtq Vdq,Wdq (66),(VEX),(o128)
|
||||
38: pminsb Vdq,Wdq (66),(VEX),(o128)
|
||||
39: pminsd Vdq,Wdq (66),(VEX),(o128)
|
||||
3a: pminuw Vdq,Wdq (66),(VEX),(o128)
|
||||
3b: pminud Vdq,Wdq (66),(VEX),(o128)
|
||||
3c: pmaxsb Vdq,Wdq (66),(VEX),(o128)
|
||||
3d: pmaxsd Vdq,Wdq (66),(VEX),(o128)
|
||||
3e: pmaxuw Vdq,Wdq (66),(VEX),(o128)
|
||||
3f: pmaxud Vdq,Wdq (66),(VEX),(o128)
|
||||
30: vpmovzxbw Vx,Ux/Mq (66),(v1)
|
||||
31: vpmovzxbd Vx,Ux/Md (66),(v1)
|
||||
32: vpmovzxbq Vx,Ux/Mw (66),(v1)
|
||||
33: vpmovzxwd Vx,Ux/Mq (66),(v1)
|
||||
34: vpmovzxwq Vx,Ux/Md (66),(v1)
|
||||
35: vpmovzxdq Vx,Ux/Mq (66),(v1)
|
||||
36: vpermd Vqq,Hqq,Wqq (66),(v)
|
||||
37: vpcmpgtq Vx,Hx,Wx (66),(v1)
|
||||
38: vpminsb Vx,Hx,Wx (66),(v1)
|
||||
39: vpminsd Vx,Hx,Wx (66),(v1)
|
||||
3a: vpminuw Vx,Hx,Wx (66),(v1)
|
||||
3b: vpminud Vx,Hx,Wx (66),(v1)
|
||||
3c: vpmaxsb Vx,Hx,Wx (66),(v1)
|
||||
3d: vpmaxsd Vx,Hx,Wx (66),(v1)
|
||||
3e: vpmaxuw Vx,Hx,Wx (66),(v1)
|
||||
3f: vpmaxud Vx,Hx,Wx (66),(v1)
|
||||
# 0x0f 0x38 0x40-0x8f
|
||||
40: pmulld Vdq,Wdq (66),(VEX),(o128)
|
||||
41: phminposuw Vdq,Wdq (66),(VEX),(o128)
|
||||
80: INVEPT Gd/q,Mdq (66)
|
||||
81: INVPID Gd/q,Mdq (66)
|
||||
40: vpmulld Vx,Hx,Wx (66),(v1)
|
||||
41: vphminposuw Vdq,Wdq (66),(v1)
|
||||
42:
|
||||
43:
|
||||
44:
|
||||
45: vpsrlvd/q Vx,Hx,Wx (66),(v)
|
||||
46: vpsravd Vx,Hx,Wx (66),(v)
|
||||
47: vpsllvd/q Vx,Hx,Wx (66),(v)
|
||||
# Skip 0x48-0x57
|
||||
58: vpbroadcastd Vx,Wx (66),(v)
|
||||
59: vpbroadcastq Vx,Wx (66),(v)
|
||||
5a: vbroadcasti128 Vqq,Mdq (66),(v)
|
||||
# Skip 0x5b-0x77
|
||||
78: vpbroadcastb Vx,Wx (66),(v)
|
||||
79: vpbroadcastw Vx,Wx (66),(v)
|
||||
# Skip 0x7a-0x7f
|
||||
80: INVEPT Gy,Mdq (66)
|
||||
81: INVPID Gy,Mdq (66)
|
||||
82: INVPCID Gy,Mdq (66)
|
||||
8c: vpmaskmovd/q Vx,Hx,Mx (66),(v)
|
||||
8e: vpmaskmovd/q Mx,Vx,Hx (66),(v)
|
||||
# 0x0f 0x38 0x90-0xbf (FMA)
|
||||
96: vfmaddsub132pd/ps /r (66),(VEX)
|
||||
97: vfmsubadd132pd/ps /r (66),(VEX)
|
||||
98: vfmadd132pd/ps /r (66),(VEX)
|
||||
99: vfmadd132sd/ss /r (66),(VEX),(o128)
|
||||
9a: vfmsub132pd/ps /r (66),(VEX)
|
||||
9b: vfmsub132sd/ss /r (66),(VEX),(o128)
|
||||
9c: vfnmadd132pd/ps /r (66),(VEX)
|
||||
9d: vfnmadd132sd/ss /r (66),(VEX),(o128)
|
||||
9e: vfnmsub132pd/ps /r (66),(VEX)
|
||||
9f: vfnmsub132sd/ss /r (66),(VEX),(o128)
|
||||
a6: vfmaddsub213pd/ps /r (66),(VEX)
|
||||
a7: vfmsubadd213pd/ps /r (66),(VEX)
|
||||
a8: vfmadd213pd/ps /r (66),(VEX)
|
||||
a9: vfmadd213sd/ss /r (66),(VEX),(o128)
|
||||
aa: vfmsub213pd/ps /r (66),(VEX)
|
||||
ab: vfmsub213sd/ss /r (66),(VEX),(o128)
|
||||
ac: vfnmadd213pd/ps /r (66),(VEX)
|
||||
ad: vfnmadd213sd/ss /r (66),(VEX),(o128)
|
||||
ae: vfnmsub213pd/ps /r (66),(VEX)
|
||||
af: vfnmsub213sd/ss /r (66),(VEX),(o128)
|
||||
b6: vfmaddsub231pd/ps /r (66),(VEX)
|
||||
b7: vfmsubadd231pd/ps /r (66),(VEX)
|
||||
b8: vfmadd231pd/ps /r (66),(VEX)
|
||||
b9: vfmadd231sd/ss /r (66),(VEX),(o128)
|
||||
ba: vfmsub231pd/ps /r (66),(VEX)
|
||||
bb: vfmsub231sd/ss /r (66),(VEX),(o128)
|
||||
bc: vfnmadd231pd/ps /r (66),(VEX)
|
||||
bd: vfnmadd231sd/ss /r (66),(VEX),(o128)
|
||||
be: vfnmsub231pd/ps /r (66),(VEX)
|
||||
bf: vfnmsub231sd/ss /r (66),(VEX),(o128)
|
||||
90: vgatherdd/q Vx,Hx,Wx (66),(v)
|
||||
91: vgatherqd/q Vx,Hx,Wx (66),(v)
|
||||
92: vgatherdps/d Vx,Hx,Wx (66),(v)
|
||||
93: vgatherqps/d Vx,Hx,Wx (66),(v)
|
||||
94:
|
||||
95:
|
||||
96: vfmaddsub132ps/d Vx,Hx,Wx (66),(v)
|
||||
97: vfmsubadd132ps/d Vx,Hx,Wx (66),(v)
|
||||
98: vfmadd132ps/d Vx,Hx,Wx (66),(v)
|
||||
99: vfmadd132ss/d Vx,Hx,Wx (66),(v),(v1)
|
||||
9a: vfmsub132ps/d Vx,Hx,Wx (66),(v)
|
||||
9b: vfmsub132ss/d Vx,Hx,Wx (66),(v),(v1)
|
||||
9c: vfnmadd132ps/d Vx,Hx,Wx (66),(v)
|
||||
9d: vfnmadd132ss/d Vx,Hx,Wx (66),(v),(v1)
|
||||
9e: vfnmsub132ps/d Vx,Hx,Wx (66),(v)
|
||||
9f: vfnmsub132ss/d Vx,Hx,Wx (66),(v),(v1)
|
||||
a6: vfmaddsub213ps/d Vx,Hx,Wx (66),(v)
|
||||
a7: vfmsubadd213ps/d Vx,Hx,Wx (66),(v)
|
||||
a8: vfmadd213ps/d Vx,Hx,Wx (66),(v)
|
||||
a9: vfmadd213ss/d Vx,Hx,Wx (66),(v),(v1)
|
||||
aa: vfmsub213ps/d Vx,Hx,Wx (66),(v)
|
||||
ab: vfmsub213ss/d Vx,Hx,Wx (66),(v),(v1)
|
||||
ac: vfnmadd213ps/d Vx,Hx,Wx (66),(v)
|
||||
ad: vfnmadd213ss/d Vx,Hx,Wx (66),(v),(v1)
|
||||
ae: vfnmsub213ps/d Vx,Hx,Wx (66),(v)
|
||||
af: vfnmsub213ss/d Vx,Hx,Wx (66),(v),(v1)
|
||||
b6: vfmaddsub231ps/d Vx,Hx,Wx (66),(v)
|
||||
b7: vfmsubadd231ps/d Vx,Hx,Wx (66),(v)
|
||||
b8: vfmadd231ps/d Vx,Hx,Wx (66),(v)
|
||||
b9: vfmadd231ss/d Vx,Hx,Wx (66),(v),(v1)
|
||||
ba: vfmsub231ps/d Vx,Hx,Wx (66),(v)
|
||||
bb: vfmsub231ss/d Vx,Hx,Wx (66),(v),(v1)
|
||||
bc: vfnmadd231ps/d Vx,Hx,Wx (66),(v)
|
||||
bd: vfnmadd231ss/d Vx,Hx,Wx (66),(v),(v1)
|
||||
be: vfnmsub231ps/d Vx,Hx,Wx (66),(v)
|
||||
bf: vfnmsub231ss/d Vx,Hx,Wx (66),(v),(v1)
|
||||
# 0x0f 0x38 0xc0-0xff
|
||||
db: aesimc Vdq,Wdq (66),(VEX),(o128)
|
||||
dc: aesenc Vdq,Wdq (66),(VEX),(o128)
|
||||
dd: aesenclast Vdq,Wdq (66),(VEX),(o128)
|
||||
de: aesdec Vdq,Wdq (66),(VEX),(o128)
|
||||
df: aesdeclast Vdq,Wdq (66),(VEX),(o128)
|
||||
f0: MOVBE Gv,Mv | CRC32 Gd,Eb (F2)
|
||||
f1: MOVBE Mv,Gv | CRC32 Gd,Ev (F2)
|
||||
db: VAESIMC Vdq,Wdq (66),(v1)
|
||||
dc: VAESENC Vdq,Hdq,Wdq (66),(v1)
|
||||
dd: VAESENCLAST Vdq,Hdq,Wdq (66),(v1)
|
||||
de: VAESDEC Vdq,Hdq,Wdq (66),(v1)
|
||||
df: VAESDECLAST Vdq,Hdq,Wdq (66),(v1)
|
||||
f0: MOVBE Gy,My | MOVBE Gw,Mw (66) | CRC32 Gd,Eb (F2)
|
||||
f1: MOVBE My,Gy | MOVBE Mw,Gw (66) | CRC32 Gd,Ey (F2)
|
||||
f3: ANDN Gy,By,Ey (v)
|
||||
f4: Grp17 (1A)
|
||||
f5: BZHI Gy,Ey,By (v) | PEXT Gy,By,Ey (F3),(v) | PDEP Gy,By,Ey (F2),(v)
|
||||
f6: MULX By,Gy,rDX,Ey (F2),(v)
|
||||
f7: BEXTR Gy,Ey,By (v) | SHLX Gy,Ey,By (66),(v) | SARX Gy,Ey,By (F3),(v) | SHRX Gy,Ey,By (F2),(v)
|
||||
EndTable
|
||||
|
||||
Table: 3-byte opcode 2 (0x0f 0x3a)
|
||||
Referrer: 3-byte escape 2
|
||||
AVXcode: 3
|
||||
# 0x0f 0x3a 0x00-0xff
|
||||
04: vpermilps /r,Ib (66),(oVEX)
|
||||
05: vpermilpd /r,Ib (66),(oVEX)
|
||||
06: vperm2f128 /r,Ib (66),(oVEX),(o256)
|
||||
08: roundps Vdq,Wdq,Ib (66),(VEX)
|
||||
09: roundpd Vdq,Wdq,Ib (66),(VEX)
|
||||
0a: roundss Vss,Wss,Ib (66),(VEX),(o128)
|
||||
0b: roundsd Vsd,Wsd,Ib (66),(VEX),(o128)
|
||||
0c: blendps Vdq,Wdq,Ib (66),(VEX)
|
||||
0d: blendpd Vdq,Wdq,Ib (66),(VEX)
|
||||
0e: pblendw Vdq,Wdq,Ib (66),(VEX),(o128)
|
||||
0f: palignr Pq,Qq,Ib | palignr Vdq,Wdq,Ib (66),(VEX),(o128)
|
||||
14: pextrb Rd/Mb,Vdq,Ib (66),(VEX),(o128)
|
||||
15: pextrw Rd/Mw,Vdq,Ib (66),(VEX),(o128)
|
||||
16: pextrd/pextrq Ed/q,Vdq,Ib (66),(VEX),(o128)
|
||||
17: extractps Ed,Vdq,Ib (66),(VEX),(o128)
|
||||
18: vinsertf128 /r,Ib (66),(oVEX),(o256)
|
||||
19: vextractf128 /r,Ib (66),(oVEX),(o256)
|
||||
20: pinsrb Vdq,Rd/q/Mb,Ib (66),(VEX),(o128)
|
||||
21: insertps Vdq,Udq/Md,Ib (66),(VEX),(o128)
|
||||
22: pinsrd/pinsrq Vdq,Ed/q,Ib (66),(VEX),(o128)
|
||||
40: dpps Vdq,Wdq,Ib (66),(VEX)
|
||||
41: dppd Vdq,Wdq,Ib (66),(VEX),(o128)
|
||||
42: mpsadbw Vdq,Wdq,Ib (66),(VEX),(o128)
|
||||
44: pclmulq Vdq,Wdq,Ib (66),(VEX),(o128)
|
||||
4a: vblendvps /r,Ib (66),(oVEX)
|
||||
4b: vblendvpd /r,Ib (66),(oVEX)
|
||||
4c: vpblendvb /r,Ib (66),(oVEX),(o128)
|
||||
60: pcmpestrm Vdq,Wdq,Ib (66),(VEX),(o128)
|
||||
61: pcmpestri Vdq,Wdq,Ib (66),(VEX),(o128)
|
||||
62: pcmpistrm Vdq,Wdq,Ib (66),(VEX),(o128)
|
||||
63: pcmpistri Vdq,Wdq,Ib (66),(VEX),(o128)
|
||||
df: aeskeygenassist Vdq,Wdq,Ib (66),(VEX),(o128)
|
||||
00: vpermq Vqq,Wqq,Ib (66),(v)
|
||||
01: vpermpd Vqq,Wqq,Ib (66),(v)
|
||||
02: vpblendd Vx,Hx,Wx,Ib (66),(v)
|
||||
03:
|
||||
04: vpermilps Vx,Wx,Ib (66),(v)
|
||||
05: vpermilpd Vx,Wx,Ib (66),(v)
|
||||
06: vperm2f128 Vqq,Hqq,Wqq,Ib (66),(v)
|
||||
07:
|
||||
08: vroundps Vx,Wx,Ib (66)
|
||||
09: vroundpd Vx,Wx,Ib (66)
|
||||
0a: vroundss Vss,Wss,Ib (66),(v1)
|
||||
0b: vroundsd Vsd,Wsd,Ib (66),(v1)
|
||||
0c: vblendps Vx,Hx,Wx,Ib (66)
|
||||
0d: vblendpd Vx,Hx,Wx,Ib (66)
|
||||
0e: vpblendw Vx,Hx,Wx,Ib (66),(v1)
|
||||
0f: palignr Pq,Qq,Ib | vpalignr Vx,Hx,Wx,Ib (66),(v1)
|
||||
14: vpextrb Rd/Mb,Vdq,Ib (66),(v1)
|
||||
15: vpextrw Rd/Mw,Vdq,Ib (66),(v1)
|
||||
16: vpextrd/q Ey,Vdq,Ib (66),(v1)
|
||||
17: vextractps Ed,Vdq,Ib (66),(v1)
|
||||
18: vinsertf128 Vqq,Hqq,Wqq,Ib (66),(v)
|
||||
19: vextractf128 Wdq,Vqq,Ib (66),(v)
|
||||
1d: vcvtps2ph Wx,Vx,Ib (66),(v)
|
||||
20: vpinsrb Vdq,Hdq,Ry/Mb,Ib (66),(v1)
|
||||
21: vinsertps Vdq,Hdq,Udq/Md,Ib (66),(v1)
|
||||
22: vpinsrd/q Vdq,Hdq,Ey,Ib (66),(v1)
|
||||
38: vinserti128 Vqq,Hqq,Wqq,Ib (66),(v)
|
||||
39: vextracti128 Wdq,Vqq,Ib (66),(v)
|
||||
40: vdpps Vx,Hx,Wx,Ib (66)
|
||||
41: vdppd Vdq,Hdq,Wdq,Ib (66),(v1)
|
||||
42: vmpsadbw Vx,Hx,Wx,Ib (66),(v1)
|
||||
44: vpclmulqdq Vdq,Hdq,Wdq,Ib (66),(v1)
|
||||
46: vperm2i128 Vqq,Hqq,Wqq,Ib (66),(v)
|
||||
4a: vblendvps Vx,Hx,Wx,Lx (66),(v)
|
||||
4b: vblendvpd Vx,Hx,Wx,Lx (66),(v)
|
||||
4c: vpblendvb Vx,Hx,Wx,Lx (66),(v1)
|
||||
60: vpcmpestrm Vdq,Wdq,Ib (66),(v1)
|
||||
61: vpcmpestri Vdq,Wdq,Ib (66),(v1)
|
||||
62: vpcmpistrm Vdq,Wdq,Ib (66),(v1)
|
||||
63: vpcmpistri Vdq,Wdq,Ib (66),(v1)
|
||||
df: VAESKEYGEN Vdq,Wdq,Ib (66),(v1)
|
||||
f0: RORX Gy,Ey,Ib (F2),(v)
|
||||
EndTable
|
||||
|
||||
GrpTable: Grp1
|
||||
@ -790,7 +843,7 @@ GrpTable: Grp5
|
||||
2: CALLN Ev (f64)
|
||||
3: CALLF Ep
|
||||
4: JMPN Ev (f64)
|
||||
5: JMPF Ep
|
||||
5: JMPF Mp
|
||||
6: PUSH Ev (d64)
|
||||
7:
|
||||
EndTable
|
||||
@ -807,7 +860,7 @@ EndTable
|
||||
GrpTable: Grp7
|
||||
0: SGDT Ms | VMCALL (001),(11B) | VMLAUNCH (010),(11B) | VMRESUME (011),(11B) | VMXOFF (100),(11B)
|
||||
1: SIDT Ms | MONITOR (000),(11B) | MWAIT (001)
|
||||
2: LGDT Ms | XGETBV (000),(11B) | XSETBV (001),(11B)
|
||||
2: LGDT Ms | XGETBV (000),(11B) | XSETBV (001),(11B) | VMFUNC (100),(11B)
|
||||
3: LIDT Ms
|
||||
4: SMSW Mw/Rv
|
||||
5:
|
||||
@ -824,44 +877,45 @@ EndTable
|
||||
|
||||
GrpTable: Grp9
|
||||
1: CMPXCHG8B/16B Mq/Mdq
|
||||
6: VMPTRLD Mq | VMCLEAR Mq (66) | VMXON Mq (F3)
|
||||
7: VMPTRST Mq
|
||||
6: VMPTRLD Mq | VMCLEAR Mq (66) | VMXON Mq (F3) | RDRAND Rv (11B)
|
||||
7: VMPTRST Mq | VMPTRST Mq (F3)
|
||||
EndTable
|
||||
|
||||
GrpTable: Grp10
|
||||
EndTable
|
||||
|
||||
GrpTable: Grp11
|
||||
# Note: the operands are given by group opcode
|
||||
0: MOV
|
||||
EndTable
|
||||
|
||||
GrpTable: Grp12
|
||||
2: psrlw Nq,Ib (11B) | psrlw Udq,Ib (66),(11B),(VEX),(o128)
|
||||
4: psraw Nq,Ib (11B) | psraw Udq,Ib (66),(11B),(VEX),(o128)
|
||||
6: psllw Nq,Ib (11B) | psllw Udq,Ib (66),(11B),(VEX),(o128)
|
||||
2: psrlw Nq,Ib (11B) | vpsrlw Hx,Ux,Ib (66),(11B),(v1)
|
||||
4: psraw Nq,Ib (11B) | vpsraw Hx,Ux,Ib (66),(11B),(v1)
|
||||
6: psllw Nq,Ib (11B) | vpsllw Hx,Ux,Ib (66),(11B),(v1)
|
||||
EndTable
|
||||
|
||||
GrpTable: Grp13
|
||||
2: psrld Nq,Ib (11B) | psrld Udq,Ib (66),(11B),(VEX),(o128)
|
||||
4: psrad Nq,Ib (11B) | psrad Udq,Ib (66),(11B),(VEX),(o128)
|
||||
6: pslld Nq,Ib (11B) | pslld Udq,Ib (66),(11B),(VEX),(o128)
|
||||
2: psrld Nq,Ib (11B) | vpsrld Hx,Ux,Ib (66),(11B),(v1)
|
||||
4: psrad Nq,Ib (11B) | vpsrad Hx,Ux,Ib (66),(11B),(v1)
|
||||
6: pslld Nq,Ib (11B) | vpslld Hx,Ux,Ib (66),(11B),(v1)
|
||||
EndTable
|
||||
|
||||
GrpTable: Grp14
|
||||
2: psrlq Nq,Ib (11B) | psrlq Udq,Ib (66),(11B),(VEX),(o128)
|
||||
3: psrldq Udq,Ib (66),(11B),(VEX),(o128)
|
||||
6: psllq Nq,Ib (11B) | psllq Udq,Ib (66),(11B),(VEX),(o128)
|
||||
7: pslldq Udq,Ib (66),(11B),(VEX),(o128)
|
||||
2: psrlq Nq,Ib (11B) | vpsrlq Hx,Ux,Ib (66),(11B),(v1)
|
||||
3: vpsrldq Hx,Ux,Ib (66),(11B),(v1)
|
||||
6: psllq Nq,Ib (11B) | vpsllq Hx,Ux,Ib (66),(11B),(v1)
|
||||
7: vpslldq Hx,Ux,Ib (66),(11B),(v1)
|
||||
EndTable
|
||||
|
||||
GrpTable: Grp15
|
||||
0: fxsave
|
||||
1: fxstor
|
||||
2: ldmxcsr (VEX)
|
||||
3: stmxcsr (VEX)
|
||||
0: fxsave | RDFSBASE Ry (F3),(11B)
|
||||
1: fxstor | RDGSBASE Ry (F3),(11B)
|
||||
2: vldmxcsr Md (v1) | WRFSBASE Ry (F3),(11B)
|
||||
3: vstmxcsr Md (v1) | WRGSBASE Ry (F3),(11B)
|
||||
4: XSAVE
|
||||
5: XRSTOR | lfence (11B)
|
||||
6: mfence (11B)
|
||||
6: XSAVEOPT | mfence (11B)
|
||||
7: clflush | sfence (11B)
|
||||
EndTable
|
||||
|
||||
@ -872,6 +926,12 @@ GrpTable: Grp16
|
||||
3: prefetch T2
|
||||
EndTable
|
||||
|
||||
GrpTable: Grp17
|
||||
1: BLSR By,Ey (v)
|
||||
2: BLSMSK By,Ey (v)
|
||||
3: BLSI By,Ey (v)
|
||||
EndTable
|
||||
|
||||
# AMD's Prefetch Group
|
||||
GrpTable: GrpP
|
||||
0: PREFETCH
|
||||
|
@ -4,9 +4,8 @@ DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \
|
||||
oprof.o cpu_buffer.o buffer_sync.o \
|
||||
event_buffer.o oprofile_files.o \
|
||||
oprofilefs.o oprofile_stats.o \
|
||||
timer_int.o )
|
||||
timer_int.o nmi_timer_int.o )
|
||||
|
||||
oprofile-y := $(DRIVER_OBJS) init.o backtrace.o
|
||||
oprofile-$(CONFIG_X86_LOCAL_APIC) += nmi_int.o op_model_amd.o \
|
||||
op_model_ppro.o op_model_p4.o
|
||||
oprofile-$(CONFIG_X86_IO_APIC) += nmi_timer_int.o
|
||||
|
@ -16,37 +16,23 @@
|
||||
* with the NMI mode driver.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_X86_LOCAL_APIC
|
||||
extern int op_nmi_init(struct oprofile_operations *ops);
|
||||
extern int op_nmi_timer_init(struct oprofile_operations *ops);
|
||||
extern void op_nmi_exit(void);
|
||||
extern void x86_backtrace(struct pt_regs * const regs, unsigned int depth);
|
||||
#else
|
||||
static int op_nmi_init(struct oprofile_operations *ops) { return -ENODEV; }
|
||||
static void op_nmi_exit(void) { }
|
||||
#endif
|
||||
|
||||
static int nmi_timer;
|
||||
extern void x86_backtrace(struct pt_regs * const regs, unsigned int depth);
|
||||
|
||||
int __init oprofile_arch_init(struct oprofile_operations *ops)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = -ENODEV;
|
||||
|
||||
#ifdef CONFIG_X86_LOCAL_APIC
|
||||
ret = op_nmi_init(ops);
|
||||
#endif
|
||||
nmi_timer = (ret != 0);
|
||||
#ifdef CONFIG_X86_IO_APIC
|
||||
if (nmi_timer)
|
||||
ret = op_nmi_timer_init(ops);
|
||||
#endif
|
||||
ops->backtrace = x86_backtrace;
|
||||
|
||||
return ret;
|
||||
return op_nmi_init(ops);
|
||||
}
|
||||
|
||||
|
||||
void oprofile_arch_exit(void)
|
||||
{
|
||||
#ifdef CONFIG_X86_LOCAL_APIC
|
||||
if (!nmi_timer)
|
||||
op_nmi_exit();
|
||||
#endif
|
||||
op_nmi_exit();
|
||||
}
|
||||
|
@ -595,24 +595,36 @@ static int __init p4_init(char **cpu_type)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int force_arch_perfmon;
|
||||
static int force_cpu_type(const char *str, struct kernel_param *kp)
|
||||
enum __force_cpu_type {
|
||||
reserved = 0, /* do not force */
|
||||
timer,
|
||||
arch_perfmon,
|
||||
};
|
||||
|
||||
static int force_cpu_type;
|
||||
|
||||
static int set_cpu_type(const char *str, struct kernel_param *kp)
|
||||
{
|
||||
if (!strcmp(str, "arch_perfmon")) {
|
||||
force_arch_perfmon = 1;
|
||||
if (!strcmp(str, "timer")) {
|
||||
force_cpu_type = timer;
|
||||
printk(KERN_INFO "oprofile: forcing NMI timer mode\n");
|
||||
} else if (!strcmp(str, "arch_perfmon")) {
|
||||
force_cpu_type = arch_perfmon;
|
||||
printk(KERN_INFO "oprofile: forcing architectural perfmon\n");
|
||||
} else {
|
||||
force_cpu_type = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
module_param_call(cpu_type, force_cpu_type, NULL, NULL, 0);
|
||||
module_param_call(cpu_type, set_cpu_type, NULL, NULL, 0);
|
||||
|
||||
static int __init ppro_init(char **cpu_type)
|
||||
{
|
||||
__u8 cpu_model = boot_cpu_data.x86_model;
|
||||
struct op_x86_model_spec *spec = &op_ppro_spec; /* default */
|
||||
|
||||
if (force_arch_perfmon && cpu_has_arch_perfmon)
|
||||
if (force_cpu_type == arch_perfmon && cpu_has_arch_perfmon)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
@ -679,6 +691,9 @@ int __init op_nmi_init(struct oprofile_operations *ops)
|
||||
if (!cpu_has_apic)
|
||||
return -ENODEV;
|
||||
|
||||
if (force_cpu_type == timer)
|
||||
return -ENODEV;
|
||||
|
||||
switch (vendor) {
|
||||
case X86_VENDOR_AMD:
|
||||
/* Needs to be at least an Athlon (or hammer in 32bit mode) */
|
||||
|
@ -1,50 +0,0 @@
|
||||
/**
|
||||
* @file nmi_timer_int.c
|
||||
*
|
||||
* @remark Copyright 2003 OProfile authors
|
||||
* @remark Read the file COPYING
|
||||
*
|
||||
* @author Zwane Mwaikambo <zwane@linuxpower.ca>
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/oprofile.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/kdebug.h>
|
||||
|
||||
#include <asm/nmi.h>
|
||||
#include <asm/apic.h>
|
||||
#include <asm/ptrace.h>
|
||||
|
||||
static int profile_timer_exceptions_notify(unsigned int val, struct pt_regs *regs)
|
||||
{
|
||||
oprofile_add_sample(regs, 0);
|
||||
return NMI_HANDLED;
|
||||
}
|
||||
|
||||
static int timer_start(void)
|
||||
{
|
||||
if (register_nmi_handler(NMI_LOCAL, profile_timer_exceptions_notify,
|
||||
0, "oprofile-timer"))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void timer_stop(void)
|
||||
{
|
||||
unregister_nmi_handler(NMI_LOCAL, "oprofile-timer");
|
||||
synchronize_sched(); /* Allow already-started NMIs to complete. */
|
||||
}
|
||||
|
||||
|
||||
int __init op_nmi_timer_init(struct oprofile_operations *ops)
|
||||
{
|
||||
ops->start = timer_start;
|
||||
ops->stop = timer_stop;
|
||||
ops->cpu_type = "timer";
|
||||
printk(KERN_INFO "oprofile: using NMI timer interrupt.\n");
|
||||
return 0;
|
||||
}
|
@ -18,14 +18,21 @@ chkobjdump = $(srctree)/arch/x86/tools/chkobjdump.awk
|
||||
quiet_cmd_posttest = TEST $@
|
||||
cmd_posttest = ($(OBJDUMP) -v | $(AWK) -f $(chkobjdump)) || $(OBJDUMP) -d -j .text $(objtree)/vmlinux | $(AWK) -f $(distill_awk) | $(obj)/test_get_len $(posttest_64bit) $(posttest_verbose)
|
||||
|
||||
posttest: $(obj)/test_get_len vmlinux
|
||||
$(call cmd,posttest)
|
||||
quiet_cmd_sanitytest = TEST $@
|
||||
cmd_sanitytest = $(obj)/insn_sanity $(posttest_64bit) -m 1000000
|
||||
|
||||
hostprogs-y := test_get_len
|
||||
posttest: $(obj)/test_get_len vmlinux $(obj)/insn_sanity
|
||||
$(call cmd,posttest)
|
||||
$(call cmd,sanitytest)
|
||||
|
||||
hostprogs-y += test_get_len insn_sanity
|
||||
|
||||
# -I needed for generated C source and C source which in the kernel tree.
|
||||
HOSTCFLAGS_test_get_len.o := -Wall -I$(objtree)/arch/x86/lib/ -I$(srctree)/arch/x86/include/ -I$(srctree)/arch/x86/lib/ -I$(srctree)/include/
|
||||
|
||||
HOSTCFLAGS_insn_sanity.o := -Wall -I$(objtree)/arch/x86/lib/ -I$(srctree)/arch/x86/include/ -I$(srctree)/arch/x86/lib/ -I$(srctree)/include/
|
||||
|
||||
# Dependencies are also needed.
|
||||
$(obj)/test_get_len.o: $(srctree)/arch/x86/lib/insn.c $(srctree)/arch/x86/lib/inat.c $(srctree)/arch/x86/include/asm/inat_types.h $(srctree)/arch/x86/include/asm/inat.h $(srctree)/arch/x86/include/asm/insn.h $(objtree)/arch/x86/lib/inat-tables.c
|
||||
|
||||
$(obj)/insn_sanity.o: $(srctree)/arch/x86/lib/insn.c $(srctree)/arch/x86/lib/inat.c $(srctree)/arch/x86/include/asm/inat_types.h $(srctree)/arch/x86/include/asm/inat.h $(srctree)/arch/x86/include/asm/insn.h $(objtree)/arch/x86/lib/inat-tables.c
|
||||
|
@ -47,7 +47,7 @@ BEGIN {
|
||||
sep_expr = "^\\|$"
|
||||
group_expr = "^Grp[0-9A-Za-z]+"
|
||||
|
||||
imm_expr = "^[IJAO][a-z]"
|
||||
imm_expr = "^[IJAOL][a-z]"
|
||||
imm_flag["Ib"] = "INAT_MAKE_IMM(INAT_IMM_BYTE)"
|
||||
imm_flag["Jb"] = "INAT_MAKE_IMM(INAT_IMM_BYTE)"
|
||||
imm_flag["Iw"] = "INAT_MAKE_IMM(INAT_IMM_WORD)"
|
||||
@ -59,6 +59,7 @@ BEGIN {
|
||||
imm_flag["Iv"] = "INAT_MAKE_IMM(INAT_IMM_VWORD)"
|
||||
imm_flag["Ob"] = "INAT_MOFFSET"
|
||||
imm_flag["Ov"] = "INAT_MOFFSET"
|
||||
imm_flag["Lx"] = "INAT_MAKE_IMM(INAT_IMM_BYTE)"
|
||||
|
||||
modrm_expr = "^([CDEGMNPQRSUVW/][a-z]+|NTA|T[012])"
|
||||
force64_expr = "\\([df]64\\)"
|
||||
@ -70,8 +71,12 @@ BEGIN {
|
||||
lprefix3_expr = "\\(F2\\)"
|
||||
max_lprefix = 4
|
||||
|
||||
vexok_expr = "\\(VEX\\)"
|
||||
vexonly_expr = "\\(oVEX\\)"
|
||||
# All opcodes starting with lower-case 'v' or with (v1) superscript
|
||||
# accepts VEX prefix
|
||||
vexok_opcode_expr = "^v.*"
|
||||
vexok_expr = "\\(v1\\)"
|
||||
# All opcodes with (v) superscript supports *only* VEX prefix
|
||||
vexonly_expr = "\\(v\\)"
|
||||
|
||||
prefix_expr = "\\(Prefix\\)"
|
||||
prefix_num["Operand-Size"] = "INAT_PFX_OPNDSZ"
|
||||
@ -85,8 +90,8 @@ BEGIN {
|
||||
prefix_num["SEG=GS"] = "INAT_PFX_GS"
|
||||
prefix_num["SEG=SS"] = "INAT_PFX_SS"
|
||||
prefix_num["Address-Size"] = "INAT_PFX_ADDRSZ"
|
||||
prefix_num["2bytes-VEX"] = "INAT_PFX_VEX2"
|
||||
prefix_num["3bytes-VEX"] = "INAT_PFX_VEX3"
|
||||
prefix_num["VEX+1byte"] = "INAT_PFX_VEX2"
|
||||
prefix_num["VEX+2byte"] = "INAT_PFX_VEX3"
|
||||
|
||||
clear_vars()
|
||||
}
|
||||
@ -310,12 +315,10 @@ function convert_operands(count,opnd, i,j,imm,mod)
|
||||
if (match(opcode, fpu_expr))
|
||||
flags = add_flags(flags, "INAT_MODRM")
|
||||
|
||||
# check VEX only code
|
||||
# check VEX codes
|
||||
if (match(ext, vexonly_expr))
|
||||
flags = add_flags(flags, "INAT_VEXOK | INAT_VEXONLY")
|
||||
|
||||
# check VEX only code
|
||||
if (match(ext, vexok_expr))
|
||||
else if (match(ext, vexok_expr) || match(opcode, vexok_opcode_expr))
|
||||
flags = add_flags(flags, "INAT_VEXOK")
|
||||
|
||||
# check prefixes
|
||||
|
275
arch/x86/tools/insn_sanity.c
Normal file
275
arch/x86/tools/insn_sanity.c
Normal file
@ -0,0 +1,275 @@
|
||||
/*
|
||||
* x86 decoder sanity test - based on test_get_insn.c
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* Copyright (C) IBM Corporation, 2009
|
||||
* Copyright (C) Hitachi, Ltd., 2011
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#define unlikely(cond) (cond)
|
||||
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
|
||||
|
||||
#include <asm/insn.h>
|
||||
#include <inat.c>
|
||||
#include <insn.c>
|
||||
|
||||
/*
|
||||
* Test of instruction analysis against tampering.
|
||||
* Feed random binary to instruction decoder and ensure not to
|
||||
* access out-of-instruction-buffer.
|
||||
*/
|
||||
|
||||
#define DEFAULT_MAX_ITER 10000
|
||||
#define INSN_NOP 0x90
|
||||
|
||||
static const char *prog; /* Program name */
|
||||
static int verbose; /* Verbosity */
|
||||
static int x86_64; /* x86-64 bit mode flag */
|
||||
static unsigned int seed; /* Random seed */
|
||||
static unsigned long iter_start; /* Start of iteration number */
|
||||
static unsigned long iter_end = DEFAULT_MAX_ITER; /* End of iteration number */
|
||||
static FILE *input_file; /* Input file name */
|
||||
|
||||
static void usage(const char *err)
|
||||
{
|
||||
if (err)
|
||||
fprintf(stderr, "Error: %s\n\n", err);
|
||||
fprintf(stderr, "Usage: %s [-y|-n|-v] [-s seed[,no]] [-m max] [-i input]\n", prog);
|
||||
fprintf(stderr, "\t-y 64bit mode\n");
|
||||
fprintf(stderr, "\t-n 32bit mode\n");
|
||||
fprintf(stderr, "\t-v Verbosity(-vv dumps any decoded result)\n");
|
||||
fprintf(stderr, "\t-s Give a random seed (and iteration number)\n");
|
||||
fprintf(stderr, "\t-m Give a maximum iteration number\n");
|
||||
fprintf(stderr, "\t-i Give an input file with decoded binary\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void dump_field(FILE *fp, const char *name, const char *indent,
|
||||
struct insn_field *field)
|
||||
{
|
||||
fprintf(fp, "%s.%s = {\n", indent, name);
|
||||
fprintf(fp, "%s\t.value = %d, bytes[] = {%x, %x, %x, %x},\n",
|
||||
indent, field->value, field->bytes[0], field->bytes[1],
|
||||
field->bytes[2], field->bytes[3]);
|
||||
fprintf(fp, "%s\t.got = %d, .nbytes = %d},\n", indent,
|
||||
field->got, field->nbytes);
|
||||
}
|
||||
|
||||
static void dump_insn(FILE *fp, struct insn *insn)
|
||||
{
|
||||
fprintf(fp, "Instruction = {\n");
|
||||
dump_field(fp, "prefixes", "\t", &insn->prefixes);
|
||||
dump_field(fp, "rex_prefix", "\t", &insn->rex_prefix);
|
||||
dump_field(fp, "vex_prefix", "\t", &insn->vex_prefix);
|
||||
dump_field(fp, "opcode", "\t", &insn->opcode);
|
||||
dump_field(fp, "modrm", "\t", &insn->modrm);
|
||||
dump_field(fp, "sib", "\t", &insn->sib);
|
||||
dump_field(fp, "displacement", "\t", &insn->displacement);
|
||||
dump_field(fp, "immediate1", "\t", &insn->immediate1);
|
||||
dump_field(fp, "immediate2", "\t", &insn->immediate2);
|
||||
fprintf(fp, "\t.attr = %x, .opnd_bytes = %d, .addr_bytes = %d,\n",
|
||||
insn->attr, insn->opnd_bytes, insn->addr_bytes);
|
||||
fprintf(fp, "\t.length = %d, .x86_64 = %d, .kaddr = %p}\n",
|
||||
insn->length, insn->x86_64, insn->kaddr);
|
||||
}
|
||||
|
||||
static void dump_stream(FILE *fp, const char *msg, unsigned long nr_iter,
|
||||
unsigned char *insn_buf, struct insn *insn)
|
||||
{
|
||||
int i;
|
||||
|
||||
fprintf(fp, "%s:\n", msg);
|
||||
|
||||
dump_insn(fp, insn);
|
||||
|
||||
fprintf(fp, "You can reproduce this with below command(s);\n");
|
||||
|
||||
/* Input a decoded instruction sequence directly */
|
||||
fprintf(fp, " $ echo ");
|
||||
for (i = 0; i < MAX_INSN_SIZE; i++)
|
||||
fprintf(fp, " %02x", insn_buf[i]);
|
||||
fprintf(fp, " | %s -i -\n", prog);
|
||||
|
||||
if (!input_file) {
|
||||
fprintf(fp, "Or \n");
|
||||
/* Give a seed and iteration number */
|
||||
fprintf(fp, " $ %s -s 0x%x,%lu\n", prog, seed, nr_iter);
|
||||
}
|
||||
}
|
||||
|
||||
static void init_random_seed(void)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = open("/dev/urandom", O_RDONLY);
|
||||
if (fd < 0)
|
||||
goto fail;
|
||||
|
||||
if (read(fd, &seed, sizeof(seed)) != sizeof(seed))
|
||||
goto fail;
|
||||
|
||||
close(fd);
|
||||
return;
|
||||
fail:
|
||||
usage("Failed to open /dev/urandom");
|
||||
}
|
||||
|
||||
/* Read given instruction sequence from the input file */
|
||||
static int read_next_insn(unsigned char *insn_buf)
|
||||
{
|
||||
char buf[256] = "", *tmp;
|
||||
int i;
|
||||
|
||||
tmp = fgets(buf, ARRAY_SIZE(buf), input_file);
|
||||
if (tmp == NULL || feof(input_file))
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < MAX_INSN_SIZE; i++) {
|
||||
insn_buf[i] = (unsigned char)strtoul(tmp, &tmp, 16);
|
||||
if (*tmp != ' ')
|
||||
break;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static int generate_insn(unsigned char *insn_buf)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (input_file)
|
||||
return read_next_insn(insn_buf);
|
||||
|
||||
/* Fills buffer with random binary up to MAX_INSN_SIZE */
|
||||
for (i = 0; i < MAX_INSN_SIZE - 1; i += 2)
|
||||
*(unsigned short *)(&insn_buf[i]) = random() & 0xffff;
|
||||
|
||||
while (i < MAX_INSN_SIZE)
|
||||
insn_buf[i++] = random() & 0xff;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static void parse_args(int argc, char **argv)
|
||||
{
|
||||
int c;
|
||||
char *tmp = NULL;
|
||||
int set_seed = 0;
|
||||
|
||||
prog = argv[0];
|
||||
while ((c = getopt(argc, argv, "ynvs:m:i:")) != -1) {
|
||||
switch (c) {
|
||||
case 'y':
|
||||
x86_64 = 1;
|
||||
break;
|
||||
case 'n':
|
||||
x86_64 = 0;
|
||||
break;
|
||||
case 'v':
|
||||
verbose++;
|
||||
break;
|
||||
case 'i':
|
||||
if (strcmp("-", optarg) == 0)
|
||||
input_file = stdin;
|
||||
else
|
||||
input_file = fopen(optarg, "r");
|
||||
if (!input_file)
|
||||
usage("Failed to open input file");
|
||||
break;
|
||||
case 's':
|
||||
seed = (unsigned int)strtoul(optarg, &tmp, 0);
|
||||
if (*tmp == ',') {
|
||||
optarg = tmp + 1;
|
||||
iter_start = strtoul(optarg, &tmp, 0);
|
||||
}
|
||||
if (*tmp != '\0' || tmp == optarg)
|
||||
usage("Failed to parse seed");
|
||||
set_seed = 1;
|
||||
break;
|
||||
case 'm':
|
||||
iter_end = strtoul(optarg, &tmp, 0);
|
||||
if (*tmp != '\0' || tmp == optarg)
|
||||
usage("Failed to parse max_iter");
|
||||
break;
|
||||
default:
|
||||
usage(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check errors */
|
||||
if (iter_end < iter_start)
|
||||
usage("Max iteration number must be bigger than iter-num");
|
||||
|
||||
if (set_seed && input_file)
|
||||
usage("Don't use input file (-i) with random seed (-s)");
|
||||
|
||||
/* Initialize random seed */
|
||||
if (!input_file) {
|
||||
if (!set_seed) /* No seed is given */
|
||||
init_random_seed();
|
||||
srand(seed);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct insn insn;
|
||||
int insns = 0;
|
||||
int errors = 0;
|
||||
unsigned long i;
|
||||
unsigned char insn_buf[MAX_INSN_SIZE * 2];
|
||||
|
||||
parse_args(argc, argv);
|
||||
|
||||
/* Prepare stop bytes with NOPs */
|
||||
memset(insn_buf + MAX_INSN_SIZE, INSN_NOP, MAX_INSN_SIZE);
|
||||
|
||||
for (i = 0; i < iter_end; i++) {
|
||||
if (generate_insn(insn_buf) <= 0)
|
||||
break;
|
||||
|
||||
if (i < iter_start) /* Skip to given iteration number */
|
||||
continue;
|
||||
|
||||
/* Decode an instruction */
|
||||
insn_init(&insn, insn_buf, x86_64);
|
||||
insn_get_length(&insn);
|
||||
|
||||
if (insn.next_byte <= insn.kaddr ||
|
||||
insn.kaddr + MAX_INSN_SIZE < insn.next_byte) {
|
||||
/* Access out-of-range memory */
|
||||
dump_stream(stderr, "Error: Found an access violation", i, insn_buf, &insn);
|
||||
errors++;
|
||||
} else if (verbose && !insn_complete(&insn))
|
||||
dump_stream(stdout, "Info: Found an undecodable input", i, insn_buf, &insn);
|
||||
else if (verbose >= 2)
|
||||
dump_insn(stdout, &insn);
|
||||
insns++;
|
||||
}
|
||||
|
||||
fprintf(stdout, "%s: decoded and checked %d %s instructions with %d errors (seed:0x%x)\n", (errors) ? "Failure" : "Success", insns, (input_file) ? "given" : "random", errors, seed);
|
||||
|
||||
return errors ? 1 : 0;
|
||||
}
|
173
drivers/oprofile/nmi_timer_int.c
Normal file
173
drivers/oprofile/nmi_timer_int.c
Normal file
@ -0,0 +1,173 @@
|
||||
/**
|
||||
* @file nmi_timer_int.c
|
||||
*
|
||||
* @remark Copyright 2011 Advanced Micro Devices, Inc.
|
||||
*
|
||||
* @author Robert Richter <robert.richter@amd.com>
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/oprofile.h>
|
||||
#include <linux/perf_event.h>
|
||||
|
||||
#ifdef CONFIG_OPROFILE_NMI_TIMER
|
||||
|
||||
static DEFINE_PER_CPU(struct perf_event *, nmi_timer_events);
|
||||
static int ctr_running;
|
||||
|
||||
static struct perf_event_attr nmi_timer_attr = {
|
||||
.type = PERF_TYPE_HARDWARE,
|
||||
.config = PERF_COUNT_HW_CPU_CYCLES,
|
||||
.size = sizeof(struct perf_event_attr),
|
||||
.pinned = 1,
|
||||
.disabled = 1,
|
||||
};
|
||||
|
||||
static void nmi_timer_callback(struct perf_event *event,
|
||||
struct perf_sample_data *data,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
event->hw.interrupts = 0; /* don't throttle interrupts */
|
||||
oprofile_add_sample(regs, 0);
|
||||
}
|
||||
|
||||
static int nmi_timer_start_cpu(int cpu)
|
||||
{
|
||||
struct perf_event *event = per_cpu(nmi_timer_events, cpu);
|
||||
|
||||
if (!event) {
|
||||
event = perf_event_create_kernel_counter(&nmi_timer_attr, cpu, NULL,
|
||||
nmi_timer_callback, NULL);
|
||||
if (IS_ERR(event))
|
||||
return PTR_ERR(event);
|
||||
per_cpu(nmi_timer_events, cpu) = event;
|
||||
}
|
||||
|
||||
if (event && ctr_running)
|
||||
perf_event_enable(event);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nmi_timer_stop_cpu(int cpu)
|
||||
{
|
||||
struct perf_event *event = per_cpu(nmi_timer_events, cpu);
|
||||
|
||||
if (event && ctr_running)
|
||||
perf_event_disable(event);
|
||||
}
|
||||
|
||||
static int nmi_timer_cpu_notifier(struct notifier_block *b, unsigned long action,
|
||||
void *data)
|
||||
{
|
||||
int cpu = (unsigned long)data;
|
||||
switch (action) {
|
||||
case CPU_DOWN_FAILED:
|
||||
case CPU_ONLINE:
|
||||
nmi_timer_start_cpu(cpu);
|
||||
break;
|
||||
case CPU_DOWN_PREPARE:
|
||||
nmi_timer_stop_cpu(cpu);
|
||||
break;
|
||||
}
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block nmi_timer_cpu_nb = {
|
||||
.notifier_call = nmi_timer_cpu_notifier
|
||||
};
|
||||
|
||||
static int nmi_timer_start(void)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
get_online_cpus();
|
||||
ctr_running = 1;
|
||||
for_each_online_cpu(cpu)
|
||||
nmi_timer_start_cpu(cpu);
|
||||
put_online_cpus();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nmi_timer_stop(void)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
get_online_cpus();
|
||||
for_each_online_cpu(cpu)
|
||||
nmi_timer_stop_cpu(cpu);
|
||||
ctr_running = 0;
|
||||
put_online_cpus();
|
||||
}
|
||||
|
||||
static void nmi_timer_shutdown(void)
|
||||
{
|
||||
struct perf_event *event;
|
||||
int cpu;
|
||||
|
||||
get_online_cpus();
|
||||
unregister_cpu_notifier(&nmi_timer_cpu_nb);
|
||||
for_each_possible_cpu(cpu) {
|
||||
event = per_cpu(nmi_timer_events, cpu);
|
||||
if (!event)
|
||||
continue;
|
||||
perf_event_disable(event);
|
||||
per_cpu(nmi_timer_events, cpu) = NULL;
|
||||
perf_event_release_kernel(event);
|
||||
}
|
||||
|
||||
put_online_cpus();
|
||||
}
|
||||
|
||||
static int nmi_timer_setup(void)
|
||||
{
|
||||
int cpu, err;
|
||||
u64 period;
|
||||
|
||||
/* clock cycles per tick: */
|
||||
period = (u64)cpu_khz * 1000;
|
||||
do_div(period, HZ);
|
||||
nmi_timer_attr.sample_period = period;
|
||||
|
||||
get_online_cpus();
|
||||
err = register_cpu_notifier(&nmi_timer_cpu_nb);
|
||||
if (err)
|
||||
goto out;
|
||||
/* can't attach events to offline cpus: */
|
||||
for_each_online_cpu(cpu) {
|
||||
err = nmi_timer_start_cpu(cpu);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
if (err)
|
||||
nmi_timer_shutdown();
|
||||
out:
|
||||
put_online_cpus();
|
||||
return err;
|
||||
}
|
||||
|
||||
int __init op_nmi_timer_init(struct oprofile_operations *ops)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
err = nmi_timer_setup();
|
||||
if (err)
|
||||
return err;
|
||||
nmi_timer_shutdown(); /* only check, don't alloc */
|
||||
|
||||
ops->create_files = NULL;
|
||||
ops->setup = nmi_timer_setup;
|
||||
ops->shutdown = nmi_timer_shutdown;
|
||||
ops->start = nmi_timer_start;
|
||||
ops->stop = nmi_timer_stop;
|
||||
ops->cpu_type = "timer";
|
||||
|
||||
printk(KERN_INFO "oprofile: using NMI timer interrupt.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
@ -246,37 +246,31 @@ static int __init oprofile_init(void)
|
||||
int err;
|
||||
|
||||
/* always init architecture to setup backtrace support */
|
||||
timer_mode = 0;
|
||||
err = oprofile_arch_init(&oprofile_ops);
|
||||
if (!err) {
|
||||
if (!timer && !oprofilefs_register())
|
||||
return 0;
|
||||
oprofile_arch_exit();
|
||||
}
|
||||
|
||||
timer_mode = err || timer; /* fall back to timer mode on errors */
|
||||
if (timer_mode) {
|
||||
if (!err)
|
||||
oprofile_arch_exit();
|
||||
/* setup timer mode: */
|
||||
timer_mode = 1;
|
||||
/* no nmi timer mode if oprofile.timer is set */
|
||||
if (timer || op_nmi_timer_init(&oprofile_ops)) {
|
||||
err = oprofile_timer_init(&oprofile_ops);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = oprofilefs_register();
|
||||
if (!err)
|
||||
return 0;
|
||||
|
||||
/* failed */
|
||||
if (timer_mode)
|
||||
oprofile_timer_exit();
|
||||
else
|
||||
oprofile_arch_exit();
|
||||
|
||||
return err;
|
||||
return oprofilefs_register();
|
||||
}
|
||||
|
||||
|
||||
static void __exit oprofile_exit(void)
|
||||
{
|
||||
oprofilefs_unregister();
|
||||
if (timer_mode)
|
||||
oprofile_timer_exit();
|
||||
else
|
||||
if (!timer_mode)
|
||||
oprofile_arch_exit();
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,15 @@ struct dentry;
|
||||
void oprofile_create_files(struct super_block *sb, struct dentry *root);
|
||||
int oprofile_timer_init(struct oprofile_operations *ops);
|
||||
void oprofile_timer_exit(void);
|
||||
#ifdef CONFIG_OPROFILE_NMI_TIMER
|
||||
int op_nmi_timer_init(struct oprofile_operations *ops);
|
||||
#else
|
||||
static inline int op_nmi_timer_init(struct oprofile_operations *ops)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
int oprofile_set_ulong(unsigned long *addr, unsigned long val);
|
||||
int oprofile_set_timeout(unsigned long time);
|
||||
|
@ -97,24 +97,24 @@ static struct notifier_block __refdata oprofile_cpu_notifier = {
|
||||
.notifier_call = oprofile_cpu_notify,
|
||||
};
|
||||
|
||||
int oprofile_timer_init(struct oprofile_operations *ops)
|
||||
static int oprofile_hrtimer_setup(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = register_hotcpu_notifier(&oprofile_cpu_notifier);
|
||||
if (rc)
|
||||
return rc;
|
||||
ops->create_files = NULL;
|
||||
ops->setup = NULL;
|
||||
ops->shutdown = NULL;
|
||||
ops->start = oprofile_hrtimer_start;
|
||||
ops->stop = oprofile_hrtimer_stop;
|
||||
ops->cpu_type = "timer";
|
||||
printk(KERN_INFO "oprofile: using timer interrupt.\n");
|
||||
return 0;
|
||||
return register_hotcpu_notifier(&oprofile_cpu_notifier);
|
||||
}
|
||||
|
||||
void oprofile_timer_exit(void)
|
||||
static void oprofile_hrtimer_shutdown(void)
|
||||
{
|
||||
unregister_hotcpu_notifier(&oprofile_cpu_notifier);
|
||||
}
|
||||
|
||||
int oprofile_timer_init(struct oprofile_operations *ops)
|
||||
{
|
||||
ops->create_files = NULL;
|
||||
ops->setup = oprofile_hrtimer_setup;
|
||||
ops->shutdown = oprofile_hrtimer_shutdown;
|
||||
ops->start = oprofile_hrtimer_start;
|
||||
ops->stop = oprofile_hrtimer_stop;
|
||||
ops->cpu_type = "timer";
|
||||
printk(KERN_INFO "oprofile: using timer interrupt.\n");
|
||||
return 0;
|
||||
}
|
||||
|
@ -22,8 +22,14 @@ extern unsigned long __sw_hweight64(__u64 w);
|
||||
#include <asm/bitops.h>
|
||||
|
||||
#define for_each_set_bit(bit, addr, size) \
|
||||
for ((bit) = find_first_bit((addr), (size)); \
|
||||
(bit) < (size); \
|
||||
for ((bit) = find_first_bit((addr), (size)); \
|
||||
(bit) < (size); \
|
||||
(bit) = find_next_bit((addr), (size), (bit) + 1))
|
||||
|
||||
/* same as for_each_set_bit() but use bit as value to start with */
|
||||
#define for_each_set_bit_cont(bit, addr, size) \
|
||||
for ((bit) = find_next_bit((addr), (size), (bit)); \
|
||||
(bit) < (size); \
|
||||
(bit) = find_next_bit((addr), (size), (bit) + 1))
|
||||
|
||||
static __inline__ int get_bitmask_order(unsigned int count)
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#if defined(CC_HAVE_ASM_GOTO) && defined(CONFIG_JUMP_LABEL)
|
||||
|
||||
@ -14,6 +15,12 @@ struct jump_label_key {
|
||||
#endif
|
||||
};
|
||||
|
||||
struct jump_label_key_deferred {
|
||||
struct jump_label_key key;
|
||||
unsigned long timeout;
|
||||
struct delayed_work work;
|
||||
};
|
||||
|
||||
# include <asm/jump_label.h>
|
||||
# define HAVE_JUMP_LABEL
|
||||
#endif /* CC_HAVE_ASM_GOTO && CONFIG_JUMP_LABEL */
|
||||
@ -51,8 +58,11 @@ extern void arch_jump_label_transform_static(struct jump_entry *entry,
|
||||
extern int jump_label_text_reserved(void *start, void *end);
|
||||
extern void jump_label_inc(struct jump_label_key *key);
|
||||
extern void jump_label_dec(struct jump_label_key *key);
|
||||
extern void jump_label_dec_deferred(struct jump_label_key_deferred *key);
|
||||
extern bool jump_label_enabled(struct jump_label_key *key);
|
||||
extern void jump_label_apply_nops(struct module *mod);
|
||||
extern void jump_label_rate_limit(struct jump_label_key_deferred *key,
|
||||
unsigned long rl);
|
||||
|
||||
#else /* !HAVE_JUMP_LABEL */
|
||||
|
||||
@ -68,6 +78,10 @@ static __always_inline void jump_label_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
struct jump_label_key_deferred {
|
||||
struct jump_label_key key;
|
||||
};
|
||||
|
||||
static __always_inline bool static_branch(struct jump_label_key *key)
|
||||
{
|
||||
if (unlikely(atomic_read(&key->enabled)))
|
||||
@ -85,6 +99,11 @@ static inline void jump_label_dec(struct jump_label_key *key)
|
||||
atomic_dec(&key->enabled);
|
||||
}
|
||||
|
||||
static inline void jump_label_dec_deferred(struct jump_label_key_deferred *key)
|
||||
{
|
||||
jump_label_dec(&key->key);
|
||||
}
|
||||
|
||||
static inline int jump_label_text_reserved(void *start, void *end)
|
||||
{
|
||||
return 0;
|
||||
@ -102,6 +121,14 @@ static inline int jump_label_apply_nops(struct module *mod)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void jump_label_rate_limit(struct jump_label_key_deferred *key,
|
||||
unsigned long rl)
|
||||
{
|
||||
}
|
||||
#endif /* HAVE_JUMP_LABEL */
|
||||
|
||||
#define jump_label_key_enabled ((struct jump_label_key){ .enabled = ATOMIC_INIT(1), })
|
||||
#define jump_label_key_disabled ((struct jump_label_key){ .enabled = ATOMIC_INIT(0), })
|
||||
|
||||
#endif /* _LINUX_JUMP_LABEL_H */
|
||||
|
@ -890,6 +890,7 @@ struct perf_event_context {
|
||||
int nr_active;
|
||||
int is_active;
|
||||
int nr_stat;
|
||||
int nr_freq;
|
||||
int rotate_disable;
|
||||
atomic_t refcount;
|
||||
struct task_struct *task;
|
||||
@ -1063,12 +1064,12 @@ perf_sw_event(u32 event_id, u64 nr, struct pt_regs *regs, u64 addr)
|
||||
}
|
||||
}
|
||||
|
||||
extern struct jump_label_key perf_sched_events;
|
||||
extern struct jump_label_key_deferred perf_sched_events;
|
||||
|
||||
static inline void perf_event_task_sched_in(struct task_struct *prev,
|
||||
struct task_struct *task)
|
||||
{
|
||||
if (static_branch(&perf_sched_events))
|
||||
if (static_branch(&perf_sched_events.key))
|
||||
__perf_event_task_sched_in(prev, task);
|
||||
}
|
||||
|
||||
@ -1077,7 +1078,7 @@ static inline void perf_event_task_sched_out(struct task_struct *prev,
|
||||
{
|
||||
perf_sw_event(PERF_COUNT_SW_CONTEXT_SWITCHES, 1, NULL, 0);
|
||||
|
||||
if (static_branch(&perf_sched_events))
|
||||
if (static_branch(&perf_sched_events.key))
|
||||
__perf_event_task_sched_out(prev, next);
|
||||
}
|
||||
|
||||
|
@ -2,5 +2,5 @@ ifdef CONFIG_FUNCTION_TRACER
|
||||
CFLAGS_REMOVE_core.o = -pg
|
||||
endif
|
||||
|
||||
obj-y := core.o ring_buffer.o
|
||||
obj-y := core.o ring_buffer.o callchain.o
|
||||
obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o
|
||||
|
191
kernel/events/callchain.c
Normal file
191
kernel/events/callchain.c
Normal file
@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Performance events callchain code, extracted from core.c:
|
||||
*
|
||||
* Copyright (C) 2008 Thomas Gleixner <tglx@linutronix.de>
|
||||
* Copyright (C) 2008-2011 Red Hat, Inc., Ingo Molnar
|
||||
* Copyright (C) 2008-2011 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com>
|
||||
* Copyright © 2009 Paul Mackerras, IBM Corp. <paulus@au1.ibm.com>
|
||||
*
|
||||
* For licensing details see kernel-base/COPYING
|
||||
*/
|
||||
|
||||
#include <linux/perf_event.h>
|
||||
#include <linux/slab.h>
|
||||
#include "internal.h"
|
||||
|
||||
struct callchain_cpus_entries {
|
||||
struct rcu_head rcu_head;
|
||||
struct perf_callchain_entry *cpu_entries[0];
|
||||
};
|
||||
|
||||
static DEFINE_PER_CPU(int, callchain_recursion[PERF_NR_CONTEXTS]);
|
||||
static atomic_t nr_callchain_events;
|
||||
static DEFINE_MUTEX(callchain_mutex);
|
||||
static struct callchain_cpus_entries *callchain_cpus_entries;
|
||||
|
||||
|
||||
__weak void perf_callchain_kernel(struct perf_callchain_entry *entry,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
}
|
||||
|
||||
__weak void perf_callchain_user(struct perf_callchain_entry *entry,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
}
|
||||
|
||||
static void release_callchain_buffers_rcu(struct rcu_head *head)
|
||||
{
|
||||
struct callchain_cpus_entries *entries;
|
||||
int cpu;
|
||||
|
||||
entries = container_of(head, struct callchain_cpus_entries, rcu_head);
|
||||
|
||||
for_each_possible_cpu(cpu)
|
||||
kfree(entries->cpu_entries[cpu]);
|
||||
|
||||
kfree(entries);
|
||||
}
|
||||
|
||||
static void release_callchain_buffers(void)
|
||||
{
|
||||
struct callchain_cpus_entries *entries;
|
||||
|
||||
entries = callchain_cpus_entries;
|
||||
rcu_assign_pointer(callchain_cpus_entries, NULL);
|
||||
call_rcu(&entries->rcu_head, release_callchain_buffers_rcu);
|
||||
}
|
||||
|
||||
static int alloc_callchain_buffers(void)
|
||||
{
|
||||
int cpu;
|
||||
int size;
|
||||
struct callchain_cpus_entries *entries;
|
||||
|
||||
/*
|
||||
* We can't use the percpu allocation API for data that can be
|
||||
* accessed from NMI. Use a temporary manual per cpu allocation
|
||||
* until that gets sorted out.
|
||||
*/
|
||||
size = offsetof(struct callchain_cpus_entries, cpu_entries[nr_cpu_ids]);
|
||||
|
||||
entries = kzalloc(size, GFP_KERNEL);
|
||||
if (!entries)
|
||||
return -ENOMEM;
|
||||
|
||||
size = sizeof(struct perf_callchain_entry) * PERF_NR_CONTEXTS;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
entries->cpu_entries[cpu] = kmalloc_node(size, GFP_KERNEL,
|
||||
cpu_to_node(cpu));
|
||||
if (!entries->cpu_entries[cpu])
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rcu_assign_pointer(callchain_cpus_entries, entries);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
for_each_possible_cpu(cpu)
|
||||
kfree(entries->cpu_entries[cpu]);
|
||||
kfree(entries);
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
int get_callchain_buffers(void)
|
||||
{
|
||||
int err = 0;
|
||||
int count;
|
||||
|
||||
mutex_lock(&callchain_mutex);
|
||||
|
||||
count = atomic_inc_return(&nr_callchain_events);
|
||||
if (WARN_ON_ONCE(count < 1)) {
|
||||
err = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (count > 1) {
|
||||
/* If the allocation failed, give up */
|
||||
if (!callchain_cpus_entries)
|
||||
err = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
err = alloc_callchain_buffers();
|
||||
if (err)
|
||||
release_callchain_buffers();
|
||||
exit:
|
||||
mutex_unlock(&callchain_mutex);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void put_callchain_buffers(void)
|
||||
{
|
||||
if (atomic_dec_and_mutex_lock(&nr_callchain_events, &callchain_mutex)) {
|
||||
release_callchain_buffers();
|
||||
mutex_unlock(&callchain_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
static struct perf_callchain_entry *get_callchain_entry(int *rctx)
|
||||
{
|
||||
int cpu;
|
||||
struct callchain_cpus_entries *entries;
|
||||
|
||||
*rctx = get_recursion_context(__get_cpu_var(callchain_recursion));
|
||||
if (*rctx == -1)
|
||||
return NULL;
|
||||
|
||||
entries = rcu_dereference(callchain_cpus_entries);
|
||||
if (!entries)
|
||||
return NULL;
|
||||
|
||||
cpu = smp_processor_id();
|
||||
|
||||
return &entries->cpu_entries[cpu][*rctx];
|
||||
}
|
||||
|
||||
static void
|
||||
put_callchain_entry(int rctx)
|
||||
{
|
||||
put_recursion_context(__get_cpu_var(callchain_recursion), rctx);
|
||||
}
|
||||
|
||||
struct perf_callchain_entry *perf_callchain(struct pt_regs *regs)
|
||||
{
|
||||
int rctx;
|
||||
struct perf_callchain_entry *entry;
|
||||
|
||||
|
||||
entry = get_callchain_entry(&rctx);
|
||||
if (rctx == -1)
|
||||
return NULL;
|
||||
|
||||
if (!entry)
|
||||
goto exit_put;
|
||||
|
||||
entry->nr = 0;
|
||||
|
||||
if (!user_mode(regs)) {
|
||||
perf_callchain_store(entry, PERF_CONTEXT_KERNEL);
|
||||
perf_callchain_kernel(entry, regs);
|
||||
if (current->mm)
|
||||
regs = task_pt_regs(current);
|
||||
else
|
||||
regs = NULL;
|
||||
}
|
||||
|
||||
if (regs) {
|
||||
perf_callchain_store(entry, PERF_CONTEXT_USER);
|
||||
perf_callchain_user(entry, regs);
|
||||
}
|
||||
|
||||
exit_put:
|
||||
put_callchain_entry(rctx);
|
||||
|
||||
return entry;
|
||||
}
|
@ -128,7 +128,7 @@ enum event_type_t {
|
||||
* perf_sched_events : >0 events exist
|
||||
* perf_cgroup_events: >0 per-cpu cgroup events exist on this cpu
|
||||
*/
|
||||
struct jump_label_key perf_sched_events __read_mostly;
|
||||
struct jump_label_key_deferred perf_sched_events __read_mostly;
|
||||
static DEFINE_PER_CPU(atomic_t, perf_cgroup_events);
|
||||
|
||||
static atomic_t nr_mmap_events __read_mostly;
|
||||
@ -1130,6 +1130,8 @@ event_sched_out(struct perf_event *event,
|
||||
if (!is_software_event(event))
|
||||
cpuctx->active_oncpu--;
|
||||
ctx->nr_active--;
|
||||
if (event->attr.freq && event->attr.sample_freq)
|
||||
ctx->nr_freq--;
|
||||
if (event->attr.exclusive || !cpuctx->active_oncpu)
|
||||
cpuctx->exclusive = 0;
|
||||
}
|
||||
@ -1325,6 +1327,7 @@ void perf_event_disable(struct perf_event *event)
|
||||
}
|
||||
raw_spin_unlock_irq(&ctx->lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(perf_event_disable);
|
||||
|
||||
static void perf_set_shadow_time(struct perf_event *event,
|
||||
struct perf_event_context *ctx,
|
||||
@ -1406,6 +1409,8 @@ event_sched_in(struct perf_event *event,
|
||||
if (!is_software_event(event))
|
||||
cpuctx->active_oncpu++;
|
||||
ctx->nr_active++;
|
||||
if (event->attr.freq && event->attr.sample_freq)
|
||||
ctx->nr_freq++;
|
||||
|
||||
if (event->attr.exclusive)
|
||||
cpuctx->exclusive = 1;
|
||||
@ -1662,8 +1667,7 @@ perf_install_in_context(struct perf_event_context *ctx,
|
||||
* Note: this works for group members as well as group leaders
|
||||
* since the non-leader members' sibling_lists will be empty.
|
||||
*/
|
||||
static void __perf_event_mark_enabled(struct perf_event *event,
|
||||
struct perf_event_context *ctx)
|
||||
static void __perf_event_mark_enabled(struct perf_event *event)
|
||||
{
|
||||
struct perf_event *sub;
|
||||
u64 tstamp = perf_event_time(event);
|
||||
@ -1701,7 +1705,7 @@ static int __perf_event_enable(void *info)
|
||||
*/
|
||||
perf_cgroup_set_timestamp(current, ctx);
|
||||
|
||||
__perf_event_mark_enabled(event, ctx);
|
||||
__perf_event_mark_enabled(event);
|
||||
|
||||
if (!event_filter_match(event)) {
|
||||
if (is_cgroup_event(event))
|
||||
@ -1782,7 +1786,7 @@ void perf_event_enable(struct perf_event *event)
|
||||
|
||||
retry:
|
||||
if (!ctx->is_active) {
|
||||
__perf_event_mark_enabled(event, ctx);
|
||||
__perf_event_mark_enabled(event);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -1809,6 +1813,7 @@ void perf_event_enable(struct perf_event *event)
|
||||
out:
|
||||
raw_spin_unlock_irq(&ctx->lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(perf_event_enable);
|
||||
|
||||
int perf_event_refresh(struct perf_event *event, int refresh)
|
||||
{
|
||||
@ -2327,6 +2332,9 @@ static void perf_ctx_adjust_freq(struct perf_event_context *ctx, u64 period)
|
||||
u64 interrupts, now;
|
||||
s64 delta;
|
||||
|
||||
if (!ctx->nr_freq)
|
||||
return;
|
||||
|
||||
list_for_each_entry_rcu(event, &ctx->event_list, event_entry) {
|
||||
if (event->state != PERF_EVENT_STATE_ACTIVE)
|
||||
continue;
|
||||
@ -2382,12 +2390,14 @@ static void perf_rotate_context(struct perf_cpu_context *cpuctx)
|
||||
{
|
||||
u64 interval = (u64)cpuctx->jiffies_interval * TICK_NSEC;
|
||||
struct perf_event_context *ctx = NULL;
|
||||
int rotate = 0, remove = 1;
|
||||
int rotate = 0, remove = 1, freq = 0;
|
||||
|
||||
if (cpuctx->ctx.nr_events) {
|
||||
remove = 0;
|
||||
if (cpuctx->ctx.nr_events != cpuctx->ctx.nr_active)
|
||||
rotate = 1;
|
||||
if (cpuctx->ctx.nr_freq)
|
||||
freq = 1;
|
||||
}
|
||||
|
||||
ctx = cpuctx->task_ctx;
|
||||
@ -2395,33 +2405,40 @@ static void perf_rotate_context(struct perf_cpu_context *cpuctx)
|
||||
remove = 0;
|
||||
if (ctx->nr_events != ctx->nr_active)
|
||||
rotate = 1;
|
||||
if (ctx->nr_freq)
|
||||
freq = 1;
|
||||
}
|
||||
|
||||
if (!rotate && !freq)
|
||||
goto done;
|
||||
|
||||
perf_ctx_lock(cpuctx, cpuctx->task_ctx);
|
||||
perf_pmu_disable(cpuctx->ctx.pmu);
|
||||
perf_ctx_adjust_freq(&cpuctx->ctx, interval);
|
||||
if (ctx)
|
||||
perf_ctx_adjust_freq(ctx, interval);
|
||||
|
||||
if (!rotate)
|
||||
goto done;
|
||||
if (freq) {
|
||||
perf_ctx_adjust_freq(&cpuctx->ctx, interval);
|
||||
if (ctx)
|
||||
perf_ctx_adjust_freq(ctx, interval);
|
||||
}
|
||||
|
||||
cpu_ctx_sched_out(cpuctx, EVENT_FLEXIBLE);
|
||||
if (ctx)
|
||||
ctx_sched_out(ctx, cpuctx, EVENT_FLEXIBLE);
|
||||
if (rotate) {
|
||||
cpu_ctx_sched_out(cpuctx, EVENT_FLEXIBLE);
|
||||
if (ctx)
|
||||
ctx_sched_out(ctx, cpuctx, EVENT_FLEXIBLE);
|
||||
|
||||
rotate_ctx(&cpuctx->ctx);
|
||||
if (ctx)
|
||||
rotate_ctx(ctx);
|
||||
rotate_ctx(&cpuctx->ctx);
|
||||
if (ctx)
|
||||
rotate_ctx(ctx);
|
||||
|
||||
perf_event_sched_in(cpuctx, ctx, current);
|
||||
perf_event_sched_in(cpuctx, ctx, current);
|
||||
}
|
||||
|
||||
perf_pmu_enable(cpuctx->ctx.pmu);
|
||||
perf_ctx_unlock(cpuctx, cpuctx->task_ctx);
|
||||
|
||||
done:
|
||||
if (remove)
|
||||
list_del_init(&cpuctx->rotation_list);
|
||||
|
||||
perf_pmu_enable(cpuctx->ctx.pmu);
|
||||
perf_ctx_unlock(cpuctx, cpuctx->task_ctx);
|
||||
}
|
||||
|
||||
void perf_event_task_tick(void)
|
||||
@ -2448,7 +2465,7 @@ static int event_enable_on_exec(struct perf_event *event,
|
||||
if (event->state >= PERF_EVENT_STATE_INACTIVE)
|
||||
return 0;
|
||||
|
||||
__perf_event_mark_enabled(event, ctx);
|
||||
__perf_event_mark_enabled(event);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -2480,13 +2497,7 @@ static void perf_event_enable_on_exec(struct perf_event_context *ctx)
|
||||
raw_spin_lock(&ctx->lock);
|
||||
task_ctx_sched_out(ctx);
|
||||
|
||||
list_for_each_entry(event, &ctx->pinned_groups, group_entry) {
|
||||
ret = event_enable_on_exec(event, ctx);
|
||||
if (ret)
|
||||
enabled = 1;
|
||||
}
|
||||
|
||||
list_for_each_entry(event, &ctx->flexible_groups, group_entry) {
|
||||
list_for_each_entry(event, &ctx->event_list, event_entry) {
|
||||
ret = event_enable_on_exec(event, ctx);
|
||||
if (ret)
|
||||
enabled = 1;
|
||||
@ -2573,215 +2584,6 @@ static u64 perf_event_read(struct perf_event *event)
|
||||
return perf_event_count(event);
|
||||
}
|
||||
|
||||
/*
|
||||
* Callchain support
|
||||
*/
|
||||
|
||||
struct callchain_cpus_entries {
|
||||
struct rcu_head rcu_head;
|
||||
struct perf_callchain_entry *cpu_entries[0];
|
||||
};
|
||||
|
||||
static DEFINE_PER_CPU(int, callchain_recursion[PERF_NR_CONTEXTS]);
|
||||
static atomic_t nr_callchain_events;
|
||||
static DEFINE_MUTEX(callchain_mutex);
|
||||
struct callchain_cpus_entries *callchain_cpus_entries;
|
||||
|
||||
|
||||
__weak void perf_callchain_kernel(struct perf_callchain_entry *entry,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
}
|
||||
|
||||
__weak void perf_callchain_user(struct perf_callchain_entry *entry,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
}
|
||||
|
||||
static void release_callchain_buffers_rcu(struct rcu_head *head)
|
||||
{
|
||||
struct callchain_cpus_entries *entries;
|
||||
int cpu;
|
||||
|
||||
entries = container_of(head, struct callchain_cpus_entries, rcu_head);
|
||||
|
||||
for_each_possible_cpu(cpu)
|
||||
kfree(entries->cpu_entries[cpu]);
|
||||
|
||||
kfree(entries);
|
||||
}
|
||||
|
||||
static void release_callchain_buffers(void)
|
||||
{
|
||||
struct callchain_cpus_entries *entries;
|
||||
|
||||
entries = callchain_cpus_entries;
|
||||
rcu_assign_pointer(callchain_cpus_entries, NULL);
|
||||
call_rcu(&entries->rcu_head, release_callchain_buffers_rcu);
|
||||
}
|
||||
|
||||
static int alloc_callchain_buffers(void)
|
||||
{
|
||||
int cpu;
|
||||
int size;
|
||||
struct callchain_cpus_entries *entries;
|
||||
|
||||
/*
|
||||
* We can't use the percpu allocation API for data that can be
|
||||
* accessed from NMI. Use a temporary manual per cpu allocation
|
||||
* until that gets sorted out.
|
||||
*/
|
||||
size = offsetof(struct callchain_cpus_entries, cpu_entries[nr_cpu_ids]);
|
||||
|
||||
entries = kzalloc(size, GFP_KERNEL);
|
||||
if (!entries)
|
||||
return -ENOMEM;
|
||||
|
||||
size = sizeof(struct perf_callchain_entry) * PERF_NR_CONTEXTS;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
entries->cpu_entries[cpu] = kmalloc_node(size, GFP_KERNEL,
|
||||
cpu_to_node(cpu));
|
||||
if (!entries->cpu_entries[cpu])
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rcu_assign_pointer(callchain_cpus_entries, entries);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
for_each_possible_cpu(cpu)
|
||||
kfree(entries->cpu_entries[cpu]);
|
||||
kfree(entries);
|
||||
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int get_callchain_buffers(void)
|
||||
{
|
||||
int err = 0;
|
||||
int count;
|
||||
|
||||
mutex_lock(&callchain_mutex);
|
||||
|
||||
count = atomic_inc_return(&nr_callchain_events);
|
||||
if (WARN_ON_ONCE(count < 1)) {
|
||||
err = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (count > 1) {
|
||||
/* If the allocation failed, give up */
|
||||
if (!callchain_cpus_entries)
|
||||
err = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
err = alloc_callchain_buffers();
|
||||
if (err)
|
||||
release_callchain_buffers();
|
||||
exit:
|
||||
mutex_unlock(&callchain_mutex);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void put_callchain_buffers(void)
|
||||
{
|
||||
if (atomic_dec_and_mutex_lock(&nr_callchain_events, &callchain_mutex)) {
|
||||
release_callchain_buffers();
|
||||
mutex_unlock(&callchain_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
static int get_recursion_context(int *recursion)
|
||||
{
|
||||
int rctx;
|
||||
|
||||
if (in_nmi())
|
||||
rctx = 3;
|
||||
else if (in_irq())
|
||||
rctx = 2;
|
||||
else if (in_softirq())
|
||||
rctx = 1;
|
||||
else
|
||||
rctx = 0;
|
||||
|
||||
if (recursion[rctx])
|
||||
return -1;
|
||||
|
||||
recursion[rctx]++;
|
||||
barrier();
|
||||
|
||||
return rctx;
|
||||
}
|
||||
|
||||
static inline void put_recursion_context(int *recursion, int rctx)
|
||||
{
|
||||
barrier();
|
||||
recursion[rctx]--;
|
||||
}
|
||||
|
||||
static struct perf_callchain_entry *get_callchain_entry(int *rctx)
|
||||
{
|
||||
int cpu;
|
||||
struct callchain_cpus_entries *entries;
|
||||
|
||||
*rctx = get_recursion_context(__get_cpu_var(callchain_recursion));
|
||||
if (*rctx == -1)
|
||||
return NULL;
|
||||
|
||||
entries = rcu_dereference(callchain_cpus_entries);
|
||||
if (!entries)
|
||||
return NULL;
|
||||
|
||||
cpu = smp_processor_id();
|
||||
|
||||
return &entries->cpu_entries[cpu][*rctx];
|
||||
}
|
||||
|
||||
static void
|
||||
put_callchain_entry(int rctx)
|
||||
{
|
||||
put_recursion_context(__get_cpu_var(callchain_recursion), rctx);
|
||||
}
|
||||
|
||||
static struct perf_callchain_entry *perf_callchain(struct pt_regs *regs)
|
||||
{
|
||||
int rctx;
|
||||
struct perf_callchain_entry *entry;
|
||||
|
||||
|
||||
entry = get_callchain_entry(&rctx);
|
||||
if (rctx == -1)
|
||||
return NULL;
|
||||
|
||||
if (!entry)
|
||||
goto exit_put;
|
||||
|
||||
entry->nr = 0;
|
||||
|
||||
if (!user_mode(regs)) {
|
||||
perf_callchain_store(entry, PERF_CONTEXT_KERNEL);
|
||||
perf_callchain_kernel(entry, regs);
|
||||
if (current->mm)
|
||||
regs = task_pt_regs(current);
|
||||
else
|
||||
regs = NULL;
|
||||
}
|
||||
|
||||
if (regs) {
|
||||
perf_callchain_store(entry, PERF_CONTEXT_USER);
|
||||
perf_callchain_user(entry, regs);
|
||||
}
|
||||
|
||||
exit_put:
|
||||
put_callchain_entry(rctx);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the perf_event context in a task_struct:
|
||||
*/
|
||||
@ -2946,7 +2748,7 @@ static void free_event(struct perf_event *event)
|
||||
|
||||
if (!event->parent) {
|
||||
if (event->attach_state & PERF_ATTACH_TASK)
|
||||
jump_label_dec(&perf_sched_events);
|
||||
jump_label_dec_deferred(&perf_sched_events);
|
||||
if (event->attr.mmap || event->attr.mmap_data)
|
||||
atomic_dec(&nr_mmap_events);
|
||||
if (event->attr.comm)
|
||||
@ -2957,7 +2759,7 @@ static void free_event(struct perf_event *event)
|
||||
put_callchain_buffers();
|
||||
if (is_cgroup_event(event)) {
|
||||
atomic_dec(&per_cpu(perf_cgroup_events, event->cpu));
|
||||
jump_label_dec(&perf_sched_events);
|
||||
jump_label_dec_deferred(&perf_sched_events);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4820,7 +4622,6 @@ static void perf_swevent_overflow(struct perf_event *event, u64 overflow,
|
||||
struct hw_perf_event *hwc = &event->hw;
|
||||
int throttle = 0;
|
||||
|
||||
data->period = event->hw.last_period;
|
||||
if (!overflow)
|
||||
overflow = perf_swevent_set_period(event);
|
||||
|
||||
@ -4854,6 +4655,12 @@ static void perf_swevent_event(struct perf_event *event, u64 nr,
|
||||
if (!is_sampling_event(event))
|
||||
return;
|
||||
|
||||
if ((event->attr.sample_type & PERF_SAMPLE_PERIOD) && !event->attr.freq) {
|
||||
data->period = nr;
|
||||
return perf_swevent_overflow(event, 1, data, regs);
|
||||
} else
|
||||
data->period = event->hw.last_period;
|
||||
|
||||
if (nr == 1 && hwc->sample_period == 1 && !event->attr.freq)
|
||||
return perf_swevent_overflow(event, 1, data, regs);
|
||||
|
||||
@ -5981,7 +5788,7 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
|
||||
|
||||
if (!event->parent) {
|
||||
if (event->attach_state & PERF_ATTACH_TASK)
|
||||
jump_label_inc(&perf_sched_events);
|
||||
jump_label_inc(&perf_sched_events.key);
|
||||
if (event->attr.mmap || event->attr.mmap_data)
|
||||
atomic_inc(&nr_mmap_events);
|
||||
if (event->attr.comm)
|
||||
@ -6219,7 +6026,7 @@ SYSCALL_DEFINE5(perf_event_open,
|
||||
* - that may need work on context switch
|
||||
*/
|
||||
atomic_inc(&per_cpu(perf_cgroup_events, event->cpu));
|
||||
jump_label_inc(&perf_sched_events);
|
||||
jump_label_inc(&perf_sched_events.key);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -7065,6 +6872,9 @@ void __init perf_event_init(void)
|
||||
|
||||
ret = init_hw_breakpoint();
|
||||
WARN(ret, "hw_breakpoint initialization failed with: %d", ret);
|
||||
|
||||
/* do not patch jump label more than once per second */
|
||||
jump_label_rate_limit(&perf_sched_events, HZ);
|
||||
}
|
||||
|
||||
static int __init perf_event_sysfs_init(void)
|
||||
|
@ -1,6 +1,10 @@
|
||||
#ifndef _KERNEL_EVENTS_INTERNAL_H
|
||||
#define _KERNEL_EVENTS_INTERNAL_H
|
||||
|
||||
#include <linux/hardirq.h>
|
||||
|
||||
/* Buffer handling */
|
||||
|
||||
#define RING_BUFFER_WRITABLE 0x01
|
||||
|
||||
struct ring_buffer {
|
||||
@ -67,7 +71,7 @@ static inline int page_order(struct ring_buffer *rb)
|
||||
}
|
||||
#endif
|
||||
|
||||
static unsigned long perf_data_size(struct ring_buffer *rb)
|
||||
static inline unsigned long perf_data_size(struct ring_buffer *rb)
|
||||
{
|
||||
return rb->nr_pages << (PAGE_SHIFT + page_order(rb));
|
||||
}
|
||||
@ -96,4 +100,37 @@ __output_copy(struct perf_output_handle *handle,
|
||||
} while (len);
|
||||
}
|
||||
|
||||
/* Callchain handling */
|
||||
extern struct perf_callchain_entry *perf_callchain(struct pt_regs *regs);
|
||||
extern int get_callchain_buffers(void);
|
||||
extern void put_callchain_buffers(void);
|
||||
|
||||
static inline int get_recursion_context(int *recursion)
|
||||
{
|
||||
int rctx;
|
||||
|
||||
if (in_nmi())
|
||||
rctx = 3;
|
||||
else if (in_irq())
|
||||
rctx = 2;
|
||||
else if (in_softirq())
|
||||
rctx = 1;
|
||||
else
|
||||
rctx = 0;
|
||||
|
||||
if (recursion[rctx])
|
||||
return -1;
|
||||
|
||||
recursion[rctx]++;
|
||||
barrier();
|
||||
|
||||
return rctx;
|
||||
}
|
||||
|
||||
static inline void put_recursion_context(int *recursion, int rctx)
|
||||
{
|
||||
barrier();
|
||||
recursion[rctx]--;
|
||||
}
|
||||
|
||||
#endif /* _KERNEL_EVENTS_INTERNAL_H */
|
||||
|
@ -73,16 +73,47 @@ void jump_label_inc(struct jump_label_key *key)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(jump_label_inc);
|
||||
|
||||
void jump_label_dec(struct jump_label_key *key)
|
||||
static void __jump_label_dec(struct jump_label_key *key,
|
||||
unsigned long rate_limit, struct delayed_work *work)
|
||||
{
|
||||
if (!atomic_dec_and_mutex_lock(&key->enabled, &jump_label_mutex))
|
||||
return;
|
||||
|
||||
jump_label_update(key, JUMP_LABEL_DISABLE);
|
||||
if (rate_limit) {
|
||||
atomic_inc(&key->enabled);
|
||||
schedule_delayed_work(work, rate_limit);
|
||||
} else
|
||||
jump_label_update(key, JUMP_LABEL_DISABLE);
|
||||
|
||||
jump_label_unlock();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(jump_label_dec);
|
||||
|
||||
static void jump_label_update_timeout(struct work_struct *work)
|
||||
{
|
||||
struct jump_label_key_deferred *key =
|
||||
container_of(work, struct jump_label_key_deferred, work.work);
|
||||
__jump_label_dec(&key->key, 0, NULL);
|
||||
}
|
||||
|
||||
void jump_label_dec(struct jump_label_key *key)
|
||||
{
|
||||
__jump_label_dec(key, 0, NULL);
|
||||
}
|
||||
|
||||
void jump_label_dec_deferred(struct jump_label_key_deferred *key)
|
||||
{
|
||||
__jump_label_dec(&key->key, key->timeout, &key->work);
|
||||
}
|
||||
|
||||
|
||||
void jump_label_rate_limit(struct jump_label_key_deferred *key,
|
||||
unsigned long rl)
|
||||
{
|
||||
key->timeout = rl;
|
||||
INIT_DELAYED_WORK(&key->work, jump_label_update_timeout);
|
||||
}
|
||||
|
||||
static int addr_conflict(struct jump_entry *entry, void *start, void *end)
|
||||
{
|
||||
if (entry->code <= (unsigned long)end &&
|
||||
@ -113,7 +144,7 @@ static int __jump_label_text_reserved(struct jump_entry *iter_start,
|
||||
* running code can override this to make the non-live update case
|
||||
* cheaper.
|
||||
*/
|
||||
void __weak arch_jump_label_transform_static(struct jump_entry *entry,
|
||||
void __weak __init_or_module arch_jump_label_transform_static(struct jump_entry *entry,
|
||||
enum jump_label_type type)
|
||||
{
|
||||
arch_jump_label_transform(entry, type);
|
||||
@ -219,8 +250,13 @@ void jump_label_apply_nops(struct module *mod)
|
||||
if (iter_start == iter_stop)
|
||||
return;
|
||||
|
||||
for (iter = iter_start; iter < iter_stop; iter++)
|
||||
arch_jump_label_transform_static(iter, JUMP_LABEL_DISABLE);
|
||||
for (iter = iter_start; iter < iter_stop; iter++) {
|
||||
struct jump_label_key *iterk;
|
||||
|
||||
iterk = (struct jump_label_key *)(unsigned long)iter->key;
|
||||
arch_jump_label_transform_static(iter, jump_label_enabled(iterk) ?
|
||||
JUMP_LABEL_ENABLE : JUMP_LABEL_DISABLE);
|
||||
}
|
||||
}
|
||||
|
||||
static int jump_label_add_module(struct module *mod)
|
||||
@ -260,8 +296,7 @@ static int jump_label_add_module(struct module *mod)
|
||||
key->next = jlm;
|
||||
|
||||
if (jump_label_enabled(key))
|
||||
__jump_label_update(key, iter, iter_stop,
|
||||
JUMP_LABEL_ENABLE);
|
||||
__jump_label_update(key, iter, iter_stop, JUMP_LABEL_ENABLE);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -499,36 +499,32 @@ void get_usage_chars(struct lock_class *class, char usage[LOCK_USAGE_CHARS])
|
||||
usage[i] = '\0';
|
||||
}
|
||||
|
||||
static int __print_lock_name(struct lock_class *class)
|
||||
static void __print_lock_name(struct lock_class *class)
|
||||
{
|
||||
char str[KSYM_NAME_LEN];
|
||||
const char *name;
|
||||
|
||||
name = class->name;
|
||||
if (!name)
|
||||
name = __get_key_name(class->key, str);
|
||||
|
||||
return printk("%s", name);
|
||||
}
|
||||
|
||||
static void print_lock_name(struct lock_class *class)
|
||||
{
|
||||
char str[KSYM_NAME_LEN], usage[LOCK_USAGE_CHARS];
|
||||
const char *name;
|
||||
|
||||
get_usage_chars(class, usage);
|
||||
|
||||
name = class->name;
|
||||
if (!name) {
|
||||
name = __get_key_name(class->key, str);
|
||||
printk(" (%s", name);
|
||||
printk("%s", name);
|
||||
} else {
|
||||
printk(" (%s", name);
|
||||
printk("%s", name);
|
||||
if (class->name_version > 1)
|
||||
printk("#%d", class->name_version);
|
||||
if (class->subclass)
|
||||
printk("/%d", class->subclass);
|
||||
}
|
||||
}
|
||||
|
||||
static void print_lock_name(struct lock_class *class)
|
||||
{
|
||||
char usage[LOCK_USAGE_CHARS];
|
||||
|
||||
get_usage_chars(class, usage);
|
||||
|
||||
printk(" (");
|
||||
__print_lock_name(class);
|
||||
printk("){%s}", usage);
|
||||
}
|
||||
|
||||
|
@ -338,7 +338,8 @@ static DECLARE_WAIT_QUEUE_HEAD(trace_wait);
|
||||
/* trace_flags holds trace_options default values */
|
||||
unsigned long trace_flags = TRACE_ITER_PRINT_PARENT | TRACE_ITER_PRINTK |
|
||||
TRACE_ITER_ANNOTATE | TRACE_ITER_CONTEXT_INFO | TRACE_ITER_SLEEP_TIME |
|
||||
TRACE_ITER_GRAPH_TIME | TRACE_ITER_RECORD_CMD | TRACE_ITER_OVERWRITE;
|
||||
TRACE_ITER_GRAPH_TIME | TRACE_ITER_RECORD_CMD | TRACE_ITER_OVERWRITE |
|
||||
TRACE_ITER_IRQ_INFO;
|
||||
|
||||
static int trace_stop_count;
|
||||
static DEFINE_RAW_SPINLOCK(tracing_start_lock);
|
||||
@ -426,6 +427,7 @@ static const char *trace_options[] = {
|
||||
"record-cmd",
|
||||
"overwrite",
|
||||
"disable_on_free",
|
||||
"irq-info",
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -1843,6 +1845,33 @@ static void s_stop(struct seq_file *m, void *p)
|
||||
trace_event_read_unlock();
|
||||
}
|
||||
|
||||
static void
|
||||
get_total_entries(struct trace_array *tr, unsigned long *total, unsigned long *entries)
|
||||
{
|
||||
unsigned long count;
|
||||
int cpu;
|
||||
|
||||
*total = 0;
|
||||
*entries = 0;
|
||||
|
||||
for_each_tracing_cpu(cpu) {
|
||||
count = ring_buffer_entries_cpu(tr->buffer, cpu);
|
||||
/*
|
||||
* If this buffer has skipped entries, then we hold all
|
||||
* entries for the trace and we need to ignore the
|
||||
* ones before the time stamp.
|
||||
*/
|
||||
if (tr->data[cpu]->skipped_entries) {
|
||||
count -= tr->data[cpu]->skipped_entries;
|
||||
/* total is the same as the entries */
|
||||
*total += count;
|
||||
} else
|
||||
*total += count +
|
||||
ring_buffer_overrun_cpu(tr->buffer, cpu);
|
||||
*entries += count;
|
||||
}
|
||||
}
|
||||
|
||||
static void print_lat_help_header(struct seq_file *m)
|
||||
{
|
||||
seq_puts(m, "# _------=> CPU# \n");
|
||||
@ -1855,12 +1884,35 @@ static void print_lat_help_header(struct seq_file *m)
|
||||
seq_puts(m, "# \\ / ||||| \\ | / \n");
|
||||
}
|
||||
|
||||
static void print_func_help_header(struct seq_file *m)
|
||||
static void print_event_info(struct trace_array *tr, struct seq_file *m)
|
||||
{
|
||||
seq_puts(m, "# TASK-PID CPU# TIMESTAMP FUNCTION\n");
|
||||
unsigned long total;
|
||||
unsigned long entries;
|
||||
|
||||
get_total_entries(tr, &total, &entries);
|
||||
seq_printf(m, "# entries-in-buffer/entries-written: %lu/%lu #P:%d\n",
|
||||
entries, total, num_online_cpus());
|
||||
seq_puts(m, "#\n");
|
||||
}
|
||||
|
||||
static void print_func_help_header(struct trace_array *tr, struct seq_file *m)
|
||||
{
|
||||
print_event_info(tr, m);
|
||||
seq_puts(m, "# TASK-PID CPU# TIMESTAMP FUNCTION\n");
|
||||
seq_puts(m, "# | | | | |\n");
|
||||
}
|
||||
|
||||
static void print_func_help_header_irq(struct trace_array *tr, struct seq_file *m)
|
||||
{
|
||||
print_event_info(tr, m);
|
||||
seq_puts(m, "# _-----=> irqs-off\n");
|
||||
seq_puts(m, "# / _----=> need-resched\n");
|
||||
seq_puts(m, "# | / _---=> hardirq/softirq\n");
|
||||
seq_puts(m, "# || / _--=> preempt-depth\n");
|
||||
seq_puts(m, "# ||| / delay\n");
|
||||
seq_puts(m, "# TASK-PID CPU# |||| TIMESTAMP FUNCTION\n");
|
||||
seq_puts(m, "# | | | |||| | |\n");
|
||||
}
|
||||
|
||||
void
|
||||
print_trace_header(struct seq_file *m, struct trace_iterator *iter)
|
||||
@ -1869,32 +1921,14 @@ print_trace_header(struct seq_file *m, struct trace_iterator *iter)
|
||||
struct trace_array *tr = iter->tr;
|
||||
struct trace_array_cpu *data = tr->data[tr->cpu];
|
||||
struct tracer *type = current_trace;
|
||||
unsigned long entries = 0;
|
||||
unsigned long total = 0;
|
||||
unsigned long count;
|
||||
unsigned long entries;
|
||||
unsigned long total;
|
||||
const char *name = "preemption";
|
||||
int cpu;
|
||||
|
||||
if (type)
|
||||
name = type->name;
|
||||
|
||||
|
||||
for_each_tracing_cpu(cpu) {
|
||||
count = ring_buffer_entries_cpu(tr->buffer, cpu);
|
||||
/*
|
||||
* If this buffer has skipped entries, then we hold all
|
||||
* entries for the trace and we need to ignore the
|
||||
* ones before the time stamp.
|
||||
*/
|
||||
if (tr->data[cpu]->skipped_entries) {
|
||||
count -= tr->data[cpu]->skipped_entries;
|
||||
/* total is the same as the entries */
|
||||
total += count;
|
||||
} else
|
||||
total += count +
|
||||
ring_buffer_overrun_cpu(tr->buffer, cpu);
|
||||
entries += count;
|
||||
}
|
||||
get_total_entries(tr, &total, &entries);
|
||||
|
||||
seq_printf(m, "# %s latency trace v1.1.5 on %s\n",
|
||||
name, UTS_RELEASE);
|
||||
@ -2140,6 +2174,21 @@ enum print_line_t print_trace_line(struct trace_iterator *iter)
|
||||
return print_trace_fmt(iter);
|
||||
}
|
||||
|
||||
void trace_latency_header(struct seq_file *m)
|
||||
{
|
||||
struct trace_iterator *iter = m->private;
|
||||
|
||||
/* print nothing if the buffers are empty */
|
||||
if (trace_empty(iter))
|
||||
return;
|
||||
|
||||
if (iter->iter_flags & TRACE_FILE_LAT_FMT)
|
||||
print_trace_header(m, iter);
|
||||
|
||||
if (!(trace_flags & TRACE_ITER_VERBOSE))
|
||||
print_lat_help_header(m);
|
||||
}
|
||||
|
||||
void trace_default_header(struct seq_file *m)
|
||||
{
|
||||
struct trace_iterator *iter = m->private;
|
||||
@ -2155,8 +2204,12 @@ void trace_default_header(struct seq_file *m)
|
||||
if (!(trace_flags & TRACE_ITER_VERBOSE))
|
||||
print_lat_help_header(m);
|
||||
} else {
|
||||
if (!(trace_flags & TRACE_ITER_VERBOSE))
|
||||
print_func_help_header(m);
|
||||
if (!(trace_flags & TRACE_ITER_VERBOSE)) {
|
||||
if (trace_flags & TRACE_ITER_IRQ_INFO)
|
||||
print_func_help_header_irq(iter->tr, m);
|
||||
else
|
||||
print_func_help_header(iter->tr, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -370,6 +370,7 @@ void trace_graph_function(struct trace_array *tr,
|
||||
unsigned long ip,
|
||||
unsigned long parent_ip,
|
||||
unsigned long flags, int pc);
|
||||
void trace_latency_header(struct seq_file *m);
|
||||
void trace_default_header(struct seq_file *m);
|
||||
void print_trace_header(struct seq_file *m, struct trace_iterator *iter);
|
||||
int trace_empty(struct trace_iterator *iter);
|
||||
@ -654,6 +655,7 @@ enum trace_iterator_flags {
|
||||
TRACE_ITER_RECORD_CMD = 0x100000,
|
||||
TRACE_ITER_OVERWRITE = 0x200000,
|
||||
TRACE_ITER_STOP_ON_FREE = 0x400000,
|
||||
TRACE_ITER_IRQ_INFO = 0x800000,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -27,6 +27,12 @@
|
||||
#include "trace.h"
|
||||
#include "trace_output.h"
|
||||
|
||||
#define DEFAULT_SYS_FILTER_MESSAGE \
|
||||
"### global filter ###\n" \
|
||||
"# Use this to set filters for multiple events.\n" \
|
||||
"# Only events with the given fields will be affected.\n" \
|
||||
"# If no events are modified, an error message will be displayed here"
|
||||
|
||||
enum filter_op_ids
|
||||
{
|
||||
OP_OR,
|
||||
@ -646,7 +652,7 @@ void print_subsystem_event_filter(struct event_subsystem *system,
|
||||
if (filter && filter->filter_string)
|
||||
trace_seq_printf(s, "%s\n", filter->filter_string);
|
||||
else
|
||||
trace_seq_printf(s, "none\n");
|
||||
trace_seq_printf(s, DEFAULT_SYS_FILTER_MESSAGE "\n");
|
||||
mutex_unlock(&event_mutex);
|
||||
}
|
||||
|
||||
@ -1838,7 +1844,10 @@ int apply_subsystem_event_filter(struct event_subsystem *system,
|
||||
if (!filter)
|
||||
goto out;
|
||||
|
||||
replace_filter_string(filter, filter_string);
|
||||
/* System filters just show a default message */
|
||||
kfree(filter->filter_string);
|
||||
filter->filter_string = NULL;
|
||||
|
||||
/*
|
||||
* No event actually uses the system filter
|
||||
* we can free it without synchronize_sched().
|
||||
@ -1848,14 +1857,12 @@ int apply_subsystem_event_filter(struct event_subsystem *system,
|
||||
|
||||
parse_init(ps, filter_ops, filter_string);
|
||||
err = filter_parse(ps);
|
||||
if (err) {
|
||||
append_filter_err(ps, system->filter);
|
||||
goto out;
|
||||
}
|
||||
if (err)
|
||||
goto err_filter;
|
||||
|
||||
err = replace_system_preds(system, ps, filter_string);
|
||||
if (err)
|
||||
append_filter_err(ps, system->filter);
|
||||
goto err_filter;
|
||||
|
||||
out:
|
||||
filter_opstack_clear(ps);
|
||||
@ -1865,6 +1872,11 @@ int apply_subsystem_event_filter(struct event_subsystem *system,
|
||||
mutex_unlock(&event_mutex);
|
||||
|
||||
return err;
|
||||
|
||||
err_filter:
|
||||
replace_filter_string(filter, filter_string);
|
||||
append_filter_err(ps, system->filter);
|
||||
goto out;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PERF_EVENTS
|
||||
|
@ -280,9 +280,20 @@ static enum print_line_t irqsoff_print_line(struct trace_iterator *iter)
|
||||
}
|
||||
|
||||
static void irqsoff_graph_return(struct ftrace_graph_ret *trace) { }
|
||||
static void irqsoff_print_header(struct seq_file *s) { }
|
||||
static void irqsoff_trace_open(struct trace_iterator *iter) { }
|
||||
static void irqsoff_trace_close(struct trace_iterator *iter) { }
|
||||
|
||||
#ifdef CONFIG_FUNCTION_TRACER
|
||||
static void irqsoff_print_header(struct seq_file *s)
|
||||
{
|
||||
trace_default_header(s);
|
||||
}
|
||||
#else
|
||||
static void irqsoff_print_header(struct seq_file *s)
|
||||
{
|
||||
trace_latency_header(s);
|
||||
}
|
||||
#endif /* CONFIG_FUNCTION_TRACER */
|
||||
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
|
||||
|
||||
/*
|
||||
|
@ -627,11 +627,23 @@ int trace_print_context(struct trace_iterator *iter)
|
||||
unsigned long usec_rem = do_div(t, USEC_PER_SEC);
|
||||
unsigned long secs = (unsigned long)t;
|
||||
char comm[TASK_COMM_LEN];
|
||||
int ret;
|
||||
|
||||
trace_find_cmdline(entry->pid, comm);
|
||||
|
||||
return trace_seq_printf(s, "%16s-%-5d [%03d] %5lu.%06lu: ",
|
||||
comm, entry->pid, iter->cpu, secs, usec_rem);
|
||||
ret = trace_seq_printf(s, "%16s-%-5d [%03d] ",
|
||||
comm, entry->pid, iter->cpu);
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
if (trace_flags & TRACE_ITER_IRQ_INFO) {
|
||||
ret = trace_print_lat_fmt(s, entry);
|
||||
if (!ret)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return trace_seq_printf(s, " %5lu.%06lu: ",
|
||||
secs, usec_rem);
|
||||
}
|
||||
|
||||
int trace_print_lat_context(struct trace_iterator *iter)
|
||||
|
@ -280,9 +280,20 @@ static enum print_line_t wakeup_print_line(struct trace_iterator *iter)
|
||||
}
|
||||
|
||||
static void wakeup_graph_return(struct ftrace_graph_ret *trace) { }
|
||||
static void wakeup_print_header(struct seq_file *s) { }
|
||||
static void wakeup_trace_open(struct trace_iterator *iter) { }
|
||||
static void wakeup_trace_close(struct trace_iterator *iter) { }
|
||||
|
||||
#ifdef CONFIG_FUNCTION_TRACER
|
||||
static void wakeup_print_header(struct seq_file *s)
|
||||
{
|
||||
trace_default_header(s);
|
||||
}
|
||||
#else
|
||||
static void wakeup_print_header(struct seq_file *s)
|
||||
{
|
||||
trace_latency_header(s);
|
||||
}
|
||||
#endif /* CONFIG_FUNCTION_TRACER */
|
||||
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
|
||||
|
||||
/*
|
||||
|
@ -66,7 +66,7 @@ OPTIONS
|
||||
used. This interfaces starts by centering on the line with more
|
||||
samples, TAB/UNTAB cycles through the lines with more samples.
|
||||
|
||||
-c::
|
||||
-C::
|
||||
--cpu:: Only report samples for the list of CPUs provided. Multiple CPUs can
|
||||
be provided as a comma-separated list with no space: 0,1. Ranges of
|
||||
CPUs are specified with -: 0-2. Default is to report samples on all
|
||||
|
@ -39,7 +39,7 @@ OPTIONS
|
||||
-T::
|
||||
--threads::
|
||||
Show per-thread event counters
|
||||
-C::
|
||||
-c::
|
||||
--comms=::
|
||||
Only consider symbols in these comms. CSV that understands
|
||||
file://filename entries.
|
||||
@ -128,7 +128,7 @@ OPTIONS
|
||||
--symfs=<directory>::
|
||||
Look for files with symbols relative to this directory.
|
||||
|
||||
-c::
|
||||
-C::
|
||||
--cpu:: Only report samples for the list of CPUs provided. Multiple CPUs can
|
||||
be provided as a comma-separated list with no space: 0,1. Ranges of
|
||||
CPUs are specified with -: 0-2. Default is to report samples on all
|
||||
|
@ -182,12 +182,17 @@ OPTIONS
|
||||
--hide-call-graph::
|
||||
When printing symbols do not display call chain.
|
||||
|
||||
-c::
|
||||
-C::
|
||||
--cpu:: Only report samples for the list of CPUs provided. Multiple CPUs can
|
||||
be provided as a comma-separated list with no space: 0,1. Ranges of
|
||||
CPUs are specified with -: 0-2. Default is to report samples on all
|
||||
CPUs.
|
||||
|
||||
-c::
|
||||
--comms=::
|
||||
Only display events for these comms. CSV that understands
|
||||
file://filename entries.
|
||||
|
||||
-I::
|
||||
--show-info::
|
||||
Display extended information about the perf.data file. This adds
|
||||
|
@ -8,13 +8,19 @@ perf-test - Runs sanity tests.
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf test <options>'
|
||||
'perf test [<options>] [{list <test-name-fragment>|[<test-name-fragments>|<test-numbers>]}]'
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
This command does assorted sanity tests, initially through linked routines but
|
||||
also will look for a directory with more tests in the form of scripts.
|
||||
|
||||
To get a list of available tests use 'perf test list', specifying a test name
|
||||
fragment will show all tests that have it.
|
||||
|
||||
To run just specific tests, inform test name fragments or the numbers obtained
|
||||
from 'perf test list'.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-v::
|
||||
|
@ -278,6 +278,7 @@ LIB_H += util/strbuf.h
|
||||
LIB_H += util/strlist.h
|
||||
LIB_H += util/strfilter.h
|
||||
LIB_H += util/svghelper.h
|
||||
LIB_H += util/tool.h
|
||||
LIB_H += util/run-command.h
|
||||
LIB_H += util/sigchain.h
|
||||
LIB_H += util/symbol.h
|
||||
|
@ -27,32 +27,32 @@
|
||||
#include "util/sort.h"
|
||||
#include "util/hist.h"
|
||||
#include "util/session.h"
|
||||
#include "util/tool.h"
|
||||
|
||||
#include <linux/bitmap.h>
|
||||
|
||||
static char const *input_name = "perf.data";
|
||||
struct perf_annotate {
|
||||
struct perf_tool tool;
|
||||
char const *input_name;
|
||||
bool force, use_tui, use_stdio;
|
||||
bool full_paths;
|
||||
bool print_line;
|
||||
const char *sym_hist_filter;
|
||||
const char *cpu_list;
|
||||
DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
|
||||
};
|
||||
|
||||
static bool force, use_tui, use_stdio;
|
||||
|
||||
static bool full_paths;
|
||||
|
||||
static bool print_line;
|
||||
|
||||
static const char *sym_hist_filter;
|
||||
|
||||
static const char *cpu_list;
|
||||
static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
|
||||
|
||||
static int perf_evlist__add_sample(struct perf_evlist *evlist,
|
||||
struct perf_sample *sample,
|
||||
struct perf_evsel *evsel,
|
||||
struct addr_location *al)
|
||||
static int perf_evsel__add_sample(struct perf_evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct addr_location *al,
|
||||
struct perf_annotate *ann)
|
||||
{
|
||||
struct hist_entry *he;
|
||||
int ret;
|
||||
|
||||
if (sym_hist_filter != NULL &&
|
||||
(al->sym == NULL || strcmp(sym_hist_filter, al->sym->name) != 0)) {
|
||||
if (ann->sym_hist_filter != NULL &&
|
||||
(al->sym == NULL ||
|
||||
strcmp(ann->sym_hist_filter, al->sym->name) != 0)) {
|
||||
/* We're only interested in a symbol named sym_hist_filter */
|
||||
if (al->sym != NULL) {
|
||||
rb_erase(&al->sym->rb_node,
|
||||
@ -69,8 +69,7 @@ static int perf_evlist__add_sample(struct perf_evlist *evlist,
|
||||
ret = 0;
|
||||
if (he->ms.sym != NULL) {
|
||||
struct annotation *notes = symbol__annotation(he->ms.sym);
|
||||
if (notes->src == NULL &&
|
||||
symbol__alloc_hist(he->ms.sym, evlist->nr_entries) < 0)
|
||||
if (notes->src == NULL && symbol__alloc_hist(he->ms.sym) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = hist_entry__inc_addr_samples(he, evsel->idx, al->addr);
|
||||
@ -81,25 +80,26 @@ static int perf_evlist__add_sample(struct perf_evlist *evlist,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int process_sample_event(union perf_event *event,
|
||||
static int process_sample_event(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct perf_evsel *evsel,
|
||||
struct perf_session *session)
|
||||
struct machine *machine)
|
||||
{
|
||||
struct perf_annotate *ann = container_of(tool, struct perf_annotate, tool);
|
||||
struct addr_location al;
|
||||
|
||||
if (perf_event__preprocess_sample(event, session, &al, sample,
|
||||
if (perf_event__preprocess_sample(event, machine, &al, sample,
|
||||
symbol__annotate_init) < 0) {
|
||||
pr_warning("problem processing %d event, skipping it.\n",
|
||||
event->header.type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cpu_list && !test_bit(sample->cpu, cpu_bitmap))
|
||||
if (ann->cpu_list && !test_bit(sample->cpu, ann->cpu_bitmap))
|
||||
return 0;
|
||||
|
||||
if (!al.filtered &&
|
||||
perf_evlist__add_sample(session->evlist, sample, evsel, &al)) {
|
||||
if (!al.filtered && perf_evsel__add_sample(evsel, sample, &al, ann)) {
|
||||
pr_warning("problem incrementing symbol count, "
|
||||
"skipping event\n");
|
||||
return -1;
|
||||
@ -108,14 +108,15 @@ static int process_sample_event(union perf_event *event,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hist_entry__tty_annotate(struct hist_entry *he, int evidx)
|
||||
static int hist_entry__tty_annotate(struct hist_entry *he, int evidx,
|
||||
struct perf_annotate *ann)
|
||||
{
|
||||
return symbol__tty_annotate(he->ms.sym, he->ms.map, evidx,
|
||||
print_line, full_paths, 0, 0);
|
||||
ann->print_line, ann->full_paths, 0, 0);
|
||||
}
|
||||
|
||||
static void hists__find_annotations(struct hists *self, int evidx,
|
||||
int nr_events)
|
||||
struct perf_annotate *ann)
|
||||
{
|
||||
struct rb_node *nd = rb_first(&self->entries), *next;
|
||||
int key = K_RIGHT;
|
||||
@ -138,8 +139,7 @@ static void hists__find_annotations(struct hists *self, int evidx,
|
||||
}
|
||||
|
||||
if (use_browser > 0) {
|
||||
key = hist_entry__tui_annotate(he, evidx, nr_events,
|
||||
NULL, NULL, 0);
|
||||
key = hist_entry__tui_annotate(he, evidx, NULL, NULL, 0);
|
||||
switch (key) {
|
||||
case K_RIGHT:
|
||||
next = rb_next(nd);
|
||||
@ -154,7 +154,7 @@ static void hists__find_annotations(struct hists *self, int evidx,
|
||||
if (next != NULL)
|
||||
nd = next;
|
||||
} else {
|
||||
hist_entry__tty_annotate(he, evidx);
|
||||
hist_entry__tty_annotate(he, evidx, ann);
|
||||
nd = rb_next(nd);
|
||||
/*
|
||||
* Since we have a hist_entry per IP for the same
|
||||
@ -167,33 +167,26 @@ static void hists__find_annotations(struct hists *self, int evidx,
|
||||
}
|
||||
}
|
||||
|
||||
static struct perf_event_ops event_ops = {
|
||||
.sample = process_sample_event,
|
||||
.mmap = perf_event__process_mmap,
|
||||
.comm = perf_event__process_comm,
|
||||
.fork = perf_event__process_task,
|
||||
.ordered_samples = true,
|
||||
.ordering_requires_timestamps = true,
|
||||
};
|
||||
|
||||
static int __cmd_annotate(void)
|
||||
static int __cmd_annotate(struct perf_annotate *ann)
|
||||
{
|
||||
int ret;
|
||||
struct perf_session *session;
|
||||
struct perf_evsel *pos;
|
||||
u64 total_nr_samples;
|
||||
|
||||
session = perf_session__new(input_name, O_RDONLY, force, false, &event_ops);
|
||||
session = perf_session__new(ann->input_name, O_RDONLY,
|
||||
ann->force, false, &ann->tool);
|
||||
if (session == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (cpu_list) {
|
||||
ret = perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap);
|
||||
if (ann->cpu_list) {
|
||||
ret = perf_session__cpu_bitmap(session, ann->cpu_list,
|
||||
ann->cpu_bitmap);
|
||||
if (ret)
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
ret = perf_session__process_events(session, &event_ops);
|
||||
ret = perf_session__process_events(session, &ann->tool);
|
||||
if (ret)
|
||||
goto out_delete;
|
||||
|
||||
@ -217,13 +210,12 @@ static int __cmd_annotate(void)
|
||||
total_nr_samples += nr_samples;
|
||||
hists__collapse_resort(hists);
|
||||
hists__output_resort(hists);
|
||||
hists__find_annotations(hists, pos->idx,
|
||||
session->evlist->nr_entries);
|
||||
hists__find_annotations(hists, pos->idx, ann);
|
||||
}
|
||||
}
|
||||
|
||||
if (total_nr_samples == 0) {
|
||||
ui__warning("The %s file has no samples!\n", input_name);
|
||||
ui__warning("The %s file has no samples!\n", ann->input_name);
|
||||
goto out_delete;
|
||||
}
|
||||
out_delete:
|
||||
@ -247,29 +239,42 @@ static const char * const annotate_usage[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
OPT_STRING('i', "input", &input_name, "file",
|
||||
int cmd_annotate(int argc, const char **argv, const char *prefix __used)
|
||||
{
|
||||
struct perf_annotate annotate = {
|
||||
.tool = {
|
||||
.sample = process_sample_event,
|
||||
.mmap = perf_event__process_mmap,
|
||||
.comm = perf_event__process_comm,
|
||||
.fork = perf_event__process_task,
|
||||
.ordered_samples = true,
|
||||
.ordering_requires_timestamps = true,
|
||||
},
|
||||
.input_name = "perf.data",
|
||||
};
|
||||
const struct option options[] = {
|
||||
OPT_STRING('i', "input", &annotate.input_name, "file",
|
||||
"input file name"),
|
||||
OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
|
||||
"only consider symbols in these dsos"),
|
||||
OPT_STRING('s', "symbol", &sym_hist_filter, "symbol",
|
||||
OPT_STRING('s', "symbol", &annotate.sym_hist_filter, "symbol",
|
||||
"symbol to annotate"),
|
||||
OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
|
||||
OPT_BOOLEAN('f', "force", &annotate.force, "don't complain, do it"),
|
||||
OPT_INCR('v', "verbose", &verbose,
|
||||
"be more verbose (show symbol address, etc)"),
|
||||
OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
|
||||
"dump raw trace in ASCII"),
|
||||
OPT_BOOLEAN(0, "tui", &use_tui, "Use the TUI interface"),
|
||||
OPT_BOOLEAN(0, "stdio", &use_stdio, "Use the stdio interface"),
|
||||
OPT_BOOLEAN(0, "tui", &annotate.use_tui, "Use the TUI interface"),
|
||||
OPT_BOOLEAN(0, "stdio", &annotate.use_stdio, "Use the stdio interface"),
|
||||
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
|
||||
"file", "vmlinux pathname"),
|
||||
OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
|
||||
"load module symbols - WARNING: use only with -k and LIVE kernel"),
|
||||
OPT_BOOLEAN('l', "print-line", &print_line,
|
||||
OPT_BOOLEAN('l', "print-line", &annotate.print_line,
|
||||
"print matching source lines (may be slow)"),
|
||||
OPT_BOOLEAN('P', "full-paths", &full_paths,
|
||||
OPT_BOOLEAN('P', "full-paths", &annotate.full_paths,
|
||||
"Don't shorten the displayed pathnames"),
|
||||
OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"),
|
||||
OPT_STRING('C', "cpu", &annotate.cpu_list, "cpu", "list of cpus to profile"),
|
||||
OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
|
||||
"Look for files with symbols relative to this directory"),
|
||||
OPT_BOOLEAN(0, "source", &symbol_conf.annotate_src,
|
||||
@ -279,15 +284,13 @@ static const struct option options[] = {
|
||||
OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style",
|
||||
"Specify disassembler style (e.g. -M intel for intel syntax)"),
|
||||
OPT_END()
|
||||
};
|
||||
};
|
||||
|
||||
int cmd_annotate(int argc, const char **argv, const char *prefix __used)
|
||||
{
|
||||
argc = parse_options(argc, argv, options, annotate_usage, 0);
|
||||
|
||||
if (use_stdio)
|
||||
if (annotate.use_stdio)
|
||||
use_browser = 0;
|
||||
else if (use_tui)
|
||||
else if (annotate.use_tui)
|
||||
use_browser = 1;
|
||||
|
||||
setup_browser(true);
|
||||
@ -308,7 +311,7 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __used)
|
||||
if (argc > 1)
|
||||
usage_with_options(annotate_usage, options);
|
||||
|
||||
sym_hist_filter = argv[0];
|
||||
annotate.sym_hist_filter = argv[0];
|
||||
}
|
||||
|
||||
if (field_sep && *field_sep == '.') {
|
||||
@ -316,5 +319,5 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __used)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return __cmd_annotate();
|
||||
return __cmd_annotate(&annotate);
|
||||
}
|
||||
|
@ -9,7 +9,9 @@
|
||||
#include "util/debug.h"
|
||||
#include "util/event.h"
|
||||
#include "util/hist.h"
|
||||
#include "util/evsel.h"
|
||||
#include "util/session.h"
|
||||
#include "util/tool.h"
|
||||
#include "util/sort.h"
|
||||
#include "util/symbol.h"
|
||||
#include "util/util.h"
|
||||
@ -30,14 +32,15 @@ static int hists__add_entry(struct hists *self,
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int diff__process_sample_event(union perf_event *event,
|
||||
static int diff__process_sample_event(struct perf_tool *tool __used,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct perf_evsel *evsel __used,
|
||||
struct perf_session *session)
|
||||
struct machine *machine)
|
||||
{
|
||||
struct addr_location al;
|
||||
|
||||
if (perf_event__preprocess_sample(event, session, &al, sample, NULL) < 0) {
|
||||
if (perf_event__preprocess_sample(event, machine, &al, sample, NULL) < 0) {
|
||||
pr_warning("problem processing %d event, skipping it.\n",
|
||||
event->header.type);
|
||||
return -1;
|
||||
@ -46,16 +49,16 @@ static int diff__process_sample_event(union perf_event *event,
|
||||
if (al.filtered || al.sym == NULL)
|
||||
return 0;
|
||||
|
||||
if (hists__add_entry(&session->hists, &al, sample->period)) {
|
||||
if (hists__add_entry(&evsel->hists, &al, sample->period)) {
|
||||
pr_warning("problem incrementing symbol period, skipping event\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
session->hists.stats.total_period += sample->period;
|
||||
evsel->hists.stats.total_period += sample->period;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct perf_event_ops event_ops = {
|
||||
static struct perf_tool perf_diff = {
|
||||
.sample = diff__process_sample_event,
|
||||
.mmap = perf_event__process_mmap,
|
||||
.comm = perf_event__process_comm,
|
||||
@ -145,13 +148,13 @@ static int __cmd_diff(void)
|
||||
int ret, i;
|
||||
struct perf_session *session[2];
|
||||
|
||||
session[0] = perf_session__new(input_old, O_RDONLY, force, false, &event_ops);
|
||||
session[1] = perf_session__new(input_new, O_RDONLY, force, false, &event_ops);
|
||||
session[0] = perf_session__new(input_old, O_RDONLY, force, false, &perf_diff);
|
||||
session[1] = perf_session__new(input_new, O_RDONLY, force, false, &perf_diff);
|
||||
if (session[0] == NULL || session[1] == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < 2; ++i) {
|
||||
ret = perf_session__process_events(session[i], &event_ops);
|
||||
ret = perf_session__process_events(session[i], &perf_diff);
|
||||
if (ret)
|
||||
goto out_delete;
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include "perf.h"
|
||||
#include "util/session.h"
|
||||
#include "util/tool.h"
|
||||
#include "util/debug.h"
|
||||
|
||||
#include "util/parse-options.h"
|
||||
@ -16,8 +17,9 @@
|
||||
static char const *input_name = "-";
|
||||
static bool inject_build_ids;
|
||||
|
||||
static int perf_event__repipe_synth(union perf_event *event,
|
||||
struct perf_session *session __used)
|
||||
static int perf_event__repipe_synth(struct perf_tool *tool __used,
|
||||
union perf_event *event,
|
||||
struct machine *machine __used)
|
||||
{
|
||||
uint32_t size;
|
||||
void *buf = event;
|
||||
@ -36,41 +38,70 @@ static int perf_event__repipe_synth(union perf_event *event,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int perf_event__repipe(union perf_event *event,
|
||||
struct perf_sample *sample __used,
|
||||
struct perf_session *session)
|
||||
static int perf_event__repipe_op2_synth(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_session *session __used)
|
||||
{
|
||||
return perf_event__repipe_synth(event, session);
|
||||
return perf_event__repipe_synth(tool, event, NULL);
|
||||
}
|
||||
|
||||
static int perf_event__repipe_sample(union perf_event *event,
|
||||
static int perf_event__repipe_event_type_synth(struct perf_tool *tool,
|
||||
union perf_event *event)
|
||||
{
|
||||
return perf_event__repipe_synth(tool, event, NULL);
|
||||
}
|
||||
|
||||
static int perf_event__repipe_tracing_data_synth(union perf_event *event,
|
||||
struct perf_session *session __used)
|
||||
{
|
||||
return perf_event__repipe_synth(NULL, event, NULL);
|
||||
}
|
||||
|
||||
static int perf_event__repipe_attr(union perf_event *event,
|
||||
struct perf_evlist **pevlist __used)
|
||||
{
|
||||
return perf_event__repipe_synth(NULL, event, NULL);
|
||||
}
|
||||
|
||||
static int perf_event__repipe(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __used,
|
||||
struct machine *machine)
|
||||
{
|
||||
return perf_event__repipe_synth(tool, event, machine);
|
||||
}
|
||||
|
||||
static int perf_event__repipe_sample(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __used,
|
||||
struct perf_evsel *evsel __used,
|
||||
struct perf_session *session)
|
||||
struct machine *machine)
|
||||
{
|
||||
return perf_event__repipe_synth(event, session);
|
||||
return perf_event__repipe_synth(tool, event, machine);
|
||||
}
|
||||
|
||||
static int perf_event__repipe_mmap(union perf_event *event,
|
||||
static int perf_event__repipe_mmap(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct perf_session *session)
|
||||
struct machine *machine)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = perf_event__process_mmap(event, sample, session);
|
||||
perf_event__repipe(event, sample, session);
|
||||
err = perf_event__process_mmap(tool, event, sample, machine);
|
||||
perf_event__repipe(tool, event, sample, machine);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int perf_event__repipe_task(union perf_event *event,
|
||||
static int perf_event__repipe_task(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct perf_session *session)
|
||||
struct machine *machine)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = perf_event__process_task(event, sample, session);
|
||||
perf_event__repipe(event, sample, session);
|
||||
err = perf_event__process_task(tool, event, sample, machine);
|
||||
perf_event__repipe(tool, event, sample, machine);
|
||||
|
||||
return err;
|
||||
}
|
||||
@ -80,7 +111,7 @@ static int perf_event__repipe_tracing_data(union perf_event *event,
|
||||
{
|
||||
int err;
|
||||
|
||||
perf_event__repipe_synth(event, session);
|
||||
perf_event__repipe_synth(NULL, event, NULL);
|
||||
err = perf_event__process_tracing_data(event, session);
|
||||
|
||||
return err;
|
||||
@ -100,10 +131,10 @@ static int dso__read_build_id(struct dso *self)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int dso__inject_build_id(struct dso *self, struct perf_session *session)
|
||||
static int dso__inject_build_id(struct dso *self, struct perf_tool *tool,
|
||||
struct machine *machine)
|
||||
{
|
||||
u16 misc = PERF_RECORD_MISC_USER;
|
||||
struct machine *machine;
|
||||
int err;
|
||||
|
||||
if (dso__read_build_id(self) < 0) {
|
||||
@ -111,17 +142,11 @@ static int dso__inject_build_id(struct dso *self, struct perf_session *session)
|
||||
return -1;
|
||||
}
|
||||
|
||||
machine = perf_session__find_host_machine(session);
|
||||
if (machine == NULL) {
|
||||
pr_err("Can't find machine for session\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (self->kernel)
|
||||
misc = PERF_RECORD_MISC_KERNEL;
|
||||
|
||||
err = perf_event__synthesize_build_id(self, misc, perf_event__repipe,
|
||||
machine, session);
|
||||
err = perf_event__synthesize_build_id(tool, self, misc, perf_event__repipe,
|
||||
machine);
|
||||
if (err) {
|
||||
pr_err("Can't synthesize build_id event for %s\n", self->long_name);
|
||||
return -1;
|
||||
@ -130,10 +155,11 @@ static int dso__inject_build_id(struct dso *self, struct perf_session *session)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int perf_event__inject_buildid(union perf_event *event,
|
||||
static int perf_event__inject_buildid(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct perf_evsel *evsel __used,
|
||||
struct perf_session *session)
|
||||
struct machine *machine)
|
||||
{
|
||||
struct addr_location al;
|
||||
struct thread *thread;
|
||||
@ -141,21 +167,21 @@ static int perf_event__inject_buildid(union perf_event *event,
|
||||
|
||||
cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
|
||||
|
||||
thread = perf_session__findnew(session, event->ip.pid);
|
||||
thread = machine__findnew_thread(machine, event->ip.pid);
|
||||
if (thread == NULL) {
|
||||
pr_err("problem processing %d event, skipping it.\n",
|
||||
event->header.type);
|
||||
goto repipe;
|
||||
}
|
||||
|
||||
thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION,
|
||||
event->ip.pid, event->ip.ip, &al);
|
||||
thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION,
|
||||
event->ip.ip, &al);
|
||||
|
||||
if (al.map != NULL) {
|
||||
if (!al.map->dso->hit) {
|
||||
al.map->dso->hit = 1;
|
||||
if (map__load(al.map, NULL) >= 0) {
|
||||
dso__inject_build_id(al.map->dso, session);
|
||||
dso__inject_build_id(al.map->dso, tool, machine);
|
||||
/*
|
||||
* If this fails, too bad, let the other side
|
||||
* account this as unresolved.
|
||||
@ -168,24 +194,24 @@ static int perf_event__inject_buildid(union perf_event *event,
|
||||
}
|
||||
|
||||
repipe:
|
||||
perf_event__repipe(event, sample, session);
|
||||
perf_event__repipe(tool, event, sample, machine);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct perf_event_ops inject_ops = {
|
||||
struct perf_tool perf_inject = {
|
||||
.sample = perf_event__repipe_sample,
|
||||
.mmap = perf_event__repipe,
|
||||
.comm = perf_event__repipe,
|
||||
.fork = perf_event__repipe,
|
||||
.exit = perf_event__repipe,
|
||||
.lost = perf_event__repipe,
|
||||
.read = perf_event__repipe,
|
||||
.read = perf_event__repipe_sample,
|
||||
.throttle = perf_event__repipe,
|
||||
.unthrottle = perf_event__repipe,
|
||||
.attr = perf_event__repipe_synth,
|
||||
.event_type = perf_event__repipe_synth,
|
||||
.tracing_data = perf_event__repipe_synth,
|
||||
.build_id = perf_event__repipe_synth,
|
||||
.attr = perf_event__repipe_attr,
|
||||
.event_type = perf_event__repipe_event_type_synth,
|
||||
.tracing_data = perf_event__repipe_tracing_data_synth,
|
||||
.build_id = perf_event__repipe_op2_synth,
|
||||
};
|
||||
|
||||
extern volatile int session_done;
|
||||
@ -203,17 +229,17 @@ static int __cmd_inject(void)
|
||||
signal(SIGINT, sig_handler);
|
||||
|
||||
if (inject_build_ids) {
|
||||
inject_ops.sample = perf_event__inject_buildid;
|
||||
inject_ops.mmap = perf_event__repipe_mmap;
|
||||
inject_ops.fork = perf_event__repipe_task;
|
||||
inject_ops.tracing_data = perf_event__repipe_tracing_data;
|
||||
perf_inject.sample = perf_event__inject_buildid;
|
||||
perf_inject.mmap = perf_event__repipe_mmap;
|
||||
perf_inject.fork = perf_event__repipe_task;
|
||||
perf_inject.tracing_data = perf_event__repipe_tracing_data;
|
||||
}
|
||||
|
||||
session = perf_session__new(input_name, O_RDONLY, false, true, &inject_ops);
|
||||
session = perf_session__new(input_name, O_RDONLY, false, true, &perf_inject);
|
||||
if (session == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = perf_session__process_events(session, &inject_ops);
|
||||
ret = perf_session__process_events(session, &perf_inject);
|
||||
|
||||
perf_session__delete(session);
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "util/thread.h"
|
||||
#include "util/header.h"
|
||||
#include "util/session.h"
|
||||
#include "util/tool.h"
|
||||
|
||||
#include "util/parse-options.h"
|
||||
#include "util/trace-event.h"
|
||||
@ -303,12 +304,13 @@ static void process_raw_event(union perf_event *raw_event __used, void *data,
|
||||
}
|
||||
}
|
||||
|
||||
static int process_sample_event(union perf_event *event,
|
||||
static int process_sample_event(struct perf_tool *tool __used,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct perf_evsel *evsel __used,
|
||||
struct perf_session *session)
|
||||
struct machine *machine)
|
||||
{
|
||||
struct thread *thread = perf_session__findnew(session, event->ip.pid);
|
||||
struct thread *thread = machine__findnew_thread(machine, event->ip.pid);
|
||||
|
||||
if (thread == NULL) {
|
||||
pr_debug("problem processing %d event, skipping it.\n",
|
||||
@ -324,7 +326,7 @@ static int process_sample_event(union perf_event *event,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct perf_event_ops event_ops = {
|
||||
static struct perf_tool perf_kmem = {
|
||||
.sample = process_sample_event,
|
||||
.comm = perf_event__process_comm,
|
||||
.ordered_samples = true,
|
||||
@ -483,7 +485,7 @@ static int __cmd_kmem(void)
|
||||
{
|
||||
int err = -EINVAL;
|
||||
struct perf_session *session = perf_session__new(input_name, O_RDONLY,
|
||||
0, false, &event_ops);
|
||||
0, false, &perf_kmem);
|
||||
if (session == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -494,7 +496,7 @@ static int __cmd_kmem(void)
|
||||
goto out_delete;
|
||||
|
||||
setup_pager();
|
||||
err = perf_session__process_events(session, &event_ops);
|
||||
err = perf_session__process_events(session, &perf_kmem);
|
||||
if (err != 0)
|
||||
goto out_delete;
|
||||
sort_result();
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include "util/debug.h"
|
||||
#include "util/session.h"
|
||||
#include "util/tool.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/prctl.h>
|
||||
@ -845,12 +846,13 @@ static void dump_info(void)
|
||||
die("Unknown type of information\n");
|
||||
}
|
||||
|
||||
static int process_sample_event(union perf_event *event,
|
||||
static int process_sample_event(struct perf_tool *tool __used,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct perf_evsel *evsel __used,
|
||||
struct perf_session *s)
|
||||
struct machine *machine)
|
||||
{
|
||||
struct thread *thread = perf_session__findnew(s, sample->tid);
|
||||
struct thread *thread = machine__findnew_thread(machine, sample->tid);
|
||||
|
||||
if (thread == NULL) {
|
||||
pr_debug("problem processing %d event, skipping it.\n",
|
||||
@ -863,7 +865,7 @@ static int process_sample_event(union perf_event *event,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct perf_event_ops eops = {
|
||||
static struct perf_tool eops = {
|
||||
.sample = process_sample_event,
|
||||
.comm = perf_event__process_comm,
|
||||
.ordered_samples = true,
|
||||
|
@ -46,7 +46,6 @@
|
||||
|
||||
#define DEFAULT_VAR_FILTER "!__k???tab_* & !__crc_*"
|
||||
#define DEFAULT_FUNC_FILTER "!_*"
|
||||
#define MAX_PATH_LEN 256
|
||||
|
||||
/* Session management structure */
|
||||
static struct {
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "util/evsel.h"
|
||||
#include "util/debug.h"
|
||||
#include "util/session.h"
|
||||
#include "util/tool.h"
|
||||
#include "util/symbol.h"
|
||||
#include "util/cpumap.h"
|
||||
#include "util/thread_map.h"
|
||||
@ -35,55 +36,36 @@ enum write_mode_t {
|
||||
WRITE_APPEND
|
||||
};
|
||||
|
||||
static u64 user_interval = ULLONG_MAX;
|
||||
static u64 default_interval = 0;
|
||||
struct perf_record {
|
||||
struct perf_tool tool;
|
||||
struct perf_record_opts opts;
|
||||
u64 bytes_written;
|
||||
const char *output_name;
|
||||
struct perf_evlist *evlist;
|
||||
struct perf_session *session;
|
||||
const char *progname;
|
||||
int output;
|
||||
unsigned int page_size;
|
||||
int realtime_prio;
|
||||
enum write_mode_t write_mode;
|
||||
bool no_buildid;
|
||||
bool no_buildid_cache;
|
||||
bool force;
|
||||
bool file_new;
|
||||
bool append_file;
|
||||
long samples;
|
||||
off_t post_processing_offset;
|
||||
};
|
||||
|
||||
static unsigned int page_size;
|
||||
static unsigned int mmap_pages = UINT_MAX;
|
||||
static unsigned int user_freq = UINT_MAX;
|
||||
static int freq = 1000;
|
||||
static int output;
|
||||
static int pipe_output = 0;
|
||||
static const char *output_name = NULL;
|
||||
static bool group = false;
|
||||
static int realtime_prio = 0;
|
||||
static bool nodelay = false;
|
||||
static bool raw_samples = false;
|
||||
static bool sample_id_all_avail = true;
|
||||
static bool system_wide = false;
|
||||
static pid_t target_pid = -1;
|
||||
static pid_t target_tid = -1;
|
||||
static pid_t child_pid = -1;
|
||||
static bool no_inherit = false;
|
||||
static enum write_mode_t write_mode = WRITE_FORCE;
|
||||
static bool call_graph = false;
|
||||
static bool inherit_stat = false;
|
||||
static bool no_samples = false;
|
||||
static bool sample_address = false;
|
||||
static bool sample_time = false;
|
||||
static bool no_buildid = false;
|
||||
static bool no_buildid_cache = false;
|
||||
static struct perf_evlist *evsel_list;
|
||||
|
||||
static long samples = 0;
|
||||
static u64 bytes_written = 0;
|
||||
|
||||
static int file_new = 1;
|
||||
static off_t post_processing_offset;
|
||||
|
||||
static struct perf_session *session;
|
||||
static const char *cpu_list;
|
||||
static const char *progname;
|
||||
|
||||
static void advance_output(size_t size)
|
||||
static void advance_output(struct perf_record *rec, size_t size)
|
||||
{
|
||||
bytes_written += size;
|
||||
rec->bytes_written += size;
|
||||
}
|
||||
|
||||
static void write_output(void *buf, size_t size)
|
||||
static void write_output(struct perf_record *rec, void *buf, size_t size)
|
||||
{
|
||||
while (size) {
|
||||
int ret = write(output, buf, size);
|
||||
int ret = write(rec->output, buf, size);
|
||||
|
||||
if (ret < 0)
|
||||
die("failed to write");
|
||||
@ -91,30 +73,33 @@ static void write_output(void *buf, size_t size)
|
||||
size -= ret;
|
||||
buf += ret;
|
||||
|
||||
bytes_written += ret;
|
||||
rec->bytes_written += ret;
|
||||
}
|
||||
}
|
||||
|
||||
static int process_synthesized_event(union perf_event *event,
|
||||
static int process_synthesized_event(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __used,
|
||||
struct perf_session *self __used)
|
||||
struct machine *machine __used)
|
||||
{
|
||||
write_output(event, event->header.size);
|
||||
struct perf_record *rec = container_of(tool, struct perf_record, tool);
|
||||
write_output(rec, event, event->header.size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mmap_read(struct perf_mmap *md)
|
||||
static void perf_record__mmap_read(struct perf_record *rec,
|
||||
struct perf_mmap *md)
|
||||
{
|
||||
unsigned int head = perf_mmap__read_head(md);
|
||||
unsigned int old = md->prev;
|
||||
unsigned char *data = md->base + page_size;
|
||||
unsigned char *data = md->base + rec->page_size;
|
||||
unsigned long size;
|
||||
void *buf;
|
||||
|
||||
if (old == head)
|
||||
return;
|
||||
|
||||
samples++;
|
||||
rec->samples++;
|
||||
|
||||
size = head - old;
|
||||
|
||||
@ -123,14 +108,14 @@ static void mmap_read(struct perf_mmap *md)
|
||||
size = md->mask + 1 - (old & md->mask);
|
||||
old += size;
|
||||
|
||||
write_output(buf, size);
|
||||
write_output(rec, buf, size);
|
||||
}
|
||||
|
||||
buf = &data[old & md->mask];
|
||||
size = head - old;
|
||||
old += size;
|
||||
|
||||
write_output(buf, size);
|
||||
write_output(rec, buf, size);
|
||||
|
||||
md->prev = old;
|
||||
perf_mmap__write_tail(md, old);
|
||||
@ -149,17 +134,18 @@ static void sig_handler(int sig)
|
||||
signr = sig;
|
||||
}
|
||||
|
||||
static void sig_atexit(void)
|
||||
static void perf_record__sig_exit(int exit_status __used, void *arg)
|
||||
{
|
||||
struct perf_record *rec = arg;
|
||||
int status;
|
||||
|
||||
if (child_pid > 0) {
|
||||
if (rec->evlist->workload.pid > 0) {
|
||||
if (!child_finished)
|
||||
kill(child_pid, SIGTERM);
|
||||
kill(rec->evlist->workload.pid, SIGTERM);
|
||||
|
||||
wait(&status);
|
||||
if (WIFSIGNALED(status))
|
||||
psignal(WTERMSIG(status), progname);
|
||||
psignal(WTERMSIG(status), rec->progname);
|
||||
}
|
||||
|
||||
if (signr == -1 || signr == SIGUSR1)
|
||||
@ -169,78 +155,6 @@ static void sig_atexit(void)
|
||||
kill(getpid(), signr);
|
||||
}
|
||||
|
||||
static void config_attr(struct perf_evsel *evsel, struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_event_attr *attr = &evsel->attr;
|
||||
int track = !evsel->idx; /* only the first counter needs these */
|
||||
|
||||
attr->disabled = 1;
|
||||
attr->inherit = !no_inherit;
|
||||
attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED |
|
||||
PERF_FORMAT_TOTAL_TIME_RUNNING |
|
||||
PERF_FORMAT_ID;
|
||||
|
||||
attr->sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID;
|
||||
|
||||
if (evlist->nr_entries > 1)
|
||||
attr->sample_type |= PERF_SAMPLE_ID;
|
||||
|
||||
/*
|
||||
* We default some events to a 1 default interval. But keep
|
||||
* it a weak assumption overridable by the user.
|
||||
*/
|
||||
if (!attr->sample_period || (user_freq != UINT_MAX &&
|
||||
user_interval != ULLONG_MAX)) {
|
||||
if (freq) {
|
||||
attr->sample_type |= PERF_SAMPLE_PERIOD;
|
||||
attr->freq = 1;
|
||||
attr->sample_freq = freq;
|
||||
} else {
|
||||
attr->sample_period = default_interval;
|
||||
}
|
||||
}
|
||||
|
||||
if (no_samples)
|
||||
attr->sample_freq = 0;
|
||||
|
||||
if (inherit_stat)
|
||||
attr->inherit_stat = 1;
|
||||
|
||||
if (sample_address) {
|
||||
attr->sample_type |= PERF_SAMPLE_ADDR;
|
||||
attr->mmap_data = track;
|
||||
}
|
||||
|
||||
if (call_graph)
|
||||
attr->sample_type |= PERF_SAMPLE_CALLCHAIN;
|
||||
|
||||
if (system_wide)
|
||||
attr->sample_type |= PERF_SAMPLE_CPU;
|
||||
|
||||
if (sample_id_all_avail &&
|
||||
(sample_time || system_wide || !no_inherit || cpu_list))
|
||||
attr->sample_type |= PERF_SAMPLE_TIME;
|
||||
|
||||
if (raw_samples) {
|
||||
attr->sample_type |= PERF_SAMPLE_TIME;
|
||||
attr->sample_type |= PERF_SAMPLE_RAW;
|
||||
attr->sample_type |= PERF_SAMPLE_CPU;
|
||||
}
|
||||
|
||||
if (nodelay) {
|
||||
attr->watermark = 0;
|
||||
attr->wakeup_events = 1;
|
||||
}
|
||||
|
||||
attr->mmap = track;
|
||||
attr->comm = track;
|
||||
|
||||
if (target_pid == -1 && target_tid == -1 && !system_wide) {
|
||||
attr->disabled = 1;
|
||||
attr->enable_on_exec = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static bool perf_evlist__equal(struct perf_evlist *evlist,
|
||||
struct perf_evlist *other)
|
||||
{
|
||||
@ -260,15 +174,17 @@ static bool perf_evlist__equal(struct perf_evlist *evlist,
|
||||
return true;
|
||||
}
|
||||
|
||||
static void open_counters(struct perf_evlist *evlist)
|
||||
static void perf_record__open(struct perf_record *rec)
|
||||
{
|
||||
struct perf_evsel *pos, *first;
|
||||
|
||||
if (evlist->cpus->map[0] < 0)
|
||||
no_inherit = true;
|
||||
struct perf_evlist *evlist = rec->evlist;
|
||||
struct perf_session *session = rec->session;
|
||||
struct perf_record_opts *opts = &rec->opts;
|
||||
|
||||
first = list_entry(evlist->entries.next, struct perf_evsel, node);
|
||||
|
||||
perf_evlist__config_attrs(evlist, opts);
|
||||
|
||||
list_for_each_entry(pos, &evlist->entries, node) {
|
||||
struct perf_event_attr *attr = &pos->attr;
|
||||
struct xyarray *group_fd = NULL;
|
||||
@ -286,29 +202,27 @@ static void open_counters(struct perf_evlist *evlist)
|
||||
*/
|
||||
bool time_needed = attr->sample_type & PERF_SAMPLE_TIME;
|
||||
|
||||
if (group && pos != first)
|
||||
if (opts->group && pos != first)
|
||||
group_fd = first->fd;
|
||||
|
||||
config_attr(pos, evlist);
|
||||
retry_sample_id:
|
||||
attr->sample_id_all = sample_id_all_avail ? 1 : 0;
|
||||
attr->sample_id_all = opts->sample_id_all_avail ? 1 : 0;
|
||||
try_again:
|
||||
if (perf_evsel__open(pos, evlist->cpus, evlist->threads, group,
|
||||
group_fd) < 0) {
|
||||
if (perf_evsel__open(pos, evlist->cpus, evlist->threads,
|
||||
opts->group, group_fd) < 0) {
|
||||
int err = errno;
|
||||
|
||||
if (err == EPERM || err == EACCES) {
|
||||
ui__error_paranoid();
|
||||
exit(EXIT_FAILURE);
|
||||
} else if (err == ENODEV && cpu_list) {
|
||||
} else if (err == ENODEV && opts->cpu_list) {
|
||||
die("No such device - did you specify"
|
||||
" an out-of-range profile CPU?\n");
|
||||
} else if (err == EINVAL && sample_id_all_avail) {
|
||||
} else if (err == EINVAL && opts->sample_id_all_avail) {
|
||||
/*
|
||||
* Old kernel, no attr->sample_id_type_all field
|
||||
*/
|
||||
sample_id_all_avail = false;
|
||||
if (!sample_time && !raw_samples && !time_needed)
|
||||
opts->sample_id_all_avail = false;
|
||||
if (!opts->sample_time && !opts->raw_samples && !time_needed)
|
||||
attr->sample_type &= ~PERF_SAMPLE_TIME;
|
||||
|
||||
goto retry_sample_id;
|
||||
@ -358,10 +272,10 @@ static void open_counters(struct perf_evlist *evlist)
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (perf_evlist__mmap(evlist, mmap_pages, false) < 0)
|
||||
if (perf_evlist__mmap(evlist, opts->mmap_pages, false) < 0)
|
||||
die("failed to mmap with %d (%s)\n", errno, strerror(errno));
|
||||
|
||||
if (file_new)
|
||||
if (rec->file_new)
|
||||
session->evlist = evlist;
|
||||
else {
|
||||
if (!perf_evlist__equal(session->evlist, evlist)) {
|
||||
@ -373,29 +287,32 @@ static void open_counters(struct perf_evlist *evlist)
|
||||
perf_session__update_sample_type(session);
|
||||
}
|
||||
|
||||
static int process_buildids(void)
|
||||
static int process_buildids(struct perf_record *rec)
|
||||
{
|
||||
u64 size = lseek(output, 0, SEEK_CUR);
|
||||
u64 size = lseek(rec->output, 0, SEEK_CUR);
|
||||
|
||||
if (size == 0)
|
||||
return 0;
|
||||
|
||||
session->fd = output;
|
||||
return __perf_session__process_events(session, post_processing_offset,
|
||||
size - post_processing_offset,
|
||||
rec->session->fd = rec->output;
|
||||
return __perf_session__process_events(rec->session, rec->post_processing_offset,
|
||||
size - rec->post_processing_offset,
|
||||
size, &build_id__mark_dso_hit_ops);
|
||||
}
|
||||
|
||||
static void atexit_header(void)
|
||||
static void perf_record__exit(int status __used, void *arg)
|
||||
{
|
||||
if (!pipe_output) {
|
||||
session->header.data_size += bytes_written;
|
||||
struct perf_record *rec = arg;
|
||||
|
||||
if (!no_buildid)
|
||||
process_buildids();
|
||||
perf_session__write_header(session, evsel_list, output, true);
|
||||
perf_session__delete(session);
|
||||
perf_evlist__delete(evsel_list);
|
||||
if (!rec->opts.pipe_output) {
|
||||
rec->session->header.data_size += rec->bytes_written;
|
||||
|
||||
if (!rec->no_buildid)
|
||||
process_buildids(rec);
|
||||
perf_session__write_header(rec->session, rec->evlist,
|
||||
rec->output, true);
|
||||
perf_session__delete(rec->session);
|
||||
perf_evlist__delete(rec->evlist);
|
||||
symbol__exit();
|
||||
}
|
||||
}
|
||||
@ -403,7 +320,7 @@ static void atexit_header(void)
|
||||
static void perf_event__synthesize_guest_os(struct machine *machine, void *data)
|
||||
{
|
||||
int err;
|
||||
struct perf_session *psession = data;
|
||||
struct perf_tool *tool = data;
|
||||
|
||||
if (machine__is_host(machine))
|
||||
return;
|
||||
@ -416,8 +333,8 @@ static void perf_event__synthesize_guest_os(struct machine *machine, void *data)
|
||||
*method is used to avoid symbol missing when the first addr is
|
||||
*in module instead of in guest kernel.
|
||||
*/
|
||||
err = perf_event__synthesize_modules(process_synthesized_event,
|
||||
psession, machine);
|
||||
err = perf_event__synthesize_modules(tool, process_synthesized_event,
|
||||
machine);
|
||||
if (err < 0)
|
||||
pr_err("Couldn't record guest kernel [%d]'s reference"
|
||||
" relocation symbol.\n", machine->pid);
|
||||
@ -426,12 +343,11 @@ static void perf_event__synthesize_guest_os(struct machine *machine, void *data)
|
||||
* We use _stext for guest kernel because guest kernel's /proc/kallsyms
|
||||
* have no _text sometimes.
|
||||
*/
|
||||
err = perf_event__synthesize_kernel_mmap(process_synthesized_event,
|
||||
psession, machine, "_text");
|
||||
err = perf_event__synthesize_kernel_mmap(tool, process_synthesized_event,
|
||||
machine, "_text");
|
||||
if (err < 0)
|
||||
err = perf_event__synthesize_kernel_mmap(process_synthesized_event,
|
||||
psession, machine,
|
||||
"_stext");
|
||||
err = perf_event__synthesize_kernel_mmap(tool, process_synthesized_event,
|
||||
machine, "_stext");
|
||||
if (err < 0)
|
||||
pr_err("Couldn't record guest kernel [%d]'s reference"
|
||||
" relocation symbol.\n", machine->pid);
|
||||
@ -442,73 +358,71 @@ static struct perf_event_header finished_round_event = {
|
||||
.type = PERF_RECORD_FINISHED_ROUND,
|
||||
};
|
||||
|
||||
static void mmap_read_all(void)
|
||||
static void perf_record__mmap_read_all(struct perf_record *rec)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < evsel_list->nr_mmaps; i++) {
|
||||
if (evsel_list->mmap[i].base)
|
||||
mmap_read(&evsel_list->mmap[i]);
|
||||
for (i = 0; i < rec->evlist->nr_mmaps; i++) {
|
||||
if (rec->evlist->mmap[i].base)
|
||||
perf_record__mmap_read(rec, &rec->evlist->mmap[i]);
|
||||
}
|
||||
|
||||
if (perf_header__has_feat(&session->header, HEADER_TRACE_INFO))
|
||||
write_output(&finished_round_event, sizeof(finished_round_event));
|
||||
if (perf_header__has_feat(&rec->session->header, HEADER_TRACE_INFO))
|
||||
write_output(rec, &finished_round_event, sizeof(finished_round_event));
|
||||
}
|
||||
|
||||
static int __cmd_record(int argc, const char **argv)
|
||||
static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
|
||||
{
|
||||
struct stat st;
|
||||
int flags;
|
||||
int err;
|
||||
int err, output;
|
||||
unsigned long waking = 0;
|
||||
int child_ready_pipe[2], go_pipe[2];
|
||||
const bool forks = argc > 0;
|
||||
char buf;
|
||||
struct machine *machine;
|
||||
struct perf_tool *tool = &rec->tool;
|
||||
struct perf_record_opts *opts = &rec->opts;
|
||||
struct perf_evlist *evsel_list = rec->evlist;
|
||||
const char *output_name = rec->output_name;
|
||||
struct perf_session *session;
|
||||
|
||||
progname = argv[0];
|
||||
rec->progname = argv[0];
|
||||
|
||||
page_size = sysconf(_SC_PAGE_SIZE);
|
||||
rec->page_size = sysconf(_SC_PAGE_SIZE);
|
||||
|
||||
atexit(sig_atexit);
|
||||
on_exit(perf_record__sig_exit, rec);
|
||||
signal(SIGCHLD, sig_handler);
|
||||
signal(SIGINT, sig_handler);
|
||||
signal(SIGUSR1, sig_handler);
|
||||
|
||||
if (forks && (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0)) {
|
||||
perror("failed to create pipes");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (!output_name) {
|
||||
if (!fstat(STDOUT_FILENO, &st) && S_ISFIFO(st.st_mode))
|
||||
pipe_output = 1;
|
||||
opts->pipe_output = true;
|
||||
else
|
||||
output_name = "perf.data";
|
||||
rec->output_name = output_name = "perf.data";
|
||||
}
|
||||
if (output_name) {
|
||||
if (!strcmp(output_name, "-"))
|
||||
pipe_output = 1;
|
||||
opts->pipe_output = true;
|
||||
else if (!stat(output_name, &st) && st.st_size) {
|
||||
if (write_mode == WRITE_FORCE) {
|
||||
if (rec->write_mode == WRITE_FORCE) {
|
||||
char oldname[PATH_MAX];
|
||||
snprintf(oldname, sizeof(oldname), "%s.old",
|
||||
output_name);
|
||||
unlink(oldname);
|
||||
rename(output_name, oldname);
|
||||
}
|
||||
} else if (write_mode == WRITE_APPEND) {
|
||||
write_mode = WRITE_FORCE;
|
||||
} else if (rec->write_mode == WRITE_APPEND) {
|
||||
rec->write_mode = WRITE_FORCE;
|
||||
}
|
||||
}
|
||||
|
||||
flags = O_CREAT|O_RDWR;
|
||||
if (write_mode == WRITE_APPEND)
|
||||
file_new = 0;
|
||||
if (rec->write_mode == WRITE_APPEND)
|
||||
rec->file_new = 0;
|
||||
else
|
||||
flags |= O_TRUNC;
|
||||
|
||||
if (pipe_output)
|
||||
if (opts->pipe_output)
|
||||
output = STDOUT_FILENO;
|
||||
else
|
||||
output = open(output_name, flags, S_IRUSR | S_IWUSR);
|
||||
@ -517,17 +431,21 @@ static int __cmd_record(int argc, const char **argv)
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
rec->output = output;
|
||||
|
||||
session = perf_session__new(output_name, O_WRONLY,
|
||||
write_mode == WRITE_FORCE, false, NULL);
|
||||
rec->write_mode == WRITE_FORCE, false, NULL);
|
||||
if (session == NULL) {
|
||||
pr_err("Not enough memory for reading perf file header\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!no_buildid)
|
||||
rec->session = session;
|
||||
|
||||
if (!rec->no_buildid)
|
||||
perf_header__set_feat(&session->header, HEADER_BUILD_ID);
|
||||
|
||||
if (!file_new) {
|
||||
if (!rec->file_new) {
|
||||
err = perf_session__read_header(session, output);
|
||||
if (err < 0)
|
||||
goto out_delete_session;
|
||||
@ -549,94 +467,50 @@ static int __cmd_record(int argc, const char **argv)
|
||||
perf_header__set_feat(&session->header, HEADER_NUMA_TOPOLOGY);
|
||||
perf_header__set_feat(&session->header, HEADER_CPUID);
|
||||
|
||||
/* 512 kiB: default amount of unprivileged mlocked memory */
|
||||
if (mmap_pages == UINT_MAX)
|
||||
mmap_pages = (512 * 1024) / page_size;
|
||||
|
||||
if (forks) {
|
||||
child_pid = fork();
|
||||
if (child_pid < 0) {
|
||||
perror("failed to fork");
|
||||
exit(-1);
|
||||
err = perf_evlist__prepare_workload(evsel_list, opts, argv);
|
||||
if (err < 0) {
|
||||
pr_err("Couldn't run the workload!\n");
|
||||
goto out_delete_session;
|
||||
}
|
||||
|
||||
if (!child_pid) {
|
||||
if (pipe_output)
|
||||
dup2(2, 1);
|
||||
close(child_ready_pipe[0]);
|
||||
close(go_pipe[1]);
|
||||
fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC);
|
||||
|
||||
/*
|
||||
* Do a dummy execvp to get the PLT entry resolved,
|
||||
* so we avoid the resolver overhead on the real
|
||||
* execvp call.
|
||||
*/
|
||||
execvp("", (char **)argv);
|
||||
|
||||
/*
|
||||
* Tell the parent we're ready to go
|
||||
*/
|
||||
close(child_ready_pipe[1]);
|
||||
|
||||
/*
|
||||
* Wait until the parent tells us to go.
|
||||
*/
|
||||
if (read(go_pipe[0], &buf, 1) == -1)
|
||||
perror("unable to read pipe");
|
||||
|
||||
execvp(argv[0], (char **)argv);
|
||||
|
||||
perror(argv[0]);
|
||||
kill(getppid(), SIGUSR1);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (!system_wide && target_tid == -1 && target_pid == -1)
|
||||
evsel_list->threads->map[0] = child_pid;
|
||||
|
||||
close(child_ready_pipe[1]);
|
||||
close(go_pipe[0]);
|
||||
/*
|
||||
* wait for child to settle
|
||||
*/
|
||||
if (read(child_ready_pipe[0], &buf, 1) == -1) {
|
||||
perror("unable to read pipe");
|
||||
exit(-1);
|
||||
}
|
||||
close(child_ready_pipe[0]);
|
||||
}
|
||||
|
||||
open_counters(evsel_list);
|
||||
perf_record__open(rec);
|
||||
|
||||
/*
|
||||
* perf_session__delete(session) will be called at atexit_header()
|
||||
* perf_session__delete(session) will be called at perf_record__exit()
|
||||
*/
|
||||
atexit(atexit_header);
|
||||
on_exit(perf_record__exit, rec);
|
||||
|
||||
if (pipe_output) {
|
||||
if (opts->pipe_output) {
|
||||
err = perf_header__write_pipe(output);
|
||||
if (err < 0)
|
||||
return err;
|
||||
} else if (file_new) {
|
||||
} else if (rec->file_new) {
|
||||
err = perf_session__write_header(session, evsel_list,
|
||||
output, false);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
post_processing_offset = lseek(output, 0, SEEK_CUR);
|
||||
rec->post_processing_offset = lseek(output, 0, SEEK_CUR);
|
||||
|
||||
if (pipe_output) {
|
||||
err = perf_session__synthesize_attrs(session,
|
||||
process_synthesized_event);
|
||||
machine = perf_session__find_host_machine(session);
|
||||
if (!machine) {
|
||||
pr_err("Couldn't find native kernel information.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (opts->pipe_output) {
|
||||
err = perf_event__synthesize_attrs(tool, session,
|
||||
process_synthesized_event);
|
||||
if (err < 0) {
|
||||
pr_err("Couldn't synthesize attrs.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = perf_event__synthesize_event_types(process_synthesized_event,
|
||||
session);
|
||||
err = perf_event__synthesize_event_types(tool, process_synthesized_event,
|
||||
machine);
|
||||
if (err < 0) {
|
||||
pr_err("Couldn't synthesize event_types.\n");
|
||||
return err;
|
||||
@ -651,56 +525,49 @@ static int __cmd_record(int argc, const char **argv)
|
||||
* return this more properly and also
|
||||
* propagate errors that now are calling die()
|
||||
*/
|
||||
err = perf_event__synthesize_tracing_data(output, evsel_list,
|
||||
process_synthesized_event,
|
||||
session);
|
||||
err = perf_event__synthesize_tracing_data(tool, output, evsel_list,
|
||||
process_synthesized_event);
|
||||
if (err <= 0) {
|
||||
pr_err("Couldn't record tracing data.\n");
|
||||
return err;
|
||||
}
|
||||
advance_output(err);
|
||||
advance_output(rec, err);
|
||||
}
|
||||
}
|
||||
|
||||
machine = perf_session__find_host_machine(session);
|
||||
if (!machine) {
|
||||
pr_err("Couldn't find native kernel information.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = perf_event__synthesize_kernel_mmap(process_synthesized_event,
|
||||
session, machine, "_text");
|
||||
err = perf_event__synthesize_kernel_mmap(tool, process_synthesized_event,
|
||||
machine, "_text");
|
||||
if (err < 0)
|
||||
err = perf_event__synthesize_kernel_mmap(process_synthesized_event,
|
||||
session, machine, "_stext");
|
||||
err = perf_event__synthesize_kernel_mmap(tool, process_synthesized_event,
|
||||
machine, "_stext");
|
||||
if (err < 0)
|
||||
pr_err("Couldn't record kernel reference relocation symbol\n"
|
||||
"Symbol resolution may be skewed if relocation was used (e.g. kexec).\n"
|
||||
"Check /proc/kallsyms permission or run as root.\n");
|
||||
|
||||
err = perf_event__synthesize_modules(process_synthesized_event,
|
||||
session, machine);
|
||||
err = perf_event__synthesize_modules(tool, process_synthesized_event,
|
||||
machine);
|
||||
if (err < 0)
|
||||
pr_err("Couldn't record kernel module information.\n"
|
||||
"Symbol resolution may be skewed if relocation was used (e.g. kexec).\n"
|
||||
"Check /proc/modules permission or run as root.\n");
|
||||
|
||||
if (perf_guest)
|
||||
perf_session__process_machines(session,
|
||||
perf_session__process_machines(session, tool,
|
||||
perf_event__synthesize_guest_os);
|
||||
|
||||
if (!system_wide)
|
||||
perf_event__synthesize_thread_map(evsel_list->threads,
|
||||
if (!opts->system_wide)
|
||||
perf_event__synthesize_thread_map(tool, evsel_list->threads,
|
||||
process_synthesized_event,
|
||||
session);
|
||||
machine);
|
||||
else
|
||||
perf_event__synthesize_threads(process_synthesized_event,
|
||||
session);
|
||||
perf_event__synthesize_threads(tool, process_synthesized_event,
|
||||
machine);
|
||||
|
||||
if (realtime_prio) {
|
||||
if (rec->realtime_prio) {
|
||||
struct sched_param param;
|
||||
|
||||
param.sched_priority = realtime_prio;
|
||||
param.sched_priority = rec->realtime_prio;
|
||||
if (sched_setscheduler(0, SCHED_FIFO, ¶m)) {
|
||||
pr_err("Could not set realtime priority.\n");
|
||||
exit(-1);
|
||||
@ -713,14 +580,14 @@ static int __cmd_record(int argc, const char **argv)
|
||||
* Let the child rip
|
||||
*/
|
||||
if (forks)
|
||||
close(go_pipe[1]);
|
||||
perf_evlist__start_workload(evsel_list);
|
||||
|
||||
for (;;) {
|
||||
int hits = samples;
|
||||
int hits = rec->samples;
|
||||
|
||||
mmap_read_all();
|
||||
perf_record__mmap_read_all(rec);
|
||||
|
||||
if (hits == samples) {
|
||||
if (hits == rec->samples) {
|
||||
if (done)
|
||||
break;
|
||||
err = poll(evsel_list->pollfd, evsel_list->nr_fds, -1);
|
||||
@ -741,9 +608,9 @@ static int __cmd_record(int argc, const char **argv)
|
||||
*/
|
||||
fprintf(stderr,
|
||||
"[ perf record: Captured and wrote %.3f MB %s (~%" PRIu64 " samples) ]\n",
|
||||
(double)bytes_written / 1024.0 / 1024.0,
|
||||
(double)rec->bytes_written / 1024.0 / 1024.0,
|
||||
output_name,
|
||||
bytes_written / 24);
|
||||
rec->bytes_written / 24);
|
||||
|
||||
return 0;
|
||||
|
||||
@ -758,58 +625,88 @@ static const char * const record_usage[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
static bool force, append_file;
|
||||
/*
|
||||
* XXX Ideally would be local to cmd_record() and passed to a perf_record__new
|
||||
* because we need to have access to it in perf_record__exit, that is called
|
||||
* after cmd_record() exits, but since record_options need to be accessible to
|
||||
* builtin-script, leave it here.
|
||||
*
|
||||
* At least we don't ouch it in all the other functions here directly.
|
||||
*
|
||||
* Just say no to tons of global variables, sigh.
|
||||
*/
|
||||
static struct perf_record record = {
|
||||
.opts = {
|
||||
.target_pid = -1,
|
||||
.target_tid = -1,
|
||||
.mmap_pages = UINT_MAX,
|
||||
.user_freq = UINT_MAX,
|
||||
.user_interval = ULLONG_MAX,
|
||||
.freq = 1000,
|
||||
.sample_id_all_avail = true,
|
||||
},
|
||||
.write_mode = WRITE_FORCE,
|
||||
.file_new = true,
|
||||
};
|
||||
|
||||
/*
|
||||
* XXX Will stay a global variable till we fix builtin-script.c to stop messing
|
||||
* with it and switch to use the library functions in perf_evlist that came
|
||||
* from builtin-record.c, i.e. use perf_record_opts,
|
||||
* perf_evlist__prepare_workload, etc instead of fork+exec'in 'perf record',
|
||||
* using pipes, etc.
|
||||
*/
|
||||
const struct option record_options[] = {
|
||||
OPT_CALLBACK('e', "event", &evsel_list, "event",
|
||||
OPT_CALLBACK('e', "event", &record.evlist, "event",
|
||||
"event selector. use 'perf list' to list available events",
|
||||
parse_events_option),
|
||||
OPT_CALLBACK(0, "filter", &evsel_list, "filter",
|
||||
OPT_CALLBACK(0, "filter", &record.evlist, "filter",
|
||||
"event filter", parse_filter),
|
||||
OPT_INTEGER('p', "pid", &target_pid,
|
||||
OPT_INTEGER('p', "pid", &record.opts.target_pid,
|
||||
"record events on existing process id"),
|
||||
OPT_INTEGER('t', "tid", &target_tid,
|
||||
OPT_INTEGER('t', "tid", &record.opts.target_tid,
|
||||
"record events on existing thread id"),
|
||||
OPT_INTEGER('r', "realtime", &realtime_prio,
|
||||
OPT_INTEGER('r', "realtime", &record.realtime_prio,
|
||||
"collect data with this RT SCHED_FIFO priority"),
|
||||
OPT_BOOLEAN('D', "no-delay", &nodelay,
|
||||
OPT_BOOLEAN('D', "no-delay", &record.opts.no_delay,
|
||||
"collect data without buffering"),
|
||||
OPT_BOOLEAN('R', "raw-samples", &raw_samples,
|
||||
OPT_BOOLEAN('R', "raw-samples", &record.opts.raw_samples,
|
||||
"collect raw sample records from all opened counters"),
|
||||
OPT_BOOLEAN('a', "all-cpus", &system_wide,
|
||||
OPT_BOOLEAN('a', "all-cpus", &record.opts.system_wide,
|
||||
"system-wide collection from all CPUs"),
|
||||
OPT_BOOLEAN('A', "append", &append_file,
|
||||
OPT_BOOLEAN('A', "append", &record.append_file,
|
||||
"append to the output file to do incremental profiling"),
|
||||
OPT_STRING('C', "cpu", &cpu_list, "cpu",
|
||||
OPT_STRING('C', "cpu", &record.opts.cpu_list, "cpu",
|
||||
"list of cpus to monitor"),
|
||||
OPT_BOOLEAN('f', "force", &force,
|
||||
OPT_BOOLEAN('f', "force", &record.force,
|
||||
"overwrite existing data file (deprecated)"),
|
||||
OPT_U64('c', "count", &user_interval, "event period to sample"),
|
||||
OPT_STRING('o', "output", &output_name, "file",
|
||||
OPT_U64('c', "count", &record.opts.user_interval, "event period to sample"),
|
||||
OPT_STRING('o', "output", &record.output_name, "file",
|
||||
"output file name"),
|
||||
OPT_BOOLEAN('i', "no-inherit", &no_inherit,
|
||||
OPT_BOOLEAN('i', "no-inherit", &record.opts.no_inherit,
|
||||
"child tasks do not inherit counters"),
|
||||
OPT_UINTEGER('F', "freq", &user_freq, "profile at this frequency"),
|
||||
OPT_UINTEGER('m', "mmap-pages", &mmap_pages, "number of mmap data pages"),
|
||||
OPT_BOOLEAN(0, "group", &group,
|
||||
OPT_UINTEGER('F', "freq", &record.opts.user_freq, "profile at this frequency"),
|
||||
OPT_UINTEGER('m', "mmap-pages", &record.opts.mmap_pages,
|
||||
"number of mmap data pages"),
|
||||
OPT_BOOLEAN(0, "group", &record.opts.group,
|
||||
"put the counters into a counter group"),
|
||||
OPT_BOOLEAN('g', "call-graph", &call_graph,
|
||||
OPT_BOOLEAN('g', "call-graph", &record.opts.call_graph,
|
||||
"do call-graph (stack chain/backtrace) recording"),
|
||||
OPT_INCR('v', "verbose", &verbose,
|
||||
"be more verbose (show counter open errors, etc)"),
|
||||
OPT_BOOLEAN('q', "quiet", &quiet, "don't print any message"),
|
||||
OPT_BOOLEAN('s', "stat", &inherit_stat,
|
||||
OPT_BOOLEAN('s', "stat", &record.opts.inherit_stat,
|
||||
"per thread counts"),
|
||||
OPT_BOOLEAN('d', "data", &sample_address,
|
||||
OPT_BOOLEAN('d', "data", &record.opts.sample_address,
|
||||
"Sample addresses"),
|
||||
OPT_BOOLEAN('T', "timestamp", &sample_time, "Sample timestamps"),
|
||||
OPT_BOOLEAN('n', "no-samples", &no_samples,
|
||||
OPT_BOOLEAN('T', "timestamp", &record.opts.sample_time, "Sample timestamps"),
|
||||
OPT_BOOLEAN('n', "no-samples", &record.opts.no_samples,
|
||||
"don't sample"),
|
||||
OPT_BOOLEAN('N', "no-buildid-cache", &no_buildid_cache,
|
||||
OPT_BOOLEAN('N', "no-buildid-cache", &record.no_buildid_cache,
|
||||
"do not update the buildid cache"),
|
||||
OPT_BOOLEAN('B', "no-buildid", &no_buildid,
|
||||
OPT_BOOLEAN('B', "no-buildid", &record.no_buildid,
|
||||
"do not collect buildids in perf.data"),
|
||||
OPT_CALLBACK('G', "cgroup", &evsel_list, "name",
|
||||
OPT_CALLBACK('G', "cgroup", &record.evlist, "name",
|
||||
"monitor event in cgroup name only",
|
||||
parse_cgroups),
|
||||
OPT_END()
|
||||
@ -819,6 +716,8 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
|
||||
{
|
||||
int err = -ENOMEM;
|
||||
struct perf_evsel *pos;
|
||||
struct perf_evlist *evsel_list;
|
||||
struct perf_record *rec = &record;
|
||||
|
||||
perf_header__set_cmdline(argc, argv);
|
||||
|
||||
@ -826,23 +725,25 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
|
||||
if (evsel_list == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
rec->evlist = evsel_list;
|
||||
|
||||
argc = parse_options(argc, argv, record_options, record_usage,
|
||||
PARSE_OPT_STOP_AT_NON_OPTION);
|
||||
if (!argc && target_pid == -1 && target_tid == -1 &&
|
||||
!system_wide && !cpu_list)
|
||||
if (!argc && rec->opts.target_pid == -1 && rec->opts.target_tid == -1 &&
|
||||
!rec->opts.system_wide && !rec->opts.cpu_list)
|
||||
usage_with_options(record_usage, record_options);
|
||||
|
||||
if (force && append_file) {
|
||||
if (rec->force && rec->append_file) {
|
||||
fprintf(stderr, "Can't overwrite and append at the same time."
|
||||
" You need to choose between -f and -A");
|
||||
usage_with_options(record_usage, record_options);
|
||||
} else if (append_file) {
|
||||
write_mode = WRITE_APPEND;
|
||||
} else if (rec->append_file) {
|
||||
rec->write_mode = WRITE_APPEND;
|
||||
} else {
|
||||
write_mode = WRITE_FORCE;
|
||||
rec->write_mode = WRITE_FORCE;
|
||||
}
|
||||
|
||||
if (nr_cgroups && !system_wide) {
|
||||
if (nr_cgroups && !rec->opts.system_wide) {
|
||||
fprintf(stderr, "cgroup monitoring only available in"
|
||||
" system-wide mode\n");
|
||||
usage_with_options(record_usage, record_options);
|
||||
@ -860,7 +761,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
|
||||
"If some relocation was applied (e.g. kexec) symbols may be misresolved\n"
|
||||
"even with a suitable vmlinux or kallsyms file.\n\n");
|
||||
|
||||
if (no_buildid_cache || no_buildid)
|
||||
if (rec->no_buildid_cache || rec->no_buildid)
|
||||
disable_buildid_cache();
|
||||
|
||||
if (evsel_list->nr_entries == 0 &&
|
||||
@ -869,43 +770,37 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
|
||||
goto out_symbol_exit;
|
||||
}
|
||||
|
||||
if (target_pid != -1)
|
||||
target_tid = target_pid;
|
||||
if (rec->opts.target_pid != -1)
|
||||
rec->opts.target_tid = rec->opts.target_pid;
|
||||
|
||||
if (perf_evlist__create_maps(evsel_list, target_pid,
|
||||
target_tid, cpu_list) < 0)
|
||||
if (perf_evlist__create_maps(evsel_list, rec->opts.target_pid,
|
||||
rec->opts.target_tid, rec->opts.cpu_list) < 0)
|
||||
usage_with_options(record_usage, record_options);
|
||||
|
||||
list_for_each_entry(pos, &evsel_list->entries, node) {
|
||||
if (perf_evsel__alloc_fd(pos, evsel_list->cpus->nr,
|
||||
evsel_list->threads->nr) < 0)
|
||||
goto out_free_fd;
|
||||
if (perf_header__push_event(pos->attr.config, event_name(pos)))
|
||||
goto out_free_fd;
|
||||
}
|
||||
|
||||
if (perf_evlist__alloc_pollfd(evsel_list) < 0)
|
||||
goto out_free_fd;
|
||||
|
||||
if (user_interval != ULLONG_MAX)
|
||||
default_interval = user_interval;
|
||||
if (user_freq != UINT_MAX)
|
||||
freq = user_freq;
|
||||
if (rec->opts.user_interval != ULLONG_MAX)
|
||||
rec->opts.default_interval = rec->opts.user_interval;
|
||||
if (rec->opts.user_freq != UINT_MAX)
|
||||
rec->opts.freq = rec->opts.user_freq;
|
||||
|
||||
/*
|
||||
* User specified count overrides default frequency.
|
||||
*/
|
||||
if (default_interval)
|
||||
freq = 0;
|
||||
else if (freq) {
|
||||
default_interval = freq;
|
||||
if (rec->opts.default_interval)
|
||||
rec->opts.freq = 0;
|
||||
else if (rec->opts.freq) {
|
||||
rec->opts.default_interval = rec->opts.freq;
|
||||
} else {
|
||||
fprintf(stderr, "frequency and count are zero, aborting\n");
|
||||
err = -EINVAL;
|
||||
goto out_free_fd;
|
||||
}
|
||||
|
||||
err = __cmd_record(argc, argv);
|
||||
err = __cmd_record(&record, argc, argv);
|
||||
out_free_fd:
|
||||
perf_evlist__delete_maps(evsel_list);
|
||||
out_symbol_exit:
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "util/evsel.h"
|
||||
#include "util/header.h"
|
||||
#include "util/session.h"
|
||||
#include "util/tool.h"
|
||||
|
||||
#include "util/parse-options.h"
|
||||
#include "util/parse-events.h"
|
||||
@ -35,38 +36,35 @@
|
||||
|
||||
#include <linux/bitmap.h>
|
||||
|
||||
static char const *input_name = "perf.data";
|
||||
struct perf_report {
|
||||
struct perf_tool tool;
|
||||
struct perf_session *session;
|
||||
char const *input_name;
|
||||
bool force, use_tui, use_stdio;
|
||||
bool hide_unresolved;
|
||||
bool dont_use_callchains;
|
||||
bool show_full_info;
|
||||
bool show_threads;
|
||||
bool inverted_callchain;
|
||||
struct perf_read_values show_threads_values;
|
||||
const char *pretty_printing_style;
|
||||
symbol_filter_t annotate_init;
|
||||
const char *cpu_list;
|
||||
DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
|
||||
};
|
||||
|
||||
static bool force, use_tui, use_stdio;
|
||||
static bool hide_unresolved;
|
||||
static bool dont_use_callchains;
|
||||
static bool show_full_info;
|
||||
|
||||
static bool show_threads;
|
||||
static struct perf_read_values show_threads_values;
|
||||
|
||||
static const char default_pretty_printing_style[] = "normal";
|
||||
static const char *pretty_printing_style = default_pretty_printing_style;
|
||||
|
||||
static char callchain_default_opt[] = "fractal,0.5,callee";
|
||||
static bool inverted_callchain;
|
||||
static symbol_filter_t annotate_init;
|
||||
|
||||
static const char *cpu_list;
|
||||
static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
|
||||
|
||||
static int perf_session__add_hist_entry(struct perf_session *session,
|
||||
struct addr_location *al,
|
||||
struct perf_sample *sample,
|
||||
struct perf_evsel *evsel)
|
||||
static int perf_evsel__add_hist_entry(struct perf_evsel *evsel,
|
||||
struct addr_location *al,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
{
|
||||
struct symbol *parent = NULL;
|
||||
int err = 0;
|
||||
struct hist_entry *he;
|
||||
|
||||
if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) {
|
||||
err = perf_session__resolve_callchain(session, al->thread,
|
||||
sample->callchain, &parent);
|
||||
err = machine__resolve_callchain(machine, evsel, al->thread,
|
||||
sample->callchain, &parent);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
@ -76,7 +74,8 @@ static int perf_session__add_hist_entry(struct perf_session *session,
|
||||
return -ENOMEM;
|
||||
|
||||
if (symbol_conf.use_callchain) {
|
||||
err = callchain_append(he->callchain, &session->callchain_cursor,
|
||||
err = callchain_append(he->callchain,
|
||||
&evsel->hists.callchain_cursor,
|
||||
sample->period);
|
||||
if (err)
|
||||
return err;
|
||||
@ -92,8 +91,7 @@ static int perf_session__add_hist_entry(struct perf_session *session,
|
||||
assert(evsel != NULL);
|
||||
|
||||
err = -ENOMEM;
|
||||
if (notes->src == NULL &&
|
||||
symbol__alloc_hist(he->ms.sym, session->evlist->nr_entries) < 0)
|
||||
if (notes->src == NULL && symbol__alloc_hist(he->ms.sym) < 0)
|
||||
goto out;
|
||||
|
||||
err = hist_entry__inc_addr_samples(he, evsel->idx, al->addr);
|
||||
@ -106,30 +104,32 @@ static int perf_session__add_hist_entry(struct perf_session *session,
|
||||
}
|
||||
|
||||
|
||||
static int process_sample_event(union perf_event *event,
|
||||
static int process_sample_event(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct perf_evsel *evsel,
|
||||
struct perf_session *session)
|
||||
struct machine *machine)
|
||||
{
|
||||
struct perf_report *rep = container_of(tool, struct perf_report, tool);
|
||||
struct addr_location al;
|
||||
|
||||
if (perf_event__preprocess_sample(event, session, &al, sample,
|
||||
annotate_init) < 0) {
|
||||
if (perf_event__preprocess_sample(event, machine, &al, sample,
|
||||
rep->annotate_init) < 0) {
|
||||
fprintf(stderr, "problem processing %d event, skipping it.\n",
|
||||
event->header.type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (al.filtered || (hide_unresolved && al.sym == NULL))
|
||||
if (al.filtered || (rep->hide_unresolved && al.sym == NULL))
|
||||
return 0;
|
||||
|
||||
if (cpu_list && !test_bit(sample->cpu, cpu_bitmap))
|
||||
if (rep->cpu_list && !test_bit(sample->cpu, rep->cpu_bitmap))
|
||||
return 0;
|
||||
|
||||
if (al.map != NULL)
|
||||
al.map->dso->hit = 1;
|
||||
|
||||
if (perf_session__add_hist_entry(session, &al, sample, evsel)) {
|
||||
if (perf_evsel__add_hist_entry(evsel, &al, sample, machine)) {
|
||||
pr_debug("problem incrementing symbol period, skipping event\n");
|
||||
return -1;
|
||||
}
|
||||
@ -137,15 +137,17 @@ static int process_sample_event(union perf_event *event,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_read_event(union perf_event *event,
|
||||
static int process_read_event(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __used,
|
||||
struct perf_session *session)
|
||||
struct perf_evsel *evsel,
|
||||
struct machine *machine __used)
|
||||
{
|
||||
struct perf_evsel *evsel = perf_evlist__id2evsel(session->evlist,
|
||||
event->read.id);
|
||||
if (show_threads) {
|
||||
struct perf_report *rep = container_of(tool, struct perf_report, tool);
|
||||
|
||||
if (rep->show_threads) {
|
||||
const char *name = evsel ? event_name(evsel) : "unknown";
|
||||
perf_read_values_add_value(&show_threads_values,
|
||||
perf_read_values_add_value(&rep->show_threads_values,
|
||||
event->read.pid, event->read.tid,
|
||||
event->read.id,
|
||||
name,
|
||||
@ -159,8 +161,10 @@ static int process_read_event(union perf_event *event,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int perf_session__setup_sample_type(struct perf_session *self)
|
||||
static int perf_report__setup_sample_type(struct perf_report *rep)
|
||||
{
|
||||
struct perf_session *self = rep->session;
|
||||
|
||||
if (!(self->sample_type & PERF_SAMPLE_CALLCHAIN)) {
|
||||
if (sort__has_parent) {
|
||||
ui__warning("Selected --sort parent, but no "
|
||||
@ -173,7 +177,8 @@ static int perf_session__setup_sample_type(struct perf_session *self)
|
||||
"you call 'perf record' without -g?\n");
|
||||
return -1;
|
||||
}
|
||||
} else if (!dont_use_callchains && callchain_param.mode != CHAIN_NONE &&
|
||||
} else if (!rep->dont_use_callchains &&
|
||||
callchain_param.mode != CHAIN_NONE &&
|
||||
!symbol_conf.use_callchain) {
|
||||
symbol_conf.use_callchain = true;
|
||||
if (callchain_register_param(&callchain_param) < 0) {
|
||||
@ -186,22 +191,6 @@ static int perf_session__setup_sample_type(struct perf_session *self)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct perf_event_ops event_ops = {
|
||||
.sample = process_sample_event,
|
||||
.mmap = perf_event__process_mmap,
|
||||
.comm = perf_event__process_comm,
|
||||
.exit = perf_event__process_task,
|
||||
.fork = perf_event__process_task,
|
||||
.lost = perf_event__process_lost,
|
||||
.read = process_read_event,
|
||||
.attr = perf_event__process_attr,
|
||||
.event_type = perf_event__process_event_type,
|
||||
.tracing_data = perf_event__process_tracing_data,
|
||||
.build_id = perf_event__process_build_id,
|
||||
.ordered_samples = true,
|
||||
.ordering_requires_timestamps = true,
|
||||
};
|
||||
|
||||
extern volatile int session_done;
|
||||
|
||||
static void sig_handler(int sig __used)
|
||||
@ -224,6 +213,7 @@ static size_t hists__fprintf_nr_sample_events(struct hists *self,
|
||||
}
|
||||
|
||||
static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,
|
||||
struct perf_report *rep,
|
||||
const char *help)
|
||||
{
|
||||
struct perf_evsel *pos;
|
||||
@ -241,18 +231,18 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,
|
||||
parent_pattern == default_parent_pattern) {
|
||||
fprintf(stdout, "#\n# (%s)\n#\n", help);
|
||||
|
||||
if (show_threads) {
|
||||
bool style = !strcmp(pretty_printing_style, "raw");
|
||||
perf_read_values_display(stdout, &show_threads_values,
|
||||
if (rep->show_threads) {
|
||||
bool style = !strcmp(rep->pretty_printing_style, "raw");
|
||||
perf_read_values_display(stdout, &rep->show_threads_values,
|
||||
style);
|
||||
perf_read_values_destroy(&show_threads_values);
|
||||
perf_read_values_destroy(&rep->show_threads_values);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __cmd_report(void)
|
||||
static int __cmd_report(struct perf_report *rep)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
u64 nr_samples;
|
||||
@ -264,27 +254,31 @@ static int __cmd_report(void)
|
||||
|
||||
signal(SIGINT, sig_handler);
|
||||
|
||||
session = perf_session__new(input_name, O_RDONLY, force, false, &event_ops);
|
||||
session = perf_session__new(rep->input_name, O_RDONLY,
|
||||
rep->force, false, &rep->tool);
|
||||
if (session == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (cpu_list) {
|
||||
ret = perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap);
|
||||
rep->session = session;
|
||||
|
||||
if (rep->cpu_list) {
|
||||
ret = perf_session__cpu_bitmap(session, rep->cpu_list,
|
||||
rep->cpu_bitmap);
|
||||
if (ret)
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
if (use_browser <= 0)
|
||||
perf_session__fprintf_info(session, stdout, show_full_info);
|
||||
perf_session__fprintf_info(session, stdout, rep->show_full_info);
|
||||
|
||||
if (show_threads)
|
||||
perf_read_values_init(&show_threads_values);
|
||||
if (rep->show_threads)
|
||||
perf_read_values_init(&rep->show_threads_values);
|
||||
|
||||
ret = perf_session__setup_sample_type(session);
|
||||
ret = perf_report__setup_sample_type(rep);
|
||||
if (ret)
|
||||
goto out_delete;
|
||||
|
||||
ret = perf_session__process_events(session, &event_ops);
|
||||
ret = perf_session__process_events(session, &rep->tool);
|
||||
if (ret)
|
||||
goto out_delete;
|
||||
|
||||
@ -327,7 +321,8 @@ static int __cmd_report(void)
|
||||
}
|
||||
|
||||
if (nr_samples == 0) {
|
||||
ui__warning("The %s file has no samples!\n", input_name);
|
||||
ui__warning("The %s file has no samples!\n",
|
||||
rep->input_name);
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
@ -335,7 +330,7 @@ static int __cmd_report(void)
|
||||
perf_evlist__tui_browse_hists(session->evlist, help,
|
||||
NULL, NULL, 0);
|
||||
} else
|
||||
perf_evlist__tty_browse_hists(session->evlist, help);
|
||||
perf_evlist__tty_browse_hists(session->evlist, rep, help);
|
||||
|
||||
out_delete:
|
||||
/*
|
||||
@ -354,9 +349,9 @@ static int __cmd_report(void)
|
||||
}
|
||||
|
||||
static int
|
||||
parse_callchain_opt(const struct option *opt __used, const char *arg,
|
||||
int unset)
|
||||
parse_callchain_opt(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
struct perf_report *rep = (struct perf_report *)opt->value;
|
||||
char *tok, *tok2;
|
||||
char *endptr;
|
||||
|
||||
@ -364,7 +359,7 @@ parse_callchain_opt(const struct option *opt __used, const char *arg,
|
||||
* --no-call-graph
|
||||
*/
|
||||
if (unset) {
|
||||
dont_use_callchains = true;
|
||||
rep->dont_use_callchains = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -433,13 +428,34 @@ parse_callchain_opt(const struct option *opt __used, const char *arg,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char * const report_usage[] = {
|
||||
"perf report [<options>] <command>",
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
OPT_STRING('i', "input", &input_name, "file",
|
||||
int cmd_report(int argc, const char **argv, const char *prefix __used)
|
||||
{
|
||||
char callchain_default_opt[] = "fractal,0.5,callee";
|
||||
const char * const report_usage[] = {
|
||||
"perf report [<options>] <command>",
|
||||
NULL
|
||||
};
|
||||
struct perf_report report = {
|
||||
.tool = {
|
||||
.sample = process_sample_event,
|
||||
.mmap = perf_event__process_mmap,
|
||||
.comm = perf_event__process_comm,
|
||||
.exit = perf_event__process_task,
|
||||
.fork = perf_event__process_task,
|
||||
.lost = perf_event__process_lost,
|
||||
.read = process_read_event,
|
||||
.attr = perf_event__process_attr,
|
||||
.event_type = perf_event__process_event_type,
|
||||
.tracing_data = perf_event__process_tracing_data,
|
||||
.build_id = perf_event__process_build_id,
|
||||
.ordered_samples = true,
|
||||
.ordering_requires_timestamps = true,
|
||||
},
|
||||
.input_name = "perf.data",
|
||||
.pretty_printing_style = "normal",
|
||||
};
|
||||
const struct option options[] = {
|
||||
OPT_STRING('i', "input", &report.input_name, "file",
|
||||
"input file name"),
|
||||
OPT_INCR('v', "verbose", &verbose,
|
||||
"be more verbose (show symbol address, etc)"),
|
||||
@ -449,17 +465,18 @@ static const struct option options[] = {
|
||||
"file", "vmlinux pathname"),
|
||||
OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
|
||||
"file", "kallsyms pathname"),
|
||||
OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
|
||||
OPT_BOOLEAN('f', "force", &report.force, "don't complain, do it"),
|
||||
OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
|
||||
"load module symbols - WARNING: use only with -k and LIVE kernel"),
|
||||
OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples,
|
||||
"Show a column with the number of samples"),
|
||||
OPT_BOOLEAN('T', "threads", &show_threads,
|
||||
OPT_BOOLEAN('T', "threads", &report.show_threads,
|
||||
"Show per-thread event counters"),
|
||||
OPT_STRING(0, "pretty", &pretty_printing_style, "key",
|
||||
OPT_STRING(0, "pretty", &report.pretty_printing_style, "key",
|
||||
"pretty printing style key: normal raw"),
|
||||
OPT_BOOLEAN(0, "tui", &use_tui, "Use the TUI interface"),
|
||||
OPT_BOOLEAN(0, "stdio", &use_stdio, "Use the stdio interface"),
|
||||
OPT_BOOLEAN(0, "tui", &report.use_tui, "Use the TUI interface"),
|
||||
OPT_BOOLEAN(0, "stdio", &report.use_stdio,
|
||||
"Use the stdio interface"),
|
||||
OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
|
||||
"sort by key(s): pid, comm, dso, symbol, parent"),
|
||||
OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization,
|
||||
@ -468,13 +485,14 @@ static const struct option options[] = {
|
||||
"regex filter to identify parent, see: '--sort parent'"),
|
||||
OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other,
|
||||
"Only display entries with parent-match"),
|
||||
OPT_CALLBACK_DEFAULT('g', "call-graph", NULL, "output_type,min_percent, call_order",
|
||||
OPT_CALLBACK_DEFAULT('g', "call-graph", &report, "output_type,min_percent, call_order",
|
||||
"Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold and callchain order. "
|
||||
"Default: fractal,0.5,callee", &parse_callchain_opt, callchain_default_opt),
|
||||
OPT_BOOLEAN('G', "inverted", &inverted_callchain, "alias for inverted call graph"),
|
||||
OPT_BOOLEAN('G', "inverted", &report.inverted_callchain,
|
||||
"alias for inverted call graph"),
|
||||
OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
|
||||
"only consider symbols in these dsos"),
|
||||
OPT_STRING('C', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
|
||||
OPT_STRING('c', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
|
||||
"only consider symbols in these comms"),
|
||||
OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
|
||||
"only consider these symbols"),
|
||||
@ -484,12 +502,13 @@ static const struct option options[] = {
|
||||
OPT_STRING('t', "field-separator", &symbol_conf.field_sep, "separator",
|
||||
"separator for columns, no spaces will be added between "
|
||||
"columns '.' is reserved."),
|
||||
OPT_BOOLEAN('U', "hide-unresolved", &hide_unresolved,
|
||||
OPT_BOOLEAN('U', "hide-unresolved", &report.hide_unresolved,
|
||||
"Only display entries resolved to a symbol"),
|
||||
OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
|
||||
"Look for files with symbols relative to this directory"),
|
||||
OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"),
|
||||
OPT_BOOLEAN('I', "show-info", &show_full_info,
|
||||
OPT_STRING('C', "cpu", &report.cpu_list, "cpu",
|
||||
"list of cpus to profile"),
|
||||
OPT_BOOLEAN('I', "show-info", &report.show_full_info,
|
||||
"Display extended information about perf.data file"),
|
||||
OPT_BOOLEAN(0, "source", &symbol_conf.annotate_src,
|
||||
"Interleave source code with assembly code (default)"),
|
||||
@ -500,21 +519,19 @@ static const struct option options[] = {
|
||||
OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period,
|
||||
"Show a column with the sum of periods"),
|
||||
OPT_END()
|
||||
};
|
||||
};
|
||||
|
||||
int cmd_report(int argc, const char **argv, const char *prefix __used)
|
||||
{
|
||||
argc = parse_options(argc, argv, options, report_usage, 0);
|
||||
|
||||
if (use_stdio)
|
||||
if (report.use_stdio)
|
||||
use_browser = 0;
|
||||
else if (use_tui)
|
||||
else if (report.use_tui)
|
||||
use_browser = 1;
|
||||
|
||||
if (inverted_callchain)
|
||||
if (report.inverted_callchain)
|
||||
callchain_param.order = ORDER_CALLER;
|
||||
|
||||
if (strcmp(input_name, "-") != 0)
|
||||
if (strcmp(report.input_name, "-") != 0)
|
||||
setup_browser(true);
|
||||
else
|
||||
use_browser = 0;
|
||||
@ -525,7 +542,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
|
||||
*/
|
||||
if (use_browser > 0) {
|
||||
symbol_conf.priv_size = sizeof(struct annotation);
|
||||
annotate_init = symbol__annotate_init;
|
||||
report.annotate_init = symbol__annotate_init;
|
||||
/*
|
||||
* For searching by name on the "Browse map details".
|
||||
* providing it only in verbose mode not to bloat too
|
||||
@ -572,5 +589,5 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
|
||||
sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", stdout);
|
||||
sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout);
|
||||
|
||||
return __cmd_report();
|
||||
return __cmd_report(&report);
|
||||
}
|
||||
|
@ -2,11 +2,14 @@
|
||||
#include "perf.h"
|
||||
|
||||
#include "util/util.h"
|
||||
#include "util/evlist.h"
|
||||
#include "util/cache.h"
|
||||
#include "util/evsel.h"
|
||||
#include "util/symbol.h"
|
||||
#include "util/thread.h"
|
||||
#include "util/header.h"
|
||||
#include "util/session.h"
|
||||
#include "util/tool.h"
|
||||
|
||||
#include "util/parse-options.h"
|
||||
#include "util/trace-event.h"
|
||||
@ -723,21 +726,21 @@ struct trace_migrate_task_event {
|
||||
|
||||
struct trace_sched_handler {
|
||||
void (*switch_event)(struct trace_switch_event *,
|
||||
struct perf_session *,
|
||||
struct machine *,
|
||||
struct event *,
|
||||
int cpu,
|
||||
u64 timestamp,
|
||||
struct thread *thread);
|
||||
|
||||
void (*runtime_event)(struct trace_runtime_event *,
|
||||
struct perf_session *,
|
||||
struct machine *,
|
||||
struct event *,
|
||||
int cpu,
|
||||
u64 timestamp,
|
||||
struct thread *thread);
|
||||
|
||||
void (*wakeup_event)(struct trace_wakeup_event *,
|
||||
struct perf_session *,
|
||||
struct machine *,
|
||||
struct event *,
|
||||
int cpu,
|
||||
u64 timestamp,
|
||||
@ -750,7 +753,7 @@ struct trace_sched_handler {
|
||||
struct thread *thread);
|
||||
|
||||
void (*migrate_task_event)(struct trace_migrate_task_event *,
|
||||
struct perf_session *session,
|
||||
struct machine *machine,
|
||||
struct event *,
|
||||
int cpu,
|
||||
u64 timestamp,
|
||||
@ -760,7 +763,7 @@ struct trace_sched_handler {
|
||||
|
||||
static void
|
||||
replay_wakeup_event(struct trace_wakeup_event *wakeup_event,
|
||||
struct perf_session *session __used,
|
||||
struct machine *machine __used,
|
||||
struct event *event,
|
||||
int cpu __used,
|
||||
u64 timestamp __used,
|
||||
@ -787,7 +790,7 @@ static u64 cpu_last_switched[MAX_CPUS];
|
||||
|
||||
static void
|
||||
replay_switch_event(struct trace_switch_event *switch_event,
|
||||
struct perf_session *session __used,
|
||||
struct machine *machine __used,
|
||||
struct event *event,
|
||||
int cpu,
|
||||
u64 timestamp,
|
||||
@ -1021,7 +1024,7 @@ add_sched_in_event(struct work_atoms *atoms, u64 timestamp)
|
||||
|
||||
static void
|
||||
latency_switch_event(struct trace_switch_event *switch_event,
|
||||
struct perf_session *session,
|
||||
struct machine *machine,
|
||||
struct event *event __used,
|
||||
int cpu,
|
||||
u64 timestamp,
|
||||
@ -1045,8 +1048,8 @@ latency_switch_event(struct trace_switch_event *switch_event,
|
||||
die("hm, delta: %" PRIu64 " < 0 ?\n", delta);
|
||||
|
||||
|
||||
sched_out = perf_session__findnew(session, switch_event->prev_pid);
|
||||
sched_in = perf_session__findnew(session, switch_event->next_pid);
|
||||
sched_out = machine__findnew_thread(machine, switch_event->prev_pid);
|
||||
sched_in = machine__findnew_thread(machine, switch_event->next_pid);
|
||||
|
||||
out_events = thread_atoms_search(&atom_root, sched_out, &cmp_pid);
|
||||
if (!out_events) {
|
||||
@ -1074,13 +1077,13 @@ latency_switch_event(struct trace_switch_event *switch_event,
|
||||
|
||||
static void
|
||||
latency_runtime_event(struct trace_runtime_event *runtime_event,
|
||||
struct perf_session *session,
|
||||
struct machine *machine,
|
||||
struct event *event __used,
|
||||
int cpu,
|
||||
u64 timestamp,
|
||||
struct thread *this_thread __used)
|
||||
{
|
||||
struct thread *thread = perf_session__findnew(session, runtime_event->pid);
|
||||
struct thread *thread = machine__findnew_thread(machine, runtime_event->pid);
|
||||
struct work_atoms *atoms = thread_atoms_search(&atom_root, thread, &cmp_pid);
|
||||
|
||||
BUG_ON(cpu >= MAX_CPUS || cpu < 0);
|
||||
@ -1097,7 +1100,7 @@ latency_runtime_event(struct trace_runtime_event *runtime_event,
|
||||
|
||||
static void
|
||||
latency_wakeup_event(struct trace_wakeup_event *wakeup_event,
|
||||
struct perf_session *session,
|
||||
struct machine *machine,
|
||||
struct event *__event __used,
|
||||
int cpu __used,
|
||||
u64 timestamp,
|
||||
@ -1111,7 +1114,7 @@ latency_wakeup_event(struct trace_wakeup_event *wakeup_event,
|
||||
if (!wakeup_event->success)
|
||||
return;
|
||||
|
||||
wakee = perf_session__findnew(session, wakeup_event->pid);
|
||||
wakee = machine__findnew_thread(machine, wakeup_event->pid);
|
||||
atoms = thread_atoms_search(&atom_root, wakee, &cmp_pid);
|
||||
if (!atoms) {
|
||||
thread_atoms_insert(wakee);
|
||||
@ -1145,7 +1148,7 @@ latency_wakeup_event(struct trace_wakeup_event *wakeup_event,
|
||||
|
||||
static void
|
||||
latency_migrate_task_event(struct trace_migrate_task_event *migrate_task_event,
|
||||
struct perf_session *session,
|
||||
struct machine *machine,
|
||||
struct event *__event __used,
|
||||
int cpu __used,
|
||||
u64 timestamp,
|
||||
@ -1161,7 +1164,7 @@ latency_migrate_task_event(struct trace_migrate_task_event *migrate_task_event,
|
||||
if (profile_cpu == -1)
|
||||
return;
|
||||
|
||||
migrant = perf_session__findnew(session, migrate_task_event->pid);
|
||||
migrant = machine__findnew_thread(machine, migrate_task_event->pid);
|
||||
atoms = thread_atoms_search(&atom_root, migrant, &cmp_pid);
|
||||
if (!atoms) {
|
||||
thread_atoms_insert(migrant);
|
||||
@ -1356,12 +1359,13 @@ static void sort_lat(void)
|
||||
static struct trace_sched_handler *trace_handler;
|
||||
|
||||
static void
|
||||
process_sched_wakeup_event(void *data, struct perf_session *session,
|
||||
process_sched_wakeup_event(struct perf_tool *tool __used,
|
||||
struct event *event,
|
||||
int cpu __used,
|
||||
u64 timestamp __used,
|
||||
struct thread *thread __used)
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine,
|
||||
struct thread *thread)
|
||||
{
|
||||
void *data = sample->raw_data;
|
||||
struct trace_wakeup_event wakeup_event;
|
||||
|
||||
FILL_COMMON_FIELDS(wakeup_event, event, data);
|
||||
@ -1373,8 +1377,8 @@ process_sched_wakeup_event(void *data, struct perf_session *session,
|
||||
FILL_FIELD(wakeup_event, cpu, event, data);
|
||||
|
||||
if (trace_handler->wakeup_event)
|
||||
trace_handler->wakeup_event(&wakeup_event, session, event,
|
||||
cpu, timestamp, thread);
|
||||
trace_handler->wakeup_event(&wakeup_event, machine, event,
|
||||
sample->cpu, sample->time, thread);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1392,7 +1396,7 @@ static char next_shortname2 = '0';
|
||||
|
||||
static void
|
||||
map_switch_event(struct trace_switch_event *switch_event,
|
||||
struct perf_session *session,
|
||||
struct machine *machine,
|
||||
struct event *event __used,
|
||||
int this_cpu,
|
||||
u64 timestamp,
|
||||
@ -1420,8 +1424,8 @@ map_switch_event(struct trace_switch_event *switch_event,
|
||||
die("hm, delta: %" PRIu64 " < 0 ?\n", delta);
|
||||
|
||||
|
||||
sched_out = perf_session__findnew(session, switch_event->prev_pid);
|
||||
sched_in = perf_session__findnew(session, switch_event->next_pid);
|
||||
sched_out = machine__findnew_thread(machine, switch_event->prev_pid);
|
||||
sched_in = machine__findnew_thread(machine, switch_event->next_pid);
|
||||
|
||||
curr_thread[this_cpu] = sched_in;
|
||||
|
||||
@ -1469,14 +1473,15 @@ map_switch_event(struct trace_switch_event *switch_event,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
process_sched_switch_event(void *data, struct perf_session *session,
|
||||
process_sched_switch_event(struct perf_tool *tool __used,
|
||||
struct event *event,
|
||||
int this_cpu,
|
||||
u64 timestamp __used,
|
||||
struct thread *thread __used)
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine,
|
||||
struct thread *thread)
|
||||
{
|
||||
int this_cpu = sample->cpu;
|
||||
void *data = sample->raw_data;
|
||||
struct trace_switch_event switch_event;
|
||||
|
||||
FILL_COMMON_FIELDS(switch_event, event, data);
|
||||
@ -1498,19 +1503,20 @@ process_sched_switch_event(void *data, struct perf_session *session,
|
||||
nr_context_switch_bugs++;
|
||||
}
|
||||
if (trace_handler->switch_event)
|
||||
trace_handler->switch_event(&switch_event, session, event,
|
||||
this_cpu, timestamp, thread);
|
||||
trace_handler->switch_event(&switch_event, machine, event,
|
||||
this_cpu, sample->time, thread);
|
||||
|
||||
curr_pid[this_cpu] = switch_event.next_pid;
|
||||
}
|
||||
|
||||
static void
|
||||
process_sched_runtime_event(void *data, struct perf_session *session,
|
||||
struct event *event,
|
||||
int cpu __used,
|
||||
u64 timestamp __used,
|
||||
struct thread *thread __used)
|
||||
process_sched_runtime_event(struct perf_tool *tool __used,
|
||||
struct event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine,
|
||||
struct thread *thread)
|
||||
{
|
||||
void *data = sample->raw_data;
|
||||
struct trace_runtime_event runtime_event;
|
||||
|
||||
FILL_ARRAY(runtime_event, comm, event, data);
|
||||
@ -1519,16 +1525,18 @@ process_sched_runtime_event(void *data, struct perf_session *session,
|
||||
FILL_FIELD(runtime_event, vruntime, event, data);
|
||||
|
||||
if (trace_handler->runtime_event)
|
||||
trace_handler->runtime_event(&runtime_event, session, event, cpu, timestamp, thread);
|
||||
trace_handler->runtime_event(&runtime_event, machine, event,
|
||||
sample->cpu, sample->time, thread);
|
||||
}
|
||||
|
||||
static void
|
||||
process_sched_fork_event(void *data,
|
||||
process_sched_fork_event(struct perf_tool *tool __used,
|
||||
struct event *event,
|
||||
int cpu __used,
|
||||
u64 timestamp __used,
|
||||
struct thread *thread __used)
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine __used,
|
||||
struct thread *thread)
|
||||
{
|
||||
void *data = sample->raw_data;
|
||||
struct trace_fork_event fork_event;
|
||||
|
||||
FILL_COMMON_FIELDS(fork_event, event, data);
|
||||
@ -1540,13 +1548,14 @@ process_sched_fork_event(void *data,
|
||||
|
||||
if (trace_handler->fork_event)
|
||||
trace_handler->fork_event(&fork_event, event,
|
||||
cpu, timestamp, thread);
|
||||
sample->cpu, sample->time, thread);
|
||||
}
|
||||
|
||||
static void
|
||||
process_sched_exit_event(struct event *event,
|
||||
int cpu __used,
|
||||
u64 timestamp __used,
|
||||
process_sched_exit_event(struct perf_tool *tool __used,
|
||||
struct event *event,
|
||||
struct perf_sample *sample __used,
|
||||
struct machine *machine __used,
|
||||
struct thread *thread __used)
|
||||
{
|
||||
if (verbose)
|
||||
@ -1554,12 +1563,13 @@ process_sched_exit_event(struct event *event,
|
||||
}
|
||||
|
||||
static void
|
||||
process_sched_migrate_task_event(void *data, struct perf_session *session,
|
||||
struct event *event,
|
||||
int cpu __used,
|
||||
u64 timestamp __used,
|
||||
struct thread *thread __used)
|
||||
process_sched_migrate_task_event(struct perf_tool *tool __used,
|
||||
struct event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine,
|
||||
struct thread *thread)
|
||||
{
|
||||
void *data = sample->raw_data;
|
||||
struct trace_migrate_task_event migrate_task_event;
|
||||
|
||||
FILL_COMMON_FIELDS(migrate_task_event, event, data);
|
||||
@ -1570,67 +1580,47 @@ process_sched_migrate_task_event(void *data, struct perf_session *session,
|
||||
FILL_FIELD(migrate_task_event, cpu, event, data);
|
||||
|
||||
if (trace_handler->migrate_task_event)
|
||||
trace_handler->migrate_task_event(&migrate_task_event, session,
|
||||
event, cpu, timestamp, thread);
|
||||
trace_handler->migrate_task_event(&migrate_task_event, machine,
|
||||
event, sample->cpu,
|
||||
sample->time, thread);
|
||||
}
|
||||
|
||||
static void process_raw_event(union perf_event *raw_event __used,
|
||||
struct perf_session *session, void *data, int cpu,
|
||||
u64 timestamp, struct thread *thread)
|
||||
typedef void (*tracepoint_handler)(struct perf_tool *tool, struct event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine,
|
||||
struct thread *thread);
|
||||
|
||||
static int perf_sched__process_tracepoint_sample(struct perf_tool *tool,
|
||||
union perf_event *event __used,
|
||||
struct perf_sample *sample,
|
||||
struct perf_evsel *evsel,
|
||||
struct machine *machine)
|
||||
{
|
||||
struct event *event;
|
||||
int type;
|
||||
struct thread *thread = machine__findnew_thread(machine, sample->pid);
|
||||
|
||||
|
||||
type = trace_parse_common_type(data);
|
||||
event = trace_find_event(type);
|
||||
|
||||
if (!strcmp(event->name, "sched_switch"))
|
||||
process_sched_switch_event(data, session, event, cpu, timestamp, thread);
|
||||
if (!strcmp(event->name, "sched_stat_runtime"))
|
||||
process_sched_runtime_event(data, session, event, cpu, timestamp, thread);
|
||||
if (!strcmp(event->name, "sched_wakeup"))
|
||||
process_sched_wakeup_event(data, session, event, cpu, timestamp, thread);
|
||||
if (!strcmp(event->name, "sched_wakeup_new"))
|
||||
process_sched_wakeup_event(data, session, event, cpu, timestamp, thread);
|
||||
if (!strcmp(event->name, "sched_process_fork"))
|
||||
process_sched_fork_event(data, event, cpu, timestamp, thread);
|
||||
if (!strcmp(event->name, "sched_process_exit"))
|
||||
process_sched_exit_event(event, cpu, timestamp, thread);
|
||||
if (!strcmp(event->name, "sched_migrate_task"))
|
||||
process_sched_migrate_task_event(data, session, event, cpu, timestamp, thread);
|
||||
}
|
||||
|
||||
static int process_sample_event(union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct perf_evsel *evsel __used,
|
||||
struct perf_session *session)
|
||||
{
|
||||
struct thread *thread;
|
||||
|
||||
if (!(session->sample_type & PERF_SAMPLE_RAW))
|
||||
return 0;
|
||||
|
||||
thread = perf_session__findnew(session, sample->pid);
|
||||
if (thread == NULL) {
|
||||
pr_debug("problem processing %d event, skipping it.\n",
|
||||
event->header.type);
|
||||
pr_debug("problem processing %s event, skipping it.\n",
|
||||
evsel->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
|
||||
evsel->hists.stats.total_period += sample->period;
|
||||
hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
|
||||
|
||||
if (profile_cpu != -1 && profile_cpu != (int)sample->cpu)
|
||||
return 0;
|
||||
if (evsel->handler.func != NULL) {
|
||||
tracepoint_handler f = evsel->handler.func;
|
||||
|
||||
process_raw_event(event, session, sample->raw_data, sample->cpu,
|
||||
sample->time, thread);
|
||||
if (evsel->handler.data == NULL)
|
||||
evsel->handler.data = trace_find_event(evsel->attr.config);
|
||||
|
||||
f(tool, evsel->handler.data, sample, machine, thread);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct perf_event_ops event_ops = {
|
||||
.sample = process_sample_event,
|
||||
static struct perf_tool perf_sched = {
|
||||
.sample = perf_sched__process_tracepoint_sample,
|
||||
.comm = perf_event__process_comm,
|
||||
.lost = perf_event__process_lost,
|
||||
.fork = perf_event__process_task,
|
||||
@ -1640,13 +1630,25 @@ static struct perf_event_ops event_ops = {
|
||||
static void read_events(bool destroy, struct perf_session **psession)
|
||||
{
|
||||
int err = -EINVAL;
|
||||
const struct perf_evsel_str_handler handlers[] = {
|
||||
{ "sched:sched_switch", process_sched_switch_event, },
|
||||
{ "sched:sched_stat_runtime", process_sched_runtime_event, },
|
||||
{ "sched:sched_wakeup", process_sched_wakeup_event, },
|
||||
{ "sched:sched_wakeup_new", process_sched_wakeup_event, },
|
||||
{ "sched:sched_process_fork", process_sched_fork_event, },
|
||||
{ "sched:sched_process_exit", process_sched_exit_event, },
|
||||
{ "sched:sched_migrate_task", process_sched_migrate_task_event, },
|
||||
};
|
||||
struct perf_session *session = perf_session__new(input_name, O_RDONLY,
|
||||
0, false, &event_ops);
|
||||
0, false, &perf_sched);
|
||||
if (session == NULL)
|
||||
die("No Memory");
|
||||
|
||||
err = perf_evlist__set_tracepoints_handlers_array(session->evlist, handlers);
|
||||
assert(err == 0);
|
||||
|
||||
if (perf_session__has_traces(session, "record -R")) {
|
||||
err = perf_session__process_events(session, &event_ops);
|
||||
err = perf_session__process_events(session, &perf_sched);
|
||||
if (err)
|
||||
die("Failed to process events, error %d", err);
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "util/header.h"
|
||||
#include "util/parse-options.h"
|
||||
#include "util/session.h"
|
||||
#include "util/tool.h"
|
||||
#include "util/symbol.h"
|
||||
#include "util/thread.h"
|
||||
#include "util/trace-event.h"
|
||||
@ -315,7 +316,7 @@ static bool sample_addr_correlates_sym(struct perf_event_attr *attr)
|
||||
|
||||
static void print_sample_addr(union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct perf_session *session,
|
||||
struct machine *machine,
|
||||
struct thread *thread,
|
||||
struct perf_event_attr *attr)
|
||||
{
|
||||
@ -328,11 +329,11 @@ static void print_sample_addr(union perf_event *event,
|
||||
if (!sample_addr_correlates_sym(attr))
|
||||
return;
|
||||
|
||||
thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION,
|
||||
event->ip.pid, sample->addr, &al);
|
||||
thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION,
|
||||
sample->addr, &al);
|
||||
if (!al.map)
|
||||
thread__find_addr_map(thread, session, cpumode, MAP__VARIABLE,
|
||||
event->ip.pid, sample->addr, &al);
|
||||
thread__find_addr_map(thread, machine, cpumode, MAP__VARIABLE,
|
||||
sample->addr, &al);
|
||||
|
||||
al.cpu = sample->cpu;
|
||||
al.sym = NULL;
|
||||
@ -362,7 +363,7 @@ static void print_sample_addr(union perf_event *event,
|
||||
static void process_event(union perf_event *event __unused,
|
||||
struct perf_sample *sample,
|
||||
struct perf_evsel *evsel,
|
||||
struct perf_session *session,
|
||||
struct machine *machine,
|
||||
struct thread *thread)
|
||||
{
|
||||
struct perf_event_attr *attr = &evsel->attr;
|
||||
@ -377,15 +378,15 @@ static void process_event(union perf_event *event __unused,
|
||||
sample->raw_size);
|
||||
|
||||
if (PRINT_FIELD(ADDR))
|
||||
print_sample_addr(event, sample, session, thread, attr);
|
||||
print_sample_addr(event, sample, machine, thread, attr);
|
||||
|
||||
if (PRINT_FIELD(IP)) {
|
||||
if (!symbol_conf.use_callchain)
|
||||
printf(" ");
|
||||
else
|
||||
printf("\n");
|
||||
perf_session__print_ip(event, sample, session,
|
||||
PRINT_FIELD(SYM), PRINT_FIELD(DSO));
|
||||
perf_event__print_ip(event, sample, machine, evsel,
|
||||
PRINT_FIELD(SYM), PRINT_FIELD(DSO));
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
@ -434,12 +435,14 @@ static int cleanup_scripting(void)
|
||||
|
||||
static char const *input_name = "perf.data";
|
||||
|
||||
static int process_sample_event(union perf_event *event,
|
||||
static int process_sample_event(struct perf_tool *tool __used,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct perf_evsel *evsel,
|
||||
struct perf_session *session)
|
||||
struct machine *machine)
|
||||
{
|
||||
struct thread *thread = perf_session__findnew(session, event->ip.pid);
|
||||
struct addr_location al;
|
||||
struct thread *thread = machine__findnew_thread(machine, event->ip.pid);
|
||||
|
||||
if (thread == NULL) {
|
||||
pr_debug("problem processing %d event, skipping it.\n",
|
||||
@ -458,16 +461,25 @@ static int process_sample_event(union perf_event *event,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (perf_event__preprocess_sample(event, machine, &al, sample, 0) < 0) {
|
||||
pr_err("problem processing %d event, skipping it.\n",
|
||||
event->header.type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (al.filtered)
|
||||
return 0;
|
||||
|
||||
if (cpu_list && !test_bit(sample->cpu, cpu_bitmap))
|
||||
return 0;
|
||||
|
||||
scripting_ops->process_event(event, sample, evsel, session, thread);
|
||||
scripting_ops->process_event(event, sample, evsel, machine, thread);
|
||||
|
||||
session->hists.stats.total_period += sample->period;
|
||||
evsel->hists.stats.total_period += sample->period;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct perf_event_ops event_ops = {
|
||||
static struct perf_tool perf_script = {
|
||||
.sample = process_sample_event,
|
||||
.mmap = perf_event__process_mmap,
|
||||
.comm = perf_event__process_comm,
|
||||
@ -494,7 +506,7 @@ static int __cmd_script(struct perf_session *session)
|
||||
|
||||
signal(SIGINT, sig_handler);
|
||||
|
||||
ret = perf_session__process_events(session, &event_ops);
|
||||
ret = perf_session__process_events(session, &perf_script);
|
||||
|
||||
if (debug_mode)
|
||||
pr_err("Misordered timestamps: %" PRIu64 "\n", nr_unordered);
|
||||
@ -1083,7 +1095,9 @@ static const struct option options[] = {
|
||||
OPT_CALLBACK('f', "fields", NULL, "str",
|
||||
"comma separated output fields prepend with 'type:'. Valid types: hw,sw,trace,raw. Fields: comm,tid,pid,time,cpu,event,trace,ip,sym,dso,addr",
|
||||
parse_output_fields),
|
||||
OPT_STRING('c', "cpu", &cpu_list, "cpu", "list of cpus to profile"),
|
||||
OPT_STRING('C', "cpu", &cpu_list, "cpu", "list of cpus to profile"),
|
||||
OPT_STRING('c', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
|
||||
"only display events for these comms"),
|
||||
OPT_BOOLEAN('I', "show-info", &show_full_info,
|
||||
"display extended information from perf.data file"),
|
||||
OPT_END()
|
||||
@ -1261,7 +1275,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __used)
|
||||
if (!script_name)
|
||||
setup_pager();
|
||||
|
||||
session = perf_session__new(input_name, O_RDONLY, 0, false, &event_ops);
|
||||
session = perf_session__new(input_name, O_RDONLY, 0, false, &perf_script);
|
||||
if (session == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -1108,22 +1108,13 @@ static const struct option options[] = {
|
||||
*/
|
||||
static int add_default_attributes(void)
|
||||
{
|
||||
struct perf_evsel *pos;
|
||||
size_t attr_nr = 0;
|
||||
size_t c;
|
||||
|
||||
/* Set attrs if no event is selected and !null_run: */
|
||||
if (null_run)
|
||||
return 0;
|
||||
|
||||
if (!evsel_list->nr_entries) {
|
||||
for (c = 0; c < ARRAY_SIZE(default_attrs); c++) {
|
||||
pos = perf_evsel__new(default_attrs + c, c + attr_nr);
|
||||
if (pos == NULL)
|
||||
return -1;
|
||||
perf_evlist__add(evsel_list, pos);
|
||||
}
|
||||
attr_nr += c;
|
||||
if (perf_evlist__add_attrs_array(evsel_list, default_attrs) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Detailed events get appended to the event list: */
|
||||
@ -1132,38 +1123,21 @@ static int add_default_attributes(void)
|
||||
return 0;
|
||||
|
||||
/* Append detailed run extra attributes: */
|
||||
for (c = 0; c < ARRAY_SIZE(detailed_attrs); c++) {
|
||||
pos = perf_evsel__new(detailed_attrs + c, c + attr_nr);
|
||||
if (pos == NULL)
|
||||
return -1;
|
||||
perf_evlist__add(evsel_list, pos);
|
||||
}
|
||||
attr_nr += c;
|
||||
if (perf_evlist__add_attrs_array(evsel_list, detailed_attrs) < 0)
|
||||
return -1;
|
||||
|
||||
if (detailed_run < 2)
|
||||
return 0;
|
||||
|
||||
/* Append very detailed run extra attributes: */
|
||||
for (c = 0; c < ARRAY_SIZE(very_detailed_attrs); c++) {
|
||||
pos = perf_evsel__new(very_detailed_attrs + c, c + attr_nr);
|
||||
if (pos == NULL)
|
||||
return -1;
|
||||
perf_evlist__add(evsel_list, pos);
|
||||
}
|
||||
if (perf_evlist__add_attrs_array(evsel_list, very_detailed_attrs) < 0)
|
||||
return -1;
|
||||
|
||||
if (detailed_run < 3)
|
||||
return 0;
|
||||
|
||||
/* Append very, very detailed run extra attributes: */
|
||||
for (c = 0; c < ARRAY_SIZE(very_very_detailed_attrs); c++) {
|
||||
pos = perf_evsel__new(very_very_detailed_attrs + c, c + attr_nr);
|
||||
if (pos == NULL)
|
||||
return -1;
|
||||
perf_evlist__add(evsel_list, pos);
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
return perf_evlist__add_attrs_array(evsel_list, very_very_detailed_attrs);
|
||||
}
|
||||
|
||||
int cmd_stat(int argc, const char **argv, const char *prefix __used)
|
||||
@ -1267,8 +1241,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used)
|
||||
|
||||
list_for_each_entry(pos, &evsel_list->entries, node) {
|
||||
if (perf_evsel__alloc_stat_priv(pos) < 0 ||
|
||||
perf_evsel__alloc_counts(pos, evsel_list->cpus->nr) < 0 ||
|
||||
perf_evsel__alloc_fd(pos, evsel_list->cpus->nr, evsel_list->threads->nr) < 0)
|
||||
perf_evsel__alloc_counts(pos, evsel_list->cpus->nr) < 0)
|
||||
goto out_free_fd;
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include "util/cache.h"
|
||||
#include "util/debug.h"
|
||||
#include "util/debugfs.h"
|
||||
#include "util/evlist.h"
|
||||
#include "util/parse-options.h"
|
||||
#include "util/parse-events.h"
|
||||
@ -14,8 +15,6 @@
|
||||
#include "util/thread_map.h"
|
||||
#include "../../include/linux/hw_breakpoint.h"
|
||||
|
||||
static long page_size;
|
||||
|
||||
static int vmlinux_matches_kallsyms_filter(struct map *map __used, struct symbol *sym)
|
||||
{
|
||||
bool *visited = symbol__priv(sym);
|
||||
@ -31,6 +30,7 @@ static int test__vmlinux_matches_kallsyms(void)
|
||||
struct map *kallsyms_map, *vmlinux_map;
|
||||
struct machine kallsyms, vmlinux;
|
||||
enum map_type type = MAP__FUNCTION;
|
||||
long page_size = sysconf(_SC_PAGE_SIZE);
|
||||
struct ref_reloc_sym ref_reloc_sym = { .name = "_stext", };
|
||||
|
||||
/*
|
||||
@ -247,7 +247,7 @@ static int trace_event__id(const char *evname)
|
||||
|
||||
if (asprintf(&filename,
|
||||
"%s/syscalls/%s/id",
|
||||
debugfs_path, evname) < 0)
|
||||
tracing_events_path, evname) < 0)
|
||||
return -1;
|
||||
|
||||
fd = open(filename, O_RDONLY);
|
||||
@ -841,6 +841,336 @@ static int test__parse_events(void)
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sched__get_first_possible_cpu(pid_t pid, cpu_set_t **maskp,
|
||||
size_t *sizep)
|
||||
{
|
||||
cpu_set_t *mask;
|
||||
size_t size;
|
||||
int i, cpu = -1, nrcpus = 1024;
|
||||
realloc:
|
||||
mask = CPU_ALLOC(nrcpus);
|
||||
size = CPU_ALLOC_SIZE(nrcpus);
|
||||
CPU_ZERO_S(size, mask);
|
||||
|
||||
if (sched_getaffinity(pid, size, mask) == -1) {
|
||||
CPU_FREE(mask);
|
||||
if (errno == EINVAL && nrcpus < (1024 << 8)) {
|
||||
nrcpus = nrcpus << 2;
|
||||
goto realloc;
|
||||
}
|
||||
perror("sched_getaffinity");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < nrcpus; i++) {
|
||||
if (CPU_ISSET_S(i, size, mask)) {
|
||||
if (cpu == -1) {
|
||||
cpu = i;
|
||||
*maskp = mask;
|
||||
*sizep = size;
|
||||
} else
|
||||
CPU_CLR_S(i, size, mask);
|
||||
}
|
||||
}
|
||||
|
||||
if (cpu == -1)
|
||||
CPU_FREE(mask);
|
||||
|
||||
return cpu;
|
||||
}
|
||||
|
||||
static int test__PERF_RECORD(void)
|
||||
{
|
||||
struct perf_record_opts opts = {
|
||||
.target_pid = -1,
|
||||
.target_tid = -1,
|
||||
.no_delay = true,
|
||||
.freq = 10,
|
||||
.mmap_pages = 256,
|
||||
.sample_id_all_avail = true,
|
||||
};
|
||||
cpu_set_t *cpu_mask = NULL;
|
||||
size_t cpu_mask_size = 0;
|
||||
struct perf_evlist *evlist = perf_evlist__new(NULL, NULL);
|
||||
struct perf_evsel *evsel;
|
||||
struct perf_sample sample;
|
||||
const char *cmd = "sleep";
|
||||
const char *argv[] = { cmd, "1", NULL, };
|
||||
char *bname;
|
||||
u64 sample_type, prev_time = 0;
|
||||
bool found_cmd_mmap = false,
|
||||
found_libc_mmap = false,
|
||||
found_vdso_mmap = false,
|
||||
found_ld_mmap = false;
|
||||
int err = -1, errs = 0, i, wakeups = 0, sample_size;
|
||||
u32 cpu;
|
||||
int total_events = 0, nr_events[PERF_RECORD_MAX] = { 0, };
|
||||
|
||||
if (evlist == NULL || argv == NULL) {
|
||||
pr_debug("Not enough memory to create evlist\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need at least one evsel in the evlist, use the default
|
||||
* one: "cycles".
|
||||
*/
|
||||
err = perf_evlist__add_default(evlist);
|
||||
if (err < 0) {
|
||||
pr_debug("Not enough memory to create evsel\n");
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create maps of threads and cpus to monitor. In this case
|
||||
* we start with all threads and cpus (-1, -1) but then in
|
||||
* perf_evlist__prepare_workload we'll fill in the only thread
|
||||
* we're monitoring, the one forked there.
|
||||
*/
|
||||
err = perf_evlist__create_maps(evlist, opts.target_pid,
|
||||
opts.target_tid, opts.cpu_list);
|
||||
if (err < 0) {
|
||||
pr_debug("Not enough memory to create thread/cpu maps\n");
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare the workload in argv[] to run, it'll fork it, and then wait
|
||||
* for perf_evlist__start_workload() to exec it. This is done this way
|
||||
* so that we have time to open the evlist (calling sys_perf_event_open
|
||||
* on all the fds) and then mmap them.
|
||||
*/
|
||||
err = perf_evlist__prepare_workload(evlist, &opts, argv);
|
||||
if (err < 0) {
|
||||
pr_debug("Couldn't run the workload!\n");
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
/*
|
||||
* Config the evsels, setting attr->comm on the first one, etc.
|
||||
*/
|
||||
evsel = list_entry(evlist->entries.next, struct perf_evsel, node);
|
||||
evsel->attr.sample_type |= PERF_SAMPLE_CPU;
|
||||
evsel->attr.sample_type |= PERF_SAMPLE_TID;
|
||||
evsel->attr.sample_type |= PERF_SAMPLE_TIME;
|
||||
perf_evlist__config_attrs(evlist, &opts);
|
||||
|
||||
err = sched__get_first_possible_cpu(evlist->workload.pid, &cpu_mask,
|
||||
&cpu_mask_size);
|
||||
if (err < 0) {
|
||||
pr_debug("sched__get_first_possible_cpu: %s\n", strerror(errno));
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
cpu = err;
|
||||
|
||||
/*
|
||||
* So that we can check perf_sample.cpu on all the samples.
|
||||
*/
|
||||
if (sched_setaffinity(evlist->workload.pid, cpu_mask_size, cpu_mask) < 0) {
|
||||
pr_debug("sched_setaffinity: %s\n", strerror(errno));
|
||||
goto out_free_cpu_mask;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call sys_perf_event_open on all the fds on all the evsels,
|
||||
* grouping them if asked to.
|
||||
*/
|
||||
err = perf_evlist__open(evlist, opts.group);
|
||||
if (err < 0) {
|
||||
pr_debug("perf_evlist__open: %s\n", strerror(errno));
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
/*
|
||||
* mmap the first fd on a given CPU and ask for events for the other
|
||||
* fds in the same CPU to be injected in the same mmap ring buffer
|
||||
* (using ioctl(PERF_EVENT_IOC_SET_OUTPUT)).
|
||||
*/
|
||||
err = perf_evlist__mmap(evlist, opts.mmap_pages, false);
|
||||
if (err < 0) {
|
||||
pr_debug("perf_evlist__mmap: %s\n", strerror(errno));
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
/*
|
||||
* We'll need these two to parse the PERF_SAMPLE_* fields in each
|
||||
* event.
|
||||
*/
|
||||
sample_type = perf_evlist__sample_type(evlist);
|
||||
sample_size = __perf_evsel__sample_size(sample_type);
|
||||
|
||||
/*
|
||||
* Now that all is properly set up, enable the events, they will
|
||||
* count just on workload.pid, which will start...
|
||||
*/
|
||||
perf_evlist__enable(evlist);
|
||||
|
||||
/*
|
||||
* Now!
|
||||
*/
|
||||
perf_evlist__start_workload(evlist);
|
||||
|
||||
while (1) {
|
||||
int before = total_events;
|
||||
|
||||
for (i = 0; i < evlist->nr_mmaps; i++) {
|
||||
union perf_event *event;
|
||||
|
||||
while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) {
|
||||
const u32 type = event->header.type;
|
||||
const char *name = perf_event__name(type);
|
||||
|
||||
++total_events;
|
||||
if (type < PERF_RECORD_MAX)
|
||||
nr_events[type]++;
|
||||
|
||||
err = perf_event__parse_sample(event, sample_type,
|
||||
sample_size, true,
|
||||
&sample, false);
|
||||
if (err < 0) {
|
||||
if (verbose)
|
||||
perf_event__fprintf(event, stderr);
|
||||
pr_debug("Couldn't parse sample\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
pr_info("%" PRIu64" %d ", sample.time, sample.cpu);
|
||||
perf_event__fprintf(event, stderr);
|
||||
}
|
||||
|
||||
if (prev_time > sample.time) {
|
||||
pr_debug("%s going backwards in time, prev=%" PRIu64 ", curr=%" PRIu64 "\n",
|
||||
name, prev_time, sample.time);
|
||||
++errs;
|
||||
}
|
||||
|
||||
prev_time = sample.time;
|
||||
|
||||
if (sample.cpu != cpu) {
|
||||
pr_debug("%s with unexpected cpu, expected %d, got %d\n",
|
||||
name, cpu, sample.cpu);
|
||||
++errs;
|
||||
}
|
||||
|
||||
if ((pid_t)sample.pid != evlist->workload.pid) {
|
||||
pr_debug("%s with unexpected pid, expected %d, got %d\n",
|
||||
name, evlist->workload.pid, sample.pid);
|
||||
++errs;
|
||||
}
|
||||
|
||||
if ((pid_t)sample.tid != evlist->workload.pid) {
|
||||
pr_debug("%s with unexpected tid, expected %d, got %d\n",
|
||||
name, evlist->workload.pid, sample.tid);
|
||||
++errs;
|
||||
}
|
||||
|
||||
if ((type == PERF_RECORD_COMM ||
|
||||
type == PERF_RECORD_MMAP ||
|
||||
type == PERF_RECORD_FORK ||
|
||||
type == PERF_RECORD_EXIT) &&
|
||||
(pid_t)event->comm.pid != evlist->workload.pid) {
|
||||
pr_debug("%s with unexpected pid/tid\n", name);
|
||||
++errs;
|
||||
}
|
||||
|
||||
if ((type == PERF_RECORD_COMM ||
|
||||
type == PERF_RECORD_MMAP) &&
|
||||
event->comm.pid != event->comm.tid) {
|
||||
pr_debug("%s with different pid/tid!\n", name);
|
||||
++errs;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case PERF_RECORD_COMM:
|
||||
if (strcmp(event->comm.comm, cmd)) {
|
||||
pr_debug("%s with unexpected comm!\n", name);
|
||||
++errs;
|
||||
}
|
||||
break;
|
||||
case PERF_RECORD_EXIT:
|
||||
goto found_exit;
|
||||
case PERF_RECORD_MMAP:
|
||||
bname = strrchr(event->mmap.filename, '/');
|
||||
if (bname != NULL) {
|
||||
if (!found_cmd_mmap)
|
||||
found_cmd_mmap = !strcmp(bname + 1, cmd);
|
||||
if (!found_libc_mmap)
|
||||
found_libc_mmap = !strncmp(bname + 1, "libc", 4);
|
||||
if (!found_ld_mmap)
|
||||
found_ld_mmap = !strncmp(bname + 1, "ld", 2);
|
||||
} else if (!found_vdso_mmap)
|
||||
found_vdso_mmap = !strcmp(event->mmap.filename, "[vdso]");
|
||||
break;
|
||||
|
||||
case PERF_RECORD_SAMPLE:
|
||||
/* Just ignore samples for now */
|
||||
break;
|
||||
default:
|
||||
pr_debug("Unexpected perf_event->header.type %d!\n",
|
||||
type);
|
||||
++errs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't use poll here because at least at 3.1 times the
|
||||
* PERF_RECORD_{!SAMPLE} events don't honour
|
||||
* perf_event_attr.wakeup_events, just PERF_EVENT_SAMPLE does.
|
||||
*/
|
||||
if (total_events == before && false)
|
||||
poll(evlist->pollfd, evlist->nr_fds, -1);
|
||||
|
||||
sleep(1);
|
||||
if (++wakeups > 5) {
|
||||
pr_debug("No PERF_RECORD_EXIT event!\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
found_exit:
|
||||
if (nr_events[PERF_RECORD_COMM] > 1) {
|
||||
pr_debug("Excessive number of PERF_RECORD_COMM events!\n");
|
||||
++errs;
|
||||
}
|
||||
|
||||
if (nr_events[PERF_RECORD_COMM] == 0) {
|
||||
pr_debug("Missing PERF_RECORD_COMM for %s!\n", cmd);
|
||||
++errs;
|
||||
}
|
||||
|
||||
if (!found_cmd_mmap) {
|
||||
pr_debug("PERF_RECORD_MMAP for %s missing!\n", cmd);
|
||||
++errs;
|
||||
}
|
||||
|
||||
if (!found_libc_mmap) {
|
||||
pr_debug("PERF_RECORD_MMAP for %s missing!\n", "libc");
|
||||
++errs;
|
||||
}
|
||||
|
||||
if (!found_ld_mmap) {
|
||||
pr_debug("PERF_RECORD_MMAP for %s missing!\n", "ld");
|
||||
++errs;
|
||||
}
|
||||
|
||||
if (!found_vdso_mmap) {
|
||||
pr_debug("PERF_RECORD_MMAP for %s missing!\n", "[vdso]");
|
||||
++errs;
|
||||
}
|
||||
out_err:
|
||||
perf_evlist__munmap(evlist);
|
||||
out_free_cpu_mask:
|
||||
CPU_FREE(cpu_mask);
|
||||
out_delete_evlist:
|
||||
perf_evlist__delete(evlist);
|
||||
out:
|
||||
return (err < 0 || errs > 0) ? -1 : 0;
|
||||
}
|
||||
|
||||
static struct test {
|
||||
const char *desc;
|
||||
int (*func)(void);
|
||||
@ -865,46 +1195,90 @@ static struct test {
|
||||
.desc = "parse events tests",
|
||||
.func = test__parse_events,
|
||||
},
|
||||
{
|
||||
.desc = "Validate PERF_RECORD_* events & perf_sample fields",
|
||||
.func = test__PERF_RECORD,
|
||||
},
|
||||
{
|
||||
.func = NULL,
|
||||
},
|
||||
};
|
||||
|
||||
static int __cmd_test(void)
|
||||
static bool perf_test__matches(int curr, int argc, const char *argv[])
|
||||
{
|
||||
int i;
|
||||
|
||||
if (argc == 0)
|
||||
return true;
|
||||
|
||||
for (i = 0; i < argc; ++i) {
|
||||
char *end;
|
||||
long nr = strtoul(argv[i], &end, 10);
|
||||
|
||||
if (*end == '\0') {
|
||||
if (nr == curr + 1)
|
||||
return true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strstr(tests[curr].desc, argv[i]))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int __cmd_test(int argc, const char *argv[])
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
page_size = sysconf(_SC_PAGE_SIZE);
|
||||
|
||||
while (tests[i].func) {
|
||||
int err;
|
||||
pr_info("%2d: %s:", i + 1, tests[i].desc);
|
||||
int curr = i++, err;
|
||||
|
||||
if (!perf_test__matches(curr, argc, argv))
|
||||
continue;
|
||||
|
||||
pr_info("%2d: %s:", i, tests[curr].desc);
|
||||
pr_debug("\n--- start ---\n");
|
||||
err = tests[i].func();
|
||||
pr_debug("---- end ----\n%s:", tests[i].desc);
|
||||
err = tests[curr].func();
|
||||
pr_debug("---- end ----\n%s:", tests[curr].desc);
|
||||
pr_info(" %s\n", err ? "FAILED!\n" : "Ok");
|
||||
++i;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char * const test_usage[] = {
|
||||
"perf test [<options>]",
|
||||
NULL,
|
||||
};
|
||||
static int perf_test__list(int argc, const char **argv)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
static const struct option test_options[] = {
|
||||
OPT_INTEGER('v', "verbose", &verbose,
|
||||
"be more verbose (show symbol address, etc)"),
|
||||
OPT_END()
|
||||
};
|
||||
while (tests[i].func) {
|
||||
int curr = i++;
|
||||
|
||||
if (argc > 1 && !strstr(tests[curr].desc, argv[1]))
|
||||
continue;
|
||||
|
||||
pr_info("%2d: %s\n", i, tests[curr].desc);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_test(int argc, const char **argv, const char *prefix __used)
|
||||
{
|
||||
const char * const test_usage[] = {
|
||||
"perf test [<options>] [{list <test-name-fragment>|[<test-name-fragments>|<test-numbers>]}]",
|
||||
NULL,
|
||||
};
|
||||
const struct option test_options[] = {
|
||||
OPT_INTEGER('v', "verbose", &verbose,
|
||||
"be more verbose (show symbol address, etc)"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
argc = parse_options(argc, argv, test_options, test_usage, 0);
|
||||
if (argc)
|
||||
usage_with_options(test_usage, test_options);
|
||||
if (argc >= 1 && !strcmp(argv[0], "list"))
|
||||
return perf_test__list(argc, argv);
|
||||
|
||||
symbol_conf.priv_size = sizeof(int);
|
||||
symbol_conf.sort_by_name = true;
|
||||
@ -915,5 +1289,5 @@ int cmd_test(int argc, const char **argv, const char *prefix __used)
|
||||
|
||||
setup_pager();
|
||||
|
||||
return __cmd_test();
|
||||
return __cmd_test(argc, argv);
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "util/color.h"
|
||||
#include <linux/list.h>
|
||||
#include "util/cache.h"
|
||||
#include "util/evsel.h"
|
||||
#include <linux/rbtree.h>
|
||||
#include "util/symbol.h"
|
||||
#include "util/callchain.h"
|
||||
@ -31,6 +32,7 @@
|
||||
#include "util/event.h"
|
||||
#include "util/session.h"
|
||||
#include "util/svghelper.h"
|
||||
#include "util/tool.h"
|
||||
|
||||
#define SUPPORT_OLD_POWER_EVENTS 1
|
||||
#define PWR_EVENT_EXIT -1
|
||||
@ -273,25 +275,28 @@ static int cpus_cstate_state[MAX_CPUS];
|
||||
static u64 cpus_pstate_start_times[MAX_CPUS];
|
||||
static u64 cpus_pstate_state[MAX_CPUS];
|
||||
|
||||
static int process_comm_event(union perf_event *event,
|
||||
static int process_comm_event(struct perf_tool *tool __used,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __used,
|
||||
struct perf_session *session __used)
|
||||
struct machine *machine __used)
|
||||
{
|
||||
pid_set_comm(event->comm.tid, event->comm.comm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_fork_event(union perf_event *event,
|
||||
static int process_fork_event(struct perf_tool *tool __used,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __used,
|
||||
struct perf_session *session __used)
|
||||
struct machine *machine __used)
|
||||
{
|
||||
pid_fork(event->fork.pid, event->fork.ppid, event->fork.time);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_exit_event(union perf_event *event,
|
||||
static int process_exit_event(struct perf_tool *tool __used,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __used,
|
||||
struct perf_session *session __used)
|
||||
struct machine *machine __used)
|
||||
{
|
||||
pid_exit(event->fork.pid, event->fork.time);
|
||||
return 0;
|
||||
@ -486,14 +491,15 @@ static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te)
|
||||
}
|
||||
|
||||
|
||||
static int process_sample_event(union perf_event *event __used,
|
||||
static int process_sample_event(struct perf_tool *tool __used,
|
||||
union perf_event *event __used,
|
||||
struct perf_sample *sample,
|
||||
struct perf_evsel *evsel __used,
|
||||
struct perf_session *session)
|
||||
struct perf_evsel *evsel,
|
||||
struct machine *machine __used)
|
||||
{
|
||||
struct trace_entry *te;
|
||||
|
||||
if (session->sample_type & PERF_SAMPLE_TIME) {
|
||||
if (evsel->attr.sample_type & PERF_SAMPLE_TIME) {
|
||||
if (!first_time || first_time > sample->time)
|
||||
first_time = sample->time;
|
||||
if (last_time < sample->time)
|
||||
@ -501,7 +507,7 @@ static int process_sample_event(union perf_event *event __used,
|
||||
}
|
||||
|
||||
te = (void *)sample->raw_data;
|
||||
if (session->sample_type & PERF_SAMPLE_RAW && sample->raw_size > 0) {
|
||||
if ((evsel->attr.sample_type & PERF_SAMPLE_RAW) && sample->raw_size > 0) {
|
||||
char *event_str;
|
||||
#ifdef SUPPORT_OLD_POWER_EVENTS
|
||||
struct power_entry_old *peo;
|
||||
@ -974,7 +980,7 @@ static void write_svg_file(const char *filename)
|
||||
svg_close();
|
||||
}
|
||||
|
||||
static struct perf_event_ops event_ops = {
|
||||
static struct perf_tool perf_timechart = {
|
||||
.comm = process_comm_event,
|
||||
.fork = process_fork_event,
|
||||
.exit = process_exit_event,
|
||||
@ -985,7 +991,7 @@ static struct perf_event_ops event_ops = {
|
||||
static int __cmd_timechart(void)
|
||||
{
|
||||
struct perf_session *session = perf_session__new(input_name, O_RDONLY,
|
||||
0, false, &event_ops);
|
||||
0, false, &perf_timechart);
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (session == NULL)
|
||||
@ -994,7 +1000,7 @@ static int __cmd_timechart(void)
|
||||
if (!perf_session__has_traces(session, "timechart record"))
|
||||
goto out_delete;
|
||||
|
||||
ret = perf_session__process_events(session, &event_ops);
|
||||
ret = perf_session__process_events(session, &perf_timechart);
|
||||
if (ret)
|
||||
goto out_delete;
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -29,8 +29,6 @@ struct pager_config {
|
||||
int val;
|
||||
};
|
||||
|
||||
static char debugfs_mntpt[MAXPATHLEN];
|
||||
|
||||
static int pager_command_config(const char *var, const char *value, void *data)
|
||||
{
|
||||
struct pager_config *c = data;
|
||||
@ -81,15 +79,6 @@ static void commit_pager_choice(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void set_debugfs_path(void)
|
||||
{
|
||||
char *path;
|
||||
|
||||
path = getenv(PERF_DEBUGFS_ENVIRONMENT);
|
||||
snprintf(debugfs_path, MAXPATHLEN, "%s/%s", path ?: debugfs_mntpt,
|
||||
"tracing/events");
|
||||
}
|
||||
|
||||
static int handle_options(const char ***argv, int *argc, int *envchanged)
|
||||
{
|
||||
int handled = 0;
|
||||
@ -161,15 +150,14 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
|
||||
fprintf(stderr, "No directory given for --debugfs-dir.\n");
|
||||
usage(perf_usage_string);
|
||||
}
|
||||
strncpy(debugfs_mntpt, (*argv)[1], MAXPATHLEN);
|
||||
debugfs_mntpt[MAXPATHLEN - 1] = '\0';
|
||||
debugfs_set_path((*argv)[1]);
|
||||
if (envchanged)
|
||||
*envchanged = 1;
|
||||
(*argv)++;
|
||||
(*argc)--;
|
||||
} else if (!prefixcmp(cmd, CMD_DEBUGFS_DIR)) {
|
||||
strncpy(debugfs_mntpt, cmd + strlen(CMD_DEBUGFS_DIR), MAXPATHLEN);
|
||||
debugfs_mntpt[MAXPATHLEN - 1] = '\0';
|
||||
debugfs_set_path(cmd + strlen(CMD_DEBUGFS_DIR));
|
||||
fprintf(stderr, "dir: %s\n", debugfs_mountpoint);
|
||||
if (envchanged)
|
||||
*envchanged = 1;
|
||||
} else {
|
||||
@ -281,7 +269,6 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
|
||||
if (use_pager == -1 && p->option & USE_PAGER)
|
||||
use_pager = 1;
|
||||
commit_pager_choice();
|
||||
set_debugfs_path();
|
||||
|
||||
status = p->fn(argc, argv, prefix);
|
||||
exit_browser(status);
|
||||
@ -416,17 +403,6 @@ static int run_argv(int *argcp, const char ***argv)
|
||||
return done_alias;
|
||||
}
|
||||
|
||||
/* mini /proc/mounts parser: searching for "^blah /mount/point debugfs" */
|
||||
static void get_debugfs_mntpt(void)
|
||||
{
|
||||
const char *path = debugfs_mount(NULL);
|
||||
|
||||
if (path)
|
||||
strncpy(debugfs_mntpt, path, sizeof(debugfs_mntpt));
|
||||
else
|
||||
debugfs_mntpt[0] = '\0';
|
||||
}
|
||||
|
||||
static void pthread__block_sigwinch(void)
|
||||
{
|
||||
sigset_t set;
|
||||
@ -453,7 +429,7 @@ int main(int argc, const char **argv)
|
||||
if (!cmd)
|
||||
cmd = "perf-help";
|
||||
/* get debugfs mount point from /proc/mounts */
|
||||
get_debugfs_mntpt();
|
||||
debugfs_mount(NULL);
|
||||
/*
|
||||
* "perf-xxxx" is the same as "perf xxxx", but we obviously:
|
||||
*
|
||||
@ -476,7 +452,6 @@ int main(int argc, const char **argv)
|
||||
argc--;
|
||||
handle_options(&argv, &argc, NULL);
|
||||
commit_pager_choice();
|
||||
set_debugfs_path();
|
||||
set_buildid_dir();
|
||||
|
||||
if (argc > 0) {
|
||||
|
@ -185,4 +185,27 @@ extern const char perf_version_string[];
|
||||
|
||||
void pthread__unblock_sigwinch(void);
|
||||
|
||||
struct perf_record_opts {
|
||||
pid_t target_pid;
|
||||
pid_t target_tid;
|
||||
bool call_graph;
|
||||
bool group;
|
||||
bool inherit_stat;
|
||||
bool no_delay;
|
||||
bool no_inherit;
|
||||
bool no_samples;
|
||||
bool pipe_output;
|
||||
bool raw_samples;
|
||||
bool sample_address;
|
||||
bool sample_time;
|
||||
bool sample_id_all_avail;
|
||||
bool system_wide;
|
||||
unsigned int freq;
|
||||
unsigned int mmap_pages;
|
||||
unsigned int user_freq;
|
||||
u64 default_interval;
|
||||
u64 user_interval;
|
||||
const char *cpu_list;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -25,17 +25,17 @@ int symbol__annotate_init(struct map *map __used, struct symbol *sym)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int symbol__alloc_hist(struct symbol *sym, int nevents)
|
||||
int symbol__alloc_hist(struct symbol *sym)
|
||||
{
|
||||
struct annotation *notes = symbol__annotation(sym);
|
||||
size_t sizeof_sym_hist = (sizeof(struct sym_hist) +
|
||||
(sym->end - sym->start) * sizeof(u64));
|
||||
|
||||
notes->src = zalloc(sizeof(*notes->src) + nevents * sizeof_sym_hist);
|
||||
notes->src = zalloc(sizeof(*notes->src) + symbol_conf.nr_events * sizeof_sym_hist);
|
||||
if (notes->src == NULL)
|
||||
return -1;
|
||||
notes->src->sizeof_sym_hist = sizeof_sym_hist;
|
||||
notes->src->nr_histograms = nevents;
|
||||
notes->src->nr_histograms = symbol_conf.nr_events;
|
||||
INIT_LIST_HEAD(¬es->src->source);
|
||||
return 0;
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ static inline struct annotation *symbol__annotation(struct symbol *sym)
|
||||
|
||||
int symbol__inc_addr_samples(struct symbol *sym, struct map *map,
|
||||
int evidx, u64 addr);
|
||||
int symbol__alloc_hist(struct symbol *sym, int nevents);
|
||||
int symbol__alloc_hist(struct symbol *sym);
|
||||
void symbol__annotate_zero_histograms(struct symbol *sym);
|
||||
|
||||
int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize);
|
||||
@ -99,8 +99,7 @@ static inline int symbol__tui_annotate(struct symbol *sym __used,
|
||||
}
|
||||
#else
|
||||
int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
|
||||
int nr_events, void(*timer)(void *arg), void *arg,
|
||||
int delay_secs);
|
||||
void(*timer)(void *arg), void *arg, int delay_secs);
|
||||
#endif
|
||||
|
||||
extern const char *disassembler_style;
|
||||
|
@ -13,15 +13,18 @@
|
||||
#include "symbol.h"
|
||||
#include <linux/kernel.h>
|
||||
#include "debug.h"
|
||||
#include "session.h"
|
||||
#include "tool.h"
|
||||
|
||||
static int build_id__mark_dso_hit(union perf_event *event,
|
||||
static int build_id__mark_dso_hit(struct perf_tool *tool __used,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __used,
|
||||
struct perf_evsel *evsel __used,
|
||||
struct perf_session *session)
|
||||
struct machine *machine)
|
||||
{
|
||||
struct addr_location al;
|
||||
u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
|
||||
struct thread *thread = perf_session__findnew(session, event->ip.pid);
|
||||
struct thread *thread = machine__findnew_thread(machine, event->ip.pid);
|
||||
|
||||
if (thread == NULL) {
|
||||
pr_err("problem processing %d event, skipping it.\n",
|
||||
@ -29,8 +32,8 @@ static int build_id__mark_dso_hit(union perf_event *event,
|
||||
return -1;
|
||||
}
|
||||
|
||||
thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION,
|
||||
event->ip.pid, event->ip.ip, &al);
|
||||
thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION,
|
||||
event->ip.ip, &al);
|
||||
|
||||
if (al.map != NULL)
|
||||
al.map->dso->hit = 1;
|
||||
@ -38,25 +41,26 @@ static int build_id__mark_dso_hit(union perf_event *event,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int perf_event__exit_del_thread(union perf_event *event,
|
||||
static int perf_event__exit_del_thread(struct perf_tool *tool __used,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __used,
|
||||
struct perf_session *session)
|
||||
struct machine *machine)
|
||||
{
|
||||
struct thread *thread = perf_session__findnew(session, event->fork.tid);
|
||||
struct thread *thread = machine__findnew_thread(machine, event->fork.tid);
|
||||
|
||||
dump_printf("(%d:%d):(%d:%d)\n", event->fork.pid, event->fork.tid,
|
||||
event->fork.ppid, event->fork.ptid);
|
||||
|
||||
if (thread) {
|
||||
rb_erase(&thread->rb_node, &session->threads);
|
||||
session->last_match = NULL;
|
||||
rb_erase(&thread->rb_node, &machine->threads);
|
||||
machine->last_match = NULL;
|
||||
thread__delete(thread);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct perf_event_ops build_id__mark_dso_hit_ops = {
|
||||
struct perf_tool build_id__mark_dso_hit_ops = {
|
||||
.sample = build_id__mark_dso_hit,
|
||||
.mmap = perf_event__process_mmap,
|
||||
.fork = perf_event__process_task,
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
#include "session.h"
|
||||
|
||||
extern struct perf_event_ops build_id__mark_dso_hit_ops;
|
||||
extern struct perf_tool build_id__mark_dso_hit_ops;
|
||||
|
||||
char *dso__build_id_filename(struct dso *self, char *bf, size_t size);
|
||||
|
||||
|
@ -101,6 +101,9 @@ int callchain_append(struct callchain_root *root,
|
||||
int callchain_merge(struct callchain_cursor *cursor,
|
||||
struct callchain_root *dst, struct callchain_root *src);
|
||||
|
||||
struct ip_callchain;
|
||||
union perf_event;
|
||||
|
||||
bool ip_callchain__valid(struct ip_callchain *chain,
|
||||
const union perf_event *event);
|
||||
/*
|
||||
|
@ -3,7 +3,6 @@
|
||||
#include "parse-options.h"
|
||||
#include "evsel.h"
|
||||
#include "cgroup.h"
|
||||
#include "debugfs.h" /* MAX_PATH, STR() */
|
||||
#include "evlist.h"
|
||||
|
||||
int nr_cgroups;
|
||||
@ -12,7 +11,7 @@ static int
|
||||
cgroupfs_find_mountpoint(char *buf, size_t maxlen)
|
||||
{
|
||||
FILE *fp;
|
||||
char mountpoint[MAX_PATH+1], tokens[MAX_PATH+1], type[MAX_PATH+1];
|
||||
char mountpoint[PATH_MAX + 1], tokens[PATH_MAX + 1], type[PATH_MAX + 1];
|
||||
char *token, *saved_ptr = NULL;
|
||||
int found = 0;
|
||||
|
||||
@ -25,8 +24,8 @@ cgroupfs_find_mountpoint(char *buf, size_t maxlen)
|
||||
* and inspect every cgroupfs mount point to find one that has
|
||||
* perf_event subsystem
|
||||
*/
|
||||
while (fscanf(fp, "%*s %"STR(MAX_PATH)"s %"STR(MAX_PATH)"s %"
|
||||
STR(MAX_PATH)"s %*d %*d\n",
|
||||
while (fscanf(fp, "%*s %"STR(PATH_MAX)"s %"STR(PATH_MAX)"s %"
|
||||
STR(PATH_MAX)"s %*d %*d\n",
|
||||
mountpoint, type, tokens) == 3) {
|
||||
|
||||
if (!strcmp(type, "cgroup")) {
|
||||
@ -57,15 +56,15 @@ cgroupfs_find_mountpoint(char *buf, size_t maxlen)
|
||||
|
||||
static int open_cgroup(char *name)
|
||||
{
|
||||
char path[MAX_PATH+1];
|
||||
char mnt[MAX_PATH+1];
|
||||
char path[PATH_MAX + 1];
|
||||
char mnt[PATH_MAX + 1];
|
||||
int fd;
|
||||
|
||||
|
||||
if (cgroupfs_find_mountpoint(mnt, MAX_PATH+1))
|
||||
if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1))
|
||||
return -1;
|
||||
|
||||
snprintf(path, MAX_PATH, "%s/%s", mnt, name);
|
||||
snprintf(path, PATH_MAX, "%s/%s", mnt, name);
|
||||
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd == -1)
|
||||
|
@ -2,8 +2,12 @@
|
||||
#include "debugfs.h"
|
||||
#include "cache.h"
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
static int debugfs_premounted;
|
||||
static char debugfs_mountpoint[MAX_PATH+1];
|
||||
char debugfs_mountpoint[PATH_MAX + 1] = "/sys/kernel/debug";
|
||||
char tracing_events_path[PATH_MAX + 1] = "/sys/kernel/debug/tracing/events";
|
||||
|
||||
static const char *debugfs_known_mountpoints[] = {
|
||||
"/sys/kernel/debug/",
|
||||
@ -62,11 +66,9 @@ const char *debugfs_find_mountpoint(void)
|
||||
/* give up and parse /proc/mounts */
|
||||
fp = fopen("/proc/mounts", "r");
|
||||
if (fp == NULL)
|
||||
die("Can't open /proc/mounts for read");
|
||||
return NULL;
|
||||
|
||||
while (fscanf(fp, "%*s %"
|
||||
STR(MAX_PATH)
|
||||
"s %99s %*s %*d %*d\n",
|
||||
while (fscanf(fp, "%*s %" STR(PATH_MAX) "s %99s %*s %*d %*d\n",
|
||||
debugfs_mountpoint, type) == 2) {
|
||||
if (strcmp(type, "debugfs") == 0)
|
||||
break;
|
||||
@ -106,6 +108,12 @@ int debugfs_valid_entry(const char *path)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void debugfs_set_tracing_events_path(const char *mountpoint)
|
||||
{
|
||||
snprintf(tracing_events_path, sizeof(tracing_events_path), "%s/%s",
|
||||
mountpoint, "tracing/events");
|
||||
}
|
||||
|
||||
/* mount the debugfs somewhere if it's not mounted */
|
||||
|
||||
char *debugfs_mount(const char *mountpoint)
|
||||
@ -113,7 +121,7 @@ char *debugfs_mount(const char *mountpoint)
|
||||
/* see if it's already mounted */
|
||||
if (debugfs_find_mountpoint()) {
|
||||
debugfs_premounted = 1;
|
||||
return debugfs_mountpoint;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* if not mounted and no argument */
|
||||
@ -129,12 +137,19 @@ char *debugfs_mount(const char *mountpoint)
|
||||
return NULL;
|
||||
|
||||
/* save the mountpoint */
|
||||
strncpy(debugfs_mountpoint, mountpoint, sizeof(debugfs_mountpoint));
|
||||
debugfs_found = 1;
|
||||
|
||||
strncpy(debugfs_mountpoint, mountpoint, sizeof(debugfs_mountpoint));
|
||||
out:
|
||||
debugfs_set_tracing_events_path(debugfs_mountpoint);
|
||||
return debugfs_mountpoint;
|
||||
}
|
||||
|
||||
void debugfs_set_path(const char *mountpoint)
|
||||
{
|
||||
snprintf(debugfs_mountpoint, sizeof(debugfs_mountpoint), "%s", mountpoint);
|
||||
debugfs_set_tracing_events_path(mountpoint);
|
||||
}
|
||||
|
||||
/* umount the debugfs */
|
||||
|
||||
int debugfs_umount(void)
|
||||
@ -158,7 +173,7 @@ int debugfs_umount(void)
|
||||
|
||||
int debugfs_write(const char *entry, const char *value)
|
||||
{
|
||||
char path[MAX_PATH+1];
|
||||
char path[PATH_MAX + 1];
|
||||
int ret, count;
|
||||
int fd;
|
||||
|
||||
@ -203,7 +218,7 @@ int debugfs_write(const char *entry, const char *value)
|
||||
*/
|
||||
int debugfs_read(const char *entry, char *buffer, size_t size)
|
||||
{
|
||||
char path[MAX_PATH+1];
|
||||
char path[PATH_MAX + 1];
|
||||
int ret;
|
||||
int fd;
|
||||
|
||||
|
@ -1,25 +1,18 @@
|
||||
#ifndef __DEBUGFS_H__
|
||||
#define __DEBUGFS_H__
|
||||
|
||||
#include <sys/mount.h>
|
||||
const char *debugfs_find_mountpoint(void);
|
||||
int debugfs_valid_mountpoint(const char *debugfs);
|
||||
int debugfs_valid_entry(const char *path);
|
||||
char *debugfs_mount(const char *mountpoint);
|
||||
int debugfs_umount(void);
|
||||
void debugfs_set_path(const char *mountpoint);
|
||||
int debugfs_write(const char *entry, const char *value);
|
||||
int debugfs_read(const char *entry, char *buffer, size_t size);
|
||||
void debugfs_force_cleanup(void);
|
||||
int debugfs_make_path(const char *element, char *buffer, int size);
|
||||
|
||||
#ifndef MAX_PATH
|
||||
# define MAX_PATH 256
|
||||
#endif
|
||||
|
||||
#ifndef STR
|
||||
# define _STR(x) #x
|
||||
# define STR(x) _STR(x)
|
||||
#endif
|
||||
|
||||
extern const char *debugfs_find_mountpoint(void);
|
||||
extern int debugfs_valid_mountpoint(const char *debugfs);
|
||||
extern int debugfs_valid_entry(const char *path);
|
||||
extern char *debugfs_mount(const char *mountpoint);
|
||||
extern int debugfs_umount(void);
|
||||
extern int debugfs_write(const char *entry, const char *value);
|
||||
extern int debugfs_read(const char *entry, char *buffer, size_t size);
|
||||
extern void debugfs_force_cleanup(void);
|
||||
extern int debugfs_make_path(const char *element, char *buffer, int size);
|
||||
extern char debugfs_mountpoint[];
|
||||
extern char tracing_events_path[];
|
||||
|
||||
#endif /* __DEBUGFS_H__ */
|
||||
|
@ -1,7 +1,6 @@
|
||||
#include <linux/types.h>
|
||||
#include "event.h"
|
||||
#include "debug.h"
|
||||
#include "session.h"
|
||||
#include "sort.h"
|
||||
#include "string.h"
|
||||
#include "strlist.h"
|
||||
@ -44,9 +43,10 @@ static struct perf_sample synth_sample = {
|
||||
.period = 1,
|
||||
};
|
||||
|
||||
static pid_t perf_event__synthesize_comm(union perf_event *event, pid_t pid,
|
||||
static pid_t perf_event__synthesize_comm(struct perf_tool *tool,
|
||||
union perf_event *event, pid_t pid,
|
||||
int full, perf_event__handler_t process,
|
||||
struct perf_session *session)
|
||||
struct machine *machine)
|
||||
{
|
||||
char filename[PATH_MAX];
|
||||
char bf[BUFSIZ];
|
||||
@ -92,14 +92,14 @@ static pid_t perf_event__synthesize_comm(union perf_event *event, pid_t pid,
|
||||
|
||||
event->comm.header.type = PERF_RECORD_COMM;
|
||||
size = ALIGN(size, sizeof(u64));
|
||||
memset(event->comm.comm + size, 0, session->id_hdr_size);
|
||||
memset(event->comm.comm + size, 0, machine->id_hdr_size);
|
||||
event->comm.header.size = (sizeof(event->comm) -
|
||||
(sizeof(event->comm.comm) - size) +
|
||||
session->id_hdr_size);
|
||||
machine->id_hdr_size);
|
||||
if (!full) {
|
||||
event->comm.tid = pid;
|
||||
|
||||
process(event, &synth_sample, session);
|
||||
process(tool, event, &synth_sample, machine);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -117,7 +117,7 @@ static pid_t perf_event__synthesize_comm(union perf_event *event, pid_t pid,
|
||||
|
||||
event->comm.tid = pid;
|
||||
|
||||
process(event, &synth_sample, session);
|
||||
process(tool, event, &synth_sample, machine);
|
||||
}
|
||||
|
||||
closedir(tasks);
|
||||
@ -127,10 +127,11 @@ static pid_t perf_event__synthesize_comm(union perf_event *event, pid_t pid,
|
||||
return tgid;
|
||||
}
|
||||
|
||||
static int perf_event__synthesize_mmap_events(union perf_event *event,
|
||||
static int perf_event__synthesize_mmap_events(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
pid_t pid, pid_t tgid,
|
||||
perf_event__handler_t process,
|
||||
struct perf_session *session)
|
||||
struct machine *machine)
|
||||
{
|
||||
char filename[PATH_MAX];
|
||||
FILE *fp;
|
||||
@ -193,12 +194,12 @@ static int perf_event__synthesize_mmap_events(union perf_event *event,
|
||||
event->mmap.len -= event->mmap.start;
|
||||
event->mmap.header.size = (sizeof(event->mmap) -
|
||||
(sizeof(event->mmap.filename) - size));
|
||||
memset(event->mmap.filename + size, 0, session->id_hdr_size);
|
||||
event->mmap.header.size += session->id_hdr_size;
|
||||
memset(event->mmap.filename + size, 0, machine->id_hdr_size);
|
||||
event->mmap.header.size += machine->id_hdr_size;
|
||||
event->mmap.pid = tgid;
|
||||
event->mmap.tid = pid;
|
||||
|
||||
process(event, &synth_sample, session);
|
||||
process(tool, event, &synth_sample, machine);
|
||||
}
|
||||
}
|
||||
|
||||
@ -206,14 +207,14 @@ static int perf_event__synthesize_mmap_events(union perf_event *event,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int perf_event__synthesize_modules(perf_event__handler_t process,
|
||||
struct perf_session *session,
|
||||
int perf_event__synthesize_modules(struct perf_tool *tool,
|
||||
perf_event__handler_t process,
|
||||
struct machine *machine)
|
||||
{
|
||||
struct rb_node *nd;
|
||||
struct map_groups *kmaps = &machine->kmaps;
|
||||
union perf_event *event = zalloc((sizeof(event->mmap) +
|
||||
session->id_hdr_size));
|
||||
machine->id_hdr_size));
|
||||
if (event == NULL) {
|
||||
pr_debug("Not enough memory synthesizing mmap event "
|
||||
"for kernel modules\n");
|
||||
@ -243,15 +244,15 @@ int perf_event__synthesize_modules(perf_event__handler_t process,
|
||||
event->mmap.header.type = PERF_RECORD_MMAP;
|
||||
event->mmap.header.size = (sizeof(event->mmap) -
|
||||
(sizeof(event->mmap.filename) - size));
|
||||
memset(event->mmap.filename + size, 0, session->id_hdr_size);
|
||||
event->mmap.header.size += session->id_hdr_size;
|
||||
memset(event->mmap.filename + size, 0, machine->id_hdr_size);
|
||||
event->mmap.header.size += machine->id_hdr_size;
|
||||
event->mmap.start = pos->start;
|
||||
event->mmap.len = pos->end - pos->start;
|
||||
event->mmap.pid = machine->pid;
|
||||
|
||||
memcpy(event->mmap.filename, pos->dso->long_name,
|
||||
pos->dso->long_name_len + 1);
|
||||
process(event, &synth_sample, session);
|
||||
process(tool, event, &synth_sample, machine);
|
||||
}
|
||||
|
||||
free(event);
|
||||
@ -261,28 +262,30 @@ int perf_event__synthesize_modules(perf_event__handler_t process,
|
||||
static int __event__synthesize_thread(union perf_event *comm_event,
|
||||
union perf_event *mmap_event,
|
||||
pid_t pid, perf_event__handler_t process,
|
||||
struct perf_session *session)
|
||||
struct perf_tool *tool,
|
||||
struct machine *machine)
|
||||
{
|
||||
pid_t tgid = perf_event__synthesize_comm(comm_event, pid, 1, process,
|
||||
session);
|
||||
pid_t tgid = perf_event__synthesize_comm(tool, comm_event, pid, 1,
|
||||
process, machine);
|
||||
if (tgid == -1)
|
||||
return -1;
|
||||
return perf_event__synthesize_mmap_events(mmap_event, pid, tgid,
|
||||
process, session);
|
||||
return perf_event__synthesize_mmap_events(tool, mmap_event, pid, tgid,
|
||||
process, machine);
|
||||
}
|
||||
|
||||
int perf_event__synthesize_thread_map(struct thread_map *threads,
|
||||
int perf_event__synthesize_thread_map(struct perf_tool *tool,
|
||||
struct thread_map *threads,
|
||||
perf_event__handler_t process,
|
||||
struct perf_session *session)
|
||||
struct machine *machine)
|
||||
{
|
||||
union perf_event *comm_event, *mmap_event;
|
||||
int err = -1, thread;
|
||||
|
||||
comm_event = malloc(sizeof(comm_event->comm) + session->id_hdr_size);
|
||||
comm_event = malloc(sizeof(comm_event->comm) + machine->id_hdr_size);
|
||||
if (comm_event == NULL)
|
||||
goto out;
|
||||
|
||||
mmap_event = malloc(sizeof(mmap_event->mmap) + session->id_hdr_size);
|
||||
mmap_event = malloc(sizeof(mmap_event->mmap) + machine->id_hdr_size);
|
||||
if (mmap_event == NULL)
|
||||
goto out_free_comm;
|
||||
|
||||
@ -290,7 +293,7 @@ int perf_event__synthesize_thread_map(struct thread_map *threads,
|
||||
for (thread = 0; thread < threads->nr; ++thread) {
|
||||
if (__event__synthesize_thread(comm_event, mmap_event,
|
||||
threads->map[thread],
|
||||
process, session)) {
|
||||
process, tool, machine)) {
|
||||
err = -1;
|
||||
break;
|
||||
}
|
||||
@ -302,19 +305,20 @@ int perf_event__synthesize_thread_map(struct thread_map *threads,
|
||||
return err;
|
||||
}
|
||||
|
||||
int perf_event__synthesize_threads(perf_event__handler_t process,
|
||||
struct perf_session *session)
|
||||
int perf_event__synthesize_threads(struct perf_tool *tool,
|
||||
perf_event__handler_t process,
|
||||
struct machine *machine)
|
||||
{
|
||||
DIR *proc;
|
||||
struct dirent dirent, *next;
|
||||
union perf_event *comm_event, *mmap_event;
|
||||
int err = -1;
|
||||
|
||||
comm_event = malloc(sizeof(comm_event->comm) + session->id_hdr_size);
|
||||
comm_event = malloc(sizeof(comm_event->comm) + machine->id_hdr_size);
|
||||
if (comm_event == NULL)
|
||||
goto out;
|
||||
|
||||
mmap_event = malloc(sizeof(mmap_event->mmap) + session->id_hdr_size);
|
||||
mmap_event = malloc(sizeof(mmap_event->mmap) + machine->id_hdr_size);
|
||||
if (mmap_event == NULL)
|
||||
goto out_free_comm;
|
||||
|
||||
@ -330,7 +334,7 @@ int perf_event__synthesize_threads(perf_event__handler_t process,
|
||||
continue;
|
||||
|
||||
__event__synthesize_thread(comm_event, mmap_event, pid,
|
||||
process, session);
|
||||
process, tool, machine);
|
||||
}
|
||||
|
||||
closedir(proc);
|
||||
@ -365,8 +369,8 @@ static int find_symbol_cb(void *arg, const char *name, char type,
|
||||
return 1;
|
||||
}
|
||||
|
||||
int perf_event__synthesize_kernel_mmap(perf_event__handler_t process,
|
||||
struct perf_session *session,
|
||||
int perf_event__synthesize_kernel_mmap(struct perf_tool *tool,
|
||||
perf_event__handler_t process,
|
||||
struct machine *machine,
|
||||
const char *symbol_name)
|
||||
{
|
||||
@ -383,7 +387,7 @@ int perf_event__synthesize_kernel_mmap(perf_event__handler_t process,
|
||||
*/
|
||||
struct process_symbol_args args = { .name = symbol_name, };
|
||||
union perf_event *event = zalloc((sizeof(event->mmap) +
|
||||
session->id_hdr_size));
|
||||
machine->id_hdr_size));
|
||||
if (event == NULL) {
|
||||
pr_debug("Not enough memory synthesizing mmap event "
|
||||
"for kernel modules\n");
|
||||
@ -417,25 +421,32 @@ int perf_event__synthesize_kernel_mmap(perf_event__handler_t process,
|
||||
size = ALIGN(size, sizeof(u64));
|
||||
event->mmap.header.type = PERF_RECORD_MMAP;
|
||||
event->mmap.header.size = (sizeof(event->mmap) -
|
||||
(sizeof(event->mmap.filename) - size) + session->id_hdr_size);
|
||||
(sizeof(event->mmap.filename) - size) + machine->id_hdr_size);
|
||||
event->mmap.pgoff = args.start;
|
||||
event->mmap.start = map->start;
|
||||
event->mmap.len = map->end - event->mmap.start;
|
||||
event->mmap.pid = machine->pid;
|
||||
|
||||
err = process(event, &synth_sample, session);
|
||||
err = process(tool, event, &synth_sample, machine);
|
||||
free(event);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int perf_event__process_comm(union perf_event *event,
|
||||
struct perf_sample *sample __used,
|
||||
struct perf_session *session)
|
||||
size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp)
|
||||
{
|
||||
struct thread *thread = perf_session__findnew(session, event->comm.tid);
|
||||
return fprintf(fp, ": %s:%d\n", event->comm.comm, event->comm.tid);
|
||||
}
|
||||
|
||||
dump_printf(": %s:%d\n", event->comm.comm, event->comm.tid);
|
||||
int perf_event__process_comm(struct perf_tool *tool __used,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __used,
|
||||
struct machine *machine)
|
||||
{
|
||||
struct thread *thread = machine__findnew_thread(machine, event->comm.tid);
|
||||
|
||||
if (dump_trace)
|
||||
perf_event__fprintf_comm(event, stdout);
|
||||
|
||||
if (thread == NULL || thread__set_comm(thread, event->comm.comm)) {
|
||||
dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n");
|
||||
@ -445,13 +456,13 @@ int perf_event__process_comm(union perf_event *event,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int perf_event__process_lost(union perf_event *event,
|
||||
int perf_event__process_lost(struct perf_tool *tool __used,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __used,
|
||||
struct perf_session *session)
|
||||
struct machine *machine __used)
|
||||
{
|
||||
dump_printf(": id:%" PRIu64 ": lost:%" PRIu64 "\n",
|
||||
event->lost.id, event->lost.lost);
|
||||
session->hists.stats.total_lost += event->lost.lost;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -468,21 +479,15 @@ static void perf_event__set_kernel_mmap_len(union perf_event *event,
|
||||
maps[MAP__FUNCTION]->end = ~0ULL;
|
||||
}
|
||||
|
||||
static int perf_event__process_kernel_mmap(union perf_event *event,
|
||||
struct perf_session *session)
|
||||
static int perf_event__process_kernel_mmap(struct perf_tool *tool __used,
|
||||
union perf_event *event,
|
||||
struct machine *machine)
|
||||
{
|
||||
struct map *map;
|
||||
char kmmap_prefix[PATH_MAX];
|
||||
struct machine *machine;
|
||||
enum dso_kernel_type kernel_type;
|
||||
bool is_kernel_mmap;
|
||||
|
||||
machine = perf_session__findnew_machine(session, event->mmap.pid);
|
||||
if (!machine) {
|
||||
pr_err("Can't find id %d's machine\n", event->mmap.pid);
|
||||
goto out_problem;
|
||||
}
|
||||
|
||||
machine__mmap_name(machine, kmmap_prefix, sizeof(kmmap_prefix));
|
||||
if (machine__is_host(machine))
|
||||
kernel_type = DSO_TYPE_KERNEL;
|
||||
@ -549,9 +554,9 @@ static int perf_event__process_kernel_mmap(union perf_event *event,
|
||||
* time /proc/sys/kernel/kptr_restrict was non zero.
|
||||
*/
|
||||
if (event->mmap.pgoff != 0) {
|
||||
perf_session__set_kallsyms_ref_reloc_sym(machine->vmlinux_maps,
|
||||
symbol_name,
|
||||
event->mmap.pgoff);
|
||||
maps__set_kallsyms_ref_reloc_sym(machine->vmlinux_maps,
|
||||
symbol_name,
|
||||
event->mmap.pgoff);
|
||||
}
|
||||
|
||||
if (machine__is_default_guest(machine)) {
|
||||
@ -567,32 +572,35 @@ static int perf_event__process_kernel_mmap(union perf_event *event,
|
||||
return -1;
|
||||
}
|
||||
|
||||
int perf_event__process_mmap(union perf_event *event,
|
||||
struct perf_sample *sample __used,
|
||||
struct perf_session *session)
|
||||
size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp)
|
||||
{
|
||||
return fprintf(fp, " %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 "]: %s\n",
|
||||
event->mmap.pid, event->mmap.tid, event->mmap.start,
|
||||
event->mmap.len, event->mmap.pgoff, event->mmap.filename);
|
||||
}
|
||||
|
||||
int perf_event__process_mmap(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __used,
|
||||
struct machine *machine)
|
||||
{
|
||||
struct machine *machine;
|
||||
struct thread *thread;
|
||||
struct map *map;
|
||||
u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
|
||||
int ret = 0;
|
||||
|
||||
dump_printf(" %d/%d: [%#" PRIx64 "(%#" PRIx64 ") @ %#" PRIx64 "]: %s\n",
|
||||
event->mmap.pid, event->mmap.tid, event->mmap.start,
|
||||
event->mmap.len, event->mmap.pgoff, event->mmap.filename);
|
||||
if (dump_trace)
|
||||
perf_event__fprintf_mmap(event, stdout);
|
||||
|
||||
if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL ||
|
||||
cpumode == PERF_RECORD_MISC_KERNEL) {
|
||||
ret = perf_event__process_kernel_mmap(event, session);
|
||||
ret = perf_event__process_kernel_mmap(tool, event, machine);
|
||||
if (ret < 0)
|
||||
goto out_problem;
|
||||
return 0;
|
||||
}
|
||||
|
||||
machine = perf_session__find_host_machine(session);
|
||||
if (machine == NULL)
|
||||
goto out_problem;
|
||||
thread = perf_session__findnew(session, event->mmap.pid);
|
||||
thread = machine__findnew_thread(machine, event->mmap.pid);
|
||||
if (thread == NULL)
|
||||
goto out_problem;
|
||||
map = map__new(&machine->user_dsos, event->mmap.start,
|
||||
@ -610,18 +618,26 @@ int perf_event__process_mmap(union perf_event *event,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int perf_event__process_task(union perf_event *event,
|
||||
struct perf_sample *sample __used,
|
||||
struct perf_session *session)
|
||||
size_t perf_event__fprintf_task(union perf_event *event, FILE *fp)
|
||||
{
|
||||
struct thread *thread = perf_session__findnew(session, event->fork.tid);
|
||||
struct thread *parent = perf_session__findnew(session, event->fork.ptid);
|
||||
return fprintf(fp, "(%d:%d):(%d:%d)\n",
|
||||
event->fork.pid, event->fork.tid,
|
||||
event->fork.ppid, event->fork.ptid);
|
||||
}
|
||||
|
||||
dump_printf("(%d:%d):(%d:%d)\n", event->fork.pid, event->fork.tid,
|
||||
event->fork.ppid, event->fork.ptid);
|
||||
int perf_event__process_task(struct perf_tool *tool __used,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __used,
|
||||
struct machine *machine)
|
||||
{
|
||||
struct thread *thread = machine__findnew_thread(machine, event->fork.tid);
|
||||
struct thread *parent = machine__findnew_thread(machine, event->fork.ptid);
|
||||
|
||||
if (dump_trace)
|
||||
perf_event__fprintf_task(event, stdout);
|
||||
|
||||
if (event->header.type == PERF_RECORD_EXIT) {
|
||||
perf_session__remove_thread(session, thread);
|
||||
machine__remove_thread(machine, thread);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -634,22 +650,45 @@ int perf_event__process_task(union perf_event *event,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int perf_event__process(union perf_event *event, struct perf_sample *sample,
|
||||
struct perf_session *session)
|
||||
size_t perf_event__fprintf(union perf_event *event, FILE *fp)
|
||||
{
|
||||
size_t ret = fprintf(fp, "PERF_RECORD_%s",
|
||||
perf_event__name(event->header.type));
|
||||
|
||||
switch (event->header.type) {
|
||||
case PERF_RECORD_COMM:
|
||||
perf_event__process_comm(event, sample, session);
|
||||
break;
|
||||
case PERF_RECORD_MMAP:
|
||||
perf_event__process_mmap(event, sample, session);
|
||||
ret += perf_event__fprintf_comm(event, fp);
|
||||
break;
|
||||
case PERF_RECORD_FORK:
|
||||
case PERF_RECORD_EXIT:
|
||||
perf_event__process_task(event, sample, session);
|
||||
ret += perf_event__fprintf_task(event, fp);
|
||||
break;
|
||||
case PERF_RECORD_MMAP:
|
||||
ret += perf_event__fprintf_mmap(event, fp);
|
||||
break;
|
||||
default:
|
||||
ret += fprintf(fp, "\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int perf_event__process(struct perf_tool *tool, union perf_event *event,
|
||||
struct perf_sample *sample, struct machine *machine)
|
||||
{
|
||||
switch (event->header.type) {
|
||||
case PERF_RECORD_COMM:
|
||||
perf_event__process_comm(tool, event, sample, machine);
|
||||
break;
|
||||
case PERF_RECORD_MMAP:
|
||||
perf_event__process_mmap(tool, event, sample, machine);
|
||||
break;
|
||||
case PERF_RECORD_FORK:
|
||||
case PERF_RECORD_EXIT:
|
||||
perf_event__process_task(tool, event, sample, machine);
|
||||
break;
|
||||
case PERF_RECORD_LOST:
|
||||
perf_event__process_lost(event, sample, session);
|
||||
perf_event__process_lost(tool, event, sample, machine);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -658,36 +697,29 @@ int perf_event__process(union perf_event *event, struct perf_sample *sample,
|
||||
}
|
||||
|
||||
void thread__find_addr_map(struct thread *self,
|
||||
struct perf_session *session, u8 cpumode,
|
||||
enum map_type type, pid_t pid, u64 addr,
|
||||
struct machine *machine, u8 cpumode,
|
||||
enum map_type type, u64 addr,
|
||||
struct addr_location *al)
|
||||
{
|
||||
struct map_groups *mg = &self->mg;
|
||||
struct machine *machine = NULL;
|
||||
|
||||
al->thread = self;
|
||||
al->addr = addr;
|
||||
al->cpumode = cpumode;
|
||||
al->filtered = false;
|
||||
|
||||
if (machine == NULL) {
|
||||
al->map = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (cpumode == PERF_RECORD_MISC_KERNEL && perf_host) {
|
||||
al->level = 'k';
|
||||
machine = perf_session__find_host_machine(session);
|
||||
if (machine == NULL) {
|
||||
al->map = NULL;
|
||||
return;
|
||||
}
|
||||
mg = &machine->kmaps;
|
||||
} else if (cpumode == PERF_RECORD_MISC_USER && perf_host) {
|
||||
al->level = '.';
|
||||
machine = perf_session__find_host_machine(session);
|
||||
} else if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL && perf_guest) {
|
||||
al->level = 'g';
|
||||
machine = perf_session__find_machine(session, pid);
|
||||
if (machine == NULL) {
|
||||
al->map = NULL;
|
||||
return;
|
||||
}
|
||||
mg = &machine->kmaps;
|
||||
} else {
|
||||
/*
|
||||
@ -733,13 +765,12 @@ void thread__find_addr_map(struct thread *self,
|
||||
al->addr = al->map->map_ip(al->map, al->addr);
|
||||
}
|
||||
|
||||
void thread__find_addr_location(struct thread *self,
|
||||
struct perf_session *session, u8 cpumode,
|
||||
enum map_type type, pid_t pid, u64 addr,
|
||||
void thread__find_addr_location(struct thread *thread, struct machine *machine,
|
||||
u8 cpumode, enum map_type type, u64 addr,
|
||||
struct addr_location *al,
|
||||
symbol_filter_t filter)
|
||||
{
|
||||
thread__find_addr_map(self, session, cpumode, type, pid, addr, al);
|
||||
thread__find_addr_map(thread, machine, cpumode, type, addr, al);
|
||||
if (al->map != NULL)
|
||||
al->sym = map__find_symbol(al->map, al->addr, filter);
|
||||
else
|
||||
@ -747,13 +778,13 @@ void thread__find_addr_location(struct thread *self,
|
||||
}
|
||||
|
||||
int perf_event__preprocess_sample(const union perf_event *event,
|
||||
struct perf_session *session,
|
||||
struct machine *machine,
|
||||
struct addr_location *al,
|
||||
struct perf_sample *sample,
|
||||
symbol_filter_t filter)
|
||||
{
|
||||
u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
|
||||
struct thread *thread = perf_session__findnew(session, event->ip.pid);
|
||||
struct thread *thread = machine__findnew_thread(machine, event->ip.pid);
|
||||
|
||||
if (thread == NULL)
|
||||
return -1;
|
||||
@ -764,18 +795,18 @@ int perf_event__preprocess_sample(const union perf_event *event,
|
||||
|
||||
dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
|
||||
/*
|
||||
* Have we already created the kernel maps for the host machine?
|
||||
* Have we already created the kernel maps for this machine?
|
||||
*
|
||||
* This should have happened earlier, when we processed the kernel MMAP
|
||||
* events, but for older perf.data files there was no such thing, so do
|
||||
* it now.
|
||||
*/
|
||||
if (cpumode == PERF_RECORD_MISC_KERNEL &&
|
||||
session->host_machine.vmlinux_maps[MAP__FUNCTION] == NULL)
|
||||
machine__create_kernel_maps(&session->host_machine);
|
||||
machine->vmlinux_maps[MAP__FUNCTION] == NULL)
|
||||
machine__create_kernel_maps(machine);
|
||||
|
||||
thread__find_addr_map(thread, session, cpumode, MAP__FUNCTION,
|
||||
event->ip.pid, event->ip.ip, al);
|
||||
thread__find_addr_map(thread, machine, cpumode, MAP__FUNCTION,
|
||||
event->ip.ip, al);
|
||||
dump_printf(" ...... dso: %s\n",
|
||||
al->map ? al->map->dso->long_name :
|
||||
al->level == 'H' ? "[hypervisor]" : "<not found>");
|
||||
|
@ -2,6 +2,7 @@
|
||||
#define __PERF_RECORD_H
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "../perf.h"
|
||||
#include "map.h"
|
||||
@ -141,43 +142,54 @@ union perf_event {
|
||||
|
||||
void perf_event__print_totals(void);
|
||||
|
||||
struct perf_session;
|
||||
struct perf_tool;
|
||||
struct thread_map;
|
||||
|
||||
typedef int (*perf_event__handler_synth_t)(union perf_event *event,
|
||||
struct perf_session *session);
|
||||
typedef int (*perf_event__handler_t)(union perf_event *event,
|
||||
typedef int (*perf_event__handler_t)(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct perf_session *session);
|
||||
struct machine *machine);
|
||||
|
||||
int perf_event__synthesize_thread_map(struct thread_map *threads,
|
||||
int perf_event__synthesize_thread_map(struct perf_tool *tool,
|
||||
struct thread_map *threads,
|
||||
perf_event__handler_t process,
|
||||
struct perf_session *session);
|
||||
int perf_event__synthesize_threads(perf_event__handler_t process,
|
||||
struct perf_session *session);
|
||||
int perf_event__synthesize_kernel_mmap(perf_event__handler_t process,
|
||||
struct perf_session *session,
|
||||
struct machine *machine);
|
||||
int perf_event__synthesize_threads(struct perf_tool *tool,
|
||||
perf_event__handler_t process,
|
||||
struct machine *machine);
|
||||
int perf_event__synthesize_kernel_mmap(struct perf_tool *tool,
|
||||
perf_event__handler_t process,
|
||||
struct machine *machine,
|
||||
const char *symbol_name);
|
||||
|
||||
int perf_event__synthesize_modules(perf_event__handler_t process,
|
||||
struct perf_session *session,
|
||||
int perf_event__synthesize_modules(struct perf_tool *tool,
|
||||
perf_event__handler_t process,
|
||||
struct machine *machine);
|
||||
|
||||
int perf_event__process_comm(union perf_event *event, struct perf_sample *sample,
|
||||
struct perf_session *session);
|
||||
int perf_event__process_lost(union perf_event *event, struct perf_sample *sample,
|
||||
struct perf_session *session);
|
||||
int perf_event__process_mmap(union perf_event *event, struct perf_sample *sample,
|
||||
struct perf_session *session);
|
||||
int perf_event__process_task(union perf_event *event, struct perf_sample *sample,
|
||||
struct perf_session *session);
|
||||
int perf_event__process(union perf_event *event, struct perf_sample *sample,
|
||||
struct perf_session *session);
|
||||
int perf_event__process_comm(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine);
|
||||
int perf_event__process_lost(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine);
|
||||
int perf_event__process_mmap(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine);
|
||||
int perf_event__process_task(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine);
|
||||
int perf_event__process(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine);
|
||||
|
||||
struct addr_location;
|
||||
int perf_event__preprocess_sample(const union perf_event *self,
|
||||
struct perf_session *session,
|
||||
struct machine *machine,
|
||||
struct addr_location *al,
|
||||
struct perf_sample *sample,
|
||||
symbol_filter_t filter);
|
||||
@ -188,4 +200,9 @@ int perf_event__parse_sample(const union perf_event *event, u64 type,
|
||||
int sample_size, bool sample_id_all,
|
||||
struct perf_sample *sample, bool swapped);
|
||||
|
||||
size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp);
|
||||
size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp);
|
||||
size_t perf_event__fprintf_task(union perf_event *event, FILE *fp);
|
||||
size_t perf_event__fprintf(union perf_event *event, FILE *fp);
|
||||
|
||||
#endif /* __PERF_RECORD_H */
|
||||
|
@ -6,12 +6,16 @@
|
||||
*
|
||||
* Released under the GPL v2. (and only v2, not any later version)
|
||||
*/
|
||||
#include "util.h"
|
||||
#include "debugfs.h"
|
||||
#include <poll.h>
|
||||
#include "cpumap.h"
|
||||
#include "thread_map.h"
|
||||
#include "evlist.h"
|
||||
#include "evsel.h"
|
||||
#include "util.h"
|
||||
#include <unistd.h>
|
||||
|
||||
#include "parse-events.h"
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
@ -30,6 +34,7 @@ void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus,
|
||||
INIT_HLIST_HEAD(&evlist->heads[i]);
|
||||
INIT_LIST_HEAD(&evlist->entries);
|
||||
perf_evlist__set_maps(evlist, cpus, threads);
|
||||
evlist->workload.pid = -1;
|
||||
}
|
||||
|
||||
struct perf_evlist *perf_evlist__new(struct cpu_map *cpus,
|
||||
@ -43,6 +48,22 @@ struct perf_evlist *perf_evlist__new(struct cpu_map *cpus,
|
||||
return evlist;
|
||||
}
|
||||
|
||||
void perf_evlist__config_attrs(struct perf_evlist *evlist,
|
||||
struct perf_record_opts *opts)
|
||||
{
|
||||
struct perf_evsel *evsel;
|
||||
|
||||
if (evlist->cpus->map[0] < 0)
|
||||
opts->no_inherit = true;
|
||||
|
||||
list_for_each_entry(evsel, &evlist->entries, node) {
|
||||
perf_evsel__config(evsel, opts);
|
||||
|
||||
if (evlist->nr_entries > 1)
|
||||
evsel->attr.sample_type |= PERF_SAMPLE_ID;
|
||||
}
|
||||
}
|
||||
|
||||
static void perf_evlist__purge(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *pos, *n;
|
||||
@ -76,6 +97,14 @@ void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry)
|
||||
++evlist->nr_entries;
|
||||
}
|
||||
|
||||
static void perf_evlist__splice_list_tail(struct perf_evlist *evlist,
|
||||
struct list_head *list,
|
||||
int nr_entries)
|
||||
{
|
||||
list_splice_tail(list, &evlist->entries);
|
||||
evlist->nr_entries += nr_entries;
|
||||
}
|
||||
|
||||
int perf_evlist__add_default(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_event_attr attr = {
|
||||
@ -100,6 +129,126 @@ int perf_evlist__add_default(struct perf_evlist *evlist)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
int perf_evlist__add_attrs(struct perf_evlist *evlist,
|
||||
struct perf_event_attr *attrs, size_t nr_attrs)
|
||||
{
|
||||
struct perf_evsel *evsel, *n;
|
||||
LIST_HEAD(head);
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < nr_attrs; i++) {
|
||||
evsel = perf_evsel__new(attrs + i, evlist->nr_entries + i);
|
||||
if (evsel == NULL)
|
||||
goto out_delete_partial_list;
|
||||
list_add_tail(&evsel->node, &head);
|
||||
}
|
||||
|
||||
perf_evlist__splice_list_tail(evlist, &head, nr_attrs);
|
||||
|
||||
return 0;
|
||||
|
||||
out_delete_partial_list:
|
||||
list_for_each_entry_safe(evsel, n, &head, node)
|
||||
perf_evsel__delete(evsel);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int trace_event__id(const char *evname)
|
||||
{
|
||||
char *filename, *colon;
|
||||
int err = -1, fd;
|
||||
|
||||
if (asprintf(&filename, "%s/%s/id", tracing_events_path, evname) < 0)
|
||||
return -1;
|
||||
|
||||
colon = strrchr(filename, ':');
|
||||
if (colon != NULL)
|
||||
*colon = '/';
|
||||
|
||||
fd = open(filename, O_RDONLY);
|
||||
if (fd >= 0) {
|
||||
char id[16];
|
||||
if (read(fd, id, sizeof(id)) > 0)
|
||||
err = atoi(id);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
free(filename);
|
||||
return err;
|
||||
}
|
||||
|
||||
int perf_evlist__add_tracepoints(struct perf_evlist *evlist,
|
||||
const char *tracepoints[],
|
||||
size_t nr_tracepoints)
|
||||
{
|
||||
int err;
|
||||
size_t i;
|
||||
struct perf_event_attr *attrs = zalloc(nr_tracepoints * sizeof(*attrs));
|
||||
|
||||
if (attrs == NULL)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < nr_tracepoints; i++) {
|
||||
err = trace_event__id(tracepoints[i]);
|
||||
|
||||
if (err < 0)
|
||||
goto out_free_attrs;
|
||||
|
||||
attrs[i].type = PERF_TYPE_TRACEPOINT;
|
||||
attrs[i].config = err;
|
||||
attrs[i].sample_type = (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME |
|
||||
PERF_SAMPLE_CPU);
|
||||
attrs[i].sample_period = 1;
|
||||
}
|
||||
|
||||
err = perf_evlist__add_attrs(evlist, attrs, nr_tracepoints);
|
||||
out_free_attrs:
|
||||
free(attrs);
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct perf_evsel *
|
||||
perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id)
|
||||
{
|
||||
struct perf_evsel *evsel;
|
||||
|
||||
list_for_each_entry(evsel, &evlist->entries, node) {
|
||||
if (evsel->attr.type == PERF_TYPE_TRACEPOINT &&
|
||||
(int)evsel->attr.config == id)
|
||||
return evsel;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int perf_evlist__set_tracepoints_handlers(struct perf_evlist *evlist,
|
||||
const struct perf_evsel_str_handler *assocs,
|
||||
size_t nr_assocs)
|
||||
{
|
||||
struct perf_evsel *evsel;
|
||||
int err;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < nr_assocs; i++) {
|
||||
err = trace_event__id(assocs[i].name);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
evsel = perf_evlist__find_tracepoint_by_id(evlist, err);
|
||||
if (evsel == NULL)
|
||||
continue;
|
||||
|
||||
err = -EEXIST;
|
||||
if (evsel->handler.func != NULL)
|
||||
goto out;
|
||||
evsel->handler.func = assocs[i].handler;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
void perf_evlist__disable(struct perf_evlist *evlist)
|
||||
{
|
||||
int cpu, thread;
|
||||
@ -126,7 +275,7 @@ void perf_evlist__enable(struct perf_evlist *evlist)
|
||||
}
|
||||
}
|
||||
|
||||
int perf_evlist__alloc_pollfd(struct perf_evlist *evlist)
|
||||
static int perf_evlist__alloc_pollfd(struct perf_evlist *evlist)
|
||||
{
|
||||
int nfds = evlist->cpus->nr * evlist->threads->nr * evlist->nr_entries;
|
||||
evlist->pollfd = malloc(sizeof(struct pollfd) * nfds);
|
||||
@ -282,7 +431,7 @@ void perf_evlist__munmap(struct perf_evlist *evlist)
|
||||
evlist->mmap = NULL;
|
||||
}
|
||||
|
||||
int perf_evlist__alloc_mmap(struct perf_evlist *evlist)
|
||||
static int perf_evlist__alloc_mmap(struct perf_evlist *evlist)
|
||||
{
|
||||
evlist->nr_mmaps = evlist->cpus->nr;
|
||||
if (evlist->cpus->map[0] == -1)
|
||||
@ -400,14 +549,20 @@ static int perf_evlist__mmap_per_thread(struct perf_evlist *evlist, int prot, in
|
||||
*
|
||||
* Using perf_evlist__read_on_cpu does this automatically.
|
||||
*/
|
||||
int perf_evlist__mmap(struct perf_evlist *evlist, int pages, bool overwrite)
|
||||
int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,
|
||||
bool overwrite)
|
||||
{
|
||||
unsigned int page_size = sysconf(_SC_PAGE_SIZE);
|
||||
int mask = pages * page_size - 1;
|
||||
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);
|
||||
int prot = PROT_READ | (overwrite ? 0 : PROT_WRITE), mask;
|
||||
|
||||
/* 512 kiB: default amount of unprivileged mlocked memory */
|
||||
if (pages == UINT_MAX)
|
||||
pages = (512 * 1024) / page_size;
|
||||
|
||||
mask = pages * page_size - 1;
|
||||
|
||||
if (evlist->mmap == NULL && perf_evlist__alloc_mmap(evlist) < 0)
|
||||
return -ENOMEM;
|
||||
@ -512,6 +667,38 @@ u64 perf_evlist__sample_type(const struct perf_evlist *evlist)
|
||||
return first->attr.sample_type;
|
||||
}
|
||||
|
||||
u16 perf_evlist__id_hdr_size(const struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *first;
|
||||
struct perf_sample *data;
|
||||
u64 sample_type;
|
||||
u16 size = 0;
|
||||
|
||||
first = list_entry(evlist->entries.next, struct perf_evsel, node);
|
||||
|
||||
if (!first->attr.sample_id_all)
|
||||
goto out;
|
||||
|
||||
sample_type = first->attr.sample_type;
|
||||
|
||||
if (sample_type & PERF_SAMPLE_TID)
|
||||
size += sizeof(data->tid) * 2;
|
||||
|
||||
if (sample_type & PERF_SAMPLE_TIME)
|
||||
size += sizeof(data->time);
|
||||
|
||||
if (sample_type & PERF_SAMPLE_ID)
|
||||
size += sizeof(data->id);
|
||||
|
||||
if (sample_type & PERF_SAMPLE_STREAM_ID)
|
||||
size += sizeof(data->stream_id);
|
||||
|
||||
if (sample_type & PERF_SAMPLE_CPU)
|
||||
size += sizeof(data->cpu) * 2;
|
||||
out:
|
||||
return size;
|
||||
}
|
||||
|
||||
bool perf_evlist__valid_sample_id_all(const struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *pos, *first;
|
||||
@ -569,3 +756,97 @@ int perf_evlist__open(struct perf_evlist *evlist, bool group)
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int perf_evlist__prepare_workload(struct perf_evlist *evlist,
|
||||
struct perf_record_opts *opts,
|
||||
const char *argv[])
|
||||
{
|
||||
int child_ready_pipe[2], go_pipe[2];
|
||||
char bf;
|
||||
|
||||
if (pipe(child_ready_pipe) < 0) {
|
||||
perror("failed to create 'ready' pipe");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pipe(go_pipe) < 0) {
|
||||
perror("failed to create 'go' pipe");
|
||||
goto out_close_ready_pipe;
|
||||
}
|
||||
|
||||
evlist->workload.pid = fork();
|
||||
if (evlist->workload.pid < 0) {
|
||||
perror("failed to fork");
|
||||
goto out_close_pipes;
|
||||
}
|
||||
|
||||
if (!evlist->workload.pid) {
|
||||
if (opts->pipe_output)
|
||||
dup2(2, 1);
|
||||
|
||||
close(child_ready_pipe[0]);
|
||||
close(go_pipe[1]);
|
||||
fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC);
|
||||
|
||||
/*
|
||||
* Do a dummy execvp to get the PLT entry resolved,
|
||||
* so we avoid the resolver overhead on the real
|
||||
* execvp call.
|
||||
*/
|
||||
execvp("", (char **)argv);
|
||||
|
||||
/*
|
||||
* Tell the parent we're ready to go
|
||||
*/
|
||||
close(child_ready_pipe[1]);
|
||||
|
||||
/*
|
||||
* Wait until the parent tells us to go.
|
||||
*/
|
||||
if (read(go_pipe[0], &bf, 1) == -1)
|
||||
perror("unable to read pipe");
|
||||
|
||||
execvp(argv[0], (char **)argv);
|
||||
|
||||
perror(argv[0]);
|
||||
kill(getppid(), SIGUSR1);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (!opts->system_wide && opts->target_tid == -1 && opts->target_pid == -1)
|
||||
evlist->threads->map[0] = evlist->workload.pid;
|
||||
|
||||
close(child_ready_pipe[1]);
|
||||
close(go_pipe[0]);
|
||||
/*
|
||||
* wait for child to settle
|
||||
*/
|
||||
if (read(child_ready_pipe[0], &bf, 1) == -1) {
|
||||
perror("unable to read pipe");
|
||||
goto out_close_pipes;
|
||||
}
|
||||
|
||||
evlist->workload.cork_fd = go_pipe[1];
|
||||
close(child_ready_pipe[0]);
|
||||
return 0;
|
||||
|
||||
out_close_pipes:
|
||||
close(go_pipe[0]);
|
||||
close(go_pipe[1]);
|
||||
out_close_ready_pipe:
|
||||
close(child_ready_pipe[0]);
|
||||
close(child_ready_pipe[1]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int perf_evlist__start_workload(struct perf_evlist *evlist)
|
||||
{
|
||||
if (evlist->workload.cork_fd > 0) {
|
||||
/*
|
||||
* Remove the cork, let it rip!
|
||||
*/
|
||||
return close(evlist->workload.cork_fd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2,12 +2,16 @@
|
||||
#define __PERF_EVLIST_H 1
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <stdio.h>
|
||||
#include "../perf.h"
|
||||
#include "event.h"
|
||||
#include "util.h"
|
||||
#include <unistd.h>
|
||||
|
||||
struct pollfd;
|
||||
struct thread_map;
|
||||
struct cpu_map;
|
||||
struct perf_record_opts;
|
||||
|
||||
#define PERF_EVLIST__HLIST_BITS 8
|
||||
#define PERF_EVLIST__HLIST_SIZE (1 << PERF_EVLIST__HLIST_BITS)
|
||||
@ -19,6 +23,10 @@ struct perf_evlist {
|
||||
int nr_fds;
|
||||
int nr_mmaps;
|
||||
int mmap_len;
|
||||
struct {
|
||||
int cork_fd;
|
||||
pid_t pid;
|
||||
} workload;
|
||||
bool overwrite;
|
||||
union perf_event event_copy;
|
||||
struct perf_mmap *mmap;
|
||||
@ -28,6 +36,11 @@ struct perf_evlist {
|
||||
struct perf_evsel *selected;
|
||||
};
|
||||
|
||||
struct perf_evsel_str_handler {
|
||||
const char *name;
|
||||
void *handler;
|
||||
};
|
||||
|
||||
struct perf_evsel;
|
||||
|
||||
struct perf_evlist *perf_evlist__new(struct cpu_map *cpus,
|
||||
@ -39,11 +52,26 @@ void perf_evlist__delete(struct perf_evlist *evlist);
|
||||
|
||||
void perf_evlist__add(struct perf_evlist *evlist, struct perf_evsel *entry);
|
||||
int perf_evlist__add_default(struct perf_evlist *evlist);
|
||||
int perf_evlist__add_attrs(struct perf_evlist *evlist,
|
||||
struct perf_event_attr *attrs, size_t nr_attrs);
|
||||
int perf_evlist__add_tracepoints(struct perf_evlist *evlist,
|
||||
const char *tracepoints[], size_t nr_tracepoints);
|
||||
int perf_evlist__set_tracepoints_handlers(struct perf_evlist *evlist,
|
||||
const struct perf_evsel_str_handler *assocs,
|
||||
size_t nr_assocs);
|
||||
|
||||
#define perf_evlist__add_attrs_array(evlist, array) \
|
||||
perf_evlist__add_attrs(evlist, array, ARRAY_SIZE(array))
|
||||
|
||||
#define perf_evlist__add_tracepoints_array(evlist, array) \
|
||||
perf_evlist__add_tracepoints(evlist, array, ARRAY_SIZE(array))
|
||||
|
||||
#define perf_evlist__set_tracepoints_handlers_array(evlist, array) \
|
||||
perf_evlist__set_tracepoints_handlers(evlist, array, ARRAY_SIZE(array))
|
||||
|
||||
void perf_evlist__id_add(struct perf_evlist *evlist, struct perf_evsel *evsel,
|
||||
int cpu, int thread, u64 id);
|
||||
|
||||
int perf_evlist__alloc_pollfd(struct perf_evlist *evlist);
|
||||
void perf_evlist__add_pollfd(struct perf_evlist *evlist, int fd);
|
||||
|
||||
struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id);
|
||||
@ -52,8 +80,16 @@ union perf_event *perf_evlist__mmap_read(struct perf_evlist *self, int idx);
|
||||
|
||||
int perf_evlist__open(struct perf_evlist *evlist, bool group);
|
||||
|
||||
int perf_evlist__alloc_mmap(struct perf_evlist *evlist);
|
||||
int perf_evlist__mmap(struct perf_evlist *evlist, int pages, bool overwrite);
|
||||
void perf_evlist__config_attrs(struct perf_evlist *evlist,
|
||||
struct perf_record_opts *opts);
|
||||
|
||||
int perf_evlist__prepare_workload(struct perf_evlist *evlist,
|
||||
struct perf_record_opts *opts,
|
||||
const char *argv[]);
|
||||
int perf_evlist__start_workload(struct perf_evlist *evlist);
|
||||
|
||||
int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,
|
||||
bool overwrite);
|
||||
void perf_evlist__munmap(struct perf_evlist *evlist);
|
||||
|
||||
void perf_evlist__disable(struct perf_evlist *evlist);
|
||||
@ -77,6 +113,7 @@ int perf_evlist__set_filters(struct perf_evlist *evlist);
|
||||
|
||||
u64 perf_evlist__sample_type(const struct perf_evlist *evlist);
|
||||
bool perf_evlist__sample_id_all(const const struct perf_evlist *evlist);
|
||||
u16 perf_evlist__id_hdr_size(const struct perf_evlist *evlist);
|
||||
|
||||
bool perf_evlist__valid_sample_type(const struct perf_evlist *evlist);
|
||||
bool perf_evlist__valid_sample_id_all(const struct perf_evlist *evlist);
|
||||
|
@ -63,6 +63,76 @@ struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx)
|
||||
return evsel;
|
||||
}
|
||||
|
||||
void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts)
|
||||
{
|
||||
struct perf_event_attr *attr = &evsel->attr;
|
||||
int track = !evsel->idx; /* only the first counter needs these */
|
||||
|
||||
attr->sample_id_all = opts->sample_id_all_avail ? 1 : 0;
|
||||
attr->inherit = !opts->no_inherit;
|
||||
attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED |
|
||||
PERF_FORMAT_TOTAL_TIME_RUNNING |
|
||||
PERF_FORMAT_ID;
|
||||
|
||||
attr->sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID;
|
||||
|
||||
/*
|
||||
* We default some events to a 1 default interval. But keep
|
||||
* it a weak assumption overridable by the user.
|
||||
*/
|
||||
if (!attr->sample_period || (opts->user_freq != UINT_MAX &&
|
||||
opts->user_interval != ULLONG_MAX)) {
|
||||
if (opts->freq) {
|
||||
attr->sample_type |= PERF_SAMPLE_PERIOD;
|
||||
attr->freq = 1;
|
||||
attr->sample_freq = opts->freq;
|
||||
} else {
|
||||
attr->sample_period = opts->default_interval;
|
||||
}
|
||||
}
|
||||
|
||||
if (opts->no_samples)
|
||||
attr->sample_freq = 0;
|
||||
|
||||
if (opts->inherit_stat)
|
||||
attr->inherit_stat = 1;
|
||||
|
||||
if (opts->sample_address) {
|
||||
attr->sample_type |= PERF_SAMPLE_ADDR;
|
||||
attr->mmap_data = track;
|
||||
}
|
||||
|
||||
if (opts->call_graph)
|
||||
attr->sample_type |= PERF_SAMPLE_CALLCHAIN;
|
||||
|
||||
if (opts->system_wide)
|
||||
attr->sample_type |= PERF_SAMPLE_CPU;
|
||||
|
||||
if (opts->sample_id_all_avail &&
|
||||
(opts->sample_time || opts->system_wide ||
|
||||
!opts->no_inherit || opts->cpu_list))
|
||||
attr->sample_type |= PERF_SAMPLE_TIME;
|
||||
|
||||
if (opts->raw_samples) {
|
||||
attr->sample_type |= PERF_SAMPLE_TIME;
|
||||
attr->sample_type |= PERF_SAMPLE_RAW;
|
||||
attr->sample_type |= PERF_SAMPLE_CPU;
|
||||
}
|
||||
|
||||
if (opts->no_delay) {
|
||||
attr->watermark = 0;
|
||||
attr->wakeup_events = 1;
|
||||
}
|
||||
|
||||
attr->mmap = track;
|
||||
attr->comm = track;
|
||||
|
||||
if (opts->target_pid == -1 && opts->target_tid == -1 && !opts->system_wide) {
|
||||
attr->disabled = 1;
|
||||
attr->enable_on_exec = 1;
|
||||
}
|
||||
}
|
||||
|
||||
int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
|
||||
{
|
||||
int cpu, thread;
|
||||
|
@ -61,12 +61,17 @@ struct perf_evsel {
|
||||
off_t id_offset;
|
||||
};
|
||||
struct cgroup_sel *cgrp;
|
||||
struct {
|
||||
void *func;
|
||||
void *data;
|
||||
} handler;
|
||||
bool supported;
|
||||
};
|
||||
|
||||
struct cpu_map;
|
||||
struct thread_map;
|
||||
struct perf_evlist;
|
||||
struct perf_record_opts;
|
||||
|
||||
struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx);
|
||||
void perf_evsel__init(struct perf_evsel *evsel,
|
||||
@ -74,6 +79,9 @@ void perf_evsel__init(struct perf_evsel *evsel,
|
||||
void perf_evsel__exit(struct perf_evsel *evsel);
|
||||
void perf_evsel__delete(struct perf_evsel *evsel);
|
||||
|
||||
void perf_evsel__config(struct perf_evsel *evsel,
|
||||
struct perf_record_opts *opts);
|
||||
|
||||
int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads);
|
||||
int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads);
|
||||
int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus);
|
||||
|
@ -2041,6 +2041,8 @@ int perf_session__read_header(struct perf_session *session, int fd)
|
||||
lseek(fd, tmp, SEEK_SET);
|
||||
}
|
||||
|
||||
symbol_conf.nr_events = nr_attrs;
|
||||
|
||||
if (f_header.event_types.size) {
|
||||
lseek(fd, f_header.event_types.offset, SEEK_SET);
|
||||
events = malloc(f_header.event_types.size);
|
||||
@ -2068,9 +2070,9 @@ int perf_session__read_header(struct perf_session *session, int fd)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
int perf_event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id,
|
||||
perf_event__handler_t process,
|
||||
struct perf_session *session)
|
||||
int perf_event__synthesize_attr(struct perf_tool *tool,
|
||||
struct perf_event_attr *attr, u16 ids, u64 *id,
|
||||
perf_event__handler_t process)
|
||||
{
|
||||
union perf_event *ev;
|
||||
size_t size;
|
||||
@ -2092,22 +2094,23 @@ int perf_event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id,
|
||||
ev->attr.header.type = PERF_RECORD_HEADER_ATTR;
|
||||
ev->attr.header.size = size;
|
||||
|
||||
err = process(ev, NULL, session);
|
||||
err = process(tool, ev, NULL, NULL);
|
||||
|
||||
free(ev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int perf_session__synthesize_attrs(struct perf_session *session,
|
||||
int perf_event__synthesize_attrs(struct perf_tool *tool,
|
||||
struct perf_session *session,
|
||||
perf_event__handler_t process)
|
||||
{
|
||||
struct perf_evsel *attr;
|
||||
int err = 0;
|
||||
|
||||
list_for_each_entry(attr, &session->evlist->entries, node) {
|
||||
err = perf_event__synthesize_attr(&attr->attr, attr->ids,
|
||||
attr->id, process, session);
|
||||
err = perf_event__synthesize_attr(tool, &attr->attr, attr->ids,
|
||||
attr->id, process);
|
||||
if (err) {
|
||||
pr_debug("failed to create perf header attribute\n");
|
||||
return err;
|
||||
@ -2118,23 +2121,23 @@ int perf_session__synthesize_attrs(struct perf_session *session,
|
||||
}
|
||||
|
||||
int perf_event__process_attr(union perf_event *event,
|
||||
struct perf_session *session)
|
||||
struct perf_evlist **pevlist)
|
||||
{
|
||||
unsigned int i, ids, n_ids;
|
||||
struct perf_evsel *evsel;
|
||||
struct perf_evlist *evlist = *pevlist;
|
||||
|
||||
if (session->evlist == NULL) {
|
||||
session->evlist = perf_evlist__new(NULL, NULL);
|
||||
if (session->evlist == NULL)
|
||||
if (evlist == NULL) {
|
||||
*pevlist = evlist = perf_evlist__new(NULL, NULL);
|
||||
if (evlist == NULL)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
evsel = perf_evsel__new(&event->attr.attr,
|
||||
session->evlist->nr_entries);
|
||||
evsel = perf_evsel__new(&event->attr.attr, evlist->nr_entries);
|
||||
if (evsel == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
perf_evlist__add(session->evlist, evsel);
|
||||
perf_evlist__add(evlist, evsel);
|
||||
|
||||
ids = event->header.size;
|
||||
ids -= (void *)&event->attr.id - (void *)event;
|
||||
@ -2148,18 +2151,16 @@ int perf_event__process_attr(union perf_event *event,
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < n_ids; i++) {
|
||||
perf_evlist__id_add(session->evlist, evsel, 0, i,
|
||||
event->attr.id[i]);
|
||||
perf_evlist__id_add(evlist, evsel, 0, i, event->attr.id[i]);
|
||||
}
|
||||
|
||||
perf_session__update_sample_type(session);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int perf_event__synthesize_event_type(u64 event_id, char *name,
|
||||
int perf_event__synthesize_event_type(struct perf_tool *tool,
|
||||
u64 event_id, char *name,
|
||||
perf_event__handler_t process,
|
||||
struct perf_session *session)
|
||||
struct machine *machine)
|
||||
{
|
||||
union perf_event ev;
|
||||
size_t size = 0;
|
||||
@ -2177,13 +2178,14 @@ int perf_event__synthesize_event_type(u64 event_id, char *name,
|
||||
ev.event_type.header.size = sizeof(ev.event_type) -
|
||||
(sizeof(ev.event_type.event_type.name) - size);
|
||||
|
||||
err = process(&ev, NULL, session);
|
||||
err = process(tool, &ev, NULL, machine);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int perf_event__synthesize_event_types(perf_event__handler_t process,
|
||||
struct perf_session *session)
|
||||
int perf_event__synthesize_event_types(struct perf_tool *tool,
|
||||
perf_event__handler_t process,
|
||||
struct machine *machine)
|
||||
{
|
||||
struct perf_trace_event_type *type;
|
||||
int i, err = 0;
|
||||
@ -2191,9 +2193,9 @@ int perf_event__synthesize_event_types(perf_event__handler_t process,
|
||||
for (i = 0; i < event_count; i++) {
|
||||
type = &events[i];
|
||||
|
||||
err = perf_event__synthesize_event_type(type->event_id,
|
||||
err = perf_event__synthesize_event_type(tool, type->event_id,
|
||||
type->name, process,
|
||||
session);
|
||||
machine);
|
||||
if (err) {
|
||||
pr_debug("failed to create perf header event type\n");
|
||||
return err;
|
||||
@ -2203,8 +2205,8 @@ int perf_event__synthesize_event_types(perf_event__handler_t process,
|
||||
return err;
|
||||
}
|
||||
|
||||
int perf_event__process_event_type(union perf_event *event,
|
||||
struct perf_session *session __unused)
|
||||
int perf_event__process_event_type(struct perf_tool *tool __unused,
|
||||
union perf_event *event)
|
||||
{
|
||||
if (perf_header__push_event(event->event_type.event_type.event_id,
|
||||
event->event_type.event_type.name) < 0)
|
||||
@ -2213,9 +2215,9 @@ int perf_event__process_event_type(union perf_event *event,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int perf_event__synthesize_tracing_data(int fd, struct perf_evlist *evlist,
|
||||
perf_event__handler_t process,
|
||||
struct perf_session *session __unused)
|
||||
int perf_event__synthesize_tracing_data(struct perf_tool *tool, int fd,
|
||||
struct perf_evlist *evlist,
|
||||
perf_event__handler_t process)
|
||||
{
|
||||
union perf_event ev;
|
||||
struct tracing_data *tdata;
|
||||
@ -2246,7 +2248,7 @@ int perf_event__synthesize_tracing_data(int fd, struct perf_evlist *evlist,
|
||||
ev.tracing_data.header.size = sizeof(ev.tracing_data);
|
||||
ev.tracing_data.size = aligned_size;
|
||||
|
||||
process(&ev, NULL, session);
|
||||
process(tool, &ev, NULL, NULL);
|
||||
|
||||
/*
|
||||
* The put function will copy all the tracing data
|
||||
@ -2288,10 +2290,10 @@ int perf_event__process_tracing_data(union perf_event *event,
|
||||
return size_read + padding;
|
||||
}
|
||||
|
||||
int perf_event__synthesize_build_id(struct dso *pos, u16 misc,
|
||||
int perf_event__synthesize_build_id(struct perf_tool *tool,
|
||||
struct dso *pos, u16 misc,
|
||||
perf_event__handler_t process,
|
||||
struct machine *machine,
|
||||
struct perf_session *session)
|
||||
struct machine *machine)
|
||||
{
|
||||
union perf_event ev;
|
||||
size_t len;
|
||||
@ -2311,12 +2313,13 @@ int perf_event__synthesize_build_id(struct dso *pos, u16 misc,
|
||||
ev.build_id.header.size = sizeof(ev.build_id) + len;
|
||||
memcpy(&ev.build_id.filename, pos->long_name, pos->long_name_len);
|
||||
|
||||
err = process(&ev, NULL, session);
|
||||
err = process(tool, &ev, NULL, machine);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int perf_event__process_build_id(union perf_event *event,
|
||||
int perf_event__process_build_id(struct perf_tool *tool __used,
|
||||
union perf_event *event,
|
||||
struct perf_session *session)
|
||||
{
|
||||
__event_process_build_id(&event->build_id,
|
||||
|
@ -68,6 +68,7 @@ struct perf_header {
|
||||
};
|
||||
|
||||
struct perf_evlist;
|
||||
struct perf_session;
|
||||
|
||||
int perf_session__read_header(struct perf_session *session, int fd);
|
||||
int perf_session__write_header(struct perf_session *session,
|
||||
@ -96,32 +97,36 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
|
||||
const char *name, bool is_kallsyms);
|
||||
int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir);
|
||||
|
||||
int perf_event__synthesize_attr(struct perf_event_attr *attr, u16 ids, u64 *id,
|
||||
perf_event__handler_t process,
|
||||
struct perf_session *session);
|
||||
int perf_session__synthesize_attrs(struct perf_session *session,
|
||||
perf_event__handler_t process);
|
||||
int perf_event__process_attr(union perf_event *event, struct perf_session *session);
|
||||
int perf_event__synthesize_attr(struct perf_tool *tool,
|
||||
struct perf_event_attr *attr, u16 ids, u64 *id,
|
||||
perf_event__handler_t process);
|
||||
int perf_event__synthesize_attrs(struct perf_tool *tool,
|
||||
struct perf_session *session,
|
||||
perf_event__handler_t process);
|
||||
int perf_event__process_attr(union perf_event *event, struct perf_evlist **pevlist);
|
||||
|
||||
int perf_event__synthesize_event_type(u64 event_id, char *name,
|
||||
int perf_event__synthesize_event_type(struct perf_tool *tool,
|
||||
u64 event_id, char *name,
|
||||
perf_event__handler_t process,
|
||||
struct perf_session *session);
|
||||
int perf_event__synthesize_event_types(perf_event__handler_t process,
|
||||
struct perf_session *session);
|
||||
int perf_event__process_event_type(union perf_event *event,
|
||||
struct perf_session *session);
|
||||
struct machine *machine);
|
||||
int perf_event__synthesize_event_types(struct perf_tool *tool,
|
||||
perf_event__handler_t process,
|
||||
struct machine *machine);
|
||||
int perf_event__process_event_type(struct perf_tool *tool,
|
||||
union perf_event *event);
|
||||
|
||||
int perf_event__synthesize_tracing_data(int fd, struct perf_evlist *evlist,
|
||||
perf_event__handler_t process,
|
||||
struct perf_session *session);
|
||||
int perf_event__synthesize_tracing_data(struct perf_tool *tool,
|
||||
int fd, struct perf_evlist *evlist,
|
||||
perf_event__handler_t process);
|
||||
int perf_event__process_tracing_data(union perf_event *event,
|
||||
struct perf_session *session);
|
||||
|
||||
int perf_event__synthesize_build_id(struct dso *pos, u16 misc,
|
||||
int perf_event__synthesize_build_id(struct perf_tool *tool,
|
||||
struct dso *pos, u16 misc,
|
||||
perf_event__handler_t process,
|
||||
struct machine *machine,
|
||||
struct perf_session *session);
|
||||
int perf_event__process_build_id(union perf_event *event,
|
||||
struct machine *machine);
|
||||
int perf_event__process_build_id(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_session *session);
|
||||
|
||||
/*
|
||||
|
@ -117,7 +117,6 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __used,
|
||||
|
||||
static inline int hist_entry__tui_annotate(struct hist_entry *self __used,
|
||||
int evidx __used,
|
||||
int nr_events __used,
|
||||
void(*timer)(void *arg) __used,
|
||||
void *arg __used,
|
||||
int delay_secs __used)
|
||||
@ -128,7 +127,7 @@ static inline int hist_entry__tui_annotate(struct hist_entry *self __used,
|
||||
#define K_RIGHT -2
|
||||
#else
|
||||
#include "ui/keysyms.h"
|
||||
int hist_entry__tui_annotate(struct hist_entry *he, int evidx, int nr_events,
|
||||
int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
|
||||
void(*timer)(void *arg), void *arg, int delay_secs);
|
||||
|
||||
int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
|
||||
|
@ -562,6 +562,10 @@ int machine__init(struct machine *self, const char *root_dir, pid_t pid)
|
||||
INIT_LIST_HEAD(&self->user_dsos);
|
||||
INIT_LIST_HEAD(&self->kernel_dsos);
|
||||
|
||||
self->threads = RB_ROOT;
|
||||
INIT_LIST_HEAD(&self->dead_threads);
|
||||
self->last_match = NULL;
|
||||
|
||||
self->kmaps.machine = self;
|
||||
self->pid = pid;
|
||||
self->root_dir = strdup(root_dir);
|
||||
|
@ -18,9 +18,11 @@ enum map_type {
|
||||
extern const char *map_type__name[MAP__NR_TYPES];
|
||||
|
||||
struct dso;
|
||||
struct ip_callchain;
|
||||
struct ref_reloc_sym;
|
||||
struct map_groups;
|
||||
struct machine;
|
||||
struct perf_evsel;
|
||||
|
||||
struct map {
|
||||
union {
|
||||
@ -61,7 +63,11 @@ struct map_groups {
|
||||
struct machine {
|
||||
struct rb_node rb_node;
|
||||
pid_t pid;
|
||||
u16 id_hdr_size;
|
||||
char *root_dir;
|
||||
struct rb_root threads;
|
||||
struct list_head dead_threads;
|
||||
struct thread *last_match;
|
||||
struct list_head user_dsos;
|
||||
struct list_head kernel_dsos;
|
||||
struct map_groups kmaps;
|
||||
@ -148,6 +154,13 @@ int machine__init(struct machine *self, const char *root_dir, pid_t pid);
|
||||
void machine__exit(struct machine *self);
|
||||
void machine__delete(struct machine *self);
|
||||
|
||||
int machine__resolve_callchain(struct machine *machine,
|
||||
struct perf_evsel *evsel, struct thread *thread,
|
||||
struct ip_callchain *chain,
|
||||
struct symbol **parent);
|
||||
int maps__set_kallsyms_ref_reloc_sym(struct map **maps, const char *symbol_name,
|
||||
u64 addr);
|
||||
|
||||
/*
|
||||
* Default guest kernel is defined by parameter --guestkallsyms
|
||||
* and --guestmodules
|
||||
@ -190,6 +203,12 @@ struct symbol *map_groups__find_symbol_by_name(struct map_groups *mg,
|
||||
struct map **mapp,
|
||||
symbol_filter_t filter);
|
||||
|
||||
|
||||
struct thread *machine__findnew_thread(struct machine *machine, pid_t pid);
|
||||
void machine__remove_thread(struct machine *machine, struct thread *th);
|
||||
|
||||
size_t machine__fprintf(struct machine *machine, FILE *fp);
|
||||
|
||||
static inline
|
||||
struct symbol *machine__find_kernel_symbol(struct machine *self,
|
||||
enum map_type type, u64 addr,
|
||||
|
@ -25,8 +25,6 @@ enum event_result {
|
||||
EVT_HANDLED_ALL
|
||||
};
|
||||
|
||||
char debugfs_path[MAXPATHLEN];
|
||||
|
||||
#define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x
|
||||
#define CSW(x) .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_##x
|
||||
|
||||
@ -140,7 +138,7 @@ static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir)
|
||||
char evt_path[MAXPATHLEN];
|
||||
int fd;
|
||||
|
||||
snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path,
|
||||
snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", tracing_events_path,
|
||||
sys_dir->d_name, evt_dir->d_name);
|
||||
fd = open(evt_path, O_RDONLY);
|
||||
if (fd < 0)
|
||||
@ -171,16 +169,16 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config)
|
||||
char evt_path[MAXPATHLEN];
|
||||
char dir_path[MAXPATHLEN];
|
||||
|
||||
if (debugfs_valid_mountpoint(debugfs_path))
|
||||
if (debugfs_valid_mountpoint(tracing_events_path))
|
||||
return NULL;
|
||||
|
||||
sys_dir = opendir(debugfs_path);
|
||||
sys_dir = opendir(tracing_events_path);
|
||||
if (!sys_dir)
|
||||
return NULL;
|
||||
|
||||
for_each_subsystem(sys_dir, sys_dirent, sys_next) {
|
||||
|
||||
snprintf(dir_path, MAXPATHLEN, "%s/%s", debugfs_path,
|
||||
snprintf(dir_path, MAXPATHLEN, "%s/%s", tracing_events_path,
|
||||
sys_dirent.d_name);
|
||||
evt_dir = opendir(dir_path);
|
||||
if (!evt_dir)
|
||||
@ -447,7 +445,7 @@ parse_single_tracepoint_event(char *sys_name,
|
||||
u64 id;
|
||||
int fd;
|
||||
|
||||
snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path,
|
||||
snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", tracing_events_path,
|
||||
sys_name, evt_name);
|
||||
|
||||
fd = open(evt_path, O_RDONLY);
|
||||
@ -485,7 +483,7 @@ parse_multiple_tracepoint_event(struct perf_evlist *evlist, char *sys_name,
|
||||
struct dirent *evt_ent;
|
||||
DIR *evt_dir;
|
||||
|
||||
snprintf(evt_path, MAXPATHLEN, "%s/%s", debugfs_path, sys_name);
|
||||
snprintf(evt_path, MAXPATHLEN, "%s/%s", tracing_events_path, sys_name);
|
||||
evt_dir = opendir(evt_path);
|
||||
|
||||
if (!evt_dir) {
|
||||
@ -528,7 +526,7 @@ parse_tracepoint_event(struct perf_evlist *evlist, const char **strp,
|
||||
char sys_name[MAX_EVENT_LENGTH];
|
||||
unsigned int sys_length, evt_length;
|
||||
|
||||
if (debugfs_valid_mountpoint(debugfs_path))
|
||||
if (debugfs_valid_mountpoint(tracing_events_path))
|
||||
return 0;
|
||||
|
||||
evt_name = strchr(*strp, ':');
|
||||
@ -920,10 +918,10 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob)
|
||||
char evt_path[MAXPATHLEN];
|
||||
char dir_path[MAXPATHLEN];
|
||||
|
||||
if (debugfs_valid_mountpoint(debugfs_path))
|
||||
if (debugfs_valid_mountpoint(tracing_events_path))
|
||||
return;
|
||||
|
||||
sys_dir = opendir(debugfs_path);
|
||||
sys_dir = opendir(tracing_events_path);
|
||||
if (!sys_dir)
|
||||
return;
|
||||
|
||||
@ -932,7 +930,7 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob)
|
||||
!strglobmatch(sys_dirent.d_name, subsys_glob))
|
||||
continue;
|
||||
|
||||
snprintf(dir_path, MAXPATHLEN, "%s/%s", debugfs_path,
|
||||
snprintf(dir_path, MAXPATHLEN, "%s/%s", tracing_events_path,
|
||||
sys_dirent.d_name);
|
||||
evt_dir = opendir(dir_path);
|
||||
if (!evt_dir)
|
||||
@ -964,16 +962,16 @@ int is_valid_tracepoint(const char *event_string)
|
||||
char evt_path[MAXPATHLEN];
|
||||
char dir_path[MAXPATHLEN];
|
||||
|
||||
if (debugfs_valid_mountpoint(debugfs_path))
|
||||
if (debugfs_valid_mountpoint(tracing_events_path))
|
||||
return 0;
|
||||
|
||||
sys_dir = opendir(debugfs_path);
|
||||
sys_dir = opendir(tracing_events_path);
|
||||
if (!sys_dir)
|
||||
return 0;
|
||||
|
||||
for_each_subsystem(sys_dir, sys_dirent, sys_next) {
|
||||
|
||||
snprintf(dir_path, MAXPATHLEN, "%s/%s", debugfs_path,
|
||||
snprintf(dir_path, MAXPATHLEN, "%s/%s", tracing_events_path,
|
||||
sys_dirent.d_name);
|
||||
evt_dir = opendir(dir_path);
|
||||
if (!evt_dir)
|
||||
|
@ -39,7 +39,6 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob);
|
||||
int print_hwcache_events(const char *event_glob);
|
||||
extern int is_valid_tracepoint(const char *event_string);
|
||||
|
||||
extern char debugfs_path[];
|
||||
extern int valid_debugfs_mount(const char *debugfs);
|
||||
|
||||
#endif /* __PERF_PARSE_EVENTS_H */
|
||||
|
@ -5,7 +5,6 @@
|
||||
#include "util.h"
|
||||
#include "probe-event.h"
|
||||
|
||||
#define MAX_PATH_LEN 256
|
||||
#define MAX_PROBE_BUFFER 1024
|
||||
#define MAX_PROBES 128
|
||||
|
||||
|
@ -27,6 +27,8 @@
|
||||
|
||||
#include "../../perf.h"
|
||||
#include "../util.h"
|
||||
#include "../thread.h"
|
||||
#include "../event.h"
|
||||
#include "../trace-event.h"
|
||||
|
||||
#include <EXTERN.h>
|
||||
@ -248,7 +250,7 @@ static inline struct event *find_cache_event(int type)
|
||||
static void perl_process_event(union perf_event *pevent __unused,
|
||||
struct perf_sample *sample,
|
||||
struct perf_evsel *evsel,
|
||||
struct perf_session *session __unused,
|
||||
struct machine *machine __unused,
|
||||
struct thread *thread)
|
||||
{
|
||||
struct format_field *field;
|
||||
|
@ -29,6 +29,8 @@
|
||||
|
||||
#include "../../perf.h"
|
||||
#include "../util.h"
|
||||
#include "../event.h"
|
||||
#include "../thread.h"
|
||||
#include "../trace-event.h"
|
||||
|
||||
PyMODINIT_FUNC initperf_trace_context(void);
|
||||
@ -207,7 +209,7 @@ static inline struct event *find_cache_event(int type)
|
||||
static void python_process_event(union perf_event *pevent __unused,
|
||||
struct perf_sample *sample,
|
||||
struct perf_evsel *evsel __unused,
|
||||
struct perf_session *session __unused,
|
||||
struct machine *machine __unused,
|
||||
struct thread *thread)
|
||||
{
|
||||
PyObject *handler, *retval, *context, *t, *obj, *dict = NULL;
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "evlist.h"
|
||||
#include "evsel.h"
|
||||
#include "session.h"
|
||||
#include "tool.h"
|
||||
#include "sort.h"
|
||||
#include "util.h"
|
||||
#include "cpumap.h"
|
||||
@ -78,39 +79,13 @@ static int perf_session__open(struct perf_session *self, bool force)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void perf_session__id_header_size(struct perf_session *session)
|
||||
{
|
||||
struct perf_sample *data;
|
||||
u64 sample_type = session->sample_type;
|
||||
u16 size = 0;
|
||||
|
||||
if (!session->sample_id_all)
|
||||
goto out;
|
||||
|
||||
if (sample_type & PERF_SAMPLE_TID)
|
||||
size += sizeof(data->tid) * 2;
|
||||
|
||||
if (sample_type & PERF_SAMPLE_TIME)
|
||||
size += sizeof(data->time);
|
||||
|
||||
if (sample_type & PERF_SAMPLE_ID)
|
||||
size += sizeof(data->id);
|
||||
|
||||
if (sample_type & PERF_SAMPLE_STREAM_ID)
|
||||
size += sizeof(data->stream_id);
|
||||
|
||||
if (sample_type & PERF_SAMPLE_CPU)
|
||||
size += sizeof(data->cpu) * 2;
|
||||
out:
|
||||
session->id_hdr_size = size;
|
||||
}
|
||||
|
||||
void perf_session__update_sample_type(struct perf_session *self)
|
||||
{
|
||||
self->sample_type = perf_evlist__sample_type(self->evlist);
|
||||
self->sample_size = __perf_evsel__sample_size(self->sample_type);
|
||||
self->sample_id_all = perf_evlist__sample_id_all(self->evlist);
|
||||
perf_session__id_header_size(self);
|
||||
self->id_hdr_size = perf_evlist__id_hdr_size(self->evlist);
|
||||
self->host_machine.id_hdr_size = self->id_hdr_size;
|
||||
}
|
||||
|
||||
int perf_session__create_kernel_maps(struct perf_session *self)
|
||||
@ -130,7 +105,7 @@ static void perf_session__destroy_kernel_maps(struct perf_session *self)
|
||||
|
||||
struct perf_session *perf_session__new(const char *filename, int mode,
|
||||
bool force, bool repipe,
|
||||
struct perf_event_ops *ops)
|
||||
struct perf_tool *tool)
|
||||
{
|
||||
size_t len = filename ? strlen(filename) + 1 : 0;
|
||||
struct perf_session *self = zalloc(sizeof(*self) + len);
|
||||
@ -139,9 +114,6 @@ struct perf_session *perf_session__new(const char *filename, int mode,
|
||||
goto out;
|
||||
|
||||
memcpy(self->filename, filename, len);
|
||||
self->threads = RB_ROOT;
|
||||
INIT_LIST_HEAD(&self->dead_threads);
|
||||
self->last_match = NULL;
|
||||
/*
|
||||
* On 64bit we can mmap the data file in one go. No need for tiny mmap
|
||||
* slices. On 32bit we use 32MB.
|
||||
@ -171,10 +143,10 @@ struct perf_session *perf_session__new(const char *filename, int mode,
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
if (ops && ops->ordering_requires_timestamps &&
|
||||
ops->ordered_samples && !self->sample_id_all) {
|
||||
if (tool && tool->ordering_requires_timestamps &&
|
||||
tool->ordered_samples && !self->sample_id_all) {
|
||||
dump_printf("WARNING: No sample_id_all support, falling back to unordered processing\n");
|
||||
ops->ordered_samples = false;
|
||||
tool->ordered_samples = false;
|
||||
}
|
||||
|
||||
out:
|
||||
@ -184,17 +156,22 @@ struct perf_session *perf_session__new(const char *filename, int mode,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void perf_session__delete_dead_threads(struct perf_session *self)
|
||||
static void machine__delete_dead_threads(struct machine *machine)
|
||||
{
|
||||
struct thread *n, *t;
|
||||
|
||||
list_for_each_entry_safe(t, n, &self->dead_threads, node) {
|
||||
list_for_each_entry_safe(t, n, &machine->dead_threads, node) {
|
||||
list_del(&t->node);
|
||||
thread__delete(t);
|
||||
}
|
||||
}
|
||||
|
||||
static void perf_session__delete_threads(struct perf_session *self)
|
||||
static void perf_session__delete_dead_threads(struct perf_session *session)
|
||||
{
|
||||
machine__delete_dead_threads(&session->host_machine);
|
||||
}
|
||||
|
||||
static void machine__delete_threads(struct machine *self)
|
||||
{
|
||||
struct rb_node *nd = rb_first(&self->threads);
|
||||
|
||||
@ -207,6 +184,11 @@ static void perf_session__delete_threads(struct perf_session *self)
|
||||
}
|
||||
}
|
||||
|
||||
static void perf_session__delete_threads(struct perf_session *session)
|
||||
{
|
||||
machine__delete_threads(&session->host_machine);
|
||||
}
|
||||
|
||||
void perf_session__delete(struct perf_session *self)
|
||||
{
|
||||
perf_session__destroy_kernel_maps(self);
|
||||
@ -217,7 +199,7 @@ void perf_session__delete(struct perf_session *self)
|
||||
free(self);
|
||||
}
|
||||
|
||||
void perf_session__remove_thread(struct perf_session *self, struct thread *th)
|
||||
void machine__remove_thread(struct machine *self, struct thread *th)
|
||||
{
|
||||
self->last_match = NULL;
|
||||
rb_erase(&th->rb_node, &self->threads);
|
||||
@ -236,16 +218,16 @@ static bool symbol__match_parent_regex(struct symbol *sym)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int perf_session__resolve_callchain(struct perf_session *self,
|
||||
struct thread *thread,
|
||||
struct ip_callchain *chain,
|
||||
struct symbol **parent)
|
||||
int machine__resolve_callchain(struct machine *self, struct perf_evsel *evsel,
|
||||
struct thread *thread,
|
||||
struct ip_callchain *chain,
|
||||
struct symbol **parent)
|
||||
{
|
||||
u8 cpumode = PERF_RECORD_MISC_USER;
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
callchain_cursor_reset(&self->callchain_cursor);
|
||||
callchain_cursor_reset(&evsel->hists.callchain_cursor);
|
||||
|
||||
for (i = 0; i < chain->nr; i++) {
|
||||
u64 ip;
|
||||
@ -272,7 +254,7 @@ int perf_session__resolve_callchain(struct perf_session *self,
|
||||
|
||||
al.filtered = false;
|
||||
thread__find_addr_location(thread, self, cpumode,
|
||||
MAP__FUNCTION, thread->pid, ip, &al, NULL);
|
||||
MAP__FUNCTION, ip, &al, NULL);
|
||||
if (al.sym != NULL) {
|
||||
if (sort__has_parent && !*parent &&
|
||||
symbol__match_parent_regex(al.sym))
|
||||
@ -281,7 +263,7 @@ int perf_session__resolve_callchain(struct perf_session *self,
|
||||
break;
|
||||
}
|
||||
|
||||
err = callchain_cursor_append(&self->callchain_cursor,
|
||||
err = callchain_cursor_append(&evsel->hists.callchain_cursor,
|
||||
ip, al.map, al.sym);
|
||||
if (err)
|
||||
return err;
|
||||
@ -290,75 +272,91 @@ int perf_session__resolve_callchain(struct perf_session *self,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_event_synth_stub(union perf_event *event __used,
|
||||
struct perf_session *session __used)
|
||||
static int process_event_synth_tracing_data_stub(union perf_event *event __used,
|
||||
struct perf_session *session __used)
|
||||
{
|
||||
dump_printf(": unhandled!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_event_sample_stub(union perf_event *event __used,
|
||||
static int process_event_synth_attr_stub(union perf_event *event __used,
|
||||
struct perf_evlist **pevlist __used)
|
||||
{
|
||||
dump_printf(": unhandled!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_event_sample_stub(struct perf_tool *tool __used,
|
||||
union perf_event *event __used,
|
||||
struct perf_sample *sample __used,
|
||||
struct perf_evsel *evsel __used,
|
||||
struct perf_session *session __used)
|
||||
struct machine *machine __used)
|
||||
{
|
||||
dump_printf(": unhandled!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_event_stub(union perf_event *event __used,
|
||||
static int process_event_stub(struct perf_tool *tool __used,
|
||||
union perf_event *event __used,
|
||||
struct perf_sample *sample __used,
|
||||
struct perf_session *session __used)
|
||||
struct machine *machine __used)
|
||||
{
|
||||
dump_printf(": unhandled!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_finished_round_stub(union perf_event *event __used,
|
||||
struct perf_session *session __used,
|
||||
struct perf_event_ops *ops __used)
|
||||
static int process_finished_round_stub(struct perf_tool *tool __used,
|
||||
union perf_event *event __used,
|
||||
struct perf_session *perf_session __used)
|
||||
{
|
||||
dump_printf(": unhandled!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_finished_round(union perf_event *event,
|
||||
struct perf_session *session,
|
||||
struct perf_event_ops *ops);
|
||||
|
||||
static void perf_event_ops__fill_defaults(struct perf_event_ops *handler)
|
||||
static int process_event_type_stub(struct perf_tool *tool __used,
|
||||
union perf_event *event __used)
|
||||
{
|
||||
if (handler->sample == NULL)
|
||||
handler->sample = process_event_sample_stub;
|
||||
if (handler->mmap == NULL)
|
||||
handler->mmap = process_event_stub;
|
||||
if (handler->comm == NULL)
|
||||
handler->comm = process_event_stub;
|
||||
if (handler->fork == NULL)
|
||||
handler->fork = process_event_stub;
|
||||
if (handler->exit == NULL)
|
||||
handler->exit = process_event_stub;
|
||||
if (handler->lost == NULL)
|
||||
handler->lost = perf_event__process_lost;
|
||||
if (handler->read == NULL)
|
||||
handler->read = process_event_stub;
|
||||
if (handler->throttle == NULL)
|
||||
handler->throttle = process_event_stub;
|
||||
if (handler->unthrottle == NULL)
|
||||
handler->unthrottle = process_event_stub;
|
||||
if (handler->attr == NULL)
|
||||
handler->attr = process_event_synth_stub;
|
||||
if (handler->event_type == NULL)
|
||||
handler->event_type = process_event_synth_stub;
|
||||
if (handler->tracing_data == NULL)
|
||||
handler->tracing_data = process_event_synth_stub;
|
||||
if (handler->build_id == NULL)
|
||||
handler->build_id = process_event_synth_stub;
|
||||
if (handler->finished_round == NULL) {
|
||||
if (handler->ordered_samples)
|
||||
handler->finished_round = process_finished_round;
|
||||
dump_printf(": unhandled!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_finished_round(struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_session *session);
|
||||
|
||||
static void perf_tool__fill_defaults(struct perf_tool *tool)
|
||||
{
|
||||
if (tool->sample == NULL)
|
||||
tool->sample = process_event_sample_stub;
|
||||
if (tool->mmap == NULL)
|
||||
tool->mmap = process_event_stub;
|
||||
if (tool->comm == NULL)
|
||||
tool->comm = process_event_stub;
|
||||
if (tool->fork == NULL)
|
||||
tool->fork = process_event_stub;
|
||||
if (tool->exit == NULL)
|
||||
tool->exit = process_event_stub;
|
||||
if (tool->lost == NULL)
|
||||
tool->lost = perf_event__process_lost;
|
||||
if (tool->read == NULL)
|
||||
tool->read = process_event_sample_stub;
|
||||
if (tool->throttle == NULL)
|
||||
tool->throttle = process_event_stub;
|
||||
if (tool->unthrottle == NULL)
|
||||
tool->unthrottle = process_event_stub;
|
||||
if (tool->attr == NULL)
|
||||
tool->attr = process_event_synth_attr_stub;
|
||||
if (tool->event_type == NULL)
|
||||
tool->event_type = process_event_type_stub;
|
||||
if (tool->tracing_data == NULL)
|
||||
tool->tracing_data = process_event_synth_tracing_data_stub;
|
||||
if (tool->build_id == NULL)
|
||||
tool->build_id = process_finished_round_stub;
|
||||
if (tool->finished_round == NULL) {
|
||||
if (tool->ordered_samples)
|
||||
tool->finished_round = process_finished_round;
|
||||
else
|
||||
handler->finished_round = process_finished_round_stub;
|
||||
tool->finished_round = process_finished_round_stub;
|
||||
}
|
||||
}
|
||||
|
||||
@ -490,11 +488,11 @@ static void perf_session_free_sample_buffers(struct perf_session *session)
|
||||
static int perf_session_deliver_event(struct perf_session *session,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct perf_event_ops *ops,
|
||||
struct perf_tool *tool,
|
||||
u64 file_offset);
|
||||
|
||||
static void flush_sample_queue(struct perf_session *s,
|
||||
struct perf_event_ops *ops)
|
||||
struct perf_tool *tool)
|
||||
{
|
||||
struct ordered_samples *os = &s->ordered_samples;
|
||||
struct list_head *head = &os->samples;
|
||||
@ -505,7 +503,7 @@ static void flush_sample_queue(struct perf_session *s,
|
||||
unsigned idx = 0, progress_next = os->nr_samples / 16;
|
||||
int ret;
|
||||
|
||||
if (!ops->ordered_samples || !limit)
|
||||
if (!tool->ordered_samples || !limit)
|
||||
return;
|
||||
|
||||
list_for_each_entry_safe(iter, tmp, head, list) {
|
||||
@ -516,7 +514,7 @@ static void flush_sample_queue(struct perf_session *s,
|
||||
if (ret)
|
||||
pr_err("Can't parse sample, err = %d\n", ret);
|
||||
else
|
||||
perf_session_deliver_event(s, iter->event, &sample, ops,
|
||||
perf_session_deliver_event(s, iter->event, &sample, tool,
|
||||
iter->file_offset);
|
||||
|
||||
os->last_flush = iter->timestamp;
|
||||
@ -578,11 +576,11 @@ static void flush_sample_queue(struct perf_session *s,
|
||||
* Flush every events below timestamp 7
|
||||
* etc...
|
||||
*/
|
||||
static int process_finished_round(union perf_event *event __used,
|
||||
struct perf_session *session,
|
||||
struct perf_event_ops *ops)
|
||||
static int process_finished_round(struct perf_tool *tool,
|
||||
union perf_event *event __used,
|
||||
struct perf_session *session)
|
||||
{
|
||||
flush_sample_queue(session, ops);
|
||||
flush_sample_queue(session, tool);
|
||||
session->ordered_samples.next_flush = session->ordered_samples.max_timestamp;
|
||||
|
||||
return 0;
|
||||
@ -737,13 +735,26 @@ static void dump_sample(struct perf_session *session, union perf_event *event,
|
||||
callchain__printf(sample);
|
||||
}
|
||||
|
||||
static struct machine *
|
||||
perf_session__find_machine_for_cpumode(struct perf_session *session,
|
||||
union perf_event *event)
|
||||
{
|
||||
const u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
|
||||
|
||||
if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL && perf_guest)
|
||||
return perf_session__find_machine(session, event->ip.pid);
|
||||
|
||||
return perf_session__find_host_machine(session);
|
||||
}
|
||||
|
||||
static int perf_session_deliver_event(struct perf_session *session,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct perf_event_ops *ops,
|
||||
struct perf_tool *tool,
|
||||
u64 file_offset)
|
||||
{
|
||||
struct perf_evsel *evsel;
|
||||
struct machine *machine;
|
||||
|
||||
dump_event(session, event, file_offset, sample);
|
||||
|
||||
@ -765,6 +776,8 @@ static int perf_session_deliver_event(struct perf_session *session,
|
||||
hists__inc_nr_events(&evsel->hists, event->header.type);
|
||||
}
|
||||
|
||||
machine = perf_session__find_machine_for_cpumode(session, event);
|
||||
|
||||
switch (event->header.type) {
|
||||
case PERF_RECORD_SAMPLE:
|
||||
dump_sample(session, event, sample);
|
||||
@ -772,23 +785,25 @@ static int perf_session_deliver_event(struct perf_session *session,
|
||||
++session->hists.stats.nr_unknown_id;
|
||||
return -1;
|
||||
}
|
||||
return ops->sample(event, sample, evsel, session);
|
||||
return tool->sample(tool, event, sample, evsel, machine);
|
||||
case PERF_RECORD_MMAP:
|
||||
return ops->mmap(event, sample, session);
|
||||
return tool->mmap(tool, event, sample, machine);
|
||||
case PERF_RECORD_COMM:
|
||||
return ops->comm(event, sample, session);
|
||||
return tool->comm(tool, event, sample, machine);
|
||||
case PERF_RECORD_FORK:
|
||||
return ops->fork(event, sample, session);
|
||||
return tool->fork(tool, event, sample, machine);
|
||||
case PERF_RECORD_EXIT:
|
||||
return ops->exit(event, sample, session);
|
||||
return tool->exit(tool, event, sample, machine);
|
||||
case PERF_RECORD_LOST:
|
||||
return ops->lost(event, sample, session);
|
||||
if (tool->lost == perf_event__process_lost)
|
||||
session->hists.stats.total_lost += event->lost.lost;
|
||||
return tool->lost(tool, event, sample, machine);
|
||||
case PERF_RECORD_READ:
|
||||
return ops->read(event, sample, session);
|
||||
return tool->read(tool, event, sample, evsel, machine);
|
||||
case PERF_RECORD_THROTTLE:
|
||||
return ops->throttle(event, sample, session);
|
||||
return tool->throttle(tool, event, sample, machine);
|
||||
case PERF_RECORD_UNTHROTTLE:
|
||||
return ops->unthrottle(event, sample, session);
|
||||
return tool->unthrottle(tool, event, sample, machine);
|
||||
default:
|
||||
++session->hists.stats.nr_unknown_events;
|
||||
return -1;
|
||||
@ -812,24 +827,29 @@ static int perf_session__preprocess_sample(struct perf_session *session,
|
||||
}
|
||||
|
||||
static int perf_session__process_user_event(struct perf_session *session, union perf_event *event,
|
||||
struct perf_event_ops *ops, u64 file_offset)
|
||||
struct perf_tool *tool, u64 file_offset)
|
||||
{
|
||||
int err;
|
||||
|
||||
dump_event(session, event, file_offset, NULL);
|
||||
|
||||
/* These events are processed right away */
|
||||
switch (event->header.type) {
|
||||
case PERF_RECORD_HEADER_ATTR:
|
||||
return ops->attr(event, session);
|
||||
err = tool->attr(event, &session->evlist);
|
||||
if (err == 0)
|
||||
perf_session__update_sample_type(session);
|
||||
return err;
|
||||
case PERF_RECORD_HEADER_EVENT_TYPE:
|
||||
return ops->event_type(event, session);
|
||||
return tool->event_type(tool, event);
|
||||
case PERF_RECORD_HEADER_TRACING_DATA:
|
||||
/* setup for reading amidst mmap */
|
||||
lseek(session->fd, file_offset, SEEK_SET);
|
||||
return ops->tracing_data(event, session);
|
||||
return tool->tracing_data(event, session);
|
||||
case PERF_RECORD_HEADER_BUILD_ID:
|
||||
return ops->build_id(event, session);
|
||||
return tool->build_id(tool, event, session);
|
||||
case PERF_RECORD_FINISHED_ROUND:
|
||||
return ops->finished_round(event, session, ops);
|
||||
return tool->finished_round(tool, event, session);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -837,7 +857,7 @@ static int perf_session__process_user_event(struct perf_session *session, union
|
||||
|
||||
static int perf_session__process_event(struct perf_session *session,
|
||||
union perf_event *event,
|
||||
struct perf_event_ops *ops,
|
||||
struct perf_tool *tool,
|
||||
u64 file_offset)
|
||||
{
|
||||
struct perf_sample sample;
|
||||
@ -853,7 +873,7 @@ static int perf_session__process_event(struct perf_session *session,
|
||||
hists__inc_nr_events(&session->hists, event->header.type);
|
||||
|
||||
if (event->header.type >= PERF_RECORD_USER_TYPE_START)
|
||||
return perf_session__process_user_event(session, event, ops, file_offset);
|
||||
return perf_session__process_user_event(session, event, tool, file_offset);
|
||||
|
||||
/*
|
||||
* For all kernel events we get the sample data
|
||||
@ -866,14 +886,14 @@ static int perf_session__process_event(struct perf_session *session,
|
||||
if (perf_session__preprocess_sample(session, event, &sample))
|
||||
return 0;
|
||||
|
||||
if (ops->ordered_samples) {
|
||||
if (tool->ordered_samples) {
|
||||
ret = perf_session_queue_event(session, event, &sample,
|
||||
file_offset);
|
||||
if (ret != -ETIME)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return perf_session_deliver_event(session, event, &sample, ops,
|
||||
return perf_session_deliver_event(session, event, &sample, tool,
|
||||
file_offset);
|
||||
}
|
||||
|
||||
@ -884,6 +904,11 @@ void perf_event_header__bswap(struct perf_event_header *self)
|
||||
self->size = bswap_16(self->size);
|
||||
}
|
||||
|
||||
struct thread *perf_session__findnew(struct perf_session *session, pid_t pid)
|
||||
{
|
||||
return machine__findnew_thread(&session->host_machine, pid);
|
||||
}
|
||||
|
||||
static struct thread *perf_session__register_idle_thread(struct perf_session *self)
|
||||
{
|
||||
struct thread *thread = perf_session__findnew(self, 0);
|
||||
@ -897,9 +922,9 @@ static struct thread *perf_session__register_idle_thread(struct perf_session *se
|
||||
}
|
||||
|
||||
static void perf_session__warn_about_errors(const struct perf_session *session,
|
||||
const struct perf_event_ops *ops)
|
||||
const struct perf_tool *tool)
|
||||
{
|
||||
if (ops->lost == perf_event__process_lost &&
|
||||
if (tool->lost == perf_event__process_lost &&
|
||||
session->hists.stats.nr_events[PERF_RECORD_LOST] != 0) {
|
||||
ui__warning("Processed %d events and lost %d chunks!\n\n"
|
||||
"Check IO/CPU overload!\n\n",
|
||||
@ -934,7 +959,7 @@ static void perf_session__warn_about_errors(const struct perf_session *session,
|
||||
volatile int session_done;
|
||||
|
||||
static int __perf_session__process_pipe_events(struct perf_session *self,
|
||||
struct perf_event_ops *ops)
|
||||
struct perf_tool *tool)
|
||||
{
|
||||
union perf_event event;
|
||||
uint32_t size;
|
||||
@ -943,7 +968,7 @@ static int __perf_session__process_pipe_events(struct perf_session *self,
|
||||
int err;
|
||||
void *p;
|
||||
|
||||
perf_event_ops__fill_defaults(ops);
|
||||
perf_tool__fill_defaults(tool);
|
||||
|
||||
head = 0;
|
||||
more:
|
||||
@ -980,7 +1005,7 @@ static int __perf_session__process_pipe_events(struct perf_session *self,
|
||||
}
|
||||
|
||||
if (size == 0 ||
|
||||
(skip = perf_session__process_event(self, &event, ops, head)) < 0) {
|
||||
(skip = perf_session__process_event(self, &event, tool, head)) < 0) {
|
||||
dump_printf("%#" PRIx64 " [%#x]: skipping unknown header type: %d\n",
|
||||
head, event.header.size, event.header.type);
|
||||
/*
|
||||
@ -1003,7 +1028,7 @@ static int __perf_session__process_pipe_events(struct perf_session *self,
|
||||
done:
|
||||
err = 0;
|
||||
out_err:
|
||||
perf_session__warn_about_errors(self, ops);
|
||||
perf_session__warn_about_errors(self, tool);
|
||||
perf_session_free_sample_buffers(self);
|
||||
return err;
|
||||
}
|
||||
@ -1034,7 +1059,7 @@ fetch_mmaped_event(struct perf_session *session,
|
||||
|
||||
int __perf_session__process_events(struct perf_session *session,
|
||||
u64 data_offset, u64 data_size,
|
||||
u64 file_size, struct perf_event_ops *ops)
|
||||
u64 file_size, struct perf_tool *tool)
|
||||
{
|
||||
u64 head, page_offset, file_offset, file_pos, progress_next;
|
||||
int err, mmap_prot, mmap_flags, map_idx = 0;
|
||||
@ -1043,7 +1068,7 @@ int __perf_session__process_events(struct perf_session *session,
|
||||
union perf_event *event;
|
||||
uint32_t size;
|
||||
|
||||
perf_event_ops__fill_defaults(ops);
|
||||
perf_tool__fill_defaults(tool);
|
||||
|
||||
page_size = sysconf(_SC_PAGESIZE);
|
||||
|
||||
@ -1098,7 +1123,7 @@ int __perf_session__process_events(struct perf_session *session,
|
||||
size = event->header.size;
|
||||
|
||||
if (size == 0 ||
|
||||
perf_session__process_event(session, event, ops, file_pos) < 0) {
|
||||
perf_session__process_event(session, event, tool, file_pos) < 0) {
|
||||
dump_printf("%#" PRIx64 " [%#x]: skipping unknown header type: %d\n",
|
||||
file_offset + head, event->header.size,
|
||||
event->header.type);
|
||||
@ -1127,15 +1152,15 @@ int __perf_session__process_events(struct perf_session *session,
|
||||
err = 0;
|
||||
/* do the final flush for ordered samples */
|
||||
session->ordered_samples.next_flush = ULLONG_MAX;
|
||||
flush_sample_queue(session, ops);
|
||||
flush_sample_queue(session, tool);
|
||||
out_err:
|
||||
perf_session__warn_about_errors(session, ops);
|
||||
perf_session__warn_about_errors(session, tool);
|
||||
perf_session_free_sample_buffers(session);
|
||||
return err;
|
||||
}
|
||||
|
||||
int perf_session__process_events(struct perf_session *self,
|
||||
struct perf_event_ops *ops)
|
||||
struct perf_tool *tool)
|
||||
{
|
||||
int err;
|
||||
|
||||
@ -1146,9 +1171,9 @@ int perf_session__process_events(struct perf_session *self,
|
||||
err = __perf_session__process_events(self,
|
||||
self->header.data_offset,
|
||||
self->header.data_size,
|
||||
self->size, ops);
|
||||
self->size, tool);
|
||||
else
|
||||
err = __perf_session__process_pipe_events(self, ops);
|
||||
err = __perf_session__process_pipe_events(self, tool);
|
||||
|
||||
return err;
|
||||
}
|
||||
@ -1163,9 +1188,8 @@ bool perf_session__has_traces(struct perf_session *self, const char *msg)
|
||||
return true;
|
||||
}
|
||||
|
||||
int perf_session__set_kallsyms_ref_reloc_sym(struct map **maps,
|
||||
const char *symbol_name,
|
||||
u64 addr)
|
||||
int maps__set_kallsyms_ref_reloc_sym(struct map **maps,
|
||||
const char *symbol_name, u64 addr)
|
||||
{
|
||||
char *bracket;
|
||||
enum map_type i;
|
||||
@ -1224,6 +1248,27 @@ size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp)
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t perf_session__fprintf(struct perf_session *session, FILE *fp)
|
||||
{
|
||||
/*
|
||||
* FIXME: Here we have to actually print all the machines in this
|
||||
* session, not just the host...
|
||||
*/
|
||||
return machine__fprintf(&session->host_machine, fp);
|
||||
}
|
||||
|
||||
void perf_session__remove_thread(struct perf_session *session,
|
||||
struct thread *th)
|
||||
{
|
||||
/*
|
||||
* FIXME: This one makes no sense, we need to remove the thread from
|
||||
* the machine it belongs to, perf_session can have many machines, so
|
||||
* doing it always on ->host_machine is wrong. Fix when auditing all
|
||||
* the 'perf kvm' code.
|
||||
*/
|
||||
machine__remove_thread(&session->host_machine, th);
|
||||
}
|
||||
|
||||
struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,
|
||||
unsigned int type)
|
||||
{
|
||||
@ -1236,17 +1281,16 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void perf_session__print_ip(union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct perf_session *session,
|
||||
int print_sym, int print_dso)
|
||||
void perf_event__print_ip(union perf_event *event, struct perf_sample *sample,
|
||||
struct machine *machine, struct perf_evsel *evsel,
|
||||
int print_sym, int print_dso)
|
||||
{
|
||||
struct addr_location al;
|
||||
const char *symname, *dsoname;
|
||||
struct callchain_cursor *cursor = &session->callchain_cursor;
|
||||
struct callchain_cursor *cursor = &evsel->hists.callchain_cursor;
|
||||
struct callchain_cursor_node *node;
|
||||
|
||||
if (perf_event__preprocess_sample(event, session, &al, sample,
|
||||
if (perf_event__preprocess_sample(event, machine, &al, sample,
|
||||
NULL) < 0) {
|
||||
error("problem processing %d event, skipping it.\n",
|
||||
event->header.type);
|
||||
@ -1255,7 +1299,7 @@ void perf_session__print_ip(union perf_event *event,
|
||||
|
||||
if (symbol_conf.use_callchain && sample->callchain) {
|
||||
|
||||
if (perf_session__resolve_callchain(session, al.thread,
|
||||
if (machine__resolve_callchain(machine, evsel, al.thread,
|
||||
sample->callchain, NULL) != 0) {
|
||||
if (verbose)
|
||||
error("Failed to resolve callchain. Skipping\n");
|
||||
|
@ -30,9 +30,6 @@ struct perf_session {
|
||||
struct perf_header header;
|
||||
unsigned long size;
|
||||
unsigned long mmap_window;
|
||||
struct rb_root threads;
|
||||
struct list_head dead_threads;
|
||||
struct thread *last_match;
|
||||
struct machine host_machine;
|
||||
struct rb_root machines;
|
||||
struct perf_evlist *evlist;
|
||||
@ -53,65 +50,31 @@ struct perf_session {
|
||||
int cwdlen;
|
||||
char *cwd;
|
||||
struct ordered_samples ordered_samples;
|
||||
struct callchain_cursor callchain_cursor;
|
||||
char filename[0];
|
||||
};
|
||||
|
||||
struct perf_evsel;
|
||||
struct perf_event_ops;
|
||||
|
||||
typedef int (*event_sample)(union perf_event *event, struct perf_sample *sample,
|
||||
struct perf_evsel *evsel, struct perf_session *session);
|
||||
typedef int (*event_op)(union perf_event *self, struct perf_sample *sample,
|
||||
struct perf_session *session);
|
||||
typedef int (*event_synth_op)(union perf_event *self,
|
||||
struct perf_session *session);
|
||||
typedef int (*event_op2)(union perf_event *self, struct perf_session *session,
|
||||
struct perf_event_ops *ops);
|
||||
|
||||
struct perf_event_ops {
|
||||
event_sample sample;
|
||||
event_op mmap,
|
||||
comm,
|
||||
fork,
|
||||
exit,
|
||||
lost,
|
||||
read,
|
||||
throttle,
|
||||
unthrottle;
|
||||
event_synth_op attr,
|
||||
event_type,
|
||||
tracing_data,
|
||||
build_id;
|
||||
event_op2 finished_round;
|
||||
bool ordered_samples;
|
||||
bool ordering_requires_timestamps;
|
||||
};
|
||||
struct perf_tool;
|
||||
|
||||
struct perf_session *perf_session__new(const char *filename, int mode,
|
||||
bool force, bool repipe,
|
||||
struct perf_event_ops *ops);
|
||||
struct perf_tool *tool);
|
||||
void perf_session__delete(struct perf_session *self);
|
||||
|
||||
void perf_event_header__bswap(struct perf_event_header *self);
|
||||
|
||||
int __perf_session__process_events(struct perf_session *self,
|
||||
u64 data_offset, u64 data_size, u64 size,
|
||||
struct perf_event_ops *ops);
|
||||
struct perf_tool *tool);
|
||||
int perf_session__process_events(struct perf_session *self,
|
||||
struct perf_event_ops *event_ops);
|
||||
struct perf_tool *tool);
|
||||
|
||||
int perf_session__resolve_callchain(struct perf_session *self,
|
||||
int perf_session__resolve_callchain(struct perf_session *self, struct perf_evsel *evsel,
|
||||
struct thread *thread,
|
||||
struct ip_callchain *chain,
|
||||
struct symbol **parent);
|
||||
|
||||
bool perf_session__has_traces(struct perf_session *self, const char *msg);
|
||||
|
||||
int perf_session__set_kallsyms_ref_reloc_sym(struct map **maps,
|
||||
const char *symbol_name,
|
||||
u64 addr);
|
||||
|
||||
void mem_bswap_64(void *src, int byte_size);
|
||||
void perf_event__attr_swap(struct perf_event_attr *attr);
|
||||
|
||||
@ -144,12 +107,16 @@ struct machine *perf_session__findnew_machine(struct perf_session *self, pid_t p
|
||||
|
||||
static inline
|
||||
void perf_session__process_machines(struct perf_session *self,
|
||||
struct perf_tool *tool,
|
||||
machine__process_t process)
|
||||
{
|
||||
process(&self->host_machine, self);
|
||||
return machines__process(&self->machines, process, self);
|
||||
process(&self->host_machine, tool);
|
||||
return machines__process(&self->machines, process, tool);
|
||||
}
|
||||
|
||||
struct thread *perf_session__findnew(struct perf_session *self, pid_t pid);
|
||||
size_t perf_session__fprintf(struct perf_session *self, FILE *fp);
|
||||
|
||||
size_t perf_session__fprintf_dsos(struct perf_session *self, FILE *fp);
|
||||
|
||||
size_t perf_session__fprintf_dsos_buildid(struct perf_session *self,
|
||||
@ -170,10 +137,9 @@ static inline int perf_session__parse_sample(struct perf_session *session,
|
||||
struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,
|
||||
unsigned int type);
|
||||
|
||||
void perf_session__print_ip(union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct perf_session *session,
|
||||
int print_sym, int print_dso);
|
||||
void perf_event__print_ip(union perf_event *event, struct perf_sample *sample,
|
||||
struct machine *machine, struct perf_evsel *evsel,
|
||||
int print_sym, int print_dso);
|
||||
|
||||
int perf_session__cpu_bitmap(struct perf_session *session,
|
||||
const char *cpu_list, unsigned long *cpu_bitmap);
|
||||
|
@ -27,7 +27,8 @@ build_tmp = getenv('PYTHON_EXTBUILD_TMP')
|
||||
perf = Extension('perf',
|
||||
sources = ['util/python.c', 'util/ctype.c', 'util/evlist.c',
|
||||
'util/evsel.c', 'util/cpumap.c', 'util/thread_map.c',
|
||||
'util/util.c', 'util/xyarray.c', 'util/cgroup.c'],
|
||||
'util/util.c', 'util/xyarray.c', 'util/cgroup.c',
|
||||
'util/debugfs.c'],
|
||||
include_dirs = ['util/include'],
|
||||
extra_compile_args = cflags,
|
||||
)
|
||||
|
@ -68,6 +68,7 @@ struct strlist;
|
||||
|
||||
struct symbol_conf {
|
||||
unsigned short priv_size;
|
||||
unsigned short nr_events;
|
||||
bool try_vmlinux_path,
|
||||
use_modules,
|
||||
sort_by_name,
|
||||
|
@ -61,7 +61,7 @@ static size_t thread__fprintf(struct thread *self, FILE *fp)
|
||||
map_groups__fprintf(&self->mg, verbose, fp);
|
||||
}
|
||||
|
||||
struct thread *perf_session__findnew(struct perf_session *self, pid_t pid)
|
||||
struct thread *machine__findnew_thread(struct machine *self, pid_t pid)
|
||||
{
|
||||
struct rb_node **p = &self->threads.rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
@ -125,12 +125,12 @@ int thread__fork(struct thread *self, struct thread *parent)
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t perf_session__fprintf(struct perf_session *self, FILE *fp)
|
||||
size_t machine__fprintf(struct machine *machine, FILE *fp)
|
||||
{
|
||||
size_t ret = 0;
|
||||
struct rb_node *nd;
|
||||
|
||||
for (nd = rb_first(&self->threads); nd; nd = rb_next(nd)) {
|
||||
for (nd = rb_first(&machine->threads); nd; nd = rb_next(nd)) {
|
||||
struct thread *pos = rb_entry(nd, struct thread, rb_node);
|
||||
|
||||
ret += thread__fprintf(pos, fp);
|
||||
|
@ -18,16 +18,14 @@ struct thread {
|
||||
int comm_len;
|
||||
};
|
||||
|
||||
struct perf_session;
|
||||
struct machine;
|
||||
|
||||
void thread__delete(struct thread *self);
|
||||
|
||||
int thread__set_comm(struct thread *self, const char *comm);
|
||||
int thread__comm_len(struct thread *self);
|
||||
struct thread *perf_session__findnew(struct perf_session *self, pid_t pid);
|
||||
void thread__insert_map(struct thread *self, struct map *map);
|
||||
int thread__fork(struct thread *self, struct thread *parent);
|
||||
size_t perf_session__fprintf(struct perf_session *self, FILE *fp);
|
||||
|
||||
static inline struct map *thread__find_map(struct thread *self,
|
||||
enum map_type type, u64 addr)
|
||||
@ -35,14 +33,12 @@ static inline struct map *thread__find_map(struct thread *self,
|
||||
return self ? map_groups__find(&self->mg, type, addr) : NULL;
|
||||
}
|
||||
|
||||
void thread__find_addr_map(struct thread *self,
|
||||
struct perf_session *session, u8 cpumode,
|
||||
enum map_type type, pid_t pid, u64 addr,
|
||||
void thread__find_addr_map(struct thread *thread, struct machine *machine,
|
||||
u8 cpumode, enum map_type type, u64 addr,
|
||||
struct addr_location *al);
|
||||
|
||||
void thread__find_addr_location(struct thread *self,
|
||||
struct perf_session *session, u8 cpumode,
|
||||
enum map_type type, pid_t pid, u64 addr,
|
||||
void thread__find_addr_location(struct thread *thread, struct machine *machine,
|
||||
u8 cpumode, enum map_type type, u64 addr,
|
||||
struct addr_location *al,
|
||||
symbol_filter_t filter);
|
||||
#endif /* __PERF_THREAD_H */
|
||||
|
50
tools/perf/util/tool.h
Normal file
50
tools/perf/util/tool.h
Normal file
@ -0,0 +1,50 @@
|
||||
#ifndef __PERF_TOOL_H
|
||||
#define __PERF_TOOL_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
struct perf_session;
|
||||
union perf_event;
|
||||
struct perf_evlist;
|
||||
struct perf_evsel;
|
||||
struct perf_sample;
|
||||
struct perf_tool;
|
||||
struct machine;
|
||||
|
||||
typedef int (*event_sample)(struct perf_tool *tool, union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct perf_evsel *evsel, struct machine *machine);
|
||||
|
||||
typedef int (*event_op)(struct perf_tool *tool, union perf_event *event,
|
||||
struct perf_sample *sample, struct machine *machine);
|
||||
|
||||
typedef int (*event_attr_op)(union perf_event *event,
|
||||
struct perf_evlist **pevlist);
|
||||
typedef int (*event_simple_op)(struct perf_tool *tool, union perf_event *event);
|
||||
|
||||
typedef int (*event_synth_op)(union perf_event *event,
|
||||
struct perf_session *session);
|
||||
|
||||
typedef int (*event_op2)(struct perf_tool *tool, union perf_event *event,
|
||||
struct perf_session *session);
|
||||
|
||||
struct perf_tool {
|
||||
event_sample sample,
|
||||
read;
|
||||
event_op mmap,
|
||||
comm,
|
||||
fork,
|
||||
exit,
|
||||
lost,
|
||||
throttle,
|
||||
unthrottle;
|
||||
event_attr_op attr;
|
||||
event_synth_op tracing_data;
|
||||
event_simple_op event_type;
|
||||
event_op2 finished_round,
|
||||
build_id;
|
||||
bool ordered_samples;
|
||||
bool ordering_requires_timestamps;
|
||||
};
|
||||
|
||||
#endif /* __PERF_TOOL_H */
|
@ -1,15 +1,17 @@
|
||||
#ifndef __PERF_TOP_H
|
||||
#define __PERF_TOP_H 1
|
||||
|
||||
#include "tool.h"
|
||||
#include "types.h"
|
||||
#include "../perf.h"
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
struct perf_evlist;
|
||||
struct perf_evsel;
|
||||
struct perf_session;
|
||||
|
||||
struct perf_top {
|
||||
struct perf_tool tool;
|
||||
struct perf_evlist *evlist;
|
||||
/*
|
||||
* Symbols will be added here in perf_event__process_sample and will
|
||||
@ -23,10 +25,26 @@ struct perf_top {
|
||||
int freq;
|
||||
pid_t target_pid, target_tid;
|
||||
bool hide_kernel_symbols, hide_user_symbols, zero;
|
||||
bool system_wide;
|
||||
bool use_tui, use_stdio;
|
||||
bool sort_has_symbols;
|
||||
bool dont_use_callchains;
|
||||
bool kptr_restrict_warned;
|
||||
bool vmlinux_warned;
|
||||
bool inherit;
|
||||
bool group;
|
||||
bool sample_id_all_avail;
|
||||
bool dump_symtab;
|
||||
const char *cpu_list;
|
||||
struct hist_entry *sym_filter_entry;
|
||||
struct perf_evsel *sym_evsel;
|
||||
struct perf_session *session;
|
||||
struct winsize winsize;
|
||||
unsigned int mmap_pages;
|
||||
int default_interval;
|
||||
int realtime_prio;
|
||||
int sym_pcnt_filter;
|
||||
const char *sym_filter;
|
||||
};
|
||||
|
||||
size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size);
|
||||
|
@ -18,7 +18,8 @@
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <ctype.h>
|
||||
#include "util.h"
|
||||
#include <dirent.h>
|
||||
#include <mntent.h>
|
||||
#include <stdio.h>
|
||||
@ -31,7 +32,6 @@
|
||||
#include <pthread.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <linux/list.h>
|
||||
@ -44,10 +44,6 @@
|
||||
|
||||
#define VERSION "0.5"
|
||||
|
||||
#define _STR(x) #x
|
||||
#define STR(x) _STR(x)
|
||||
#define MAX_PATH 256
|
||||
|
||||
#define TRACE_CTRL "tracing_on"
|
||||
#define TRACE "trace"
|
||||
#define AVAILABLE "available_tracers"
|
||||
@ -73,26 +69,6 @@ struct events {
|
||||
};
|
||||
|
||||
|
||||
|
||||
static void die(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int ret = errno;
|
||||
|
||||
if (errno)
|
||||
perror("perf");
|
||||
else
|
||||
ret = -1;
|
||||
|
||||
va_start(ap, fmt);
|
||||
fprintf(stderr, " ");
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
exit(ret);
|
||||
}
|
||||
|
||||
void *malloc_or_die(unsigned int size)
|
||||
{
|
||||
void *data;
|
||||
|
@ -39,7 +39,7 @@ static int stop_script_unsupported(void)
|
||||
static void process_event_unsupported(union perf_event *event __unused,
|
||||
struct perf_sample *sample __unused,
|
||||
struct perf_evsel *evsel __unused,
|
||||
struct perf_session *session __unused,
|
||||
struct machine *machine __unused,
|
||||
struct thread *thread __unused)
|
||||
{
|
||||
}
|
||||
|
@ -3,7 +3,11 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "parse-events.h"
|
||||
#include "session.h"
|
||||
|
||||
struct machine;
|
||||
struct perf_sample;
|
||||
union perf_event;
|
||||
struct thread;
|
||||
|
||||
#define __unused __attribute__((unused))
|
||||
|
||||
@ -292,7 +296,7 @@ struct scripting_ops {
|
||||
void (*process_event) (union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct perf_evsel *evsel,
|
||||
struct perf_session *session,
|
||||
struct machine *machine,
|
||||
struct thread *thread);
|
||||
int (*generate_script) (const char *outfile);
|
||||
};
|
||||
|
@ -224,7 +224,7 @@ static bool annotate_browser__toggle_source(struct annotate_browser *browser)
|
||||
}
|
||||
|
||||
static int annotate_browser__run(struct annotate_browser *self, int evidx,
|
||||
int nr_events, void(*timer)(void *arg),
|
||||
void(*timer)(void *arg),
|
||||
void *arg, int delay_secs)
|
||||
{
|
||||
struct rb_node *nd = NULL;
|
||||
@ -328,8 +328,7 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx,
|
||||
notes = symbol__annotation(target);
|
||||
pthread_mutex_lock(¬es->lock);
|
||||
|
||||
if (notes->src == NULL &&
|
||||
symbol__alloc_hist(target, nr_events) < 0) {
|
||||
if (notes->src == NULL && symbol__alloc_hist(target) < 0) {
|
||||
pthread_mutex_unlock(¬es->lock);
|
||||
ui__warning("Not enough memory for annotating '%s' symbol!\n",
|
||||
target->name);
|
||||
@ -337,7 +336,7 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx,
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(¬es->lock);
|
||||
symbol__tui_annotate(target, ms->map, evidx, nr_events,
|
||||
symbol__tui_annotate(target, ms->map, evidx,
|
||||
timer, arg, delay_secs);
|
||||
}
|
||||
continue;
|
||||
@ -358,15 +357,15 @@ static int annotate_browser__run(struct annotate_browser *self, int evidx,
|
||||
return key;
|
||||
}
|
||||
|
||||
int hist_entry__tui_annotate(struct hist_entry *he, int evidx, int nr_events,
|
||||
int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
|
||||
void(*timer)(void *arg), void *arg, int delay_secs)
|
||||
{
|
||||
return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx, nr_events,
|
||||
return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx,
|
||||
timer, arg, delay_secs);
|
||||
}
|
||||
|
||||
int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
|
||||
int nr_events, void(*timer)(void *arg), void *arg,
|
||||
void(*timer)(void *arg), void *arg,
|
||||
int delay_secs)
|
||||
{
|
||||
struct objdump_line *pos, *n;
|
||||
@ -419,8 +418,7 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
|
||||
browser.b.nr_entries = browser.nr_entries;
|
||||
browser.b.entries = ¬es->src->source,
|
||||
browser.b.width += 18; /* Percentage */
|
||||
ret = annotate_browser__run(&browser, evidx, nr_events,
|
||||
timer, arg, delay_secs);
|
||||
ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs);
|
||||
list_for_each_entry_safe(pos, n, ¬es->src->source, node) {
|
||||
list_del(&pos->node);
|
||||
objdump_line__free(pos);
|
||||
|
@ -1020,7 +1020,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
||||
* Don't let this be freed, say, by hists__decay_entry.
|
||||
*/
|
||||
he->used = true;
|
||||
err = hist_entry__tui_annotate(he, evsel->idx, nr_events,
|
||||
err = hist_entry__tui_annotate(he, evsel->idx,
|
||||
timer, arg, delay_secs);
|
||||
he->used = false;
|
||||
ui_browser__update_nr_entries(&browser->b, browser->hists->nr_entries);
|
||||
|
@ -14,6 +14,9 @@ void ui_progress__update(u64 curr, u64 total, const char *title)
|
||||
if (use_browser <= 0)
|
||||
return;
|
||||
|
||||
if (total == 0)
|
||||
return;
|
||||
|
||||
ui__refresh_dimensions(true);
|
||||
pthread_mutex_lock(&ui__lock);
|
||||
y = SLtt_Screen_Rows / 2 - 2;
|
||||
|
Loading…
Reference in New Issue
Block a user