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 | |
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')
-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 | } |