-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
x86, bts, ftrace: a BTS ftrace plug-in prototype
Impact: add new ftrace plugin A prototype for a BTS ftrace plug-in. The tracer collects branch trace in a cyclic buffer for each cpu. The tracer is not configurable and the trace for each snapshot is appended when doing cat /debug/tracing/trace. This is a proof of concept that will be extended with future patches to become a (hopefully) useful tool. Signed-off-by: Markus Metzger <markus.t.metzger@intel.com> Signed-off-by: Ingo Molnar <mingo@elte.hu>
- Loading branch information
Markus Metzger
authored and
Ingo Molnar
committed
Nov 25, 2008
1 parent
8bba1bf
commit 1e9b51c
Showing
5 changed files
with
301 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,276 @@ | ||
/* | ||
* BTS tracer | ||
* | ||
* Copyright (C) 2008 Markus Metzger <markus.t.metzger@gmail.com> | ||
* | ||
*/ | ||
|
||
#include <linux/module.h> | ||
#include <linux/fs.h> | ||
#include <linux/debugfs.h> | ||
#include <linux/ftrace.h> | ||
#include <linux/kallsyms.h> | ||
|
||
#include <asm/ds.h> | ||
|
||
#include "trace.h" | ||
|
||
|
||
#define SIZEOF_BTS (1 << 13) | ||
|
||
static DEFINE_PER_CPU(struct bts_tracer *, tracer); | ||
static DEFINE_PER_CPU(unsigned char[SIZEOF_BTS], buffer); | ||
|
||
#define this_tracer per_cpu(tracer, smp_processor_id()) | ||
#define this_buffer per_cpu(buffer, smp_processor_id()) | ||
|
||
|
||
/* | ||
* Information to interpret a BTS record. | ||
* This will go into an in-kernel BTS interface. | ||
*/ | ||
static unsigned char sizeof_field; | ||
static unsigned long debugctl_mask; | ||
|
||
#define sizeof_bts (3 * sizeof_field) | ||
|
||
static void bts_trace_cpuinit(struct cpuinfo_x86 *c) | ||
{ | ||
switch (c->x86) { | ||
case 0x6: | ||
switch (c->x86_model) { | ||
case 0x0 ... 0xC: | ||
break; | ||
case 0xD: | ||
case 0xE: /* Pentium M */ | ||
sizeof_field = sizeof(long); | ||
debugctl_mask = (1<<6)|(1<<7); | ||
break; | ||
default: | ||
sizeof_field = 8; | ||
debugctl_mask = (1<<6)|(1<<7); | ||
break; | ||
} | ||
break; | ||
case 0xF: | ||
switch (c->x86_model) { | ||
case 0x0: | ||
case 0x1: | ||
case 0x2: /* Netburst */ | ||
sizeof_field = sizeof(long); | ||
debugctl_mask = (1<<2)|(1<<3); | ||
break; | ||
default: | ||
/* sorry, don't know about them */ | ||
break; | ||
} | ||
break; | ||
default: | ||
/* sorry, don't know about them */ | ||
break; | ||
} | ||
} | ||
|
||
static inline void bts_enable(void) | ||
{ | ||
unsigned long debugctl; | ||
|
||
rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctl); | ||
wrmsrl(MSR_IA32_DEBUGCTLMSR, debugctl | debugctl_mask); | ||
} | ||
|
||
static inline void bts_disable(void) | ||
{ | ||
unsigned long debugctl; | ||
|
||
rdmsrl(MSR_IA32_DEBUGCTLMSR, debugctl); | ||
wrmsrl(MSR_IA32_DEBUGCTLMSR, debugctl & ~debugctl_mask); | ||
} | ||
|
||
static void bts_trace_reset(struct trace_array *tr) | ||
{ | ||
int cpu; | ||
|
||
tr->time_start = ftrace_now(tr->cpu); | ||
|
||
for_each_online_cpu(cpu) | ||
tracing_reset(tr, cpu); | ||
} | ||
|
||
static void bts_trace_start_cpu(void *arg) | ||
{ | ||
this_tracer = | ||
ds_request_bts(/* task = */ NULL, this_buffer, SIZEOF_BTS, | ||
/* ovfl = */ NULL, /* th = */ (size_t)-1); | ||
if (IS_ERR(this_tracer)) { | ||
this_tracer = NULL; | ||
return; | ||
} | ||
|
||
bts_enable(); | ||
} | ||
|
||
static void bts_trace_start(struct trace_array *tr) | ||
{ | ||
int cpu; | ||
|
||
bts_trace_reset(tr); | ||
|
||
for_each_cpu_mask(cpu, cpu_possible_map) | ||
smp_call_function_single(cpu, bts_trace_start_cpu, NULL, 1); | ||
} | ||
|
||
static void bts_trace_stop_cpu(void *arg) | ||
{ | ||
if (this_tracer) { | ||
bts_disable(); | ||
|
||
ds_release_bts(this_tracer); | ||
this_tracer = NULL; | ||
} | ||
} | ||
|
||
static void bts_trace_stop(struct trace_array *tr) | ||
{ | ||
int cpu; | ||
|
||
for_each_cpu_mask(cpu, cpu_possible_map) | ||
smp_call_function_single(cpu, bts_trace_stop_cpu, NULL, 1); | ||
} | ||
|
||
static int bts_trace_init(struct trace_array *tr) | ||
{ | ||
bts_trace_cpuinit(&boot_cpu_data); | ||
bts_trace_reset(tr); | ||
bts_trace_start(tr); | ||
|
||
return 0; | ||
} | ||
|
||
static void bts_trace_print_header(struct seq_file *m) | ||
{ | ||
#ifdef __i386__ | ||
seq_puts(m, "# CPU# FROM TO FUNCTION\n"); | ||
seq_puts(m, "# | | | |\n"); | ||
#else | ||
seq_puts(m, | ||
"# CPU# FROM TO FUNCTION\n"); | ||
seq_puts(m, | ||
"# | | | |\n"); | ||
#endif | ||
} | ||
|
||
static enum print_line_t bts_trace_print_line(struct trace_iterator *iter) | ||
{ | ||
struct trace_entry *entry = iter->ent; | ||
struct trace_seq *seq = &iter->seq; | ||
struct bts_entry *it; | ||
|
||
trace_assign_type(it, entry); | ||
|
||
if (entry->type == TRACE_BTS) { | ||
int ret; | ||
#ifdef CONFIG_KALLSYMS | ||
char function[KSYM_SYMBOL_LEN]; | ||
sprint_symbol(function, it->from); | ||
#else | ||
char *function = "<unknown>"; | ||
#endif | ||
|
||
ret = trace_seq_printf(seq, "%4d 0x%lx -> 0x%lx [%s]\n", | ||
entry->cpu, it->from, it->to, function); | ||
if (!ret) | ||
return TRACE_TYPE_PARTIAL_LINE;; | ||
return TRACE_TYPE_HANDLED; | ||
} | ||
return TRACE_TYPE_UNHANDLED; | ||
} | ||
|
||
void trace_bts(struct trace_array *tr, unsigned long from, unsigned long to) | ||
{ | ||
struct ring_buffer_event *event; | ||
struct bts_entry *entry; | ||
unsigned long irq; | ||
|
||
event = ring_buffer_lock_reserve(tr->buffer, sizeof(*entry), &irq); | ||
if (!event) | ||
return; | ||
entry = ring_buffer_event_data(event); | ||
tracing_generic_entry_update(&entry->ent, 0, from); | ||
entry->ent.type = TRACE_BTS; | ||
entry->ent.cpu = smp_processor_id(); | ||
entry->from = from; | ||
entry->to = to; | ||
ring_buffer_unlock_commit(tr->buffer, event, irq); | ||
} | ||
|
||
static void trace_bts_at(struct trace_array *tr, size_t index) | ||
{ | ||
const void *raw = NULL; | ||
unsigned long from, to; | ||
int err; | ||
|
||
err = ds_access_bts(this_tracer, index, &raw); | ||
if (err < 0) | ||
return; | ||
|
||
from = *(const unsigned long *)raw; | ||
to = *(const unsigned long *)((const char *)raw + sizeof_field); | ||
|
||
trace_bts(tr, from, to); | ||
} | ||
|
||
static void trace_bts_cpu(void *arg) | ||
{ | ||
struct trace_array *tr = (struct trace_array *) arg; | ||
size_t index = 0, end = 0, i; | ||
int err; | ||
|
||
if (!this_tracer) | ||
return; | ||
|
||
bts_disable(); | ||
|
||
err = ds_get_bts_index(this_tracer, &index); | ||
if (err < 0) | ||
goto out; | ||
|
||
err = ds_get_bts_end(this_tracer, &end); | ||
if (err < 0) | ||
goto out; | ||
|
||
for (i = index; i < end; i++) | ||
trace_bts_at(tr, i); | ||
|
||
for (i = 0; i < index; i++) | ||
trace_bts_at(tr, i); | ||
|
||
out: | ||
bts_enable(); | ||
} | ||
|
||
static void trace_bts_prepare(struct trace_iterator *iter) | ||
{ | ||
int cpu; | ||
|
||
for_each_cpu_mask(cpu, cpu_possible_map) | ||
smp_call_function_single(cpu, trace_bts_cpu, iter->tr, 1); | ||
} | ||
|
||
struct tracer bts_tracer __read_mostly = | ||
{ | ||
.name = "bts", | ||
.init = bts_trace_init, | ||
.reset = bts_trace_stop, | ||
.print_header = bts_trace_print_header, | ||
.print_line = bts_trace_print_line, | ||
.start = bts_trace_start, | ||
.stop = bts_trace_stop, | ||
.open = trace_bts_prepare | ||
}; | ||
|
||
__init static int init_bts_trace(void) | ||
{ | ||
return register_tracer(&bts_tracer); | ||
} | ||
device_initcall(init_bts_trace); |