diff options
author | Frederic Weisbecker <fweisbec@gmail.com> | 2008-11-27 18:42:46 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-11-28 03:45:04 -0500 |
commit | 1a056155edd458eb93ef383fa8e5741d7e7c6360 (patch) | |
tree | 9d58c7c118be65899b62f0c43892e5980a9912c2 /kernel/trace | |
parent | 83a8df618eb04bd2819a758f3b409b1449862434 (diff) |
tracing/function-graph-tracer: adjustments of the trace informations
Impact: increase the visual qualities of the call-graph-tracer output
This patch applies various trace output formatting changes:
- CPU is now a decimal number, followed by a parenthesis.
- Overhead is now on the second column (gives a good visibility)
- Cost is now on the third column, can't exceed 9999.99 us. It is
followed by a virtual line based on a "|" character.
- Functions calls are now the last column on the right. This way, we
haven't dynamic column (which flow is harder to follow) on its right.
- CPU and Overhead have their own option flag. They are default-on but you
can disable them easily:
echo nofuncgraph-cpu > trace_options
echo nofuncgraph-overhead > trace_options
TODO:
_ Refactoring of the thread switch output.
_ Give a default-off option to output the thread and its pid on each row.
_ Provide headers
_ ....
Here is an example of the new trace style:
0) | mutex_unlock() {
0) 0.639 us | __mutex_unlock_slowpath();
0) 1.607 us | }
0) | remove_wait_queue() {
0) 0.616 us | _spin_lock_irqsave();
0) 0.616 us | _spin_unlock_irqrestore();
0) 2.779 us | }
0) 0.495 us | n_tty_set_room();
0) ! 9999.999 us | }
0) | tty_ldisc_deref() {
0) 0.615 us | _spin_lock_irqsave();
0) 0.616 us | _spin_unlock_irqrestore();
0) 2.793 us | }
0) | current_fs_time() {
0) 0.488 us | current_kernel_time();
0) 0.495 us | timespec_trunc();
0) 2.486 us | }
0) ! 9999.999 us | }
0) ! 9999.999 us | }
0) ! 9999.999 us | }
0) | sys_read() {
0) 0.796 us | fget_light();
0) | vfs_read() {
0) | rw_verify_area() {
0) | security_file_permission() {
0) 0.488 us | cap_file_permission();
0) 1.720 us | }
0) 3. 4 us | }
0) | tty_read() {
0) 0.488 us | tty_paranoia_check();
0) | tty_ldisc_ref_wait() {
0) | tty_ldisc_try() {
0) 0.615 us | _spin_lock_irqsave();
0) 0.615 us | _spin_unlock_irqrestore();
0) 5.436 us | }
0) 6.427 us | }
Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com>
Acked-by: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel/trace')
-rw-r--r-- | kernel/trace/trace_functions_graph.c | 142 |
1 files changed, 93 insertions, 49 deletions
diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c index b958d60377b3..596a3ee43053 100644 --- a/kernel/trace/trace_functions_graph.c +++ b/kernel/trace/trace_functions_graph.c | |||
@@ -14,20 +14,25 @@ | |||
14 | #include "trace.h" | 14 | #include "trace.h" |
15 | 15 | ||
16 | #define TRACE_GRAPH_INDENT 2 | 16 | #define TRACE_GRAPH_INDENT 2 |
17 | /* Spaces between function call and time duration */ | ||
18 | #define TRACE_GRAPH_TIMESPACE_ENTRY " " | ||
19 | /* Spaces between function call and closing braces */ | ||
20 | #define TRACE_GRAPH_TIMESPACE_RET " " | ||
21 | 17 | ||
18 | /* Flag options */ | ||
22 | #define TRACE_GRAPH_PRINT_OVERRUN 0x1 | 19 | #define TRACE_GRAPH_PRINT_OVERRUN 0x1 |
20 | #define TRACE_GRAPH_PRINT_CPU 0x2 | ||
21 | #define TRACE_GRAPH_PRINT_OVERHEAD 0x4 | ||
22 | |||
23 | static struct tracer_opt trace_opts[] = { | 23 | static struct tracer_opt trace_opts[] = { |
24 | /* Display overruns or not */ | 24 | /* Display overruns ? */ |
25 | { TRACER_OPT(overrun, TRACE_GRAPH_PRINT_OVERRUN) }, | 25 | { TRACER_OPT(funcgraph-overrun, TRACE_GRAPH_PRINT_OVERRUN) }, |
26 | /* Display CPU ? */ | ||
27 | { TRACER_OPT(funcgraph-cpu, TRACE_GRAPH_PRINT_CPU) }, | ||
28 | /* Display Overhead ? */ | ||
29 | { TRACER_OPT(funcgraph-overhead, TRACE_GRAPH_PRINT_OVERHEAD) }, | ||
26 | { } /* Empty entry */ | 30 | { } /* Empty entry */ |
27 | }; | 31 | }; |
28 | 32 | ||
29 | static struct tracer_flags tracer_flags = { | 33 | static struct tracer_flags tracer_flags = { |
30 | .val = 0, /* Don't display overruns by default */ | 34 | /* Don't display overruns by default */ |
35 | .val = TRACE_GRAPH_PRINT_CPU | TRACE_GRAPH_PRINT_OVERHEAD, | ||
31 | .opts = trace_opts | 36 | .opts = trace_opts |
32 | }; | 37 | }; |
33 | 38 | ||
@@ -56,6 +61,36 @@ static void graph_trace_reset(struct trace_array *tr) | |||
56 | unregister_ftrace_graph(); | 61 | unregister_ftrace_graph(); |
57 | } | 62 | } |
58 | 63 | ||
64 | static inline int log10_cpu(int nb) | ||
65 | { | ||
66 | if (nb / 100) | ||
67 | return 3; | ||
68 | if (nb / 10) | ||
69 | return 2; | ||
70 | return 1; | ||
71 | } | ||
72 | |||
73 | static enum print_line_t | ||
74 | print_graph_cpu(struct trace_seq *s, int cpu) | ||
75 | { | ||
76 | int i; | ||
77 | int ret; | ||
78 | int log10_this = log10_cpu(cpu); | ||
79 | int log10_all = log10_cpu(cpus_weight_nr(cpu_online_map)); | ||
80 | |||
81 | |||
82 | for (i = 0; i < log10_all - log10_this; i++) { | ||
83 | ret = trace_seq_printf(s, " "); | ||
84 | if (!ret) | ||
85 | return TRACE_TYPE_PARTIAL_LINE; | ||
86 | } | ||
87 | ret = trace_seq_printf(s, "%d) ", cpu); | ||
88 | if (!ret) | ||
89 | return TRACE_TYPE_PARTIAL_LINE; | ||
90 | return TRACE_TYPE_HANDLED; | ||
91 | } | ||
92 | |||
93 | |||
59 | /* If the pid changed since the last trace, output this event */ | 94 | /* If the pid changed since the last trace, output this event */ |
60 | static int verif_pid(struct trace_seq *s, pid_t pid, int cpu) | 95 | static int verif_pid(struct trace_seq *s, pid_t pid, int cpu) |
61 | { | 96 | { |
@@ -67,8 +102,7 @@ static int verif_pid(struct trace_seq *s, pid_t pid, int cpu) | |||
67 | last_pid[cpu] = pid; | 102 | last_pid[cpu] = pid; |
68 | comm = trace_find_cmdline(pid); | 103 | comm = trace_find_cmdline(pid); |
69 | 104 | ||
70 | return trace_seq_printf(s, "\nCPU[%03d] " | 105 | return trace_seq_printf(s, "\n------------8<---------- thread %s-%d" |
71 | " ------------8<---------- thread %s-%d" | ||
72 | " ------------8<----------\n\n", | 106 | " ------------8<----------\n\n", |
73 | cpu, comm, pid); | 107 | cpu, comm, pid); |
74 | } | 108 | } |
@@ -86,7 +120,7 @@ trace_branch_is_leaf(struct trace_iterator *iter, | |||
86 | if (!ring_iter) | 120 | if (!ring_iter) |
87 | return false; | 121 | return false; |
88 | 122 | ||
89 | event = ring_buffer_iter_peek(iter->buffer_iter[iter->cpu], NULL); | 123 | event = ring_buffer_iter_peek(ring_iter, NULL); |
90 | 124 | ||
91 | if (!event) | 125 | if (!event) |
92 | return false; | 126 | return false; |
@@ -108,7 +142,7 @@ static inline int | |||
108 | print_graph_duration(unsigned long long duration, struct trace_seq *s) | 142 | print_graph_duration(unsigned long long duration, struct trace_seq *s) |
109 | { | 143 | { |
110 | unsigned long nsecs_rem = do_div(duration, 1000); | 144 | unsigned long nsecs_rem = do_div(duration, 1000); |
111 | return trace_seq_printf(s, "+ %llu.%lu us\n", duration, nsecs_rem); | 145 | return trace_seq_printf(s, "%4llu.%3lu us | ", duration, nsecs_rem); |
112 | } | 146 | } |
113 | 147 | ||
114 | /* Signal a overhead of time execution to the output */ | 148 | /* Signal a overhead of time execution to the output */ |
@@ -136,8 +170,8 @@ print_graph_entry_leaf(struct trace_iterator *iter, | |||
136 | struct ring_buffer_event *event; | 170 | struct ring_buffer_event *event; |
137 | struct ftrace_graph_ent *call; | 171 | struct ftrace_graph_ent *call; |
138 | unsigned long long duration; | 172 | unsigned long long duration; |
139 | int i; | ||
140 | int ret; | 173 | int ret; |
174 | int i; | ||
141 | 175 | ||
142 | event = ring_buffer_read(iter->buffer_iter[iter->cpu], NULL); | 176 | event = ring_buffer_read(iter->buffer_iter[iter->cpu], NULL); |
143 | ret_entry = ring_buffer_event_data(event); | 177 | ret_entry = ring_buffer_event_data(event); |
@@ -145,8 +179,19 @@ print_graph_entry_leaf(struct trace_iterator *iter, | |||
145 | call = &entry->graph_ent; | 179 | call = &entry->graph_ent; |
146 | duration = graph_ret->rettime - graph_ret->calltime; | 180 | duration = graph_ret->rettime - graph_ret->calltime; |
147 | 181 | ||
182 | /* Must not exceed 8 characters: 9999.999 us */ | ||
183 | if (duration > 10000000ULL) | ||
184 | duration = 9999999ULL; | ||
185 | |||
148 | /* Overhead */ | 186 | /* Overhead */ |
149 | ret = print_graph_overhead(duration, s); | 187 | if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) { |
188 | ret = print_graph_overhead(duration, s); | ||
189 | if (!ret) | ||
190 | return TRACE_TYPE_PARTIAL_LINE; | ||
191 | } | ||
192 | |||
193 | /* Duration */ | ||
194 | ret = print_graph_duration(duration, s); | ||
150 | if (!ret) | 195 | if (!ret) |
151 | return TRACE_TYPE_PARTIAL_LINE; | 196 | return TRACE_TYPE_PARTIAL_LINE; |
152 | 197 | ||
@@ -161,16 +206,7 @@ print_graph_entry_leaf(struct trace_iterator *iter, | |||
161 | if (!ret) | 206 | if (!ret) |
162 | return TRACE_TYPE_PARTIAL_LINE; | 207 | return TRACE_TYPE_PARTIAL_LINE; |
163 | 208 | ||
164 | ret = trace_seq_printf(s, "();"); | 209 | ret = trace_seq_printf(s, "();\n"); |
165 | if (!ret) | ||
166 | return TRACE_TYPE_PARTIAL_LINE; | ||
167 | |||
168 | /* Duration */ | ||
169 | ret = trace_seq_printf(s, TRACE_GRAPH_TIMESPACE_ENTRY); | ||
170 | if (!ret) | ||
171 | return TRACE_TYPE_PARTIAL_LINE; | ||
172 | |||
173 | ret = print_graph_duration(duration, s); | ||
174 | if (!ret) | 210 | if (!ret) |
175 | return TRACE_TYPE_PARTIAL_LINE; | 211 | return TRACE_TYPE_PARTIAL_LINE; |
176 | 212 | ||
@@ -186,9 +222,14 @@ print_graph_entry_nested(struct ftrace_graph_ent_entry *entry, | |||
186 | struct ftrace_graph_ent *call = &entry->graph_ent; | 222 | struct ftrace_graph_ent *call = &entry->graph_ent; |
187 | 223 | ||
188 | /* No overhead */ | 224 | /* No overhead */ |
189 | ret = trace_seq_printf(s, " "); | 225 | if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) { |
190 | if (!ret) | 226 | ret = trace_seq_printf(s, " "); |
191 | return TRACE_TYPE_PARTIAL_LINE; | 227 | if (!ret) |
228 | return TRACE_TYPE_PARTIAL_LINE; | ||
229 | } | ||
230 | |||
231 | /* No time */ | ||
232 | ret = trace_seq_printf(s, " | "); | ||
192 | 233 | ||
193 | /* Function */ | 234 | /* Function */ |
194 | for (i = 0; i < call->depth * TRACE_GRAPH_INDENT; i++) { | 235 | for (i = 0; i < call->depth * TRACE_GRAPH_INDENT; i++) { |
@@ -201,12 +242,7 @@ print_graph_entry_nested(struct ftrace_graph_ent_entry *entry, | |||
201 | if (!ret) | 242 | if (!ret) |
202 | return TRACE_TYPE_PARTIAL_LINE; | 243 | return TRACE_TYPE_PARTIAL_LINE; |
203 | 244 | ||
204 | ret = trace_seq_printf(s, "() {"); | 245 | ret = trace_seq_printf(s, "() {\n"); |
205 | if (!ret) | ||
206 | return TRACE_TYPE_PARTIAL_LINE; | ||
207 | |||
208 | /* No duration to print at this state */ | ||
209 | ret = trace_seq_printf(s, TRACE_GRAPH_TIMESPACE_ENTRY "-\n"); | ||
210 | if (!ret) | 246 | if (!ret) |
211 | return TRACE_TYPE_PARTIAL_LINE; | 247 | return TRACE_TYPE_PARTIAL_LINE; |
212 | 248 | ||
@@ -220,12 +256,16 @@ print_graph_entry(struct ftrace_graph_ent_entry *field, struct trace_seq *s, | |||
220 | int ret; | 256 | int ret; |
221 | struct trace_entry *ent = iter->ent; | 257 | struct trace_entry *ent = iter->ent; |
222 | 258 | ||
259 | /* Pid */ | ||
223 | if (!verif_pid(s, ent->pid, cpu)) | 260 | if (!verif_pid(s, ent->pid, cpu)) |
224 | return TRACE_TYPE_PARTIAL_LINE; | 261 | return TRACE_TYPE_PARTIAL_LINE; |
225 | 262 | ||
226 | ret = trace_seq_printf(s, "CPU[%03d] ", cpu); | 263 | /* Cpu */ |
227 | if (!ret) | 264 | if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) { |
228 | return TRACE_TYPE_PARTIAL_LINE; | 265 | ret = print_graph_cpu(s, cpu); |
266 | if (!ret) | ||
267 | return TRACE_TYPE_PARTIAL_LINE; | ||
268 | } | ||
229 | 269 | ||
230 | if (trace_branch_is_leaf(iter, field)) | 270 | if (trace_branch_is_leaf(iter, field)) |
231 | return print_graph_entry_leaf(iter, field, s); | 271 | return print_graph_entry_leaf(iter, field, s); |
@@ -242,17 +282,30 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s, | |||
242 | int ret; | 282 | int ret; |
243 | unsigned long long duration = trace->rettime - trace->calltime; | 283 | unsigned long long duration = trace->rettime - trace->calltime; |
244 | 284 | ||
285 | /* Must not exceed 8 characters: xxxx.yyy us */ | ||
286 | if (duration > 10000000ULL) | ||
287 | duration = 9999999ULL; | ||
288 | |||
245 | /* Pid */ | 289 | /* Pid */ |
246 | if (!verif_pid(s, ent->pid, cpu)) | 290 | if (!verif_pid(s, ent->pid, cpu)) |
247 | return TRACE_TYPE_PARTIAL_LINE; | 291 | return TRACE_TYPE_PARTIAL_LINE; |
248 | 292 | ||
249 | /* Cpu */ | 293 | /* Cpu */ |
250 | ret = trace_seq_printf(s, "CPU[%03d] ", cpu); | 294 | if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) { |
251 | if (!ret) | 295 | ret = print_graph_cpu(s, cpu); |
252 | return TRACE_TYPE_PARTIAL_LINE; | 296 | if (!ret) |
297 | return TRACE_TYPE_PARTIAL_LINE; | ||
298 | } | ||
253 | 299 | ||
254 | /* Overhead */ | 300 | /* Overhead */ |
255 | ret = print_graph_overhead(duration, s); | 301 | if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) { |
302 | ret = print_graph_overhead(duration, s); | ||
303 | if (!ret) | ||
304 | return TRACE_TYPE_PARTIAL_LINE; | ||
305 | } | ||
306 | |||
307 | /* Duration */ | ||
308 | ret = print_graph_duration(duration, s); | ||
256 | if (!ret) | 309 | if (!ret) |
257 | return TRACE_TYPE_PARTIAL_LINE; | 310 | return TRACE_TYPE_PARTIAL_LINE; |
258 | 311 | ||
@@ -263,16 +316,7 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s, | |||
263 | return TRACE_TYPE_PARTIAL_LINE; | 316 | return TRACE_TYPE_PARTIAL_LINE; |
264 | } | 317 | } |
265 | 318 | ||
266 | ret = trace_seq_printf(s, "} "); | 319 | ret = trace_seq_printf(s, "}\n"); |
267 | if (!ret) | ||
268 | return TRACE_TYPE_PARTIAL_LINE; | ||
269 | |||
270 | /* Duration */ | ||
271 | ret = trace_seq_printf(s, TRACE_GRAPH_TIMESPACE_RET); | ||
272 | if (!ret) | ||
273 | return TRACE_TYPE_PARTIAL_LINE; | ||
274 | |||
275 | ret = print_graph_duration(duration, s); | ||
276 | if (!ret) | 320 | if (!ret) |
277 | return TRACE_TYPE_PARTIAL_LINE; | 321 | return TRACE_TYPE_PARTIAL_LINE; |
278 | 322 | ||