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()
  ...
This commit is contained in:
Linus Torvalds 2009-09-20 15:54:37 -07:00
commit 467f9957d9
40 changed files with 4571 additions and 760 deletions

View file

@ -1,17 +0,0 @@
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

View file

@ -33,7 +33,7 @@
#include <linux/cpufreq.h>
#include <linux/compiler.h>
#include <linux/dmi.h>
#include <trace/power.h>
#include <trace/events/power.h>
#include <linux/acpi.h>
#include <linux/io.h>
@ -72,8 +72,6 @@ static DEFINE_PER_CPU(struct acpi_cpufreq_data *, drv_data);
static DEFINE_PER_CPU(struct aperfmperf, old_perf);
DEFINE_TRACE(power_mark);
/* acpi_perf_data is a pointer to percpu data. */
static struct acpi_processor_performance *acpi_perf_data;
@ -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 i;
int result = 0;
struct power_trace it;
dprintk("acpi_cpufreq_target %d (%d)\n", target_freq, policy->cpu);
@ -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) {
case SYSTEM_INTEL_MSR_CAPABLE:

View file

@ -36,10 +36,10 @@ static u64 perf_counter_mask __read_mostly;
#define BTS_RECORD_SIZE 24
/* 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: */
#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)
local_irq_restore(flags);
}
static void intel_pmu_drain_bts_buffer(struct cpu_hw_counters *cpuc,
struct perf_sample_data *data)
static void intel_pmu_drain_bts_buffer(struct cpu_hw_counters *cpuc)
{
struct debug_store *ds = cpuc->ds;
struct bts_record {
@ -1498,8 +1497,11 @@ static void intel_pmu_drain_bts_buffer(struct cpu_hw_counters *cpuc,
u64 flags;
};
struct perf_counter *counter = cpuc->counters[X86_PMC_IDX_FIXED_BTS];
unsigned long orig_ip = data->regs->ip;
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)
return;
@ -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;
top = (struct bts_record *)(unsigned long)ds->bts_index;
if (top <= at)
return;
ds->bts_index = ds->bts_buffer_base;
for (; at < top; at++) {
data->regs->ip = at->from;
data->addr = at->to;
perf_counter_output(counter, 1, data);
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++) {
data.ip = at->from;
data.addr = at->to;
perf_output_sample(&handle, &header, &data, counter);
}
data->regs->ip = orig_ip;
data->addr = 0;
perf_output_end(&handle);
/* There's new data available. */
counter->hw.interrupts++;
counter->pending_kill = POLL_IN;
}
@ -1552,13 +1573,9 @@ static void x86_pmu_disable(struct perf_counter *counter)
x86_perf_counter_update(counter, hwc, idx);
/* Drain the remaining BTS records. */
if (unlikely(idx == X86_PMC_IDX_FIXED_BTS)) {
struct perf_sample_data data;
struct pt_regs regs;
if (unlikely(idx == X86_PMC_IDX_FIXED_BTS))
intel_pmu_drain_bts_buffer(cpuc);
data.regs = &regs;
intel_pmu_drain_bts_buffer(cpuc, &data);
}
cpuc->counters[idx] = NULL;
clear_bit(idx, cpuc->used_mask);
@ -1619,7 +1636,6 @@ static int p6_pmu_handle_irq(struct pt_regs *regs)
int idx, handled = 0;
u64 val;
data.regs = regs;
data.addr = 0;
cpuc = &__get_cpu_var(cpu_hw_counters);
@ -1644,7 +1660,7 @@ static int p6_pmu_handle_irq(struct pt_regs *regs)
if (!x86_perf_counter_set_period(counter, hwc, idx))
continue;
if (perf_counter_overflow(counter, 1, &data))
if (perf_counter_overflow(counter, 1, &data, regs))
p6_pmu_disable_counter(hwc, idx);
}
@ -1665,13 +1681,12 @@ static int intel_pmu_handle_irq(struct pt_regs *regs)
int bit, loops;
u64 ack, status;
data.regs = regs;
data.addr = 0;
cpuc = &__get_cpu_var(cpu_hw_counters);
perf_disable();
intel_pmu_drain_bts_buffer(cpuc, &data);
intel_pmu_drain_bts_buffer(cpuc);
status = intel_pmu_get_status();
if (!status) {
perf_enable();
@ -1702,7 +1717,7 @@ static int intel_pmu_handle_irq(struct pt_regs *regs)
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);
}
@ -1729,7 +1744,6 @@ static int amd_pmu_handle_irq(struct pt_regs *regs)
int idx, handled = 0;
u64 val;
data.regs = regs;
data.addr = 0;
cpuc = &__get_cpu_var(cpu_hw_counters);
@ -1754,7 +1768,7 @@ static int amd_pmu_handle_irq(struct pt_regs *regs)
if (!x86_perf_counter_set_period(counter, hwc, idx))
continue;
if (perf_counter_overflow(counter, 1, &data))
if (perf_counter_overflow(counter, 1, &data, regs))
amd_pmu_disable_counter(hwc, idx);
}

View file

@ -9,7 +9,7 @@
#include <linux/pm.h>
#include <linux/clockchips.h>
#include <linux/random.h>
#include <trace/power.h>
#include <trace/events/power.h>
#include <asm/system.h>
#include <asm/apic.h>
#include <asm/syscalls.h>
@ -25,9 +25,6 @@ EXPORT_SYMBOL(idle_nomwait);
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)
{
*dst = *src;
@ -299,9 +296,7 @@ static inline int hlt_use_halt(void)
void default_idle(void)
{
if (hlt_use_halt()) {
struct power_trace it;
trace_power_start(&it, POWER_CSTATE, 1);
trace_power_start(POWER_CSTATE, 1);
current_thread_info()->status &= ~TS_POLLING;
/*
* TS_POLLING-cleared state must be visible before we
@ -314,7 +309,6 @@ void default_idle(void)
else
local_irq_enable();
current_thread_info()->status |= TS_POLLING;
trace_power_end(&it);
} else {
local_irq_enable();
/* loop is done by the caller */
@ -372,9 +366,7 @@ EXPORT_SYMBOL_GPL(cpu_idle_wait);
*/
void mwait_idle_with_hints(unsigned long ax, unsigned long cx)
{
struct power_trace it;
trace_power_start(&it, POWER_CSTATE, (ax>>4)+1);
trace_power_start(POWER_CSTATE, (ax>>4)+1);
if (!need_resched()) {
if (cpu_has(&current_cpu_data, X86_FEATURE_CLFLUSH_MONITOR))
clflush((void *)&current_thread_info()->flags);
@ -384,15 +376,13 @@ void mwait_idle_with_hints(unsigned long ax, unsigned long cx)
if (!need_resched())
__mwait(ax, cx);
}
trace_power_end(&it);
}
/* Default MONITOR/MWAIT with no hints, used for default C1 state */
static void mwait_idle(void)
{
struct power_trace it;
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))
clflush((void *)&current_thread_info()->flags);
@ -402,7 +392,6 @@ static void mwait_idle(void)
__sti_mwait(0, 0);
else
local_irq_enable();
trace_power_end(&it);
} else
local_irq_enable();
}
@ -414,13 +403,11 @@ static void mwait_idle(void)
*/
static void poll_idle(void)
{
struct power_trace it;
trace_power_start(&it, POWER_CSTATE, 0);
trace_power_start(POWER_CSTATE, 0);
local_irq_enable();
while (!need_resched())
cpu_relax();
trace_power_end(&it);
trace_power_end(0);
}
/*

View file

@ -17,6 +17,7 @@
#include <linux/cpuidle.h>
#include <linux/ktime.h>
#include <linux/hrtimer.h>
#include <trace/events/power.h>
#include "cpuidle.h"
@ -91,6 +92,7 @@ static void cpuidle_idle_call(void)
/* give the governor an opportunity to reflect on the outcome */
if (cpuidle_curr_governor->reflect)
cpuidle_curr_governor->reflect(dev);
trace_power_end(0);
}
/**

View file

@ -199,10 +199,14 @@ struct perf_counter_attr {
inherit_stat : 1, /* per task counts */
enable_on_exec : 1, /* next exec enables */
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;
__u64 __reserved_3;
@ -332,6 +336,7 @@ enum perf_event_type {
* struct perf_event_header header;
* u32 pid, ppid;
* u32 tid, ptid;
* u64 time;
* };
*/
PERF_EVENT_EXIT = 4,
@ -352,6 +357,7 @@ enum perf_event_type {
* struct perf_event_header header;
* u32 pid, ppid;
* u32 tid, ptid;
* { u64 time; } && PERF_SAMPLE_TIME
* };
*/
PERF_EVENT_FORK = 7,
@ -521,6 +527,8 @@ struct perf_mmap_data {
atomic_t wakeup; /* needs a wakeup */
atomic_t lost; /* nr records lost */
long watermark; /* wakeup watermark */
struct perf_counter_mmap_page *user_page;
void *data_pages[0];
};
@ -685,6 +693,17 @@ struct perf_cpu_context {
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
/*
@ -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);
struct perf_sample_data {
struct pt_regs *regs;
u64 type;
u64 ip;
struct {
u32 pid;
u32 tid;
} tid_entry;
u64 time;
u64 addr;
u64 id;
u64 stream_id;
struct {
u32 cpu;
u32 reserved;
} cpu_entry;
u64 period;
struct perf_callchain_entry *callchain;
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,
struct perf_sample_data *data);
extern void perf_counter_output(struct perf_counter *counter, int nmi,
struct perf_sample_data *data);
struct perf_sample_data *data,
struct pt_regs *regs);
/*
* 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,
#define perf_instruction_pointer(regs) instruction_pointer(regs)
#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
static inline void
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) { }
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_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
#define perf_output_put(handle, x) \
perf_output_copy((handle), &(x), sizeof(x))
#endif /* __KERNEL__ */
#endif /* _LINUX_PERF_COUNTER_H */

View file

@ -0,0 +1,81 @@
#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>

View file

@ -379,6 +379,39 @@ TRACE_EVENT(sched_stat_wait,
(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,
* including iowait, see below).

View file

@ -2176,6 +2176,13 @@ static int perf_mmap_data_alloc(struct perf_counter *counter, int nr_pages)
data->nr_pages = nr_pages;
atomic_set(&data->lock, -1);
if (counter->attr.watermark) {
data->watermark = min_t(long, PAGE_SIZE * nr_pages,
counter->attr.wakeup_watermark);
}
if (!data->watermark)
data->watermark = max(PAGE_SIZE, PAGE_SIZE * nr_pages / 4);
rcu_assign_pointer(counter->data, data);
return 0;
@ -2315,7 +2322,8 @@ static int perf_mmap(struct file *file, struct vm_area_struct *vma)
lock_limit >>= PAGE_SHIFT;
locked = vma->vm_mm->locked_vm + extra;
if ((locked > lock_limit) && !capable(CAP_IPC_LOCK)) {
if ((locked > lock_limit) && perf_paranoid_tracepoint_raw() &&
!capable(CAP_IPC_LOCK)) {
ret = -EPERM;
goto unlock;
}
@ -2504,35 +2512,15 @@ __weak struct perf_callchain_entry *perf_callchain(struct pt_regs *regs)
/*
* Output
*/
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;
};
static bool perf_output_space(struct perf_mmap_data *data,
unsigned int offset, unsigned int head)
static bool perf_output_space(struct perf_mmap_data *data, unsigned long tail,
unsigned long offset, unsigned long head)
{
unsigned long tail;
unsigned long mask;
if (!data->writable)
return true;
mask = (data->nr_pages << PAGE_SHIFT) - 1;
/*
* Userspace could choose to issue a mb() before updating the tail
* pointer. So that all reads will be completed before the write is
* issued.
*/
tail = ACCESS_ONCE(data->user_page->data_tail);
smp_rmb();
offset = (offset - tail) & mask;
head = (head - tail) & mask;
@ -2633,8 +2621,8 @@ static void perf_output_unlock(struct perf_output_handle *handle)
local_irq_restore(handle->flags);
}
static void perf_output_copy(struct perf_output_handle *handle,
const void *buf, unsigned int len)
void perf_output_copy(struct perf_output_handle *handle,
const void *buf, unsigned int len)
{
unsigned int pages_mask;
unsigned int offset;
@ -2669,16 +2657,13 @@ static void perf_output_copy(struct perf_output_handle *handle,
WARN_ON_ONCE(((long)(handle->head - handle->offset)) < 0);
}
#define perf_output_put(handle, x) \
perf_output_copy((handle), &(x), sizeof(x))
static int perf_output_begin(struct perf_output_handle *handle,
struct perf_counter *counter, unsigned int size,
int nmi, int sample)
int perf_output_begin(struct perf_output_handle *handle,
struct perf_counter *counter, unsigned int size,
int nmi, int sample)
{
struct perf_counter *output_counter;
struct perf_mmap_data *data;
unsigned int offset, head;
unsigned long tail, offset, head;
int have_lost;
struct {
struct perf_event_header header;
@ -2716,16 +2701,23 @@ static int perf_output_begin(struct perf_output_handle *handle,
perf_output_lock(handle);
do {
/*
* Userspace could choose to issue a mb() before updating the
* tail pointer. So that all reads will be completed before the
* write is issued.
*/
tail = ACCESS_ONCE(data->user_page->data_tail);
smp_rmb();
offset = head = atomic_long_read(&data->head);
head += size;
if (unlikely(!perf_output_space(data, offset, head)))
if (unlikely(!perf_output_space(data, tail, offset, head)))
goto fail;
} while (atomic_long_cmpxchg(&data->head, offset, head) != offset);
handle->offset = offset;
handle->head = head;
if ((offset >> PAGE_SHIFT) != (head >> PAGE_SHIFT))
if (head - tail > data->watermark)
atomic_set(&data->wakeup, 1);
if (have_lost) {
@ -2749,7 +2741,7 @@ static int perf_output_begin(struct perf_output_handle *handle,
return -ENOSPC;
}
static void perf_output_end(struct perf_output_handle *handle)
void perf_output_end(struct perf_output_handle *handle)
{
struct perf_counter *counter = handle->counter;
struct perf_mmap_data *data = handle->data;
@ -2863,82 +2855,148 @@ static void perf_output_read(struct perf_output_handle *handle,
perf_output_read_one(handle, counter);
}
void perf_counter_output(struct perf_counter *counter, int nmi,
struct perf_sample_data *data)
void perf_output_sample(struct perf_output_handle *handle,
struct perf_event_header *header,
struct perf_sample_data *data,
struct perf_counter *counter)
{
u64 sample_type = data->type;
perf_output_put(handle, *header);
if (sample_type & PERF_SAMPLE_IP)
perf_output_put(handle, data->ip);
if (sample_type & PERF_SAMPLE_TID)
perf_output_put(handle, data->tid_entry);
if (sample_type & PERF_SAMPLE_TIME)
perf_output_put(handle, data->time);
if (sample_type & PERF_SAMPLE_ADDR)
perf_output_put(handle, data->addr);
if (sample_type & PERF_SAMPLE_ID)
perf_output_put(handle, data->id);
if (sample_type & PERF_SAMPLE_STREAM_ID)
perf_output_put(handle, data->stream_id);
if (sample_type & PERF_SAMPLE_CPU)
perf_output_put(handle, data->cpu_entry);
if (sample_type & PERF_SAMPLE_PERIOD)
perf_output_put(handle, data->period);
if (sample_type & PERF_SAMPLE_READ)
perf_output_read(handle, counter);
if (sample_type & PERF_SAMPLE_CALLCHAIN) {
if (data->callchain) {
int size = 1;
if (data->callchain)
size += data->callchain->nr;
size *= sizeof(u64);
perf_output_copy(handle, data->callchain, size);
} else {
u64 nr = 0;
perf_output_put(handle, nr);
}
}
if (sample_type & PERF_SAMPLE_RAW) {
if (data->raw) {
perf_output_put(handle, data->raw->size);
perf_output_copy(handle, data->raw->data,
data->raw->size);
} else {
struct {
u32 size;
u32 data;
} raw = {
.size = sizeof(u32),
.data = 0,
};
perf_output_put(handle, raw);
}
}
}
void perf_prepare_sample(struct perf_event_header *header,
struct perf_sample_data *data,
struct perf_counter *counter,
struct pt_regs *regs)
{
int ret;
u64 sample_type = counter->attr.sample_type;
struct perf_output_handle handle;
struct perf_event_header header;
u64 ip;
struct {
u32 pid, tid;
} tid_entry;
struct perf_callchain_entry *callchain = NULL;
int callchain_size = 0;
u64 time;
struct {
u32 cpu, reserved;
} cpu_entry;
header.type = PERF_EVENT_SAMPLE;
header.size = sizeof(header);
data->type = sample_type;
header.misc = 0;
header.misc |= perf_misc_flags(data->regs);
header->type = PERF_EVENT_SAMPLE;
header->size = sizeof(*header);
header->misc = 0;
header->misc |= perf_misc_flags(regs);
if (sample_type & PERF_SAMPLE_IP) {
ip = perf_instruction_pointer(data->regs);
header.size += sizeof(ip);
data->ip = perf_instruction_pointer(regs);
header->size += sizeof(data->ip);
}
if (sample_type & PERF_SAMPLE_TID) {
/* namespace issues */
tid_entry.pid = perf_counter_pid(counter, current);
tid_entry.tid = perf_counter_tid(counter, current);
data->tid_entry.pid = perf_counter_pid(counter, current);
data->tid_entry.tid = perf_counter_tid(counter, current);
header.size += sizeof(tid_entry);
header->size += sizeof(data->tid_entry);
}
if (sample_type & PERF_SAMPLE_TIME) {
/*
* Maybe do better on x86 and provide cpu_clock_nmi()
*/
time = sched_clock();
data->time = perf_clock();
header.size += sizeof(u64);
header->size += sizeof(data->time);
}
if (sample_type & PERF_SAMPLE_ADDR)
header.size += sizeof(u64);
header->size += sizeof(data->addr);
if (sample_type & PERF_SAMPLE_ID)
header.size += sizeof(u64);
if (sample_type & PERF_SAMPLE_ID) {
data->id = primary_counter_id(counter);
if (sample_type & PERF_SAMPLE_STREAM_ID)
header.size += sizeof(u64);
header->size += sizeof(data->id);
}
if (sample_type & PERF_SAMPLE_STREAM_ID) {
data->stream_id = counter->id;
header->size += sizeof(data->stream_id);
}
if (sample_type & PERF_SAMPLE_CPU) {
header.size += sizeof(cpu_entry);
data->cpu_entry.cpu = raw_smp_processor_id();
data->cpu_entry.reserved = 0;
cpu_entry.cpu = raw_smp_processor_id();
cpu_entry.reserved = 0;
header->size += sizeof(data->cpu_entry);
}
if (sample_type & PERF_SAMPLE_PERIOD)
header.size += sizeof(u64);
header->size += sizeof(data->period);
if (sample_type & PERF_SAMPLE_READ)
header.size += perf_counter_read_size(counter);
header->size += perf_counter_read_size(counter);
if (sample_type & PERF_SAMPLE_CALLCHAIN) {
callchain = perf_callchain(data->regs);
int size = 1;
if (callchain) {
callchain_size = (1 + callchain->nr) * sizeof(u64);
header.size += callchain_size;
} else
header.size += sizeof(u64);
data->callchain = perf_callchain(regs);
if (data->callchain)
size += data->callchain->nr;
header->size += size * sizeof(u64);
}
if (sample_type & PERF_SAMPLE_RAW) {
@ -2950,69 +3008,23 @@ void perf_counter_output(struct perf_counter *counter, int nmi,
size += sizeof(u32);
WARN_ON_ONCE(size & (sizeof(u64)-1));
header.size += size;
header->size += size;
}
}
ret = perf_output_begin(&handle, counter, header.size, nmi, 1);
if (ret)
static void perf_counter_output(struct perf_counter *counter, int nmi,
struct perf_sample_data *data,
struct pt_regs *regs)
{
struct perf_output_handle handle;
struct perf_event_header header;
perf_prepare_sample(&header, data, counter, regs);
if (perf_output_begin(&handle, counter, header.size, nmi, 1))
return;
perf_output_put(&handle, header);
if (sample_type & PERF_SAMPLE_IP)
perf_output_put(&handle, ip);
if (sample_type & PERF_SAMPLE_TID)
perf_output_put(&handle, tid_entry);
if (sample_type & PERF_SAMPLE_TIME)
perf_output_put(&handle, time);
if (sample_type & PERF_SAMPLE_ADDR)
perf_output_put(&handle, data->addr);
if (sample_type & PERF_SAMPLE_ID) {
u64 id = primary_counter_id(counter);
perf_output_put(&handle, id);
}
if (sample_type & PERF_SAMPLE_STREAM_ID)
perf_output_put(&handle, counter->id);
if (sample_type & PERF_SAMPLE_CPU)
perf_output_put(&handle, cpu_entry);
if (sample_type & PERF_SAMPLE_PERIOD)
perf_output_put(&handle, data->period);
if (sample_type & PERF_SAMPLE_READ)
perf_output_read(&handle, counter);
if (sample_type & PERF_SAMPLE_CALLCHAIN) {
if (callchain)
perf_output_copy(&handle, callchain, callchain_size);
else {
u64 nr = 0;
perf_output_put(&handle, nr);
}
}
if (sample_type & PERF_SAMPLE_RAW) {
if (data->raw) {
perf_output_put(&handle, data->raw->size);
perf_output_copy(&handle, data->raw->data, data->raw->size);
} else {
struct {
u32 size;
u32 data;
} raw = {
.size = sizeof(u32),
.data = 0,
};
perf_output_put(&handle, raw);
}
}
perf_output_sample(&handle, &header, data, counter);
perf_output_end(&handle);
}
@ -3071,6 +3083,7 @@ struct perf_task_event {
u32 ppid;
u32 tid;
u32 ptid;
u64 time;
} event;
};
@ -3078,9 +3091,12 @@ static void perf_counter_task_output(struct perf_counter *counter,
struct perf_task_event *task_event)
{
struct perf_output_handle handle;
int size = task_event->event.header.size;
int size;
struct task_struct *task = task_event->task;
int ret = perf_output_begin(&handle, counter, size, 0, 0);
int ret;
size = task_event->event.header.size;
ret = perf_output_begin(&handle, counter, size, 0, 0);
if (ret)
return;
@ -3091,7 +3107,10 @@ static void perf_counter_task_output(struct perf_counter *counter,
task_event->event.tid = perf_counter_tid(counter, task);
task_event->event.ptid = perf_counter_tid(counter, current);
task_event->event.time = perf_clock();
perf_output_put(&handle, task_event->event);
perf_output_end(&handle);
}
@ -3473,7 +3492,7 @@ static void perf_log_throttle(struct perf_counter *counter, int enable)
.misc = 0,
.size = sizeof(throttle_event),
},
.time = sched_clock(),
.time = perf_clock(),
.id = primary_counter_id(counter),
.stream_id = counter->id,
};
@ -3493,14 +3512,16 @@ static void perf_log_throttle(struct perf_counter *counter, int enable)
* Generic counter overflow handling, sampling.
*/
int perf_counter_overflow(struct perf_counter *counter, int nmi,
struct perf_sample_data *data)
static int __perf_counter_overflow(struct perf_counter *counter, int nmi,
int throttle, struct perf_sample_data *data,
struct pt_regs *regs)
{
int events = atomic_read(&counter->event_limit);
int throttle = counter->pmu->unthrottle != NULL;
struct hw_perf_counter *hwc = &counter->hw;
int ret = 0;
throttle = (throttle && counter->pmu->unthrottle != NULL);
if (!throttle) {
hwc->interrupts++;
} else {
@ -3523,7 +3544,7 @@ int perf_counter_overflow(struct perf_counter *counter, int nmi,
}
if (counter->attr.freq) {
u64 now = sched_clock();
u64 now = perf_clock();
s64 delta = now - hwc->freq_stamp;
hwc->freq_stamp = now;
@ -3549,10 +3570,17 @@ int perf_counter_overflow(struct perf_counter *counter, int nmi,
perf_counter_disable(counter);
}
perf_counter_output(counter, nmi, data);
perf_counter_output(counter, nmi, data, regs);
return ret;
}
int perf_counter_overflow(struct perf_counter *counter, int nmi,
struct perf_sample_data *data,
struct pt_regs *regs)
{
return __perf_counter_overflow(counter, nmi, 1, data, regs);
}
/*
* Generic software counter infrastructure
*/
@ -3588,9 +3616,11 @@ static u64 perf_swcounter_set_period(struct perf_counter *counter)
}
static void perf_swcounter_overflow(struct perf_counter *counter,
int nmi, struct perf_sample_data *data)
int nmi, struct perf_sample_data *data,
struct pt_regs *regs)
{
struct hw_perf_counter *hwc = &counter->hw;
int throttle = 0;
u64 overflow;
data->period = counter->hw.last_period;
@ -3600,13 +3630,15 @@ static void perf_swcounter_overflow(struct perf_counter *counter,
return;
for (; overflow; overflow--) {
if (perf_counter_overflow(counter, nmi, data)) {
if (__perf_counter_overflow(counter, nmi, throttle,
data, regs)) {
/*
* We inhibit the overflow from happening when
* hwc->interrupts == MAX_INTERRUPTS.
*/
break;
}
throttle = 1;
}
}
@ -3618,7 +3650,8 @@ static void perf_swcounter_unthrottle(struct perf_counter *counter)
}
static void perf_swcounter_add(struct perf_counter *counter, u64 nr,
int nmi, struct perf_sample_data *data)
int nmi, struct perf_sample_data *data,
struct pt_regs *regs)
{
struct hw_perf_counter *hwc = &counter->hw;
@ -3627,11 +3660,11 @@ static void perf_swcounter_add(struct perf_counter *counter, u64 nr,
if (!hwc->sample_period)
return;
if (!data->regs)
if (!regs)
return;
if (!atomic64_add_negative(nr, &hwc->period_left))
perf_swcounter_overflow(counter, nmi, data);
perf_swcounter_overflow(counter, nmi, data, regs);
}
static int perf_swcounter_is_counting(struct perf_counter *counter)
@ -3690,7 +3723,8 @@ static int perf_swcounter_match(struct perf_counter *counter,
static void perf_swcounter_ctx_event(struct perf_counter_context *ctx,
enum perf_type_id type,
u32 event, u64 nr, int nmi,
struct perf_sample_data *data)
struct perf_sample_data *data,
struct pt_regs *regs)
{
struct perf_counter *counter;
@ -3699,8 +3733,8 @@ static void perf_swcounter_ctx_event(struct perf_counter_context *ctx,
rcu_read_lock();
list_for_each_entry_rcu(counter, &ctx->event_list, event_entry) {
if (perf_swcounter_match(counter, type, event, data->regs))
perf_swcounter_add(counter, nr, nmi, data);
if (perf_swcounter_match(counter, type, event, regs))
perf_swcounter_add(counter, nr, nmi, data, regs);
}
rcu_read_unlock();
}
@ -3721,7 +3755,8 @@ static int *perf_swcounter_recursion_context(struct perf_cpu_context *cpuctx)
static void do_perf_swcounter_event(enum perf_type_id type, u32 event,
u64 nr, int nmi,
struct perf_sample_data *data)
struct perf_sample_data *data,
struct pt_regs *regs)
{
struct perf_cpu_context *cpuctx = &get_cpu_var(perf_cpu_context);
int *recursion = perf_swcounter_recursion_context(cpuctx);
@ -3734,7 +3769,7 @@ static void do_perf_swcounter_event(enum perf_type_id type, u32 event,
barrier();
perf_swcounter_ctx_event(&cpuctx->ctx, type, event,
nr, nmi, data);
nr, nmi, data, regs);
rcu_read_lock();
/*
* doesn't really matter which of the child contexts the
@ -3742,7 +3777,7 @@ static void do_perf_swcounter_event(enum perf_type_id type, u32 event,
*/
ctx = rcu_dereference(current->perf_counter_ctxp);
if (ctx)
perf_swcounter_ctx_event(ctx, type, event, nr, nmi, data);
perf_swcounter_ctx_event(ctx, type, event, nr, nmi, data, regs);
rcu_read_unlock();
barrier();
@ -3756,11 +3791,11 @@ void __perf_swcounter_event(u32 event, u64 nr, int nmi,
struct pt_regs *regs, u64 addr)
{
struct perf_sample_data data = {
.regs = regs,
.addr = addr,
};
do_perf_swcounter_event(PERF_TYPE_SOFTWARE, event, nr, nmi, &data);
do_perf_swcounter_event(PERF_TYPE_SOFTWARE, event, nr, nmi,
&data, regs);
}
static void perf_swcounter_read(struct perf_counter *counter)
@ -3797,6 +3832,7 @@ static enum hrtimer_restart perf_swcounter_hrtimer(struct hrtimer *hrtimer)
{
enum hrtimer_restart ret = HRTIMER_RESTART;
struct perf_sample_data data;
struct pt_regs *regs;
struct perf_counter *counter;
u64 period;
@ -3804,17 +3840,17 @@ static enum hrtimer_restart perf_swcounter_hrtimer(struct hrtimer *hrtimer)
counter->pmu->read(counter);
data.addr = 0;
data.regs = get_irq_regs();
regs = get_irq_regs();
/*
* In case we exclude kernel IPs or are somehow not in interrupt
* context, provide the next best thing, the user IP.
*/
if ((counter->attr.exclude_kernel || !data.regs) &&
if ((counter->attr.exclude_kernel || !regs) &&
!counter->attr.exclude_user)
data.regs = task_pt_regs(current);
regs = task_pt_regs(current);
if (data.regs) {
if (perf_counter_overflow(counter, 0, &data))
if (regs) {
if (perf_counter_overflow(counter, 0, &data, regs))
ret = HRTIMER_NORESTART;
}
@ -3950,15 +3986,17 @@ void perf_tpcounter_event(int event_id, u64 addr, u64 count, void *record,
};
struct perf_sample_data data = {
.regs = get_irq_regs(),
.addr = addr,
.raw = &raw,
};
if (!data.regs)
data.regs = task_pt_regs(current);
struct pt_regs *regs = get_irq_regs();
do_perf_swcounter_event(PERF_TYPE_TRACEPOINT, event_id, count, 1, &data);
if (!regs)
regs = task_pt_regs(current);
do_perf_swcounter_event(PERF_TYPE_TRACEPOINT, event_id, count, 1,
&data, regs);
}
EXPORT_SYMBOL_GPL(perf_tpcounter_event);
@ -4170,8 +4208,8 @@ perf_counter_alloc(struct perf_counter_attr *attr,
static int perf_copy_attr(struct perf_counter_attr __user *uattr,
struct perf_counter_attr *attr)
{
int ret;
u32 size;
int ret;
if (!access_ok(VERIFY_WRITE, uattr, PERF_ATTR_SIZE_VER0))
return -EFAULT;
@ -4196,19 +4234,19 @@ static int perf_copy_attr(struct perf_counter_attr __user *uattr,
/*
* If we're handed a bigger struct than we know of,
* ensure all the unknown bits are 0.
* ensure all the unknown bits are 0 - i.e. new
* user-space does not rely on any kernel feature
* extensions we dont know about yet.
*/
if (size > sizeof(*attr)) {
unsigned long val;
unsigned long __user *addr;
unsigned long __user *end;
unsigned char __user *addr;
unsigned char __user *end;
unsigned char val;
addr = PTR_ALIGN((void __user *)uattr + sizeof(*attr),
sizeof(unsigned long));
end = PTR_ALIGN((void __user *)uattr + size,
sizeof(unsigned long));
addr = (void __user *)uattr + sizeof(*attr);
end = (void __user *)uattr + size;
for (; addr < end; addr += sizeof(unsigned long)) {
for (; addr < end; addr++) {
ret = get_user(val, addr);
if (ret)
return ret;

View file

@ -48,13 +48,6 @@ static __read_mostly int sched_clock_running;
__read_mostly int sched_clock_stable;
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_gtod;
u64 clock;
@ -80,7 +73,6 @@ void sched_clock_init(void)
for_each_possible_cpu(cpu) {
struct sched_clock_data *scd = cpu_sdc(cpu);
scd->lock = (raw_spinlock_t)__RAW_SPIN_LOCK_UNLOCKED;
scd->tick_raw = 0;
scd->tick_gtod = ktime_now;
scd->clock = ktime_now;
@ -109,14 +101,19 @@ static inline u64 wrap_max(u64 x, u64 y)
* - filter out backward motion
* - 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 clock, min_clock, max_clock;
u64 now, clock, old_clock, min_clock, max_clock;
s64 delta;
again:
now = sched_clock();
delta = now - scd->tick_raw;
if (unlikely(delta < 0))
delta = 0;
old_clock = scd->clock;
/*
* scd->clock = clamp(scd->tick_gtod + delta,
* max(scd->tick_gtod, scd->clock),
@ -124,84 +121,73 @@ static u64 __update_sched_clock(struct sched_clock_data *scd, u64 now)
*/
clock = scd->tick_gtod + delta;
min_clock = wrap_max(scd->tick_gtod, scd->clock);
max_clock = wrap_max(scd->clock, scd->tick_gtod + TICK_NSEC);
min_clock = wrap_max(scd->tick_gtod, old_clock);
max_clock = wrap_max(old_clock, scd->tick_gtod + TICK_NSEC);
clock = wrap_max(clock, min_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,
struct sched_clock_data *data2)
static u64 sched_clock_remote(struct sched_clock_data *scd)
{
if (data1 < data2) {
__raw_spin_lock(&data1->lock);
__raw_spin_lock(&data2->lock);
struct sched_clock_data *my_scd = this_scd();
u64 this_clock, remote_clock;
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 {
__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 now, clock, this_clock, remote_clock;
struct sched_clock_data *scd;
u64 clock;
WARN_ON_ONCE(!irqs_disabled());
if (sched_clock_stable)
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))
return 0ull;
WARN_ON_ONCE(!irqs_disabled());
now = sched_clock();
scd = cpu_sdc(cpu);
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;
}
@ -223,11 +209,9 @@ void sched_clock_tick(void)
now_gtod = ktime_to_ns(ktime_get());
now = sched_clock();
__raw_spin_lock(&scd->lock);
scd->tick_raw = now;
scd->tick_gtod = now_gtod;
__update_sched_clock(scd, now);
__raw_spin_unlock(&scd->lock);
sched_clock_local(scd);
}
/*

View file

@ -513,6 +513,7 @@ static void update_curr(struct cfs_rq *cfs_rq)
if (entity_is_task(curr)) {
struct task_struct *curtask = task_of(curr);
trace_sched_stat_runtime(curtask, delta_exec, curr->vruntime);
cpuacct_charge(curtask, delta_exec);
account_group_exec_runtime(curtask, delta_exec);
}

View file

@ -42,7 +42,6 @@ obj-$(CONFIG_BOOT_TRACER) += trace_boot.o
obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += trace_functions_graph.o
obj-$(CONFIG_TRACE_BRANCH_PROFILING) += trace_branch.o
obj-$(CONFIG_HW_BRANCH_TRACER) += trace_hw_branches.o
obj-$(CONFIG_POWER_TRACER) += trace_power.o
obj-$(CONFIG_KMEMTRACE) += kmemtrace.o
obj-$(CONFIG_WORKQUEUE_TRACER) += trace_workqueue.o
obj-$(CONFIG_BLK_DEV_IO_TRACE) += blktrace.o
@ -54,5 +53,6 @@ obj-$(CONFIG_EVENT_TRACING) += trace_export.o
obj-$(CONFIG_FTRACE_SYSCALLS) += trace_syscalls.o
obj-$(CONFIG_EVENT_PROFILE) += trace_event_profile.o
obj-$(CONFIG_EVENT_TRACING) += trace_events_filter.o
obj-$(CONFIG_EVENT_TRACING) += power-traces.o
libftrace-y := ftrace.o

View file

@ -0,0 +1,20 @@
/*
* 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);

View file

@ -11,7 +11,6 @@
#include <linux/ftrace.h>
#include <trace/boot.h>
#include <linux/kmemtrace.h>
#include <trace/power.h>
#include <linux/trace_seq.h>
#include <linux/ftrace_event.h>
@ -37,7 +36,6 @@ enum trace_type {
TRACE_HW_BRANCHES,
TRACE_KMEM_ALLOC,
TRACE_KMEM_FREE,
TRACE_POWER,
TRACE_BLK,
__TRACE_LAST_TYPE,
@ -207,7 +205,6 @@ extern void __ftrace_bad_type(void);
IF_ASSIGN(var, ent, struct ftrace_graph_ret_entry, \
TRACE_GRAPH_RET); \
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, \
TRACE_KMEM_ALLOC); \
IF_ASSIGN(var, ent, struct kmemtrace_free_entry, \

View file

@ -330,23 +330,6 @@ FTRACE_ENTRY(hw_branch, hw_branch_entry,
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,
TRACE_KMEM_ALLOC,

View file

@ -1,218 +0,0 @@
/*
* 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);

View file

@ -1,108 +0,0 @@
#!/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";

View file

@ -0,0 +1,41 @@
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]

View file

@ -0,0 +1,35 @@
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]

View file

@ -0,0 +1,25 @@
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]

View file

@ -373,13 +373,16 @@ LIB_OBJS += util/thread.o
LIB_OBJS += util/trace-event-parse.o
LIB_OBJS += util/trace-event-read.o
LIB_OBJS += util/trace-event-info.o
LIB_OBJS += util/svghelper.o
BUILTIN_OBJS += builtin-annotate.o
BUILTIN_OBJS += builtin-help.o
BUILTIN_OBJS += builtin-sched.o
BUILTIN_OBJS += builtin-list.o
BUILTIN_OBJS += builtin-record.o
BUILTIN_OBJS += builtin-report.o
BUILTIN_OBJS += builtin-stat.o
BUILTIN_OBJS += builtin-timechart.o
BUILTIN_OBJS += builtin-top.o
BUILTIN_OBJS += builtin-trace.o
@ -710,6 +713,12 @@ builtin-help.o: builtin-help.c common-cmds.h PERF-CFLAGS
'-DPERF_MAN_PATH="$(mandir_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
$(QUIET_BUILT_IN)$(RM) $@ && \
ln perf$X $@ 2>/dev/null || \

View file

@ -48,6 +48,8 @@ static int call_graph = 0;
static int inherit_stat = 0;
static int no_samples = 0;
static int sample_address = 0;
static int multiplex = 0;
static int multiplex_fd = -1;
static long samples;
static struct timeval last_read;
@ -470,19 +472,28 @@ static void create_counter(int counter, int cpu, pid_t pid)
*/
if (group && group_fd == -1)
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];
event_array[nr_poll].events = POLLIN;
nr_poll++;
if (multiplex && fd[nr_cpu][counter] != multiplex_fd) {
int ret;
mmap_array[nr_cpu][counter].counter = counter;
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);
ret = ioctl(fd[nr_cpu][counter], PERF_COUNTER_IOC_SET_OUTPUT, multiplex_fd);
assert(ret != -1);
} else {
event_array[nr_poll].fd = fd[nr_cpu][counter];
event_array[nr_poll].events = POLLIN;
nr_poll++;
mmap_array[nr_cpu][counter].counter = counter;
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);
@ -513,6 +524,7 @@ static int __cmd_record(int argc, const char **argv)
pid_t pid = 0;
int flags;
int ret;
unsigned long waking = 0;
page_size = sysconf(_SC_PAGE_SIZE);
nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
@ -614,17 +626,29 @@ static int __cmd_record(int argc, const char **argv)
int hits = samples;
for (i = 0; i < nr_cpu; i++) {
for (counter = 0; counter < nr_counters; counter++)
mmap_read(&mmap_array[i][counter]);
for (counter = 0; counter < nr_counters; counter++) {
if (mmap_array[i][counter].base)
mmap_read(&mmap_array[i][counter]);
}
}
if (hits == samples) {
if (done)
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.
*/
@ -681,6 +705,8 @@ static const struct option options[] = {
"Sample addresses"),
OPT_BOOLEAN('n', "no-samples", &no_samples,
"don't sample"),
OPT_BOOLEAN('M', "multiplex", &multiplex,
"multiplex counter output in a single channel"),
OPT_END()
};

2004
tools/perf/builtin-sched.c Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -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_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_report(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_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_version(int argc, const char **argv, const char *prefix);
#endif

View file

@ -4,7 +4,10 @@
#
perf-annotate mainporcelain common
perf-list mainporcelain common
perf-sched mainporcelain common
perf-record mainporcelain common
perf-report mainporcelain common
perf-stat mainporcelain common
perf-timechart mainporcelain common
perf-top mainporcelain common
perf-trace mainporcelain common

View file

@ -289,10 +289,12 @@ static void handle_internal_command(int argc, const char **argv)
{ "record", cmd_record, 0 },
{ "report", cmd_report, 0 },
{ "stat", cmd_stat, 0 },
{ "timechart", cmd_timechart, 0 },
{ "top", cmd_top, 0 },
{ "annotate", cmd_annotate, 0 },
{ "version", cmd_version, 0 },
{ "trace", cmd_trace, 0 },
{ "sched", cmd_sched, 0 },
};
unsigned int i;
static const char ext[] = STRIP_EXTENSION;

View file

@ -39,6 +39,7 @@ struct fork_event {
struct perf_event_header header;
u32 pid, ppid;
u32 tid, ptid;
u64 time;
};
struct lost_event {
@ -52,13 +53,19 @@ struct lost_event {
*/
struct read_event {
struct perf_event_header header;
u32 pid,tid;
u32 pid, tid;
u64 value;
u64 time_enabled;
u64 time_running;
u64 id;
};
struct sample_event{
struct perf_event_header header;
u64 array[];
};
typedef union event_union {
struct perf_event_header header;
struct ip_event ip;
@ -67,6 +74,7 @@ typedef union event_union {
struct fork_event fork;
struct lost_event lost;
struct read_event read;
struct sample_event sample;
} event_t;
struct map {

View file

@ -7,9 +7,8 @@
#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 *self = malloc(sizeof(*self));
@ -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 *self = malloc(sizeof(*self));
@ -86,6 +84,46 @@ void perf_header__add_attr(struct perf_header *self,
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";
#define PERF_MAGIC (*(u64 *)__perf_magic)
@ -106,6 +144,7 @@ struct perf_file_header {
u64 attr_size;
struct perf_file_section attrs;
struct perf_file_section data;
struct perf_file_section event_types;
};
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)
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);
@ -169,6 +213,10 @@ void perf_header__write(struct perf_header *self, int fd)
.offset = self->data_offset,
.size = self->data_size,
},
.event_types = {
.offset = self->event_offset,
.size = self->event_size,
},
};
lseek(fd, 0, SEEK_SET);
@ -234,6 +282,17 @@ struct perf_header *perf_header__read(int fd)
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_size = f_header.data.size;

View file

@ -19,6 +19,8 @@ struct perf_header {
s64 attr_offset;
u64 data_offset;
u64 data_size;
u64 event_offset;
u64 event_size;
};
struct perf_header *perf_header__read(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,
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 *
perf_header_attr__new(struct perf_counter_attr *attr);
void perf_header_attr__add_id(struct perf_header_attr *self, u64 id);

View file

@ -6,6 +6,7 @@
#include "exec_cmd.h"
#include "string.h"
#include "cache.h"
#include "header.h"
int nr_counters;
@ -18,6 +19,12 @@ struct event_symbol {
const char *alias;
};
enum event_result {
EVT_FAILED,
EVT_HANDLED,
EVT_HANDLED_ALL
};
char debugfs_path[MAXPATHLEN];
#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)
(strcmp(evt_dirent.d_name, "..")) && \
(!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)
{
@ -344,7 +351,7 @@ static int parse_aliases(const char **str, const char *names[][MAX_ALIASES], int
return -1;
}
static int
static enum event_result
parse_generic_hw_event(const char **str, struct perf_counter_attr *attr)
{
const char *s = *str;
@ -356,7 +363,7 @@ parse_generic_hw_event(const char **str, struct perf_counter_attr *attr)
* then bail out:
*/
if (cache_type == -1)
return 0;
return EVT_FAILED;
while ((cache_op == -1 || cache_result == -1) && *s == '-') {
++s;
@ -402,27 +409,115 @@ parse_generic_hw_event(const char **str, struct perf_counter_attr *attr)
attr->type = PERF_TYPE_HW_CACHE;
*str = s;
return 1;
return EVT_HANDLED;
}
static int parse_tracepoint_event(const char **strp,
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 enum event_result parse_tracepoint_event(const char **strp,
struct perf_counter_attr *attr)
{
const char *evt_name;
char *flags;
char sys_name[MAX_EVENT_LENGTH];
char id_buf[4];
int fd;
unsigned int sys_length, evt_length;
u64 id;
char evt_path[MAXPATHLEN];
if (valid_debugfs_mount(debugfs_path))
return 0;
evt_name = strchr(*strp, ':');
if (!evt_name)
return 0;
return EVT_FAILED;
sys_length = evt_name - *strp;
if (sys_length >= MAX_EVENT_LENGTH)
@ -434,32 +529,22 @@ static int parse_tracepoint_event(const char **strp,
flags = strchr(evt_name, ':');
if (flags) {
*flags = '\0';
/* split it out: */
evt_name = strndup(evt_name, flags - evt_name);
flags++;
if (!strncmp(flags, "record", strlen(flags)))
attr->sample_type |= PERF_SAMPLE_RAW;
}
evt_length = strlen(evt_name);
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) {
close(fd);
return 0;
}
close(fd);
id = atoll(id_buf);
attr->config = id;
attr->type = PERF_TYPE_TRACEPOINT;
*strp = evt_name + evt_length;
return 1;
if (!strcmp(evt_name, "*")) {
*strp = evt_name + evt_length;
return parse_subsystem_tracepoint_event(sys_name, flags);
} else
return parse_single_tracepoint_event(sys_name, evt_name,
evt_length, flags,
attr, strp);
}
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;
}
static int
static enum event_result
parse_symbolic_event(const char **strp, struct perf_counter_attr *attr)
{
const char *str = *strp;
@ -490,31 +575,32 @@ parse_symbolic_event(const char **strp, struct perf_counter_attr *attr)
attr->type = event_symbols[i].type;
attr->config = event_symbols[i].config;
*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;
u64 config;
int n;
if (*str != 'r')
return 0;
return EVT_FAILED;
n = hex2u64(str + 1, &config);
if (n > 0) {
*strp = str + n + 1;
attr->type = PERF_TYPE_RAW;
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)
{
const char *str = *strp;
@ -530,13 +616,13 @@ parse_numeric_event(const char **strp, struct perf_counter_attr *attr)
attr->type = type;
attr->config = config;
*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)
{
const char *str = *strp;
@ -569,37 +655,84 @@ parse_event_modifier(const char **strp, struct perf_counter_attr *attr)
* Each event can have multiple symbolic names.
* 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) ||
parse_raw_event(str, attr) ||
parse_numeric_event(str, attr) ||
parse_symbolic_event(str, attr) ||
parse_generic_hw_event(str, attr)))
return 0;
enum event_result ret;
ret = parse_tracepoint_event(str, attr);
if (ret != EVT_FAILED)
goto modifier;
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);
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)
{
struct perf_counter_attr attr;
enum event_result ret;
if (strchr(str, ':'))
store_event_type(str);
for (;;) {
if (nr_counters == MAX_COUNTERS)
return -1;
memset(&attr, 0, sizeof(attr));
if (!parse_event_symbols(&str, &attr))
ret = parse_event_symbols(&str, &attr);
if (ret == EVT_FAILED)
return -1;
if (!(*str == 0 || *str == ',' || isspace(*str)))
return -1;
attrs[nr_counters] = attr;
nr_counters++;
if (ret != EVT_HANDLED_ALL) {
attrs[nr_counters] = attr;
nr_counters++;
}
if (*str == 0)
break;

View file

@ -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 }
#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) }
#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) \
{ .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 }

384
tools/perf/util/svghelper.c Normal file
View file

@ -0,0 +1,384 @@
/*
* svghelper.c - helper functions for outputting svg
*
* (C) Copyright 2009 Intel Corporation
*
* Authors:
* Arjan van de Ven <arjan@linux.intel.com>
*
* 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; version 2
* of the License.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "svghelper.h"
static u64 first_time, last_time;
static u64 turbo_frequency, max_freq;
#define SLOT_MULT 30.0
#define SLOT_HEIGHT 25.0
#define WIDTH 1000.0
#define MIN_TEXT_SIZE 0.001
static u64 total_height;
static FILE *svgfile;
static double cpu2slot(int cpu)
{
return 2 * cpu + 1;
}
static double cpu2y(int cpu)
{
return cpu2slot(cpu) * SLOT_MULT;
}
static double time2pixels(u64 time)
{
double X;
X = WIDTH * (time - first_time) / (last_time - first_time);
return X;
}
void open_svg(const char *filename, int cpus, int rows)
{
svgfile = fopen(filename, "w");
if (!svgfile) {
fprintf(stderr, "Cannot open %s for output\n", filename);
return;
}
total_height = (1 + rows + cpu2slot(cpus)) * SLOT_MULT;
fprintf(svgfile, "<?xml version=\"1.0\" standalone=\"no\"?> \n");
fprintf(svgfile, "<svg width=\"%4.1f\" height=\"%llu\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n", WIDTH, total_height);
fprintf(svgfile, "<defs>\n <style type=\"text/css\">\n <![CDATA[\n");
fprintf(svgfile, " rect { stroke-width: 1; }\n");
fprintf(svgfile, " rect.process { fill:rgb(180,180,180); fill-opacity:0.9; stroke-width:1; stroke:rgb( 0, 0, 0); } \n");
fprintf(svgfile, " rect.process2 { fill:rgb(180,180,180); fill-opacity:0.9; stroke-width:0; stroke:rgb( 0, 0, 0); } \n");
fprintf(svgfile, " rect.sample { fill:rgb( 0, 0,255); fill-opacity:0.8; stroke-width:0; stroke:rgb( 0, 0, 0); } \n");
fprintf(svgfile, " rect.blocked { fill:rgb(255, 0, 0); fill-opacity:0.5; stroke-width:0; stroke:rgb( 0, 0, 0); } \n");
fprintf(svgfile, " rect.waiting { fill:rgb(255,255, 0); fill-opacity:0.3; stroke-width:0; stroke:rgb( 0, 0, 0); } \n");
fprintf(svgfile, " rect.cpu { fill:rgb(192,192,192); fill-opacity:0.2; stroke-width:0.5; stroke:rgb(128,128,128); } \n");
fprintf(svgfile, " rect.pstate { fill:rgb(128,128,128); fill-opacity:0.8; stroke-width:0; } \n");
fprintf(svgfile, " rect.c1 { fill:rgb(255,214,214); fill-opacity:0.5; stroke-width:0; } \n");
fprintf(svgfile, " rect.c2 { fill:rgb(255,172,172); fill-opacity:0.5; stroke-width:0; } \n");
fprintf(svgfile, " rect.c3 { fill:rgb(255,130,130); fill-opacity:0.5; stroke-width:0; } \n");
fprintf(svgfile, " rect.c4 { fill:rgb(255, 88, 88); fill-opacity:0.5; stroke-width:0; } \n");
fprintf(svgfile, " rect.c5 { fill:rgb(255, 44, 44); fill-opacity:0.5; stroke-width:0; } \n");
fprintf(svgfile, " rect.c6 { fill:rgb(255, 0, 0); fill-opacity:0.5; stroke-width:0; } \n");
fprintf(svgfile, " line.pstate { stroke:rgb(255,255, 0); stroke-opacity:0.8; stroke-width:2; } \n");
fprintf(svgfile, " ]]>\n </style>\n</defs>\n");
}
void svg_box(int Yslot, u64 start, u64 end, const char *type)
{
if (!svgfile)
return;
fprintf(svgfile, "<rect x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\" class=\"%s\"/>\n",
time2pixels(start), time2pixels(end)-time2pixels(start), Yslot * SLOT_MULT, SLOT_HEIGHT, type);
}
void svg_sample(int Yslot, int cpu, u64 start, u64 end, const char *type)
{
double text_size;
if (!svgfile)
return;
fprintf(svgfile, "<rect x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\" class=\"%s\"/>\n",
time2pixels(start), time2pixels(end)-time2pixels(start), Yslot * SLOT_MULT, SLOT_HEIGHT, type);
text_size = (time2pixels(end)-time2pixels(start));
if (cpu > 9)
text_size = text_size/2;
if (text_size > 1.25)
text_size = 1.25;
if (text_size > MIN_TEXT_SIZE)
fprintf(svgfile, "<text transform=\"translate(%1.8f,%1.8f)\" font-size=\"%1.6fpt\">%i</text>\n",
time2pixels(start), Yslot * SLOT_MULT + SLOT_HEIGHT - 1, text_size, cpu + 1);
}
static char *cpu_model(void)
{
static char cpu_m[255];
char buf[256];
FILE *file;
cpu_m[0] = 0;
/* CPU type */
file = fopen("/proc/cpuinfo", "r");
if (file) {
while (fgets(buf, 255, file)) {
if (strstr(buf, "model name")) {
strncpy(cpu_m, &buf[13], 255);
break;
}
}
fclose(file);
}
return cpu_m;
}
void svg_cpu_box(int cpu, u64 __max_freq, u64 __turbo_freq)
{
char cpu_string[80];
if (!svgfile)
return;
max_freq = __max_freq;
turbo_frequency = __turbo_freq;
fprintf(svgfile, "<rect x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\" class=\"cpu\"/>\n",
time2pixels(first_time),
time2pixels(last_time)-time2pixels(first_time),
cpu2y(cpu), SLOT_MULT+SLOT_HEIGHT);
sprintf(cpu_string, "CPU %i", (int)cpu+1);
fprintf(svgfile, "<text transform=\"translate(%4.8f,%4.8f)\">%s</text>\n",
10+time2pixels(first_time), cpu2y(cpu) + SLOT_HEIGHT/2, cpu_string);
fprintf(svgfile, "<text transform=\"translate(%4.8f,%4.8f)\" font-size=\"1.25pt\">%s</text>\n",
10+time2pixels(first_time), cpu2y(cpu) + SLOT_MULT + SLOT_HEIGHT - 4, cpu_model());
}
void svg_process(int cpu, u64 start, u64 end, const char *type, const char *name)
{
double width;
if (!svgfile)
return;
fprintf(svgfile, "<rect x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\" class=\"%s\"/>\n",
time2pixels(start), time2pixels(end)-time2pixels(start), cpu2y(cpu), SLOT_MULT+SLOT_HEIGHT, type);
width = time2pixels(end)-time2pixels(start);
if (width > 6)
width = 6;
if (width > MIN_TEXT_SIZE)
fprintf(svgfile, "<text transform=\"translate(%4.8f,%4.8f) rotate(90)\" font-size=\"%3.4fpt\">%s</text>\n",
time2pixels(start), cpu2y(cpu), width, name);
}
void svg_cstate(int cpu, u64 start, u64 end, int type)
{
double width;
char style[128];
if (!svgfile)
return;
if (type > 6)
type = 6;
sprintf(style, "c%i", type);
fprintf(svgfile, "<rect class=\"%s\" x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\"/>\n",
style,
time2pixels(start), time2pixels(end)-time2pixels(start),
cpu2y(cpu), SLOT_MULT+SLOT_HEIGHT);
width = time2pixels(end)-time2pixels(start);
if (width > 6)
width = 6;
if (width > MIN_TEXT_SIZE)
fprintf(svgfile, "<text transform=\"translate(%4.8f,%4.8f) rotate(90)\" font-size=\"%3.4fpt\">C%i</text>\n",
time2pixels(start), cpu2y(cpu), width, type);
}
static char *HzToHuman(unsigned long hz)
{
static char buffer[1024];
unsigned long long Hz;
memset(buffer, 0, 1024);
Hz = hz;
/* default: just put the Number in */
sprintf(buffer, "%9lli", Hz);
if (Hz > 1000)
sprintf(buffer, " %6lli Mhz", (Hz+500)/1000);
if (Hz > 1500000)
sprintf(buffer, " %6.2f Ghz", (Hz+5000.0)/1000000);
if (Hz == turbo_frequency)
sprintf(buffer, "Turbo");
return buffer;
}
void svg_pstate(int cpu, u64 start, u64 end, u64 freq)
{
double height = 0;
if (!svgfile)
return;
if (max_freq)
height = freq * 1.0 / max_freq * (SLOT_HEIGHT + SLOT_MULT);
height = 1 + cpu2y(cpu) + SLOT_MULT + SLOT_HEIGHT - height;
fprintf(svgfile, "<line x1=\"%4.8f\" x2=\"%4.8f\" y1=\"%4.1f\" y2=\"%4.1f\" class=\"pstate\"/>\n",
time2pixels(start), time2pixels(end), height, height);
fprintf(svgfile, "<text transform=\"translate(%4.8f,%4.8f)\" font-size=\"0.25pt\">%s</text>\n",
time2pixels(start), height+0.9, HzToHuman(freq));
}
void svg_partial_wakeline(u64 start, int row1, int row2)
{
double height;
if (!svgfile)
return;
if (row1 < row2) {
if (row1)
fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n",
time2pixels(start), row1 * SLOT_MULT + SLOT_HEIGHT, time2pixels(start), row1 * SLOT_MULT + SLOT_HEIGHT + SLOT_MULT/32);
if (row2)
fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n",
time2pixels(start), row2 * SLOT_MULT - SLOT_MULT/32, time2pixels(start), row2 * SLOT_MULT);
} else {
if (row2)
fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n",
time2pixels(start), row2 * SLOT_MULT + SLOT_HEIGHT, time2pixels(start), row2 * SLOT_MULT + SLOT_HEIGHT + SLOT_MULT/32);
if (row1)
fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n",
time2pixels(start), row1 * SLOT_MULT - SLOT_MULT/32, time2pixels(start), row1 * SLOT_MULT);
}
height = row1 * SLOT_MULT;
if (row2 > row1)
height += SLOT_HEIGHT;
if (row1)
fprintf(svgfile, "<circle cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\" style=\"fill:rgb(32,255,32)\"/>\n",
time2pixels(start), height);
}
void svg_wakeline(u64 start, int row1, int row2)
{
double height;
if (!svgfile)
return;
if (row1 < row2)
fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n",
time2pixels(start), row1 * SLOT_MULT + SLOT_HEIGHT, time2pixels(start), row2 * SLOT_MULT);
else
fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n",
time2pixels(start), row2 * SLOT_MULT + SLOT_HEIGHT, time2pixels(start), row1 * SLOT_MULT);
height = row1 * SLOT_MULT;
if (row2 > row1)
height += SLOT_HEIGHT;
fprintf(svgfile, "<circle cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\" style=\"fill:rgb(32,255,32)\"/>\n",
time2pixels(start), height);
}
void svg_interrupt(u64 start, int row)
{
if (!svgfile)
return;
fprintf(svgfile, "<circle cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\" style=\"fill:rgb(255,128,128)\"/>\n",
time2pixels(start), row * SLOT_MULT);
fprintf(svgfile, "<circle cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\" style=\"fill:rgb(255,128,128)\"/>\n",
time2pixels(start), row * SLOT_MULT + SLOT_HEIGHT);
}
void svg_text(int Yslot, u64 start, const char *text)
{
if (!svgfile)
return;
fprintf(svgfile, "<text transform=\"translate(%4.8f,%4.8f)\">%s</text>\n",
time2pixels(start), Yslot * SLOT_MULT+SLOT_HEIGHT/2, text);
}
static void svg_legenda_box(int X, const char *text, const char *style)
{
double boxsize;
boxsize = SLOT_HEIGHT / 2;
fprintf(svgfile, "<rect x=\"%i\" width=\"%4.8f\" y=\"0\" height=\"%4.1f\" class=\"%s\"/>\n",
X, boxsize, boxsize, style);
fprintf(svgfile, "<text transform=\"translate(%4.8f, %4.8f)\" font-size=\"%4.4fpt\">%s</text>\n",
X + boxsize + 5, boxsize, 0.8 * boxsize, text);
}
void svg_legenda(void)
{
if (!svgfile)
return;
svg_legenda_box(0, "Running", "sample");
svg_legenda_box(100, "Idle","rect.c1");
svg_legenda_box(200, "Deeper Idle", "rect.c3");
svg_legenda_box(350, "Deepest Idle", "rect.c6");
svg_legenda_box(550, "Sleeping", "process2");
svg_legenda_box(650, "Waiting for cpu", "waiting");
svg_legenda_box(800, "Blocked on IO", "blocked");
}
void svg_time_grid(u64 start, u64 end)
{
u64 i;
first_time = start;
last_time = end;
first_time = first_time / 100000000 * 100000000;
if (!svgfile)
return;
i = first_time;
while (i < last_time) {
int color = 220;
double thickness = 0.075;
if ((i % 100000000) == 0) {
thickness = 0.5;
color = 192;
}
if ((i % 1000000000) == 0) {
thickness = 2.0;
color = 128;
}
fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%llu\" style=\"stroke:rgb(%i,%i,%i);stroke-width:%1.3f\"/>\n",
time2pixels(i), SLOT_MULT/2, time2pixels(i), total_height, color, color, color, thickness);
i += 10000000;
}
}
void svg_close(void)
{
if (svgfile) {
fprintf(svgfile, "</svg>\n");
fclose(svgfile);
svgfile = NULL;
}
}

View file

@ -0,0 +1,25 @@
#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

View file

@ -8,7 +8,7 @@
static struct thread *thread__new(pid_t pid)
{
struct thread *self = malloc(sizeof(*self));
struct thread *self = calloc(1, sizeof(*self));
if (self != NULL) {
self->pid = pid;
@ -85,7 +85,7 @@ register_idle_thread(struct rb_root *threads, struct thread **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");
exit(-1);
}

View file

@ -4,10 +4,11 @@
#include "symbol.h"
struct thread {
struct rb_node rb_node;
struct list_head maps;
pid_t pid;
char *comm;
struct rb_node rb_node;
struct list_head maps;
pid_t pid;
char shortname[3];
char *comm;
};
int thread__set_comm(struct thread *self, const char *comm);

View file

@ -458,7 +458,7 @@ static void read_proc_kallsyms(void)
static void read_ftrace_printk(void)
{
unsigned int size, check_size;
const char *path;
char *path;
struct stat st;
int ret;
@ -468,14 +468,15 @@ static void read_ftrace_printk(void)
/* not found */
size = 0;
write_or_die(&size, 4);
return;
goto out;
}
size = get_size(path);
write_or_die(&size, 4);
check_size = copy_file(path);
if (size != check_size)
die("error in size of file '%s'", path);
out:
put_tracing_file(path);
}
static struct tracepoint_path *

View file

@ -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)
{
struct event *event;
@ -1799,7 +1822,7 @@ static int get_common_info(const char *type, int *offset, int *size)
return 0;
}
static int parse_common_type(void *data)
int trace_parse_common_type(void *data)
{
static int type_offset;
static int type_size;
@ -1832,7 +1855,7 @@ static int parse_common_pid(void *data)
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;
@ -2420,8 +2443,8 @@ get_return_for_leaf(int cpu, int cur_pid, unsigned long long cur_func,
int type;
int pid;
type = parse_common_type(next->data);
event = find_event(type);
type = trace_parse_common_type(next->data);
event = trace_find_event(type);
if (!event)
return NULL;
@ -2502,8 +2525,8 @@ print_graph_entry_leaf(struct event *event, void *data, struct record *ret_rec)
int type;
int i;
type = parse_common_type(ret_rec->data);
ret_event = find_event(type);
type = trace_parse_common_type(ret_rec->data);
ret_event = trace_find_event(type);
field = find_field(ret_event, "rettime");
if (!field)
@ -2696,11 +2719,13 @@ void print_event(int cpu, void *data, int size, unsigned long long nsecs,
nsecs -= secs * NSECS_PER_SEC;
usecs = nsecs / NSECS_PER_USEC;
type = parse_common_type(data);
type = trace_parse_common_type(data);
event = find_event(type);
if (!event)
die("ug! no event found for type %d", type);
event = trace_find_event(type);
if (!event) {
printf("ug! no event found for type %d\n", type);
return;
}
pid = parse_common_pid(data);

View file

@ -458,12 +458,13 @@ struct record *trace_read_data(int cpu)
return data;
}
void trace_report (void)
void trace_report(void)
{
const char *input_file = "trace.info";
char buf[BUFSIZ];
char test[] = { 23, 8, 68 };
char *version;
int show_version = 0;
int show_funcs = 0;
int show_printk = 0;
@ -480,7 +481,8 @@ void trace_report (void)
die("not a trace file (missing tracing)");
version = read_string();
printf("version = %s\n", version);
if (show_version)
printf("version = %s\n", version);
free(version);
read_or_die(buf, 1);

View file

@ -234,6 +234,11 @@ extern int header_page_data_offset;
extern int header_page_data_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);