diff options
author | Frederic Weisbecker <fweisbec@gmail.com> | 2008-11-25 18:57:25 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-11-25 19:59:45 -0500 |
commit | 287b6e68ca7209caec40b2f44f837c580a413bae (patch) | |
tree | b0867d75868f6049dc5747bd39fdae2d477dde66 /kernel/trace/trace_functions_graph.c | |
parent | fb52607afcd0629776f1dc9e657647ceae81dd50 (diff) |
tracing/function-return-tracer: set a more human readable output
Impact: feature
This patch sets a C-like output for the function graph tracing.
For this aim, we now call two handler for each function: one on the entry
and one other on return. This way we can draw a well-ordered call stack.
The pid of the previous trace is loosely stored to be compared against
the one of the current trace to see if there were a context switch.
Without this little feature, the call tree would seem broken at
some locations.
We could use the sched_tracer to capture these sched_events but this
way of processing is much more simpler.
2 spaces have been chosen for indentation to fit the screen while deep
calls. The time of execution in nanosecs is printed just after closed
braces, it seems more easy this way to find the corresponding function.
If the time was printed as a first column, it would be not so easy to
find the corresponding function if it is called on a deep depth.
I plan to output the return value but on 32 bits CPU, the return value
can be 32 or 64, and its difficult to guess on which case we are.
I don't know what would be the better solution on X86-32: only print
eax (low-part) or even edx (high-part).
Actually it's thee same problem when a function return a 8 bits value, the
high part of eax could contain junk values...
Here is an example of trace:
sys_read() {
fget_light() {
} 526
vfs_read() {
rw_verify_area() {
security_file_permission() {
cap_file_permission() {
} 519
} 1564
} 2640
do_sync_read() {
pipe_read() {
__might_sleep() {
} 511
pipe_wait() {
prepare_to_wait() {
} 760
deactivate_task() {
dequeue_task() {
dequeue_task_fair() {
dequeue_entity() {
update_curr() {
update_min_vruntime() {
} 504
} 1587
clear_buddies() {
} 512
add_cfs_task_weight() {
} 519
update_min_vruntime() {
} 511
} 5602
dequeue_entity() {
update_curr() {
update_min_vruntime() {
} 496
} 1631
clear_buddies() {
} 496
update_min_vruntime() {
} 527
} 4580
hrtick_update() {
hrtick_start_fair() {
} 488
} 1489
} 13700
} 14949
} 16016
msecs_to_jiffies() {
} 496
put_prev_task_fair() {
} 504
pick_next_task_fair() {
} 489
pick_next_task_rt() {
} 496
pick_next_task_fair() {
} 489
pick_next_task_idle() {
} 489
------------8<---------- thread 4 ------------8<----------
finish_task_switch() {
} 1203
do_softirq() {
__do_softirq() {
__local_bh_disable() {
} 669
rcu_process_callbacks() {
__rcu_process_callbacks() {
cpu_quiet() {
rcu_start_batch() {
} 503
} 1647
} 3128
__rcu_process_callbacks() {
} 542
} 5362
_local_bh_enable() {
} 587
} 8880
} 9986
kthread_should_stop() {
} 669
deactivate_task() {
dequeue_task() {
dequeue_task_fair() {
dequeue_entity() {
update_curr() {
calc_delta_mine() {
} 511
update_min_vruntime() {
} 511
} 2813
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/trace_functions_graph.c')
-rw-r--r-- | kernel/trace/trace_functions_graph.c | 104 |
1 files changed, 80 insertions, 24 deletions
diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c index f5bad4624d2b..b6f0cc2a00cb 100644 --- a/kernel/trace/trace_functions_graph.c +++ b/kernel/trace/trace_functions_graph.c | |||
@@ -13,6 +13,7 @@ | |||
13 | 13 | ||
14 | #include "trace.h" | 14 | #include "trace.h" |
15 | 15 | ||
16 | #define TRACE_GRAPH_INDENT 2 | ||
16 | 17 | ||
17 | #define TRACE_GRAPH_PRINT_OVERRUN 0x1 | 18 | #define TRACE_GRAPH_PRINT_OVERRUN 0x1 |
18 | static struct tracer_opt trace_opts[] = { | 19 | static struct tracer_opt trace_opts[] = { |
@@ -26,6 +27,8 @@ static struct tracer_flags tracer_flags = { | |||
26 | .opts = trace_opts | 27 | .opts = trace_opts |
27 | }; | 28 | }; |
28 | 29 | ||
30 | /* pid on the last trace processed */ | ||
31 | static pid_t last_pid = -1; | ||
29 | 32 | ||
30 | static int graph_trace_init(struct trace_array *tr) | 33 | static int graph_trace_init(struct trace_array *tr) |
31 | { | 34 | { |
@@ -33,7 +36,8 @@ static int graph_trace_init(struct trace_array *tr) | |||
33 | for_each_online_cpu(cpu) | 36 | for_each_online_cpu(cpu) |
34 | tracing_reset(tr, cpu); | 37 | tracing_reset(tr, cpu); |
35 | 38 | ||
36 | return register_ftrace_graph(&trace_function_graph); | 39 | return register_ftrace_graph(&trace_graph_return, |
40 | &trace_graph_entry); | ||
37 | } | 41 | } |
38 | 42 | ||
39 | static void graph_trace_reset(struct trace_array *tr) | 43 | static void graph_trace_reset(struct trace_array *tr) |
@@ -41,45 +45,97 @@ static void graph_trace_reset(struct trace_array *tr) | |||
41 | unregister_ftrace_graph(); | 45 | unregister_ftrace_graph(); |
42 | } | 46 | } |
43 | 47 | ||
48 | /* If the pid changed since the last trace, output this event */ | ||
49 | static int verif_pid(struct trace_seq *s, pid_t pid) | ||
50 | { | ||
51 | if (last_pid != -1 && last_pid == pid) | ||
52 | return 1; | ||
44 | 53 | ||
45 | enum print_line_t | 54 | last_pid = pid; |
46 | print_graph_function(struct trace_iterator *iter) | 55 | return trace_seq_printf(s, "\n------------8<---------- thread %d" |
56 | " ------------8<----------\n\n", | ||
57 | pid); | ||
58 | } | ||
59 | |||
60 | static enum print_line_t | ||
61 | print_graph_entry(struct ftrace_graph_ent *call, struct trace_seq *s, | ||
62 | struct trace_entry *ent) | ||
47 | { | 63 | { |
48 | struct trace_seq *s = &iter->seq; | 64 | int i; |
49 | struct trace_entry *entry = iter->ent; | ||
50 | struct ftrace_graph_entry *field; | ||
51 | int ret; | 65 | int ret; |
52 | 66 | ||
53 | if (entry->type == TRACE_FN_RET) { | 67 | if (!verif_pid(s, ent->pid)) |
54 | trace_assign_type(field, entry); | 68 | return TRACE_TYPE_PARTIAL_LINE; |
55 | ret = trace_seq_printf(s, "%pF -> ", (void *)field->parent_ip); | ||
56 | if (!ret) | ||
57 | return TRACE_TYPE_PARTIAL_LINE; | ||
58 | 69 | ||
59 | ret = seq_print_ip_sym(s, field->ip, | 70 | for (i = 0; i < call->depth * TRACE_GRAPH_INDENT; i++) { |
60 | trace_flags & TRACE_ITER_SYM_MASK); | 71 | ret = trace_seq_printf(s, " "); |
61 | if (!ret) | 72 | if (!ret) |
62 | return TRACE_TYPE_PARTIAL_LINE; | 73 | return TRACE_TYPE_PARTIAL_LINE; |
74 | } | ||
75 | |||
76 | ret = seq_print_ip_sym(s, call->func, 0); | ||
77 | if (!ret) | ||
78 | return TRACE_TYPE_PARTIAL_LINE; | ||
79 | |||
80 | ret = trace_seq_printf(s, "() {\n"); | ||
81 | if (!ret) | ||
82 | return TRACE_TYPE_PARTIAL_LINE; | ||
83 | return TRACE_TYPE_HANDLED; | ||
84 | } | ||
85 | |||
86 | static enum print_line_t | ||
87 | print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s, | ||
88 | struct trace_entry *ent) | ||
89 | { | ||
90 | int i; | ||
91 | int ret; | ||
92 | |||
93 | if (!verif_pid(s, ent->pid)) | ||
94 | return TRACE_TYPE_PARTIAL_LINE; | ||
63 | 95 | ||
64 | ret = trace_seq_printf(s, " (%llu ns)", | 96 | for (i = 0; i < trace->depth * TRACE_GRAPH_INDENT; i++) { |
65 | field->rettime - field->calltime); | 97 | ret = trace_seq_printf(s, " "); |
66 | if (!ret) | 98 | if (!ret) |
67 | return TRACE_TYPE_PARTIAL_LINE; | 99 | return TRACE_TYPE_PARTIAL_LINE; |
100 | } | ||
101 | |||
102 | ret = trace_seq_printf(s, "} "); | ||
103 | if (!ret) | ||
104 | return TRACE_TYPE_PARTIAL_LINE; | ||
68 | 105 | ||
69 | if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERRUN) { | 106 | ret = trace_seq_printf(s, "%llu\n", trace->rettime - trace->calltime); |
70 | ret = trace_seq_printf(s, " (Overruns: %lu)", | 107 | if (!ret) |
71 | field->overrun); | 108 | return TRACE_TYPE_PARTIAL_LINE; |
72 | if (!ret) | ||
73 | return TRACE_TYPE_PARTIAL_LINE; | ||
74 | } | ||
75 | 109 | ||
76 | ret = trace_seq_printf(s, "\n"); | 110 | if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERRUN) { |
111 | ret = trace_seq_printf(s, " (Overruns: %lu)\n", | ||
112 | trace->overrun); | ||
77 | if (!ret) | 113 | if (!ret) |
78 | return TRACE_TYPE_PARTIAL_LINE; | 114 | return TRACE_TYPE_PARTIAL_LINE; |
115 | } | ||
116 | return TRACE_TYPE_HANDLED; | ||
117 | } | ||
118 | |||
119 | enum print_line_t | ||
120 | print_graph_function(struct trace_iterator *iter) | ||
121 | { | ||
122 | struct trace_seq *s = &iter->seq; | ||
123 | struct trace_entry *entry = iter->ent; | ||
79 | 124 | ||
80 | return TRACE_TYPE_HANDLED; | 125 | switch (entry->type) { |
126 | case TRACE_GRAPH_ENT: { | ||
127 | struct ftrace_graph_ent_entry *field; | ||
128 | trace_assign_type(field, entry); | ||
129 | return print_graph_entry(&field->graph_ent, s, entry); | ||
130 | } | ||
131 | case TRACE_GRAPH_RET: { | ||
132 | struct ftrace_graph_ret_entry *field; | ||
133 | trace_assign_type(field, entry); | ||
134 | return print_graph_return(&field->ret, s, entry); | ||
135 | } | ||
136 | default: | ||
137 | return TRACE_TYPE_UNHANDLED; | ||
81 | } | 138 | } |
82 | return TRACE_TYPE_UNHANDLED; | ||
83 | } | 139 | } |
84 | 140 | ||
85 | static struct tracer graph_trace __read_mostly = { | 141 | static struct tracer graph_trace __read_mostly = { |