diff options
| author | Pekka Paalanen <pq@iki.fi> | 2008-05-12 15:20:57 -0400 |
|---|---|---|
| committer | Thomas Gleixner <tglx@linutronix.de> | 2008-05-24 05:24:53 -0400 |
| commit | bd8ac686c73c7e925fcfe0b02dc4e7b947127864 (patch) | |
| tree | 7a675c4919429b507bb8e7b09677d96ae30ec561 /kernel/trace | |
| parent | f984b51e0779a6dd30feedc41404013ca54e5d05 (diff) | |
ftrace: mmiotrace, updates
here is a patch that makes mmiotrace work almost well within the tracing
framework. The patch applies on top of my previous patch. I have my own
output formatting in place now.
Summary of changes:
- fix the NULL dereference that was due to not calling tracing_reset()
- add print_line() callback into struct tracer
- implement print_line() for mmiotrace, producing up-to-spec text
- add my output header, but that is not really called in the right place
- rewrote the main structs in mmiotrace
- added two new trace entry types: TRACE_MMIO_RW and TRACE_MMIO_MAP
- made some functions in trace.c non-static
- check current==NULL in tracing_generic_entry_update()
- fix(?) comparison in trace_seq_printf()
Things seem to work fine except a few issues. Markers (text lines injected
into mmiotrace log) are missing, I did not feel hacking them in before we
have variable length entries. My output header is printed only for 'trace'
file, but not 'trace_pipe'. For some reason, despite my quick fix,
iter->trace is NULL in print_trace_line() when called from 'trace_pipe'
file, which means I don't get proper output formatting.
I only tried by loading nouveau.ko, which just detects the card, and that
is traced fine. I didn't try further. Map, two reads and unmap. Works
perfectly.
I am missing the information about overflows, I'd prefer to have a
counter for lost events. I didn't try, but I guess currently there is no
way of knowning when it overflows?
So, not too far from being fully operational, it seems :-)
And looking at the diffstat, there also is some 700-900 lines of user space
code that just became obsolete.
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'kernel/trace')
| -rw-r--r-- | kernel/trace/trace.c | 34 | ||||
| -rw-r--r-- | kernel/trace/trace.h | 14 | ||||
| -rw-r--r-- | kernel/trace/trace_mmiotrace.c | 151 |
3 files changed, 171 insertions, 28 deletions
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 3271916ff033..d14fe49e9638 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c | |||
| @@ -831,6 +831,40 @@ ftrace(struct trace_array *tr, struct trace_array_cpu *data, | |||
| 831 | trace_function(tr, data, ip, parent_ip, flags); | 831 | trace_function(tr, data, ip, parent_ip, flags); |
| 832 | } | 832 | } |
| 833 | 833 | ||
| 834 | #ifdef CONFIG_MMIOTRACE | ||
| 835 | void __trace_mmiotrace_rw(struct trace_array *tr, struct trace_array_cpu *data, | ||
| 836 | struct mmiotrace_rw *rw) | ||
| 837 | { | ||
| 838 | struct trace_entry *entry; | ||
| 839 | unsigned long irq_flags; | ||
| 840 | |||
| 841 | spin_lock_irqsave(&data->lock, irq_flags); | ||
| 842 | entry = tracing_get_trace_entry(tr, data); | ||
| 843 | tracing_generic_entry_update(entry, 0); | ||
| 844 | entry->type = TRACE_MMIO_RW; | ||
| 845 | entry->mmiorw = *rw; | ||
| 846 | spin_unlock_irqrestore(&data->lock, irq_flags); | ||
| 847 | |||
| 848 | trace_wake_up(); | ||
| 849 | } | ||
| 850 | |||
| 851 | void __trace_mmiotrace_map(struct trace_array *tr, struct trace_array_cpu *data, | ||
| 852 | struct mmiotrace_map *map) | ||
| 853 | { | ||
| 854 | struct trace_entry *entry; | ||
| 855 | unsigned long irq_flags; | ||
| 856 | |||
| 857 | spin_lock_irqsave(&data->lock, irq_flags); | ||
| 858 | entry = tracing_get_trace_entry(tr, data); | ||
| 859 | tracing_generic_entry_update(entry, 0); | ||
| 860 | entry->type = TRACE_MMIO_MAP; | ||
| 861 | entry->mmiomap = *map; | ||
| 862 | spin_unlock_irqrestore(&data->lock, irq_flags); | ||
| 863 | |||
| 864 | trace_wake_up(); | ||
| 865 | } | ||
| 866 | #endif | ||
| 867 | |||
| 834 | void __trace_stack(struct trace_array *tr, | 868 | void __trace_stack(struct trace_array *tr, |
| 835 | struct trace_array_cpu *data, | 869 | struct trace_array_cpu *data, |
| 836 | unsigned long flags, | 870 | unsigned long flags, |
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index c460e85e94ed..0ef9ef74c806 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include <asm/atomic.h> | 5 | #include <asm/atomic.h> |
| 6 | #include <linux/sched.h> | 6 | #include <linux/sched.h> |
| 7 | #include <linux/clocksource.h> | 7 | #include <linux/clocksource.h> |
| 8 | #include <linux/mmiotrace.h> | ||
| 8 | 9 | ||
| 9 | enum trace_type { | 10 | enum trace_type { |
| 10 | __TRACE_FIRST_TYPE = 0, | 11 | __TRACE_FIRST_TYPE = 0, |
| @@ -14,6 +15,8 @@ enum trace_type { | |||
| 14 | TRACE_WAKE, | 15 | TRACE_WAKE, |
| 15 | TRACE_STACK, | 16 | TRACE_STACK, |
| 16 | TRACE_SPECIAL, | 17 | TRACE_SPECIAL, |
| 18 | TRACE_MMIO_RW, | ||
| 19 | TRACE_MMIO_MAP, | ||
| 17 | 20 | ||
| 18 | __TRACE_LAST_TYPE | 21 | __TRACE_LAST_TYPE |
| 19 | }; | 22 | }; |
| @@ -75,6 +78,8 @@ struct trace_entry { | |||
| 75 | struct ctx_switch_entry ctx; | 78 | struct ctx_switch_entry ctx; |
| 76 | struct special_entry special; | 79 | struct special_entry special; |
| 77 | struct stack_entry stack; | 80 | struct stack_entry stack; |
| 81 | struct mmiotrace_rw mmiorw; | ||
| 82 | struct mmiotrace_map mmiomap; | ||
| 78 | }; | 83 | }; |
| 79 | }; | 84 | }; |
| 80 | 85 | ||
| @@ -255,6 +260,15 @@ extern unsigned long ftrace_update_tot_cnt; | |||
| 255 | extern int DYN_FTRACE_TEST_NAME(void); | 260 | extern int DYN_FTRACE_TEST_NAME(void); |
| 256 | #endif | 261 | #endif |
| 257 | 262 | ||
| 263 | #ifdef CONFIG_MMIOTRACE | ||
| 264 | extern void __trace_mmiotrace_rw(struct trace_array *tr, | ||
| 265 | struct trace_array_cpu *data, | ||
| 266 | struct mmiotrace_rw *rw); | ||
| 267 | extern void __trace_mmiotrace_map(struct trace_array *tr, | ||
| 268 | struct trace_array_cpu *data, | ||
| 269 | struct mmiotrace_map *map); | ||
| 270 | #endif | ||
| 271 | |||
| 258 | #ifdef CONFIG_FTRACE_STARTUP_TEST | 272 | #ifdef CONFIG_FTRACE_STARTUP_TEST |
| 259 | #ifdef CONFIG_FTRACE | 273 | #ifdef CONFIG_FTRACE |
| 260 | extern int trace_selftest_startup_function(struct tracer *trace, | 274 | extern int trace_selftest_startup_function(struct tracer *trace, |
diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c index e4dd03cc5aa6..3a12b1ad0c63 100644 --- a/kernel/trace/trace_mmiotrace.c +++ b/kernel/trace/trace_mmiotrace.c | |||
| @@ -11,19 +11,26 @@ | |||
| 11 | 11 | ||
| 12 | #include "trace.h" | 12 | #include "trace.h" |
| 13 | 13 | ||
| 14 | extern void | ||
| 15 | __trace_special(void *__tr, void *__data, | ||
| 16 | unsigned long arg1, unsigned long arg2, unsigned long arg3); | ||
| 17 | |||
| 18 | static struct trace_array *mmio_trace_array; | 14 | static struct trace_array *mmio_trace_array; |
| 19 | 15 | ||
| 16 | static void mmio_reset_data(struct trace_array *tr) | ||
| 17 | { | ||
| 18 | int cpu; | ||
| 19 | |||
| 20 | tr->time_start = ftrace_now(tr->cpu); | ||
| 21 | |||
| 22 | for_each_online_cpu(cpu) | ||
| 23 | tracing_reset(tr->data[cpu]); | ||
| 24 | } | ||
| 20 | 25 | ||
| 21 | static void mmio_trace_init(struct trace_array *tr) | 26 | static void mmio_trace_init(struct trace_array *tr) |
| 22 | { | 27 | { |
| 23 | pr_debug("in %s\n", __func__); | 28 | pr_debug("in %s\n", __func__); |
| 24 | mmio_trace_array = tr; | 29 | mmio_trace_array = tr; |
| 25 | if (tr->ctrl) | 30 | if (tr->ctrl) { |
| 31 | mmio_reset_data(tr); | ||
| 26 | enable_mmiotrace(); | 32 | enable_mmiotrace(); |
| 33 | } | ||
| 27 | } | 34 | } |
| 28 | 35 | ||
| 29 | static void mmio_trace_reset(struct trace_array *tr) | 36 | static void mmio_trace_reset(struct trace_array *tr) |
| @@ -31,15 +38,110 @@ static void mmio_trace_reset(struct trace_array *tr) | |||
| 31 | pr_debug("in %s\n", __func__); | 38 | pr_debug("in %s\n", __func__); |
| 32 | if (tr->ctrl) | 39 | if (tr->ctrl) |
| 33 | disable_mmiotrace(); | 40 | disable_mmiotrace(); |
| 41 | mmio_reset_data(tr); | ||
| 42 | mmio_trace_array = NULL; | ||
| 34 | } | 43 | } |
| 35 | 44 | ||
| 36 | static void mmio_trace_ctrl_update(struct trace_array *tr) | 45 | static void mmio_trace_ctrl_update(struct trace_array *tr) |
| 37 | { | 46 | { |
| 38 | pr_debug("in %s\n", __func__); | 47 | pr_debug("in %s\n", __func__); |
| 39 | if (tr->ctrl) | 48 | if (tr->ctrl) { |
| 49 | mmio_reset_data(tr); | ||
| 40 | enable_mmiotrace(); | 50 | enable_mmiotrace(); |
| 41 | else | 51 | } else { |
| 42 | disable_mmiotrace(); | 52 | disable_mmiotrace(); |
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | /* XXX: This is not called for trace_pipe file! */ | ||
| 57 | void mmio_print_header(struct trace_iterator *iter) | ||
| 58 | { | ||
| 59 | struct trace_seq *s = &iter->seq; | ||
| 60 | trace_seq_printf(s, "VERSION broken 20070824\n"); | ||
| 61 | /* TODO: print /proc/bus/pci/devices contents as PCIDEV lines */ | ||
| 62 | } | ||
| 63 | |||
| 64 | static int mmio_print_rw(struct trace_iterator *iter) | ||
| 65 | { | ||
| 66 | struct trace_entry *entry = iter->ent; | ||
| 67 | struct mmiotrace_rw *rw = &entry->mmiorw; | ||
| 68 | struct trace_seq *s = &iter->seq; | ||
| 69 | unsigned long long t = ns2usecs(entry->t); | ||
| 70 | unsigned long usec_rem = do_div(t, 1000000ULL); | ||
| 71 | unsigned secs = (unsigned long)t; | ||
| 72 | int ret = 1; | ||
| 73 | |||
| 74 | switch (entry->mmiorw.opcode) { | ||
| 75 | case MMIO_READ: | ||
| 76 | ret = trace_seq_printf(s, | ||
| 77 | "R %d %lu.%06lu %d 0x%lx 0x%lx 0x%lx %d\n", | ||
| 78 | rw->width, secs, usec_rem, rw->map_id, rw->phys, | ||
| 79 | rw->value, rw->pc, entry->pid); | ||
| 80 | break; | ||
| 81 | case MMIO_WRITE: | ||
| 82 | ret = trace_seq_printf(s, | ||
| 83 | "W %d %lu.%06lu %d 0x%lx 0x%lx 0x%lx %d\n", | ||
| 84 | rw->width, secs, usec_rem, rw->map_id, rw->phys, | ||
| 85 | rw->value, rw->pc, entry->pid); | ||
| 86 | break; | ||
| 87 | case MMIO_UNKNOWN_OP: | ||
| 88 | ret = trace_seq_printf(s, | ||
| 89 | "UNKNOWN %lu.%06lu %d 0x%lx %02x,%02x,%02x 0x%lx %d\n", | ||
| 90 | secs, usec_rem, rw->map_id, rw->phys, | ||
| 91 | (rw->value >> 16) & 0xff, (rw->value >> 8) & 0xff, | ||
| 92 | (rw->value >> 0) & 0xff, rw->pc, entry->pid); | ||
| 93 | break; | ||
| 94 | default: | ||
| 95 | ret = trace_seq_printf(s, "rw what?\n"); | ||
| 96 | break; | ||
| 97 | } | ||
| 98 | if (ret) | ||
| 99 | return 1; | ||
| 100 | return 0; | ||
| 101 | } | ||
| 102 | |||
| 103 | static int mmio_print_map(struct trace_iterator *iter) | ||
| 104 | { | ||
| 105 | struct trace_entry *entry = iter->ent; | ||
| 106 | struct mmiotrace_map *m = &entry->mmiomap; | ||
| 107 | struct trace_seq *s = &iter->seq; | ||
| 108 | unsigned long long t = ns2usecs(entry->t); | ||
| 109 | unsigned long usec_rem = do_div(t, 1000000ULL); | ||
| 110 | unsigned secs = (unsigned long)t; | ||
| 111 | int ret = 1; | ||
| 112 | |||
| 113 | switch (entry->mmiorw.opcode) { | ||
| 114 | case MMIO_PROBE: | ||
| 115 | ret = trace_seq_printf(s, | ||
| 116 | "MAP %lu.%06lu %d 0x%lx 0x%lx 0x%lx 0x%lx %d\n", | ||
| 117 | secs, usec_rem, m->map_id, m->phys, m->virt, m->len, | ||
| 118 | 0UL, entry->pid); | ||
| 119 | break; | ||
| 120 | case MMIO_UNPROBE: | ||
| 121 | ret = trace_seq_printf(s, | ||
| 122 | "UNMAP %lu.%06lu %d 0x%lx %d\n", | ||
| 123 | secs, usec_rem, m->map_id, 0UL, entry->pid); | ||
| 124 | break; | ||
| 125 | default: | ||
| 126 | ret = trace_seq_printf(s, "map what?\n"); | ||
| 127 | break; | ||
| 128 | } | ||
| 129 | if (ret) | ||
| 130 | return 1; | ||
| 131 | return 0; | ||
| 132 | } | ||
| 133 | |||
| 134 | /* return 0 to abort printing without consuming current entry in pipe mode */ | ||
| 135 | static int mmio_print_line(struct trace_iterator *iter) | ||
| 136 | { | ||
| 137 | switch (iter->ent->type) { | ||
| 138 | case TRACE_MMIO_RW: | ||
| 139 | return mmio_print_rw(iter); | ||
| 140 | case TRACE_MMIO_MAP: | ||
| 141 | return mmio_print_map(iter); | ||
| 142 | default: | ||
| 143 | return 1; /* ignore unknown entries */ | ||
| 144 | } | ||
| 43 | } | 145 | } |
| 44 | 146 | ||
| 45 | static struct tracer mmio_tracer __read_mostly = | 147 | static struct tracer mmio_tracer __read_mostly = |
| @@ -47,38 +149,31 @@ static struct tracer mmio_tracer __read_mostly = | |||
| 47 | .name = "mmiotrace", | 149 | .name = "mmiotrace", |
| 48 | .init = mmio_trace_init, | 150 | .init = mmio_trace_init, |
| 49 | .reset = mmio_trace_reset, | 151 | .reset = mmio_trace_reset, |
| 152 | .open = mmio_print_header, | ||
| 50 | .ctrl_update = mmio_trace_ctrl_update, | 153 | .ctrl_update = mmio_trace_ctrl_update, |
| 154 | .print_line = mmio_print_line, | ||
| 51 | }; | 155 | }; |
| 52 | 156 | ||
| 53 | __init static int init_mmio_trace(void) | 157 | __init static int init_mmio_trace(void) |
| 54 | { | 158 | { |
| 55 | int ret = init_mmiotrace(); | ||
| 56 | if (ret) | ||
| 57 | return ret; | ||
| 58 | return register_tracer(&mmio_tracer); | 159 | return register_tracer(&mmio_tracer); |
| 59 | } | 160 | } |
| 60 | device_initcall(init_mmio_trace); | 161 | device_initcall(init_mmio_trace); |
| 61 | 162 | ||
| 62 | void mmio_trace_record(u32 type, unsigned long addr, unsigned long arg) | 163 | void mmio_trace_rw(struct mmiotrace_rw *rw) |
| 63 | { | 164 | { |
| 64 | struct trace_array *tr = mmio_trace_array; | 165 | struct trace_array *tr = mmio_trace_array; |
| 65 | struct trace_array_cpu *data = tr->data[smp_processor_id()]; | 166 | struct trace_array_cpu *data = tr->data[smp_processor_id()]; |
| 167 | __trace_mmiotrace_rw(tr, data, rw); | ||
| 168 | } | ||
| 66 | 169 | ||
| 67 | if (!current || current->pid == 0) { | 170 | void mmio_trace_mapping(struct mmiotrace_map *map) |
| 68 | /* | 171 | { |
| 69 | * XXX: This is a problem. We need to able to record, no | 172 | struct trace_array *tr = mmio_trace_array; |
| 70 | * matter what. tracing_generic_entry_update() would crash. | 173 | struct trace_array_cpu *data; |
| 71 | */ | 174 | |
| 72 | static unsigned limit; | 175 | preempt_disable(); |
| 73 | if (limit++ < 12) | 176 | data = tr->data[smp_processor_id()]; |
| 74 | pr_err("Error in %s: no current.\n", __func__); | 177 | __trace_mmiotrace_map(tr, data, map); |
| 75 | return; | 178 | preempt_enable(); |
| 76 | } | ||
| 77 | if (!tr || !data) { | ||
| 78 | static unsigned limit; | ||
| 79 | if (limit++ < 12) | ||
| 80 | pr_err("%s: no tr or data\n", __func__); | ||
| 81 | return; | ||
| 82 | } | ||
| 83 | __trace_special(tr, data, type, addr, arg); | ||
| 84 | } | 179 | } |
