Commit 467f9957 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'perfcounters-core-for-linus' of...

Merge branch 'perfcounters-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip

* 'perfcounters-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip: (58 commits)
  perf_counter: Fix perf_copy_attr() pointer arithmetic
  perf utils: Use a define for the maximum length of a trace event
  perf: Add timechart help text and add timechart to "perf help"
  tracing, x86, cpuidle: Move the end point of a C state in the power tracer
  perf utils: Be consistent about minimum text size in the svghelper
  perf timechart: Add "perf timechart record"
  perf: Add the timechart tool
  perf: Add a SVG helper library file
  tracing, perf: Convert the power tracer into an event tracer
  perf: Add a sample_event type to the event_union
  perf: Allow perf utilities to have "callback" options without arguments
  perf: Store trace event name/id pairs in perf.data
  perf: Add a timestamp to fork events
  sched_clock: Make it NMI safe
  perf_counter: Fix up swcounter throttling
  x86, perf_counter, bts: Optimize BTS overflow handling
  perf sched: Add --input=file option to builtin-sched.c
  perf trace: Sample timestamp and cpu when using record flag
  perf tools: Increase MAX_EVENT_LENGTH
  perf tools: Fix memory leak in read_ftrace_printk()
  ...
parents 78f28b7c cdf8073d
The power tracer collects detailed information about C-state and P-state
transitions, instead of just looking at the high-level "average"
information.
There is a helper script found in scrips/tracing/power.pl in the kernel
sources which can be used to parse this information and create a
Scalable Vector Graphics (SVG) picture from the trace data.
To use this tracer:
echo 0 > /sys/kernel/debug/tracing/tracing_enabled
echo power > /sys/kernel/debug/tracing/current_tracer
echo 1 > /sys/kernel/debug/tracing/tracing_enabled
sleep 1
echo 0 > /sys/kernel/debug/tracing/tracing_enabled
cat /sys/kernel/debug/tracing/trace | \
perl scripts/tracing/power.pl > out.sv
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
#include <linux/cpufreq.h> #include <linux/cpufreq.h>
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/dmi.h> #include <linux/dmi.h>
#include <trace/power.h> #include <trace/events/power.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/io.h> #include <linux/io.h>
...@@ -72,8 +72,6 @@ static DEFINE_PER_CPU(struct acpi_cpufreq_data *, drv_data); ...@@ -72,8 +72,6 @@ static DEFINE_PER_CPU(struct acpi_cpufreq_data *, drv_data);
static DEFINE_PER_CPU(struct aperfmperf, old_perf); static DEFINE_PER_CPU(struct aperfmperf, old_perf);
DEFINE_TRACE(power_mark);
/* acpi_perf_data is a pointer to percpu data. */ /* acpi_perf_data is a pointer to percpu data. */
static struct acpi_processor_performance *acpi_perf_data; static struct acpi_processor_performance *acpi_perf_data;
...@@ -332,7 +330,6 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy, ...@@ -332,7 +330,6 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy,
unsigned int next_perf_state = 0; /* Index into perf table */ unsigned int next_perf_state = 0; /* Index into perf table */
unsigned int i; unsigned int i;
int result = 0; int result = 0;
struct power_trace it;
dprintk("acpi_cpufreq_target %d (%d)\n", target_freq, policy->cpu); dprintk("acpi_cpufreq_target %d (%d)\n", target_freq, policy->cpu);
...@@ -364,7 +361,7 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy, ...@@ -364,7 +361,7 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy,
} }
} }
trace_power_mark(&it, POWER_PSTATE, next_perf_state); trace_power_frequency(POWER_PSTATE, data->freq_table[next_state].frequency);
switch (data->cpu_feature) { switch (data->cpu_feature) {
case SYSTEM_INTEL_MSR_CAPABLE: case SYSTEM_INTEL_MSR_CAPABLE:
......
...@@ -36,10 +36,10 @@ static u64 perf_counter_mask __read_mostly; ...@@ -36,10 +36,10 @@ static u64 perf_counter_mask __read_mostly;
#define BTS_RECORD_SIZE 24 #define BTS_RECORD_SIZE 24
/* The size of a per-cpu BTS buffer in bytes: */ /* The size of a per-cpu BTS buffer in bytes: */
#define BTS_BUFFER_SIZE (BTS_RECORD_SIZE * 1024) #define BTS_BUFFER_SIZE (BTS_RECORD_SIZE * 2048)
/* The BTS overflow threshold in bytes from the end of the buffer: */ /* The BTS overflow threshold in bytes from the end of the buffer: */
#define BTS_OVFL_TH (BTS_RECORD_SIZE * 64) #define BTS_OVFL_TH (BTS_RECORD_SIZE * 128)
/* /*
...@@ -1488,8 +1488,7 @@ void perf_counter_print_debug(void) ...@@ -1488,8 +1488,7 @@ void perf_counter_print_debug(void)
local_irq_restore(flags); local_irq_restore(flags);
} }
static void intel_pmu_drain_bts_buffer(struct cpu_hw_counters *cpuc, static void intel_pmu_drain_bts_buffer(struct cpu_hw_counters *cpuc)
struct perf_sample_data *data)
{ {
struct debug_store *ds = cpuc->ds; struct debug_store *ds = cpuc->ds;
struct bts_record { struct bts_record {
...@@ -1498,8 +1497,11 @@ static void intel_pmu_drain_bts_buffer(struct cpu_hw_counters *cpuc, ...@@ -1498,8 +1497,11 @@ static void intel_pmu_drain_bts_buffer(struct cpu_hw_counters *cpuc,
u64 flags; u64 flags;
}; };
struct perf_counter *counter = cpuc->counters[X86_PMC_IDX_FIXED_BTS]; struct perf_counter *counter = cpuc->counters[X86_PMC_IDX_FIXED_BTS];
unsigned long orig_ip = data->regs->ip;
struct bts_record *at, *top; struct bts_record *at, *top;
struct perf_output_handle handle;
struct perf_event_header header;
struct perf_sample_data data;
struct pt_regs regs;
if (!counter) if (!counter)
return; return;
...@@ -1510,19 +1512,38 @@ static void intel_pmu_drain_bts_buffer(struct cpu_hw_counters *cpuc, ...@@ -1510,19 +1512,38 @@ static void intel_pmu_drain_bts_buffer(struct cpu_hw_counters *cpuc,
at = (struct bts_record *)(unsigned long)ds->bts_buffer_base; at = (struct bts_record *)(unsigned long)ds->bts_buffer_base;
top = (struct bts_record *)(unsigned long)ds->bts_index; top = (struct bts_record *)(unsigned long)ds->bts_index;
if (top <= at)
return;
ds->bts_index = ds->bts_buffer_base; ds->bts_index = ds->bts_buffer_base;
data.period = counter->hw.last_period;
data.addr = 0;
regs.ip = 0;
/*
* Prepare a generic sample, i.e. fill in the invariant fields.
* We will overwrite the from and to address before we output
* the sample.
*/
perf_prepare_sample(&header, &data, counter, &regs);
if (perf_output_begin(&handle, counter,
header.size * (top - at), 1, 1))
return;
for (; at < top; at++) { for (; at < top; at++) {
data->regs->ip = at->from; data.ip = at->from;
data->addr = at->to; data.addr = at->to;
perf_counter_output(counter, 1, data); perf_output_sample(&handle, &header, &data, counter);
} }
data->regs->ip = orig_ip; perf_output_end(&handle);
data->addr = 0;
/* There's new data available. */ /* There's new data available. */
counter->hw.interrupts++;
counter->pending_kill = POLL_IN; counter->pending_kill = POLL_IN;
} }
...@@ -1552,13 +1573,9 @@ static void x86_pmu_disable(struct perf_counter *counter) ...@@ -1552,13 +1573,9 @@ static void x86_pmu_disable(struct perf_counter *counter)
x86_perf_counter_update(counter, hwc, idx); x86_perf_counter_update(counter, hwc, idx);
/* Drain the remaining BTS records. */ /* Drain the remaining BTS records. */
if (unlikely(idx == X86_PMC_IDX_FIXED_BTS)) { if (unlikely(idx == X86_PMC_IDX_FIXED_BTS))
struct perf_sample_data data; intel_pmu_drain_bts_buffer(cpuc);
struct pt_regs regs;
data.regs = &regs;
intel_pmu_drain_bts_buffer(cpuc, &data);
}
cpuc->counters[idx] = NULL; cpuc->counters[idx] = NULL;
clear_bit(idx, cpuc->used_mask); clear_bit(idx, cpuc->used_mask);
...@@ -1619,7 +1636,6 @@ static int p6_pmu_handle_irq(struct pt_regs *regs) ...@@ -1619,7 +1636,6 @@ static int p6_pmu_handle_irq(struct pt_regs *regs)
int idx, handled = 0; int idx, handled = 0;
u64 val; u64 val;
data.regs = regs;
data.addr = 0; data.addr = 0;
cpuc = &__get_cpu_var(cpu_hw_counters); cpuc = &__get_cpu_var(cpu_hw_counters);
...@@ -1644,7 +1660,7 @@ static int p6_pmu_handle_irq(struct pt_regs *regs) ...@@ -1644,7 +1660,7 @@ static int p6_pmu_handle_irq(struct pt_regs *regs)
if (!x86_perf_counter_set_period(counter, hwc, idx)) if (!x86_perf_counter_set_period(counter, hwc, idx))
continue; continue;
if (perf_counter_overflow(counter, 1, &data)) if (perf_counter_overflow(counter, 1, &data, regs))
p6_pmu_disable_counter(hwc, idx); p6_pmu_disable_counter(hwc, idx);
} }
...@@ -1665,13 +1681,12 @@ static int intel_pmu_handle_irq(struct pt_regs *regs) ...@@ -1665,13 +1681,12 @@ static int intel_pmu_handle_irq(struct pt_regs *regs)
int bit, loops; int bit, loops;
u64 ack, status; u64 ack, status;
data.regs = regs;
data.addr = 0; data.addr = 0;
cpuc = &__get_cpu_var(cpu_hw_counters); cpuc = &__get_cpu_var(cpu_hw_counters);
perf_disable(); perf_disable();
intel_pmu_drain_bts_buffer(cpuc, &data); intel_pmu_drain_bts_buffer(cpuc);
status = intel_pmu_get_status(); status = intel_pmu_get_status();
if (!status) { if (!status) {
perf_enable(); perf_enable();
...@@ -1702,7 +1717,7 @@ static int intel_pmu_handle_irq(struct pt_regs *regs) ...@@ -1702,7 +1717,7 @@ static int intel_pmu_handle_irq(struct pt_regs *regs)
data.period = counter->hw.last_period; data.period = counter->hw.last_period;
if (perf_counter_overflow(counter, 1, &data)) if (perf_counter_overflow(counter, 1, &data, regs))
intel_pmu_disable_counter(&counter->hw, bit); intel_pmu_disable_counter(&counter->hw, bit);
} }
...@@ -1729,7 +1744,6 @@ static int amd_pmu_handle_irq(struct pt_regs *regs) ...@@ -1729,7 +1744,6 @@ static int amd_pmu_handle_irq(struct pt_regs *regs)
int idx, handled = 0; int idx, handled = 0;
u64 val; u64 val;
data.regs = regs;
data.addr = 0; data.addr = 0;
cpuc = &__get_cpu_var(cpu_hw_counters); cpuc = &__get_cpu_var(cpu_hw_counters);
...@@ -1754,7 +1768,7 @@ static int amd_pmu_handle_irq(struct pt_regs *regs) ...@@ -1754,7 +1768,7 @@ static int amd_pmu_handle_irq(struct pt_regs *regs)
if (!x86_perf_counter_set_period(counter, hwc, idx)) if (!x86_perf_counter_set_period(counter, hwc, idx))
continue; continue;
if (perf_counter_overflow(counter, 1, &data)) if (perf_counter_overflow(counter, 1, &data, regs))
amd_pmu_disable_counter(hwc, idx); amd_pmu_disable_counter(hwc, idx);
} }
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/clockchips.h> #include <linux/clockchips.h>
#include <linux/random.h> #include <linux/random.h>
#include <trace/power.h> #include <trace/events/power.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/apic.h> #include <asm/apic.h>
#include <asm/syscalls.h> #include <asm/syscalls.h>
...@@ -25,9 +25,6 @@ EXPORT_SYMBOL(idle_nomwait); ...@@ -25,9 +25,6 @@ EXPORT_SYMBOL(idle_nomwait);
struct kmem_cache *task_xstate_cachep; struct kmem_cache *task_xstate_cachep;
DEFINE_TRACE(power_start);
DEFINE_TRACE(power_end);
int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src) int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
{ {
*dst = *src; *dst = *src;
...@@ -299,9 +296,7 @@ static inline int hlt_use_halt(void) ...@@ -299,9 +296,7 @@ static inline int hlt_use_halt(void)
void default_idle(void) void default_idle(void)
{ {
if (hlt_use_halt()) { if (hlt_use_halt()) {
struct power_trace it; trace_power_start(POWER_CSTATE, 1);
trace_power_start(&it, POWER_CSTATE, 1);
current_thread_info()->status &= ~TS_POLLING; current_thread_info()->status &= ~TS_POLLING;
/* /*
* TS_POLLING-cleared state must be visible before we * TS_POLLING-cleared state must be visible before we
...@@ -314,7 +309,6 @@ void default_idle(void) ...@@ -314,7 +309,6 @@ void default_idle(void)
else else
local_irq_enable(); local_irq_enable();
current_thread_info()->status |= TS_POLLING; current_thread_info()->status |= TS_POLLING;
trace_power_end(&it);
} else { } else {
local_irq_enable(); local_irq_enable();
/* loop is done by the caller */ /* loop is done by the caller */
...@@ -372,9 +366,7 @@ EXPORT_SYMBOL_GPL(cpu_idle_wait); ...@@ -372,9 +366,7 @@ EXPORT_SYMBOL_GPL(cpu_idle_wait);
*/ */
void mwait_idle_with_hints(unsigned long ax, unsigned long cx) void mwait_idle_with_hints(unsigned long ax, unsigned long cx)
{ {
struct power_trace it; trace_power_start(POWER_CSTATE, (ax>>4)+1);
trace_power_start(&it, POWER_CSTATE, (ax>>4)+1);
if (!need_resched()) { if (!need_resched()) {
if (cpu_has(&current_cpu_data, X86_FEATURE_CLFLUSH_MONITOR)) if (cpu_has(&current_cpu_data, X86_FEATURE_CLFLUSH_MONITOR))
clflush((void *)&current_thread_info()->flags); clflush((void *)&current_thread_info()->flags);
...@@ -384,15 +376,13 @@ void mwait_idle_with_hints(unsigned long ax, unsigned long cx) ...@@ -384,15 +376,13 @@ void mwait_idle_with_hints(unsigned long ax, unsigned long cx)
if (!need_resched()) if (!need_resched())
__mwait(ax, cx); __mwait(ax, cx);
} }
trace_power_end(&it);
} }
/* Default MONITOR/MWAIT with no hints, used for default C1 state */ /* Default MONITOR/MWAIT with no hints, used for default C1 state */
static void mwait_idle(void) static void mwait_idle(void)
{ {
struct power_trace it;
if (!need_resched()) { if (!need_resched()) {
trace_power_start(&it, POWER_CSTATE, 1); trace_power_start(POWER_CSTATE, 1);
if (cpu_has(&current_cpu_data, X86_FEATURE_CLFLUSH_MONITOR)) if (cpu_has(&current_cpu_data, X86_FEATURE_CLFLUSH_MONITOR))
clflush((void *)&current_thread_info()->flags); clflush((void *)&current_thread_info()->flags);
...@@ -402,7 +392,6 @@ static void mwait_idle(void) ...@@ -402,7 +392,6 @@ static void mwait_idle(void)
__sti_mwait(0, 0); __sti_mwait(0, 0);
else else
local_irq_enable(); local_irq_enable();
trace_power_end(&it);
} else } else
local_irq_enable(); local_irq_enable();
} }
...@@ -414,13 +403,11 @@ static void mwait_idle(void) ...@@ -414,13 +403,11 @@ static void mwait_idle(void)
*/ */
static void poll_idle(void) static void poll_idle(void)
{ {
struct power_trace it; trace_power_start(POWER_CSTATE, 0);
trace_power_start(&it, POWER_CSTATE, 0);
local_irq_enable(); local_irq_enable();
while (!need_resched()) while (!need_resched())
cpu_relax(); cpu_relax();
trace_power_end(&it); trace_power_end(0);
} }
/* /*
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/cpuidle.h> #include <linux/cpuidle.h>
#include <linux/ktime.h> #include <linux/ktime.h>
#include <linux/hrtimer.h> #include <linux/hrtimer.h>
#include <trace/events/power.h>
#include "cpuidle.h" #include "cpuidle.h"
...@@ -91,6 +92,7 @@ static void cpuidle_idle_call(void) ...@@ -91,6 +92,7 @@ static void cpuidle_idle_call(void)
/* give the governor an opportunity to reflect on the outcome */ /* give the governor an opportunity to reflect on the outcome */
if (cpuidle_curr_governor->reflect) if (cpuidle_curr_governor->reflect)
cpuidle_curr_governor->reflect(dev); cpuidle_curr_governor->reflect(dev);
trace_power_end(0);
} }
/** /**
......
...@@ -199,10 +199,14 @@ struct perf_counter_attr { ...@@ -199,10 +199,14 @@ struct perf_counter_attr {
inherit_stat : 1, /* per task counts */ inherit_stat : 1, /* per task counts */
enable_on_exec : 1, /* next exec enables */ enable_on_exec : 1, /* next exec enables */
task : 1, /* trace fork/exit */ task : 1, /* trace fork/exit */
watermark : 1, /* wakeup_watermark */
__reserved_1 : 50; __reserved_1 : 49;
__u32 wakeup_events; /* wakeup every n events */ union {
__u32 wakeup_events; /* wakeup every n events */
__u32 wakeup_watermark; /* bytes before wakeup */
};
__u32 __reserved_2; __u32 __reserved_2;
__u64 __reserved_3; __u64 __reserved_3;
...@@ -332,6 +336,7 @@ enum perf_event_type { ...@@ -332,6 +336,7 @@ enum perf_event_type {
* struct perf_event_header header; * struct perf_event_header header;
* u32 pid, ppid; * u32 pid, ppid;
* u32 tid, ptid; * u32 tid, ptid;
* u64 time;
* }; * };
*/ */
PERF_EVENT_EXIT = 4, PERF_EVENT_EXIT = 4,
...@@ -352,6 +357,7 @@ enum perf_event_type { ...@@ -352,6 +357,7 @@ enum perf_event_type {
* struct perf_event_header header; * struct perf_event_header header;
* u32 pid, ppid; * u32 pid, ppid;
* u32 tid, ptid; * u32 tid, ptid;
* { u64 time; } && PERF_SAMPLE_TIME
* }; * };
*/ */
PERF_EVENT_FORK = 7, PERF_EVENT_FORK = 7,
...@@ -521,6 +527,8 @@ struct perf_mmap_data { ...@@ -521,6 +527,8 @@ struct perf_mmap_data {
atomic_t wakeup; /* needs a wakeup */ atomic_t wakeup; /* needs a wakeup */
atomic_t lost; /* nr records lost */ atomic_t lost; /* nr records lost */
long watermark; /* wakeup watermark */
struct perf_counter_mmap_page *user_page; struct perf_counter_mmap_page *user_page;
void *data_pages[0]; void *data_pages[0];
}; };
...@@ -685,6 +693,17 @@ struct perf_cpu_context { ...@@ -685,6 +693,17 @@ struct perf_cpu_context {
int recursion[4]; int recursion[4];
}; };
struct perf_output_handle {
struct perf_counter *counter;
struct perf_mmap_data *data;
unsigned long head;
unsigned long offset;
int nmi;
int sample;
int locked;
unsigned long flags;
};
#ifdef CONFIG_PERF_COUNTERS #ifdef CONFIG_PERF_COUNTERS
/* /*
...@@ -716,16 +735,38 @@ extern int hw_perf_group_sched_in(struct perf_counter *group_leader, ...@@ -716,16 +735,38 @@ extern int hw_perf_group_sched_in(struct perf_counter *group_leader,
extern void perf_counter_update_userpage(struct perf_counter *counter); extern void perf_counter_update_userpage(struct perf_counter *counter);
struct perf_sample_data { struct perf_sample_data {
struct pt_regs *regs; u64 type;
u64 ip;
struct {
u32 pid;
u32 tid;
} tid_entry;
u64 time;
u64 addr; u64 addr;
u64 id;
u64 stream_id;
struct {
u32 cpu;
u32 reserved;
} cpu_entry;
u64 period; u64 period;
struct perf_callchain_entry *callchain;
struct perf_raw_record *raw; struct perf_raw_record *raw;
}; };
extern void perf_output_sample(struct perf_output_handle *handle,
struct perf_event_header *header,
struct perf_sample_data *data,
struct perf_counter *counter);
extern void perf_prepare_sample(struct perf_event_header *header,
struct perf_sample_data *data,
struct perf_counter *counter,
struct pt_regs *regs);
extern int perf_counter_overflow(struct perf_counter *counter, int nmi, extern int perf_counter_overflow(struct perf_counter *counter, int nmi,
struct perf_sample_data *data); struct perf_sample_data *data,
extern void perf_counter_output(struct perf_counter *counter, int nmi, struct pt_regs *regs);
struct perf_sample_data *data);
/* /*
* Return 1 for a software counter, 0 for a hardware counter * Return 1 for a software counter, 0 for a hardware counter
...@@ -775,6 +816,12 @@ extern void perf_tpcounter_event(int event_id, u64 addr, u64 count, ...@@ -775,6 +816,12 @@ extern void perf_tpcounter_event(int event_id, u64 addr, u64 count,
#define perf_instruction_pointer(regs) instruction_pointer(regs) #define perf_instruction_pointer(regs) instruction_pointer(regs)
#endif #endif
extern int perf_output_begin(struct perf_output_handle *handle,
struct perf_counter *counter, unsigned int size,
int nmi, int sample);
extern void perf_output_end(struct perf_output_handle *handle);
extern void perf_output_copy(struct perf_output_handle *handle,
const void *buf, unsigned int len);
#else #else
static inline void static inline void
perf_counter_task_sched_in(struct task_struct *task, int cpu) { } perf_counter_task_sched_in(struct task_struct *task, int cpu) { }
...@@ -801,7 +848,28 @@ static inline void perf_counter_mmap(struct vm_area_struct *vma) { } ...@@ -801,7 +848,28 @@ static inline void perf_counter_mmap(struct vm_area_struct *vma) { }
static inline void perf_counter_comm(struct task_struct *tsk) { } static inline void perf_counter_comm(struct task_struct *tsk) { }
static inline void perf_counter_fork(struct task_struct *tsk) { } static inline void perf_counter_fork(struct task_struct *tsk) { }
static inline void perf_counter_init(void) { } static inline void perf_counter_init(void) { }
static inline int
perf_output_begin(struct perf_output_handle *handle, struct perf_counter *c,
unsigned int size, int nmi, int sample) { }
static inline void perf_output_end(struct perf_output_handle *handle) { }
static inline void
perf_output_copy(struct perf_output_handle *handle,
const void *buf, unsigned int len) { }
static inline void
perf_output_sample(struct perf_output_handle *handle,
struct perf_event_header *header,
struct perf_sample_data *data,
struct perf_counter *counter) { }
static inline void
perf_prepare_sample(struct perf_event_header *header,
struct perf_sample_data *data,
struct perf_counter *counter,
struct pt_regs *regs) { }
#endif #endif
#define perf_output_put(handle, x) \
perf_output_copy((handle), &(x), sizeof(x))
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
#endif /* _LINUX_PERF_COUNTER_H */ #endif /* _LINUX_PERF_COUNTER_H */
#undef TRACE_SYSTEM
#define TRACE_SYSTEM power
#if !defined(_TRACE_POWER_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_POWER_H
#include <linux/ktime.h>
#include <linux/tracepoint.h>
#ifndef _TRACE_POWER_ENUM_
#define _TRACE_POWER_ENUM_
enum {
POWER_NONE = 0,
POWER_CSTATE = 1,
POWER_PSTATE = 2,
};
#endif
TRACE_EVENT(power_start,
TP_PROTO(unsigned int type, unsigned int state),
TP_ARGS(type, state),
TP_STRUCT__entry(
__field( u64, type )
__field( u64, state )
),
TP_fast_assign(
__entry->type = type;
__entry->state = state;
),
TP_printk("type=%lu state=%lu", (unsigned long)__entry->type, (unsigned long)__entry->state)
);
TRACE_EVENT(power_end,
TP_PROTO(int dummy),
TP_ARGS(dummy),
TP_STRUCT__entry(
__field( u64, dummy )
),
TP_fast_assign(
__entry->dummy = 0xffff;
),
TP_printk("dummy=%lu", (unsigned long)__entry->dummy)
);
TRACE_EVENT(power_frequency,
TP_PROTO(unsigned int type, unsigned int state),
TP_ARGS(type, state),
TP_STRUCT__entry(
__field( u64, type )
__field( u64, state )
),
TP_fast_assign(
__entry->type = type;
__entry->state = state;
),
TP_printk("type=%lu state=%lu", (unsigned long)__entry->type, (unsigned long) __entry->state)
);
#endif /* _TRACE_POWER_H */
/* This part must be outside protection */
#include <trace/define_trace.h>
...@@ -379,6 +379,39 @@ TRACE_EVENT(sched_stat_wait, ...@@ -379,6 +379,39 @@ TRACE_EVENT(sched_stat_wait,
(unsigned long long)__entry->delay) (unsigned long long)__entry->delay)
); );
/*
* Tracepoint for accounting runtime (time the task is executing
* on a CPU).
*/
TRACE_EVENT(sched_stat_runtime,
TP_PROTO(struct task_struct *tsk, u64 runtime, u64 vruntime),
TP_ARGS(tsk, runtime, vruntime),
TP_STRUCT__entry(
__array( char, comm, TASK_COMM_LEN )
__field( pid_t, pid )
__field( u64, runtime )
__field( u64, vruntime )
),
TP_fast_assign(
memcpy(__entry->comm, tsk->comm, TASK_COMM_LEN);
__entry->pid = tsk->pid;
__entry->runtime = runtime;
__entry->vruntime = vruntime;
)
TP_perf_assign(
__perf_count(runtime);
),
TP_printk("task: %s:%d runtime: %Lu [ns], vruntime: %Lu [ns]",
__entry->comm, __entry->pid,
(unsigned long long)__entry->runtime,
(unsigned long long)__entry->vruntime)
);
/* /*
* Tracepoint for accounting sleep time (time the task is not runnable, * Tracepoint for accounting sleep time (time the task is not runnable,
* including iowait, see below). * including iowait, see below).
......
This diff is collapsed.
...@@ -48,13 +48,6 @@ static __read_mostly int sched_clock_running; ...@@ -48,13 +48,6 @@ static __read_mostly int sched_clock_running;
__read_mostly int sched_clock_stable; __read_mostly int sched_clock_stable;
struct sched_clock_data { struct sched_clock_data {
/*
* Raw spinlock - this is a special case: this might be called
* from within instrumentation code so we dont want to do any
* instrumentation ourselves.
*/
raw_spinlock_t lock;
u64 tick_raw; u64 tick_raw;
u64 tick_gtod; u64 tick_gtod;
u64 clock; u64 clock;
...@@ -80,7 +73,6 @@ void sched_clock_init(void) ...@@ -80,7 +73,6 @@ void sched_clock_init(void)
for_each_possible_cpu(cpu) { for_each_possible_cpu(cpu) {
struct sched_clock_data *scd = cpu_sdc(cpu); struct sched_clock_data *scd = cpu_sdc(cpu);
scd->lock = (raw_spinlock_t)__RAW_SPIN_LOCK_UNLOCKED;
scd->tick_raw = 0; scd->tick_raw = 0;
scd->tick_gtod = ktime_now; scd->tick_gtod = ktime_now;
scd->clock = ktime_now; scd->clock = ktime_now;
...@@ -109,14 +101,19 @@ static inline u64 wrap_max(u64 x, u64 y) ...@@ -109,14 +101,19 @@ static inline u64 wrap_max(u64 x, u64 y)
* - filter out backward motion * - filter out backward motion
* - use the GTOD tick value to create a window to filter crazy TSC values * - use the GTOD tick value to create a window to filter crazy TSC values
*/ */
static u64 __update_sched_clock(struct sched_clock_data *scd, u64 now) static u64 sched_clock_local(struct sched_clock_data *scd)
{ {
s64 delta = now - scd->tick_raw; u64 now, clock, old_clock, min_clock, max_clock;
u64 clock, min_clock, max_clock; s64 delta;
again:
now = sched_clock();
delta = now - scd->tick_raw;
if (unlikely(delta < 0)) if (unlikely(delta < 0))
delta = 0; delta = 0;
old_clock = scd->clock;
/* /*
* scd->clock = clamp(scd->tick_gtod + delta, * scd->clock = clamp(scd->tick_gtod + delta,
* max(scd->tick_gtod, scd->clock), * max(scd->tick_gtod, scd->clock),
...@@ -124,84 +121,73 @@ static u64 __update_sched_clock(struct sched_clock_data *scd, u64 now) ...@@ -124,84 +121,73 @@ static u64 __update_sched_clock(struct sched_clock_data *scd, u64 now)
*/ */
clock = scd->tick_gtod + delta; clock = scd->tick_gtod + delta;
min_clock = wrap_max(scd->tick_gtod, scd->clock); min_clock = wrap_max(scd->tick_gtod, old_clock);
max_clock = wrap_max(scd->clock, scd->tick_gtod + TICK_NSEC); max_clock = wrap_max(old_clock, scd->tick_gtod + TICK_NSEC);
clock = wrap_max(clock, min_clock); clock = wrap_max(clock, min_clock);
clock = wrap_min(clock, max_clock); clock = wrap_min(clock, max_clock);
scd->clock = clock; if (cmpxchg(&scd->clock, old_clock, clock) != old_clock)
goto again;
return scd->clock; return clock;
} }
static void lock_double_clock(struct sched_clock_data *data1, static u64 sched_clock_remote(struct sched_clock_data *scd)
struct sched_clock_data *data2)
{ {
if (data1 < data2) { struct sched_clock_data *my_scd = this_scd();
__raw_spin_lock(&data1->lock); u64 this_clock, remote_clock;
__raw_spin_lock(&data2->lock); u64 *ptr, old_val, val;
sched_clock_local(my_scd);
again:
this_clock = my_scd->clock;
remote_clock = scd->clock;
/*
* Use the opportunity that we have both locks
* taken to couple the two clocks: we take the
* larger time as the latest time for both
* runqueues. (this creates monotonic movement)
*/
if (likely((s64)(remote_clock - this_clock) < 0)) {
ptr = &scd->clock;
old_val = remote_clock;
val = this_clock;
} else { } else {
__raw_spin_lock(&data2->lock); /*
__raw_spin_lock(&data1->lock); * Should be rare, but possible:
*/
ptr = &my_scd->clock;
old_val = this_clock;
val = remote_clock;
} }
if (cmpxchg(ptr, old_val, val) != old_val)
goto again;
return val;
} }
u64 sched_clock_cpu(int cpu) u64 sched_clock_cpu(int cpu)
{ {
u64 now, clock, this_clock, remote_clock;
struct sched_clock_data *scd; struct sched_clock_data *scd;
u64 clock;
WARN_ON_ONCE(!irqs_disabled());
if (sched_clock_stable) if (sched_clock_stable)
return sched_clock(); return sched_clock();
scd = cpu_sdc(cpu);
/*
* Normally this is not called in NMI context - but if it is,
* trying to do any locking here is totally lethal.
*/
if (unlikely(in_nmi()))
return scd->clock;
if (unlikely(!sched_clock_running)) if (unlikely(!sched_clock_running))
return 0ull; return 0ull;
WARN_ON_ONCE(!irqs_disabled()); scd = cpu_sdc(cpu);
now = sched_clock();
if (cpu != raw_smp_processor_id()) {
struct sched_clock_data *my_scd = this_scd();
lock_double_clock(scd, my_scd);
this_clock = __update_sched_clock(my_scd, now);
remote_clock = scd->clock;
/*
* Use the opportunity that we have both locks
* taken to couple the two clocks: we take the
* larger time as the latest time for both
* runqueues. (this creates monotonic movement)
*/
if (likely((s64)(remote_clock - this_clock) < 0)) {
clock = this_clock;
scd->clock = clock;
} else {
/*
* Should be rare, but possible:
*/
clock = remote_clock;
my_scd->clock = remote_clock;
}
__raw_spin_unlock(&my_scd->lock);
} else {
__raw_spin_lock(&scd->lock);
clock = __update_sched_clock(scd, now);
}
__raw_spin_unlock(&scd->lock); if (cpu != smp_processor_id())
clock = sched_clock_remote(scd);
else
clock = sched_clock_local(scd);
return clock; return clock;
} }
...@@ -223,11 +209,9 @@ void sched_clock_tick(void) ...@@ -223,11 +209,9 @@ void sched_clock_tick(void)
now_gtod = ktime_to_ns(ktime_get()); now_gtod = ktime_to_ns(ktime_get());
now = sched_clock(); now = sched_clock();
__raw_spin_lock(&scd->lock);
scd->tick_raw = now; scd->tick_raw = now;
scd->tick_gtod = now_gtod; scd->tick_gtod = now_gtod;
__update_sched_clock(scd, now); sched_clock_local(scd);
__raw_spin_unlock(&scd->lock);
} }
/* /*
......
...@@ -513,6 +513,7 @@ static void update_curr(struct cfs_rq *cfs_rq) ...@@ -513,6 +513,7 @@ static void update_curr(struct cfs_rq *cfs_rq)
if (entity_is_task(curr)) { if (entity_is_task(curr)) {
struct task_struct *curtask = task_of(curr); struct task_struct *curtask = task_of(curr);
trace_sched_stat_runtime(curtask, delta_exec, curr->vruntime);
cpuacct_charge(curtask, delta_exec); cpuacct_charge(curtask, delta_exec);
account_group_exec_runtime(curtask, delta_exec); account_group_exec_runtime(curtask, delta_exec);
} }
......
...@@ -42,7 +42,6 @@ obj-$(CONFIG_BOOT_TRACER) += trace_boot.o ...@@ -42,7 +42,6 @@ obj-$(CONFIG_BOOT_TRACER) += trace_boot.o
obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += trace_functions_graph.o obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += trace_functions_graph.o
obj-$(CONFIG_TRACE_BRANCH_PROFILING) += trace_branch.o obj-$(CONFIG_TRACE_BRANCH_PROFILING) += trace_branch.o
obj-$(CONFIG_HW_BRANCH_TRACER) += trace_hw_branches.o obj-$(CONFIG_HW_BRANCH_TRACER) += trace_hw_branches.o
obj-$(CONFIG_POWER_TRACER) += trace_power.o
obj-$(CONFIG_KMEMTRACE) += kmemtrace.o obj-$(CONFIG_KMEMTRACE) += kmemtrace.o
obj-$(CONFIG_WORKQUEUE_TRACER) += trace_workqueue.o obj-$(CONFIG_WORKQUEUE_TRACER) += trace_workqueue.o
obj-$(CONFIG_BLK_DEV_IO_TRACE) += blktrace.o obj-$(CONFIG_BLK_DEV_IO_TRACE) += blktrace.o
...@@ -54,5 +53,6 @@ obj-$(CONFIG_EVENT_TRACING) += trace_export.o ...@@ -54,5 +53,6 @@ obj-$(CONFIG_EVENT_TRACING) += trace_export.o
obj-$(CONFIG_FTRACE_SYSCALLS) += trace_syscalls.o obj-$(CONFIG_FTRACE_SYSCALLS) += trace_syscalls.o
obj-$(CONFIG_EVENT_PROFILE) += trace_event_profile.o obj-$(CONFIG_EVENT_PROFILE) += trace_event_profile.o
obj-$(CONFIG_EVENT_TRACING) += trace_events_filter.o obj-$(CONFIG_EVENT_TRACING) += trace_events_filter.o
obj-$(CONFIG_EVENT_TRACING) += power-traces.o
libftrace-y := ftrace.o libftrace-y := ftrace.o
/*
* Power trace points
*
* Copyright (C) 2009 Arjan van de Ven <arjan@linux.intel.com>
*/
#include <linux/string.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/slab.h>
#define CREATE_TRACE_POINTS
#include <trace/events/power.h>
EXPORT_TRACEPOINT_SYMBOL_GPL(power_start);
EXPORT_TRACEPOINT_SYMBOL_GPL(power_end);
EXPORT_TRACEPOINT_SYMBOL_GPL(power_frequency);
...@@ -11,7 +11,6 @@ ...@@ -11,7 +11,6 @@
#include <linux/ftrace.h> #include <linux/ftrace.h>
#include <trace/boot.h> #include <trace/boot.h>
#include <linux/kmemtrace.h> #include <linux/kmemtrace.h>
#include <trace/power.h>
#include <linux/trace_seq.h> #include <linux/trace_seq.h>
#include <linux/ftrace_event.h> #include <linux/ftrace_event.h>
...@@ -37,7 +36,6 @@ enum trace_type { ...@@ -37,7 +36,6 @@ enum trace_type {
TRACE_HW_BRANCHES, TRACE_HW_BRANCHES,
TRACE_KMEM_ALLOC, TRACE_KMEM_ALLOC,
TRACE_KMEM_FREE, TRACE_KMEM_FREE,
TRACE_POWER,
TRACE_BLK, TRACE_BLK,
__TRACE_LAST_TYPE, __TRACE_LAST_TYPE,
...@@ -207,7 +205,6 @@ extern void __ftrace_bad_type(void); ...@@ -207,7 +205,6 @@ extern void __ftrace_bad_type(void);
IF_ASSIGN(var, ent, struct ftrace_graph_ret_entry, \ IF_ASSIGN(var, ent, struct ftrace_graph_ret_entry, \
TRACE_GRAPH_RET); \ TRACE_GRAPH_RET); \
IF_ASSIGN(var, ent, struct hw_branch_entry, TRACE_HW_BRANCHES);\ IF_ASSIGN(var, ent, struct hw_branch_entry, TRACE_HW_BRANCHES);\
IF_ASSIGN(var, ent, struct trace_power, TRACE_POWER); \
IF_ASSIGN(var, ent, struct kmemtrace_alloc_entry, \ IF_ASSIGN(var, ent, struct kmemtrace_alloc_entry, \
TRACE_KMEM_ALLOC); \ TRACE_KMEM_ALLOC); \
IF_ASSIGN(var, ent, struct kmemtrace_free_entry, \ IF_ASSIGN(var, ent, struct kmemtrace_free_entry, \
......
...@@ -330,23 +330,6 @@ FTRACE_ENTRY(hw_branch, hw_branch_entry, ...@@ -330,23 +330,6 @@ FTRACE_ENTRY(hw_branch, hw_branch_entry,
F_printk("from: %llx to: %llx", __entry->from, __entry->to) F_printk("from: %llx to: %llx", __entry->from, __entry->to)
); );
FTRACE_ENTRY(power, trace_power,
TRACE_POWER,
F_STRUCT(
__field_struct( struct power_trace, state_data )
__field_desc( s64, state_data, stamp )
__field_desc( s64, state_data, end )
__field_desc( int, state_data, type )
__field_desc( int, state_data, state )
),
F_printk("%llx->%llx type:%u state:%u",
__entry->stamp, __entry->end,
__entry->type, __entry->state)
);
FTRACE_ENTRY(kmem_alloc, kmemtrace_alloc_entry, FTRACE_ENTRY(kmem_alloc, kmemtrace_alloc_entry,
TRACE_KMEM_ALLOC, TRACE_KMEM_ALLOC,
......
/*
* ring buffer based C-state tracer
*
* Arjan van de Ven <arjan@linux.intel.com>
* Copyright (C) 2008 Intel Corporation
*
* Much is borrowed from trace_boot.c which is
* Copyright (C) 2008 Frederic Weisbecker <fweisbec@gmail.com>
*
*/
#include <linux/init.h>
#include <linux/debugfs.h>
#include <trace/power.h>
#include <linux/kallsyms.h>
#include <linux/module.h>
#include "trace.h"
#include "trace_output.h"
static struct trace_array *power_trace;
static int __read_mostly trace_power_enabled;
static void probe_power_start(struct power_trace *it, unsigned int type,
unsigned int level)
{
if (!trace_power_enabled)
return;
memset(it, 0, sizeof(struct power_trace));
it->state = level;
it->type = type;
it->stamp = ktime_get();
}
static void probe_power_end(struct power_trace *it)
{
struct ftrace_event_call *call = &event_power;
struct ring_buffer_event *event;
struct ring_buffer *buffer;
struct trace_power *entry;
struct trace_array_cpu *data;
struct trace_array *tr = power_trace;
if (!trace_power_enabled)
return;
buffer = tr->buffer;
preempt_disable();
it->end = ktime_get();
data = tr->data[smp_processor_id()];
event = trace_buffer_lock_reserve(buffer, TRACE_POWER,
sizeof(*entry), 0, 0);
if (!event)
goto out;
entry = ring_buffer_event_data(event);
entry->state_data = *it;
if (!filter_check_discard(call, entry, buffer, event))
trace_buffer_unlock_commit(buffer, event, 0, 0);
out:
preempt_enable();
}
static void probe_power_mark(struct power_trace *it, unsigned int type,
unsigned int level)
{
struct ftrace_event_call *call = &event_power;
struct ring_buffer_event *event;
struct ring_buffer *buffer;
struct trace_power *entry;
struct trace_array_cpu *data;
struct trace_array *tr = power_trace;
if (!trace_power_enabled)
return;
buffer = tr->buffer;
memset(it, 0, sizeof(struct power_trace));
it->state = level;
it->type = type;
it->stamp = ktime_get();
preempt_disable();
it->end = it->stamp;
data = tr->data[smp_processor_id()];
event = trace_buffer_lock_reserve(buffer, TRACE_POWER,
sizeof(*entry), 0, 0);
if (!event)
goto out;
entry = ring_buffer_event_data(event);
entry->state_data = *it;
if (!filter_check_discard(call, entry, buffer, event))
trace_buffer_unlock_commit(buffer, event, 0, 0);
out:
preempt_enable();
}
static int tracing_power_register(void)
{
int ret;
ret = register_trace_power_start(probe_power_start);
if (ret) {
pr_info("power trace: Couldn't activate tracepoint"
" probe to trace_power_start\n");
return ret;
}
ret = register_trace_power_end(probe_power_end);
if (ret) {
pr_info("power trace: Couldn't activate tracepoint"
" probe to trace_power_end\n");
goto fail_start;
}
ret = register_trace_power_mark(probe_power_mark);
if (ret) {
pr_info("power trace: Couldn't activate tracepoint"
" probe to trace_power_mark\n");
goto fail_end;
}
return ret;
fail_end:
unregister_trace_power_end(probe_power_end);
fail_start:
unregister_trace_power_start(probe_power_start);
return ret;
}
static void start_power_trace(struct trace_array *tr)
{
trace_power_enabled = 1;
}
static void stop_power_trace(struct trace_array *tr)
{
trace_power_enabled = 0;
}
static void power_trace_reset(struct trace_array *tr)
{
trace_power_enabled = 0;
unregister_trace_power_start(probe_power_start);
unregister_trace_power_end(probe_power_end);
unregister_trace_power_mark(probe_power_mark);
}
static int power_trace_init(struct trace_array *tr)
{
power_trace = tr;
trace_power_enabled = 1;
tracing_power_register();
tracing_reset_online_cpus(tr);
return 0;
}
static enum print_line_t power_print_line(struct trace_iterator *iter)
{
int ret = 0;
struct trace_entry *entry = iter->ent;
struct trace_power *field ;
struct power_trace *it;
struct trace_seq *s = &iter->seq;
struct timespec stamp;
struct timespec duration;
trace_assign_type(field, entry);
it = &field->state_data;
stamp = ktime_to_timespec(it->stamp);
duration = ktime_to_timespec(ktime_sub(it->end, it->stamp));
if (entry->type == TRACE_POWER) {
if (it->type == POWER_CSTATE)
ret = trace_seq_printf(s, "[%5ld.%09ld] CSTATE: Going to C%i on cpu %i for %ld.%09ld\n",
stamp.tv_sec,
stamp.tv_nsec,
it->state, iter->cpu,
duration.tv_sec,
duration.tv_nsec);
if (it->type == POWER_PSTATE)
ret = trace_seq_printf(s, "[%5ld.%09ld] PSTATE: Going to P%i on cpu %i\n",
stamp.tv_sec,
stamp.tv_nsec,
it->state, iter->cpu);
if (!ret)
return TRACE_TYPE_PARTIAL_LINE;
return TRACE_TYPE_HANDLED;
}
return TRACE_TYPE_UNHANDLED;
}
static void power_print_header(struct seq_file *s)
{
seq_puts(s, "# TIMESTAMP STATE EVENT\n");
seq_puts(s, "# | | |\n");
}
static struct tracer power_tracer __read_mostly =
{
.name = "power",
.init = power_trace_init,
.start = start_power_trace,
.stop = stop_power_trace,
.reset = power_trace_reset,
.print_line = power_print_line,
.print_header = power_print_header,
};
static int init_power_trace(void)
{
return register_tracer(&power_tracer);
}
device_initcall(init_power_trace);
#!/usr/bin/perl
# Copyright 2008, Intel Corporation
#
# This file is part of the Linux kernel
#
# This program file 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; version 2 of the License.
#
# 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 in a file named COPYING; if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301 USA
#
# Authors:
# Arjan van de Ven <arjan@linux.intel.com>
#
# This script turns a cstate ftrace output into a SVG graphic that shows
# historic C-state information
#
#
# cat /sys/kernel/debug/tracing/trace | perl power.pl > out.svg
#
my @styles;
my $base = 0;
my @pstate_last;
my @pstate_level;
$styles[0] = "fill:rgb(0,0,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)";
$styles[1] = "fill:rgb(0,255,0);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)";
$styles[2] = "fill:rgb(255,0,20);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)";
$styles[3] = "fill:rgb(255,255,20);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)";
$styles[4] = "fill:rgb(255,0,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)";
$styles[5] = "fill:rgb(0,255,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)";
$styles[6] = "fill:rgb(0,128,255);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)";
$styles[7] = "fill:rgb(0,255,128);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)";
$styles[8] = "fill:rgb(0,25,20);fill-opacity:0.5;stroke-width:1;stroke:rgb(0,0,0)";
print "<?xml version=\"1.0\" standalone=\"no\"?> \n";
print "<svg width=\"10000\" height=\"100%\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n";
my $scale = 30000.0;
while (<>) {
my $line = $_;
if ($line =~ /([0-9\.]+)\] CSTATE: Going to C([0-9]) on cpu ([0-9]+) for ([0-9\.]+)/) {
if ($base == 0) {
$base = $1;
}
my $time = $1 - $base;
$time = $time * $scale;
my $C = $2;
my $cpu = $3;
my $y = 400 * $cpu;
my $duration = $4 * $scale;
my $msec = int($4 * 100000)/100.0;
my $height = $C * 20;
$style = $styles[$C];
$y = $y + 140 - $height;
$x2 = $time + 4;
$y2 = $y + 4;
print "<rect x=\"$time\" width=\"$duration\" y=\"$y\" height=\"$height\" style=\"$style\"/>\n";
print "<text transform=\"translate($x2,$y2) rotate(90)\">C$C $msec</text>\n";
}
if ($line =~ /([0-9\.]+)\] PSTATE: Going to P([0-9]) on cpu ([0-9]+)/) {
my $time = $1 - $base;
my $state = $2;
my $cpu = $3;
if (defined($pstate_last[$cpu])) {
my $from = $pstate_last[$cpu];
my $oldstate = $pstate_state[$cpu];
my $duration = ($time-$from) * $scale;
$from = $from * $scale;
my $to = $from + $duration;
my $height = 140 - ($oldstate * (140/8));
my $y = 400 * $cpu + 200 + $height;
my $y2 = $y+4;
my $style = $styles[8];
print "<rect x=\"$from\" y=\"$y\" width=\"$duration\" height=\"5\" style=\"$style\"/>\n";
print "<text transform=\"translate($from,$y2)\">P$oldstate (cpu $cpu)</text>\n";
};
$pstate_last[$cpu] = $time;
$pstate_state[$cpu] = $state;
}
}
print "</svg>\n";
perf-sched(1)
==============
NAME
----
perf-sched - Tool to trace/measure scheduler properties (latencies)
SYNOPSIS
--------
[verse]
'perf sched' {record|latency|replay|trace}
DESCRIPTION
-----------
There's four variants of perf sched:
'perf sched record <command>' to record the scheduling events
of an arbitrary workload.
'perf sched latency' to report the per task scheduling latencies
and other scheduling properties of the workload.
'perf sched trace' to see a detailed trace of the workload that
was recorded.
'perf sched replay' to simulate the workload that was recorded
via perf sched record. (this is done by starting up mockup threads
that mimic the workload based on the events in the trace. These
threads can then replay the timings (CPU runtime and sleep patterns)
of the workload as it occured when it was recorded - and can repeat
it a number of times, measuring its performance.)
OPTIONS
-------
-D::
--dump-raw-trace=::
Display verbose dump of the sched data.
SEE ALSO
--------
linkperf:perf-record[1]
perf-timechart(1)
=================
NAME
----
perf-timechart - Tool to visualize total system behavior during a workload
SYNOPSIS
--------
[verse]
'perf timechart' {record}
DESCRIPTION
-----------
There are two variants of perf timechart:
'perf timechart record <command>' to record the system level events
of an arbitrary workload.
'perf timechart' to turn a trace into a Scalable Vector Graphics file,
that can be viewed with popular SVG viewers such as 'Inkscape'.
OPTIONS
-------
-o::
--output=::
Select the output file (default: output.svg)
-i::
--input=::
Select the input file (default: perf.data)
SEE ALSO
--------
linkperf:perf-record[1]
perf-trace(1)
==============
NAME
----
perf-trace - Read perf.data (created by perf record) and display trace output
SYNOPSIS
--------
[verse]
'perf trace' [-i <file> | --input=file] symbol_name
DESCRIPTION
-----------
This command reads the input file and displays the trace recorded.
OPTIONS
-------
-D::
--dump-raw-trace=::
Display verbose dump of the trace data.
SEE ALSO
--------
linkperf:perf-record[1]
...@@ -373,13 +373,16 @@ LIB_OBJS += util/thread.o ...@@ -373,13 +373,16 @@ LIB_OBJS += util/thread.o
LIB_OBJS += util/trace-event-parse.o LIB_OBJS += util/trace-event-parse.o
LIB_OBJS += util/trace-event-read.o LIB_OBJS += util/trace-event-read.o
LIB_OBJS += util/trace-event-info.o LIB_OBJS += util/trace-event-info.o
LIB_OBJS += util/svghelper.o
BUILTIN_OBJS += builtin-annotate.o BUILTIN_OBJS += builtin-annotate.o
BUILTIN_OBJS += builtin-help.o BUILTIN_OBJS += builtin-help.o
BUILTIN_OBJS += builtin-sched.o
BUILTIN_OBJS += builtin-list.o BUILTIN_OBJS += builtin-list.o
BUILTIN_OBJS += builtin-record.o BUILTIN_OBJS += builtin-record.o
BUILTIN_OBJS += builtin-report.o BUILTIN_OBJS += builtin-report.o
BUILTIN_OBJS += builtin-stat.o BUILTIN_OBJS += builtin-stat.o
BUILTIN_OBJS += builtin-timechart.o
BUILTIN_OBJS += builtin-top.o BUILTIN_OBJS += builtin-top.o
BUILTIN_OBJS += builtin-trace.o BUILTIN_OBJS += builtin-trace.o
...@@ -710,6 +713,12 @@ builtin-help.o: builtin-help.c common-cmds.h PERF-CFLAGS ...@@ -710,6 +713,12 @@ builtin-help.o: builtin-help.c common-cmds.h PERF-CFLAGS
'-DPERF_MAN_PATH="$(mandir_SQ)"' \ '-DPERF_MAN_PATH="$(mandir_SQ)"' \
'-DPERF_INFO_PATH="$(infodir_SQ)"' $< '-DPERF_INFO_PATH="$(infodir_SQ)"' $<
builtin-timechart.o: builtin-timechart.c common-cmds.h PERF-CFLAGS
$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \
'-DPERF_HTML_PATH="$(htmldir_SQ)"' \
'-DPERF_MAN_PATH="$(mandir_SQ)"' \
'-DPERF_INFO_PATH="$(infodir_SQ)"' $<
$(BUILT_INS): perf$X $(BUILT_INS): perf$X
$(QUIET_BUILT_IN)$(RM) $@ && \ $(QUIET_BUILT_IN)$(RM) $@ && \
ln perf$X $@ 2>/dev/null || \ ln perf$X $@ 2>/dev/null || \
......
...@@ -48,6 +48,8 @@ static int call_graph = 0; ...@@ -48,6 +48,8 @@ static int call_graph = 0;
static int inherit_stat = 0; static int inherit_stat = 0;
static int no_samples = 0; static int no_samples = 0;
static int sample_address = 0; static int sample_address = 0;
static int multiplex = 0;
static int multiplex_fd = -1;
static long samples; static long samples;
static struct timeval last_read; static struct timeval last_read;
...@@ -470,19 +472,28 @@ static void create_counter(int counter, int cpu, pid_t pid) ...@@ -470,19 +472,28 @@ static void create_counter(int counter, int cpu, pid_t pid)
*/ */
if (group && group_fd == -1) if (group && group_fd == -1)
group_fd = fd[nr_cpu][counter]; group_fd = fd[nr_cpu][counter];
if (multiplex && multiplex_fd == -1)
multiplex_fd = fd[nr_cpu][counter];
event_array[nr_poll].fd = fd[nr_cpu][counter]; if (multiplex && fd[nr_cpu][counter] != multiplex_fd) {
event_array[nr_poll].events = POLLIN; int ret;
nr_poll++;
ret = ioctl(fd[nr_cpu][counter], PERF_COUNTER_IOC_SET_OUTPUT, multiplex_fd);
mmap_array[nr_cpu][counter].counter = counter; assert(ret != -1);
mmap_array[nr_cpu][counter].prev = 0; } else {
mmap_array[nr_cpu][counter].mask = mmap_pages*page_size - 1; event_array[nr_poll].fd = fd[nr_cpu][counter];
mmap_array[nr_cpu][counter].base = mmap(NULL, (mmap_pages+1)*page_size, event_array[nr_poll].events = POLLIN;
PROT_READ|PROT_WRITE, MAP_SHARED, fd[nr_cpu][counter], 0); nr_poll++;
if (mmap_array[nr_cpu][counter].base == MAP_FAILED) {
error("failed to mmap with %d (%s)\n", errno, strerror(errno)); mmap_array[nr_cpu][counter].counter = counter;
exit(-1); mmap_array[nr_cpu][counter].prev = 0;
mmap_array[nr_cpu][counter].mask = mmap_pages*page_size - 1;
mmap_array[nr_cpu][counter].base = mmap(NULL, (mmap_pages+1)*page_size,
PROT_READ|PROT_WRITE, MAP_SHARED, fd[nr_cpu][counter], 0);
if (mmap_array[nr_cpu][counter].base == MAP_FAILED) {
error("failed to mmap with %d (%s)\n", errno, strerror(errno));
exit(-1);
}
} }
ioctl(fd[nr_cpu][counter], PERF_COUNTER_IOC_ENABLE); ioctl(fd[nr_cpu][counter], PERF_COUNTER_IOC_ENABLE);
...@@ -513,6 +524,7 @@ static int __cmd_record(int argc, const char **argv) ...@@ -513,6 +524,7 @@ static int __cmd_record(int argc, const char **argv)
pid_t pid = 0; pid_t pid = 0;
int flags; int flags;
int ret; int ret;
unsigned long waking = 0;
page_size = sysconf(_SC_PAGE_SIZE); page_size = sysconf(_SC_PAGE_SIZE);
nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
...@@ -614,17 +626,29 @@ static int __cmd_record(int argc, const char **argv) ...@@ -614,17 +626,29 @@ static int __cmd_record(int argc, const char **argv)
int hits = samples; int hits = samples;
for (i = 0; i < nr_cpu; i++) { for (i = 0; i < nr_cpu; i++) {
for (counter = 0; counter < nr_counters; counter++) for (counter = 0; counter < nr_counters; counter++) {
mmap_read(&mmap_array[i][counter]); if (mmap_array[i][counter].base)
mmap_read(&mmap_array[i][counter]);
}
} }
if (hits == samples) { if (hits == samples) {
if (done) if (done)
break; break;
ret = poll(event_array, nr_poll, 100); ret = poll(event_array, nr_poll, -1);
waking++;
}
if (done) {
for (i = 0; i < nr_cpu; i++) {
for (counter = 0; counter < nr_counters; counter++)
ioctl(fd[i][counter], PERF_COUNTER_IOC_DISABLE);
}
} }
} }
fprintf(stderr, "[ perf record: Woken up %ld times to write data ]\n", waking);
/* /*
* Approximate RIP event size: 24 bytes. * Approximate RIP event size: 24 bytes.
*/ */
...@@ -681,6 +705,8 @@ static const struct option options[] = { ...@@ -681,6 +705,8 @@ static const struct option options[] = {
"Sample addresses"), "Sample addresses"),
OPT_BOOLEAN('n', "no-samples", &no_samples, OPT_BOOLEAN('n', "no-samples", &no_samples,
"don't sample"), "don't sample"),
OPT_BOOLEAN('M', "multiplex", &multiplex,
"multiplex counter output in a single channel"),
OPT_END() OPT_END()
}; };
......
This diff is collapsed.
This diff is collapsed.
...@@ -16,12 +16,14 @@ extern int check_pager_config(const char *cmd); ...@@ -16,12 +16,14 @@ extern int check_pager_config(const char *cmd);
extern int cmd_annotate(int argc, const char **argv, const char *prefix); extern int cmd_annotate(int argc, const char **argv, const char *prefix);
extern int cmd_help(int argc, const char **argv, const char *prefix); extern int cmd_help(int argc, const char **argv, const char *prefix);
extern int cmd_sched(int argc, const char **argv, const char *prefix);
extern int cmd_list(int argc, const char **argv, const char *prefix);
extern int cmd_record(int argc, const char **argv, const char *prefix); extern int cmd_record(int argc, const char **argv, const char *prefix);
extern int cmd_report(int argc, const char **argv, const char *prefix); extern int cmd_report(int argc, const char **argv, const char *prefix);
extern int cmd_stat(int argc, const char **argv, const char *prefix); extern int cmd_stat(int argc, const char **argv, const char *prefix);
extern int cmd_timechart(int argc, const char **argv, const char *prefix);
extern int cmd_top(int argc, const char **argv, const char *prefix); extern int cmd_top(int argc, const char **argv, const char *prefix);
extern int cmd_version(int argc, const char **argv, const char *prefix);
extern int cmd_list(int argc, const char **argv, const char *prefix);
extern int cmd_trace(int argc, const char **argv, const char *prefix); extern int cmd_trace(int argc, const char **argv, const char *prefix);
extern int cmd_version(int argc, const char **argv, const char *prefix);
#endif #endif
...@@ -4,7 +4,10 @@ ...@@ -4,7 +4,10 @@
# #
perf-annotate mainporcelain common perf-annotate mainporcelain common
perf-list mainporcelain common perf-list mainporcelain common
perf-sched mainporcelain common
perf-record mainporcelain common perf-record mainporcelain common
perf-report mainporcelain common perf-report mainporcelain common
perf-stat mainporcelain common perf-stat mainporcelain common
perf-timechart mainporcelain common
perf-top mainporcelain common perf-top mainporcelain common
perf-trace mainporcelain common
...@@ -289,10 +289,12 @@ static void handle_internal_command(int argc, const char **argv) ...@@ -289,10 +289,12 @@ static void handle_internal_command(int argc, const char **argv)
{ "record", cmd_record, 0 }, { "record", cmd_record, 0 },
{ "report", cmd_report, 0 }, { "report", cmd_report, 0 },
{ "stat", cmd_stat, 0 }, { "stat", cmd_stat, 0 },
{ "timechart", cmd_timechart, 0 },
{ "top", cmd_top, 0 }, { "top", cmd_top, 0 },
{ "annotate", cmd_annotate, 0 }, { "annotate", cmd_annotate, 0 },
{ "version", cmd_version, 0 }, { "version", cmd_version, 0 },
{ "trace", cmd_trace, 0 }, { "trace", cmd_trace, 0 },
{ "sched", cmd_sched, 0 },
}; };
unsigned int i; unsigned int i;
static const char ext[] = STRIP_EXTENSION; static const char ext[] = STRIP_EXTENSION;
......
...@@ -39,6 +39,7 @@ struct fork_event { ...@@ -39,6 +39,7 @@ struct fork_event {
struct perf_event_header header; struct perf_event_header header;
u32 pid, ppid; u32 pid, ppid;
u32 tid, ptid; u32 tid, ptid;
u64 time;
}; };
struct lost_event { struct lost_event {
...@@ -52,13 +53,19 @@ struct lost_event { ...@@ -52,13 +53,19 @@ struct lost_event {
*/ */
struct read_event { struct read_event {
struct perf_event_header header; struct perf_event_header header;
u32 pid,tid; u32 pid, tid;
u64 value; u64 value;
u64 time_enabled; u64 time_enabled;
u64 time_running; u64 time_running;
u64 id; u64 id;
}; };
struct sample_event{
struct perf_event_header header;
u64 array[];
};
typedef union event_union { typedef union event_union {
struct perf_event_header header; struct perf_event_header header;
struct ip_event ip; struct ip_event ip;
...@@ -67,6 +74,7 @@ typedef union event_union { ...@@ -67,6 +74,7 @@ typedef union event_union {
struct fork_event fork; struct fork_event fork;
struct lost_event lost; struct lost_event lost;
struct read_event read; struct read_event read;
struct sample_event sample;
} event_t; } event_t;
struct map { struct map {
......
...@@ -7,9 +7,8 @@ ...@@ -7,9 +7,8 @@
#include "header.h" #include "header.h"
/* /*
* * Create new perf.data header attribute:
*/ */
struct perf_header_attr *perf_header_attr__new(struct perf_counter_attr *attr) struct perf_header_attr *perf_header_attr__new(struct perf_counter_attr *attr)
{ {
struct perf_header_attr *self = malloc(sizeof(*self)); struct perf_header_attr *self = malloc(sizeof(*self));
...@@ -43,9 +42,8 @@ void perf_header_attr__add_id(struct perf_header_attr *self, u64 id) ...@@ -43,9 +42,8 @@ void perf_header_attr__add_id(struct perf_header_attr *self, u64 id)
} }
/* /*
* * Create new perf.data header:
*/ */
struct perf_header *perf_header__new(void) struct perf_header *perf_header__new(void)
{ {
struct perf_header *self = malloc(sizeof(*self)); struct perf_header *self = malloc(sizeof(*self));
...@@ -86,6 +84,46 @@ void perf_header__add_attr(struct perf_header *self, ...@@ -86,6 +84,46 @@ void perf_header__add_attr(struct perf_header *self,
self->attr[pos] = attr; self->attr[pos] = attr;
} }
#define MAX_EVENT_NAME 64
struct perf_trace_event_type {
u64 event_id;
char name[MAX_EVENT_NAME];
};
static int event_count;
static struct perf_trace_event_type *events;
void perf_header__push_event(u64 id, const char *name)
{
if (strlen(name) > MAX_EVENT_NAME)
printf("Event %s will be truncated\n", name);
if (!events) {
events = malloc(sizeof(struct perf_trace_event_type));
if (!events)
die("nomem");
} else {
events = realloc(events, (event_count + 1) * sizeof(struct perf_trace_event_type));
if (!events)
die("nomem");
}
memset(&events[event_count], 0, sizeof(struct perf_trace_event_type));
events[event_count].event_id = id;
strncpy(events[event_count].name, name, MAX_EVENT_NAME - 1);
event_count++;
}
char *perf_header__find_event(u64 id)
{
int i;
for (i = 0 ; i < event_count; i++) {
if (events[i].event_id == id)
return events[i].name;
}
return NULL;
}
static const char *__perf_magic = "PERFFILE"; static const char *__perf_magic = "PERFFILE";
#define PERF_MAGIC (*(u64 *)__perf_magic) #define PERF_MAGIC (*(u64 *)__perf_magic)
...@@ -106,6 +144,7 @@ struct perf_file_header { ...@@ -106,6 +144,7 @@ struct perf_file_header {
u64 attr_size; u64 attr_size;
struct perf_file_section attrs; struct perf_file_section attrs;
struct perf_file_section data; struct perf_file_section data;
struct perf_file_section event_types;
}; };
static void do_write(int fd, void *buf, size_t size) static void do_write(int fd, void *buf, size_t size)
...@@ -154,6 +193,11 @@ void perf_header__write(struct perf_header *self, int fd) ...@@ -154,6 +193,11 @@ void perf_header__write(struct perf_header *self, int fd)
do_write(fd, &f_attr, sizeof(f_attr)); do_write(fd, &f_attr, sizeof(f_attr));
} }
self->event_offset = lseek(fd, 0, SEEK_CUR);
self->event_size = event_count * sizeof(struct perf_trace_event_type);
if (events)
do_write(fd, events, self->event_size);
self->data_offset = lseek(fd, 0, SEEK_CUR); self->data_offset = lseek(fd, 0, SEEK_CUR);
...@@ -169,6 +213,10 @@ void perf_header__write(struct perf_header *self, int fd) ...@@ -169,6 +213,10 @@ void perf_header__write(struct perf_header *self, int fd)
.offset = self->data_offset, .offset = self->data_offset,
.size = self->data_size, .size = self->data_size,
}, },
.event_types = {
.offset = self->event_offset,
.size = self->event_size,
},
}; };
lseek(fd, 0, SEEK_SET); lseek(fd, 0, SEEK_SET);
...@@ -234,6 +282,17 @@ struct perf_header *perf_header__read(int fd) ...@@ -234,6 +282,17 @@ struct perf_header *perf_header__read(int fd)
lseek(fd, tmp, SEEK_SET); lseek(fd, tmp, SEEK_SET);
} }
if (f_header.event_types.size) {
lseek(fd, f_header.event_types.offset, SEEK_SET);
events = malloc(f_header.event_types.size);
if (!events)
die("nomem");
do_read(fd, events, f_header.event_types.size);
event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type);
}
self->event_offset = f_header.event_types.offset;
self->event_size = f_header.event_types.size;
self->data_offset = f_header.data.offset; self->data_offset = f_header.data.offset;
self->data_size = f_header.data.size; self->data_size = f_header.data.size;
......
...@@ -19,6 +19,8 @@ struct perf_header { ...@@ -19,6 +19,8 @@ struct perf_header {
s64 attr_offset; s64 attr_offset;
u64 data_offset; u64 data_offset;
u64 data_size; u64 data_size;
u64 event_offset;
u64 event_size;
}; };
struct perf_header *perf_header__read(int fd); struct perf_header *perf_header__read(int fd);
...@@ -27,6 +29,10 @@ void perf_header__write(struct perf_header *self, int fd); ...@@ -27,6 +29,10 @@ void perf_header__write(struct perf_header *self, int fd);
void perf_header__add_attr(struct perf_header *self, void perf_header__add_attr(struct perf_header *self,
struct perf_header_attr *attr); struct perf_header_attr *attr);
void perf_header__push_event(u64 id, const char *name);
char *perf_header__find_event(u64 id);
struct perf_header_attr * struct perf_header_attr *
perf_header_attr__new(struct perf_counter_attr *attr); perf_header_attr__new(struct perf_counter_attr *attr);
void perf_header_attr__add_id(struct perf_header_attr *self, u64 id); void perf_header_attr__add_id(struct perf_header_attr *self, u64 id);
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "exec_cmd.h" #include "exec_cmd.h"
#include "string.h" #include "string.h"
#include "cache.h" #include "cache.h"
#include "header.h"
int nr_counters; int nr_counters;
...@@ -18,6 +19,12 @@ struct event_symbol { ...@@ -18,6 +19,12 @@ struct event_symbol {
const char *alias; const char *alias;
}; };
enum event_result {
EVT_FAILED,
EVT_HANDLED,
EVT_HANDLED_ALL
};
char debugfs_path[MAXPATHLEN]; char debugfs_path[MAXPATHLEN];
#define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x #define CHW(x) .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_##x
...@@ -139,7 +146,7 @@ static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir) ...@@ -139,7 +146,7 @@ static int tp_event_has_id(struct dirent *sys_dir, struct dirent *evt_dir)
(strcmp(evt_dirent.d_name, "..")) && \ (strcmp(evt_dirent.d_name, "..")) && \
(!tp_event_has_id(&sys_dirent, &evt_dirent))) (!tp_event_has_id(&sys_dirent, &evt_dirent)))
#define MAX_EVENT_LENGTH 30 #define MAX_EVENT_LENGTH 512
int valid_debugfs_mount(const char *debugfs) int valid_debugfs_mount(const char *debugfs)
{ {
...@@ -344,7 +351,7 @@ static int parse_aliases(const char **str, const char *names[][MAX_ALIASES], int ...@@ -344,7 +351,7 @@ static int parse_aliases(const char **str, const char *names[][MAX_ALIASES], int
return -1; return -1;
} }
static int static enum event_result
parse_generic_hw_event(const char **str, struct perf_counter_attr *attr) parse_generic_hw_event(const char **str, struct perf_counter_attr *attr)
{ {
const char *s = *str; const char *s = *str;
...@@ -356,7 +363,7 @@ parse_generic_hw_event(const char **str, struct perf_counter_attr *attr) ...@@ -356,7 +363,7 @@ parse_generic_hw_event(const char **str, struct perf_counter_attr *attr)
* then bail out: * then bail out:
*/ */
if (cache_type == -1) if (cache_type == -1)
return 0; return EVT_FAILED;
while ((cache_op == -1 || cache_result == -1) && *s == '-') { while ((cache_op == -1 || cache_result == -1) && *s == '-') {
++s; ++s;
...@@ -402,27 +409,115 @@ parse_generic_hw_event(const char **str, struct perf_counter_attr *attr) ...@@ -402,27 +409,115 @@ parse_generic_hw_event(const char **str, struct perf_counter_attr *attr)
attr->type = PERF_TYPE_HW_CACHE; attr->type = PERF_TYPE_HW_CACHE;
*str = s; *str = s;
return 1; return EVT_HANDLED;
}
static enum event_result
parse_single_tracepoint_event(char *sys_name,
const char *evt_name,
unsigned int evt_length,
char *flags,
struct perf_counter_attr *attr,
const char **strp)
{
char evt_path[MAXPATHLEN];
char id_buf[4];
u64 id;
int fd;
if (flags) {
if (!strncmp(flags, "record", strlen(flags))) {
attr->sample_type |= PERF_SAMPLE_RAW;
attr->sample_type |= PERF_SAMPLE_TIME;
attr->sample_type |= PERF_SAMPLE_CPU;
}
}
snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path,
sys_name, evt_name);
fd = open(evt_path, O_RDONLY);
if (fd < 0)
return EVT_FAILED;
if (read(fd, id_buf, sizeof(id_buf)) < 0) {
close(fd);
return EVT_FAILED;
}
close(fd);
id = atoll(id_buf);
attr->config = id;
attr->type = PERF_TYPE_TRACEPOINT;
*strp = evt_name + evt_length;
return EVT_HANDLED;
}
/* sys + ':' + event + ':' + flags*/
#define MAX_EVOPT_LEN (MAX_EVENT_LENGTH * 2 + 2 + 128)
static enum event_result
parse_subsystem_tracepoint_event(char *sys_name, char *flags)
{
char evt_path[MAXPATHLEN];
struct dirent *evt_ent;
DIR *evt_dir;
snprintf(evt_path, MAXPATHLEN, "%s/%s", debugfs_path, sys_name);
evt_dir = opendir(evt_path);
if (!evt_dir) {
perror("Can't open event dir");
return EVT_FAILED;
}
while ((evt_ent = readdir(evt_dir))) {
char event_opt[MAX_EVOPT_LEN + 1];
int len;
unsigned int rem = MAX_EVOPT_LEN;
if (!strcmp(evt_ent->d_name, ".")
|| !strcmp(evt_ent->d_name, "..")
|| !strcmp(evt_ent->d_name, "enable")
|| !strcmp(evt_ent->d_name, "filter"))
continue;
len = snprintf(event_opt, MAX_EVOPT_LEN, "%s:%s", sys_name,
evt_ent->d_name);
if (len < 0)
return EVT_FAILED;
rem -= len;
if (flags) {
if (rem < strlen(flags) + 1)
return EVT_FAILED;
strcat(event_opt, ":");
strcat(event_opt, flags);
}
if (parse_events(NULL, event_opt, 0))
return EVT_FAILED;
}
return EVT_HANDLED_ALL;
} }
static int parse_tracepoint_event(const char **strp,
static enum event_result parse_tracepoint_event(const char **strp,
struct perf_counter_attr *attr) struct perf_counter_attr *attr)
{ {
const char *evt_name; const char *evt_name;
char *flags; char *flags;
char sys_name[MAX_EVENT_LENGTH]; char sys_name[MAX_EVENT_LENGTH];
char id_buf[4];
int fd;
unsigned int sys_length, evt_length; unsigned int sys_length, evt_length;
u64 id;
char evt_path[MAXPATHLEN];
if (valid_debugfs_mount(debugfs_path)) if (valid_debugfs_mount(debugfs_path))
return 0; return 0;
evt_name = strchr(*strp, ':'); evt_name = strchr(*strp, ':');
if (!evt_name) if (!evt_name)
return 0; return EVT_FAILED;
sys_length = evt_name - *strp; sys_length = evt_name - *strp;
if (sys_length >= MAX_EVENT_LENGTH) if (sys_length >= MAX_EVENT_LENGTH)
...@@ -434,32 +529,22 @@ static int parse_tracepoint_event(const char **strp, ...@@ -434,32 +529,22 @@ static int parse_tracepoint_event(const char **strp,
flags = strchr(evt_name, ':'); flags = strchr(evt_name, ':');
if (flags) { if (flags) {
*flags = '\0'; /* split it out: */
evt_name = strndup(evt_name, flags - evt_name);
flags++; flags++;
if (!strncmp(flags, "record", strlen(flags)))
attr->sample_type |= PERF_SAMPLE_RAW;
} }
evt_length = strlen(evt_name); evt_length = strlen(evt_name);
if (evt_length >= MAX_EVENT_LENGTH) if (evt_length >= MAX_EVENT_LENGTH)
return 0; return EVT_FAILED;
snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", debugfs_path,
sys_name, evt_name);
fd = open(evt_path, O_RDONLY);
if (fd < 0)
return 0;
if (read(fd, id_buf, sizeof(id_buf)) < 0) { if (!strcmp(evt_name, "*")) {
close(fd); *strp = evt_name + evt_length;
return 0; return parse_subsystem_tracepoint_event(sys_name, flags);
} } else
close(fd); return parse_single_tracepoint_event(sys_name, evt_name,
id = atoll(id_buf); evt_length, flags,
attr->config = id; attr, strp);
attr->type = PERF_TYPE_TRACEPOINT;
*strp = evt_name + evt_length;
return 1;
} }
static int check_events(const char *str, unsigned int i) static int check_events(const char *str, unsigned int i)
...@@ -477,7 +562,7 @@ static int check_events(const char *str, unsigned int i) ...@@ -477,7 +562,7 @@ static int check_events(const char *str, unsigned int i)
return 0; return 0;
} }
static int static enum event_result
parse_symbolic_event(const char **strp, struct perf_counter_attr *attr) parse_symbolic_event(const char **strp, struct perf_counter_attr *attr)
{ {
const char *str = *strp; const char *str = *strp;
...@@ -490,31 +575,32 @@ parse_symbolic_event(const char **strp, struct perf_counter_attr *attr) ...@@ -490,31 +575,32 @@ parse_symbolic_event(const char **strp, struct perf_counter_attr *attr)
attr->type = event_symbols[i].type; attr->type = event_symbols[i].type;
attr->config = event_symbols[i].config; attr->config = event_symbols[i].config;
*strp = str + n; *strp = str + n;
return 1; return EVT_HANDLED;
} }
} }
return 0; return EVT_FAILED;
} }
static int parse_raw_event(const char **strp, struct perf_counter_attr *attr) static enum event_result
parse_raw_event(const char **strp, struct perf_counter_attr *attr)
{ {
const char *str = *strp; const char *str = *strp;
u64 config; u64 config;
int n; int n;
if (*str != 'r') if (*str != 'r')
return 0; return EVT_FAILED;
n = hex2u64(str + 1, &config); n = hex2u64(str + 1, &config);
if (n > 0) { if (n > 0) {
*strp = str + n + 1; *strp = str + n + 1;
attr->type = PERF_TYPE_RAW; attr->type = PERF_TYPE_RAW;
attr->config = config; attr->config = config;
return 1; return EVT_HANDLED;
} }
return 0; return EVT_FAILED;
} }
static int static enum event_result
parse_numeric_event(const char **strp, struct perf_counter_attr *attr) parse_numeric_event(const char **strp, struct perf_counter_attr *attr)
{ {
const char *str = *strp; const char *str = *strp;
...@@ -530,13 +616,13 @@ parse_numeric_event(const char **strp, struct perf_counter_attr *attr) ...@@ -530,13 +616,13 @@ parse_numeric_event(const char **strp, struct perf_counter_attr *attr)
attr->type = type; attr->type = type;
attr->config = config; attr->config = config;
*strp = endp; *strp = endp;
return 1; return EVT_HANDLED;
} }
} }
return 0; return EVT_FAILED;
} }
static int static enum event_result
parse_event_modifier(const char **strp, struct perf_counter_attr *attr) parse_event_modifier(const char **strp, struct perf_counter_attr *attr)
{ {
const char *str = *strp; const char *str = *strp;
...@@ -569,37 +655,84 @@ parse_event_modifier(const char **strp, struct perf_counter_attr *attr) ...@@ -569,37 +655,84 @@ parse_event_modifier(const char **strp, struct perf_counter_attr *attr)
* Each event can have multiple symbolic names. * Each event can have multiple symbolic names.
* Symbolic names are (almost) exactly matched. * Symbolic names are (almost) exactly matched.
*/ */
static int parse_event_symbols(const char **str, struct perf_counter_attr *attr) static enum event_result
parse_event_symbols(const char **str, struct perf_counter_attr *attr)
{ {
if (!(parse_tracepoint_event(str, attr) || enum event_result ret;
parse_raw_event(str, attr) ||
parse_numeric_event(str, attr) || ret = parse_tracepoint_event(str, attr);
parse_symbolic_event(str, attr) || if (ret != EVT_FAILED)
parse_generic_hw_event(str, attr))) goto modifier;
return 0;
ret = parse_raw_event(str, attr);
if (ret != EVT_FAILED)
goto modifier;
ret = parse_numeric_event(str, attr);
if (ret != EVT_FAILED)
goto modifier;
ret = parse_symbolic_event(str, attr);
if (ret != EVT_FAILED)
goto modifier;
ret = parse_generic_hw_event(str, attr);
if (ret != EVT_FAILED)
goto modifier;
return EVT_FAILED;
modifier:
parse_event_modifier(str, attr); parse_event_modifier(str, attr);
return 1; return ret;
} }
static void store_event_type(const char *orgname)
{
char filename[PATH_MAX], *c;
FILE *file;
int id;
sprintf(filename, "/sys/kernel/debug/tracing/events/%s/id", orgname);
c = strchr(filename, ':');
if (c)
*c = '/';
file = fopen(filename, "r");
if (!file)
return;
if (fscanf(file, "%i", &id) < 1)
die("cannot store event ID");
fclose(file);
perf_header__push_event(id, orgname);
}
int parse_events(const struct option *opt __used, const char *str, int unset __used) int parse_events(const struct option *opt __used, const char *str, int unset __used)
{ {
struct perf_counter_attr attr; struct perf_counter_attr attr;
enum event_result ret;
if (strchr(str, ':'))
store_event_type(str);
for (;;) { for (;;) {
if (nr_counters == MAX_COUNTERS) if (nr_counters == MAX_COUNTERS)
return -1; return -1;
memset(&attr, 0, sizeof(attr)); memset(&attr, 0, sizeof(attr));
if (!parse_event_symbols(&str, &attr)) ret = parse_event_symbols(&str, &attr);
if (ret == EVT_FAILED)
return -1; return -1;
if (!(*str == 0 || *str == ',' || isspace(*str))) if (!(*str == 0 || *str == ',' || isspace(*str)))
return -1; return -1;
attrs[nr_counters] = attr; if (ret != EVT_HANDLED_ALL) {
nr_counters++; attrs[nr_counters] = attr;
nr_counters++;
}
if (*str == 0) if (*str == 0)
break; break;
......
...@@ -104,6 +104,8 @@ struct option { ...@@ -104,6 +104,8 @@ struct option {
{ .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = "time", .help = (h), .callback = parse_opt_approxidate_cb } { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), .argh = "time", .help = (h), .callback = parse_opt_approxidate_cb }
#define OPT_CALLBACK(s, l, v, a, h, f) \ #define OPT_CALLBACK(s, l, v, a, h, f) \
{ .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f) } { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f) }
#define OPT_CALLBACK_NOOPT(s, l, v, a, h, f) \
{ .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f), .flags = PARSE_OPT_NOARG }
#define OPT_CALLBACK_DEFAULT(s, l, v, a, h, f, d) \ #define OPT_CALLBACK_DEFAULT(s, l, v, a, h, f, d) \
{ .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f), .defval = (intptr_t)d, .flags = PARSE_OPT_LASTARG_DEFAULT } { .type = OPTION_CALLBACK, .short_name = (s), .long_name = (l), .value = (v), (a), .help = (h), .callback = (f), .defval = (intptr_t)d, .flags = PARSE_OPT_LASTARG_DEFAULT }
......
This diff is collapsed.
#ifndef _INCLUDE_GUARD_SVG_HELPER_
#define _INCLUDE_GUARD_SVG_HELPER_
#include "types.h"
extern void open_svg(const char *filename, int cpus, int rows);
extern void svg_box(int Yslot, u64 start, u64 end, const char *type);
extern void svg_sample(int Yslot, int cpu, u64 start, u64 end, const char *type);
extern void svg_cpu_box(int cpu, u64 max_frequency, u64 turbo_frequency);
extern void svg_process(int cpu, u64 start, u64 end, const char *type, const char *name);
extern void svg_cstate(int cpu, u64 start, u64 end, int type);
extern void svg_pstate(int cpu, u64 start, u64 end, u64 freq);
extern void svg_time_grid(u64 start, u64 end);
extern void svg_legenda(void);
extern void svg_wakeline(u64 start, int row1, int row2);
extern void svg_partial_wakeline(u64 start, int row1, int row2);
extern void svg_interrupt(u64 start, int row);
extern void svg_text(int Yslot, u64 start, const char *text);
extern void svg_close(void);
#endif
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
static struct thread *thread__new(pid_t pid) static struct thread *thread__new(pid_t pid)
{ {
struct thread *self = malloc(sizeof(*self)); struct thread *self = calloc(1, sizeof(*self));
if (self != NULL) { if (self != NULL) {
self->pid = pid; self->pid = pid;
...@@ -85,7 +85,7 @@ register_idle_thread(struct rb_root *threads, struct thread **last_match) ...@@ -85,7 +85,7 @@ register_idle_thread(struct rb_root *threads, struct thread **last_match)
{ {
struct thread *thread = threads__findnew(0, threads, last_match); struct thread *thread = threads__findnew(0, threads, last_match);
if (!thread || thread__set_comm(thread, "[init]")) { if (!thread || thread__set_comm(thread, "swapper")) {
fprintf(stderr, "problem inserting idle task.\n"); fprintf(stderr, "problem inserting idle task.\n");
exit(-1); exit(-1);
} }
......
...@@ -4,10 +4,11 @@ ...@@ -4,10 +4,11 @@
#include "symbol.h" #include "symbol.h"
struct thread { struct thread {
struct rb_node rb_node; struct rb_node rb_node;
struct list_head maps; struct list_head maps;
pid_t pid; pid_t pid;
char *comm; char shortname[3];
char *comm;
}; };
int thread__set_comm(struct thread *self, const char *comm); int thread__set_comm(struct thread *self, const char *comm);
......
...@@ -458,7 +458,7 @@ static void read_proc_kallsyms(void) ...@@ -458,7 +458,7 @@ static void read_proc_kallsyms(void)
static void read_ftrace_printk(void) static void read_ftrace_printk(void)
{ {
unsigned int size, check_size; unsigned int size, check_size;
const char *path; char *path;
struct stat st; struct stat st;
int ret; int ret;
...@@ -468,14 +468,15 @@ static void read_ftrace_printk(void) ...@@ -468,14 +468,15 @@ static void read_ftrace_printk(void)
/* not found */ /* not found */
size = 0; size = 0;
write_or_die(&size, 4); write_or_die(&size, 4);
return; goto out;
} }
size = get_size(path); size = get_size(path);
write_or_die(&size, 4); write_or_die(&size, 4);
check_size = copy_file(path); check_size = copy_file(path);
if (size != check_size) if (size != check_size)
die("error in size of file '%s'", path); die("error in size of file '%s'", path);
out:
put_tracing_file(path);
} }
static struct tracepoint_path * static struct tracepoint_path *
......
...@@ -1776,6 +1776,29 @@ static unsigned long long read_size(void *ptr, int size) ...@@ -1776,6 +1776,29 @@ static unsigned long long read_size(void *ptr, int size)
} }
} }
unsigned long long
raw_field_value(struct event *event, const char *name, void *data)
{
struct format_field *field;
field = find_any_field(event, name);
if (!field)
return 0ULL;
return read_size(data + field->offset, field->size);
}
void *raw_field_ptr(struct event *event, const char *name, void *data)
{
struct format_field *field;
field = find_any_field(event, name);
if (!field)
return NULL;
return data + field->offset;
}
static int get_common_info(const char *type, int *offset, int *size) static int get_common_info(const char *type, int *offset, int *size)
{ {
struct event *event; struct event *event;
...@@ -1799,7 +1822,7 @@ static int get_common_info(const char *type, int *offset, int *size) ...@@ -1799,7 +1822,7 @@ static int get_common_info(const char *type, int *offset, int *size)
return 0; return 0;
} }
static int parse_common_type(void *data) int trace_parse_common_type(void *data)
{ {
static int type_offset; static int type_offset;
static int type_size; static int type_size;
...@@ -1832,7 +1855,7 @@ static int parse_common_pid(void *data) ...@@ -1832,7 +1855,7 @@ static int parse_common_pid(void *data)
return read_size(data + pid_offset, pid_size); return read_size(data + pid_offset, pid_size);
} }
static struct event *find_event(int id) struct event *trace_find_event(int id)
{ {
struct event *event; struct event *event;
...@@ -2420,8 +2443,8 @@ get_return_for_leaf(int cpu, int cur_pid, unsigned long long cur_func, ...@@ -2420,8 +2443,8 @@ get_return_for_leaf(int cpu, int cur_pid, unsigned long long cur_func,
int type; int type;
int pid; int pid;
type = parse_common_type(next->data); type = trace_parse_common_type(next->data);
event = find_event(type); event = trace_find_event(type);
if (!event) if (!event)
return NULL; return NULL;
...@@ -2502,8 +2525,8 @@ print_graph_entry_leaf(struct event *event, void *data, struct record *ret_rec) ...@@ -2502,8 +2525,8 @@ print_graph_entry_leaf(struct event *event, void *data, struct record *ret_rec)
int type; int type;
int i; int i;
type = parse_common_type(ret_rec->data); type = trace_parse_common_type(ret_rec->data);
ret_event = find_event(type); ret_event = trace_find_event(type);
field = find_field(ret_event, "rettime"); field = find_field(ret_event, "rettime");
if (!field) if (!field)
...@@ -2696,11 +2719,13 @@ void print_event(int cpu, void *data, int size, unsigned long long nsecs, ...@@ -2696,11 +2719,13 @@ void print_event(int cpu, void *data, int size, unsigned long long nsecs,
nsecs -= secs * NSECS_PER_SEC; nsecs -= secs * NSECS_PER_SEC;
usecs = nsecs / NSECS_PER_USEC; usecs = nsecs / NSECS_PER_USEC;
type = parse_common_type(data); type = trace_parse_common_type(data);
event = find_event(type); event = trace_find_event(type);
if (!event) if (!event) {
die("ug! no event found for type %d", type); printf("ug! no event found for type %d\n", type);
return;
}
pid = parse_common_pid(data); pid = parse_common_pid(data);
......
...@@ -458,12 +458,13 @@ struct record *trace_read_data(int cpu) ...@@ -458,12 +458,13 @@ struct record *trace_read_data(int cpu)
return data; return data;
} }
void trace_report (void) void trace_report(void)
{ {
const char *input_file = "trace.info"; const char *input_file = "trace.info";
char buf[BUFSIZ]; char buf[BUFSIZ];
char test[] = { 23, 8, 68 }; char test[] = { 23, 8, 68 };
char *version; char *version;
int show_version = 0;
int show_funcs = 0; int show_funcs = 0;
int show_printk = 0; int show_printk = 0;
...@@ -480,7 +481,8 @@ void trace_report (void) ...@@ -480,7 +481,8 @@ void trace_report (void)
die("not a trace file (missing tracing)"); die("not a trace file (missing tracing)");
version = read_string(); version = read_string();
printf("version = %s\n", version); if (show_version)
printf("version = %s\n", version);
free(version); free(version);
read_or_die(buf, 1); read_or_die(buf, 1);
......
...@@ -234,6 +234,11 @@ extern int header_page_data_offset; ...@@ -234,6 +234,11 @@ extern int header_page_data_offset;
extern int header_page_data_size; extern int header_page_data_size;
int parse_header_page(char *buf, unsigned long size); int parse_header_page(char *buf, unsigned long size);
int trace_parse_common_type(void *data);
struct event *trace_find_event(int id);
unsigned long long
raw_field_value(struct event *event, const char *name, void *data);
void *raw_field_ptr(struct event *event, const char *name, void *data);
void read_tracing_data(struct perf_counter_attr *pattrs, int nb_counters); void read_tracing_data(struct perf_counter_attr *pattrs, int nb_counters);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment