1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2fb52607aSFrederic Weisbecker /*
3fb52607aSFrederic Weisbecker *
4fb52607aSFrederic Weisbecker * Function graph tracer.
59005f3ebSFrederic Weisbecker * Copyright (c) 2008-2009 Frederic Weisbecker <[email protected]>
6fb52607aSFrederic Weisbecker * Mostly borrowed from function tracer which
7fb52607aSFrederic Weisbecker * is Copyright (c) Steven Rostedt <[email protected]>
8fb52607aSFrederic Weisbecker *
9fb52607aSFrederic Weisbecker */
10fb52607aSFrederic Weisbecker #include <linux/uaccess.h>
11fb52607aSFrederic Weisbecker #include <linux/ftrace.h>
12be7635e7SAlexander Potapenko #include <linux/interrupt.h>
135a0e3ad6STejun Heo #include <linux/slab.h>
14fb52607aSFrederic Weisbecker #include <linux/fs.h>
15fb52607aSFrederic Weisbecker
16fb52607aSFrederic Weisbecker #include "trace.h"
17f0868d1eSSteven Rostedt #include "trace_output.h"
18fb52607aSFrederic Weisbecker
19b304d044SSteven Rostedt /* When set, irq functions will be ignored */
20b304d044SSteven Rostedt static int ftrace_graph_skip_irqs;
21b304d044SSteven Rostedt
22be1eca39SJiri Olsa struct fgraph_cpu_data {
232fbcdb35SSteven Rostedt pid_t last_pid;
242fbcdb35SSteven Rostedt int depth;
252bd16212SJiri Olsa int depth_irq;
26be1eca39SJiri Olsa int ignore;
27f1c7f517SSteven Rostedt unsigned long enter_funcs[FTRACE_RETFUNC_DEPTH];
28be1eca39SJiri Olsa };
29be1eca39SJiri Olsa
30be1eca39SJiri Olsa struct fgraph_data {
316016ee13SNamhyung Kim struct fgraph_cpu_data __percpu *cpu_data;
32be1eca39SJiri Olsa
33be1eca39SJiri Olsa /* Place to preserve last processed entry. */
3421e92806SDonglin Peng union {
35be1eca39SJiri Olsa struct ftrace_graph_ent_entry ent;
3621e92806SDonglin Peng struct fgraph_retaddr_ent_entry rent;
3721e92806SDonglin Peng } ent;
38be1eca39SJiri Olsa struct ftrace_graph_ret_entry ret;
39be1eca39SJiri Olsa int failed;
40be1eca39SJiri Olsa int cpu;
412fbcdb35SSteven Rostedt };
422fbcdb35SSteven Rostedt
43287b6e68SFrederic Weisbecker #define TRACE_GRAPH_INDENT 2
44fb52607aSFrederic Weisbecker
451a414428SSteven Rostedt (Red Hat) unsigned int fgraph_max_depth;
468741db53SSteven Rostedt
47fb52607aSFrederic Weisbecker static struct tracer_opt trace_opts[] = {
489005f3ebSFrederic Weisbecker /* Display overruns? (for self-debug purpose) */
491a056155SFrederic Weisbecker { TRACER_OPT(funcgraph-overrun, TRACE_GRAPH_PRINT_OVERRUN) },
501a056155SFrederic Weisbecker /* Display CPU ? */
511a056155SFrederic Weisbecker { TRACER_OPT(funcgraph-cpu, TRACE_GRAPH_PRINT_CPU) },
521a056155SFrederic Weisbecker /* Display Overhead ? */
531a056155SFrederic Weisbecker { TRACER_OPT(funcgraph-overhead, TRACE_GRAPH_PRINT_OVERHEAD) },
5411e84accSFrederic Weisbecker /* Display proc name/pid */
5511e84accSFrederic Weisbecker { TRACER_OPT(funcgraph-proc, TRACE_GRAPH_PRINT_PROC) },
569005f3ebSFrederic Weisbecker /* Display duration of execution */
579005f3ebSFrederic Weisbecker { TRACER_OPT(funcgraph-duration, TRACE_GRAPH_PRINT_DURATION) },
589005f3ebSFrederic Weisbecker /* Display absolute time of an entry */
599005f3ebSFrederic Weisbecker { TRACER_OPT(funcgraph-abstime, TRACE_GRAPH_PRINT_ABS_TIME) },
602bd16212SJiri Olsa /* Display interrupts */
612bd16212SJiri Olsa { TRACER_OPT(funcgraph-irqs, TRACE_GRAPH_PRINT_IRQS) },
62607e3a29SRobert Elliott /* Display function name after trailing } */
63607e3a29SRobert Elliott { TRACER_OPT(funcgraph-tail, TRACE_GRAPH_PRINT_TAIL) },
64a1be9cccSDonglin Peng #ifdef CONFIG_FUNCTION_GRAPH_RETVAL
65a1be9cccSDonglin Peng /* Display function return value ? */
66a1be9cccSDonglin Peng { TRACER_OPT(funcgraph-retval, TRACE_GRAPH_PRINT_RETVAL) },
67a1be9cccSDonglin Peng /* Display function return value in hexadecimal format ? */
68a1be9cccSDonglin Peng { TRACER_OPT(funcgraph-retval-hex, TRACE_GRAPH_PRINT_RETVAL_HEX) },
69a1be9cccSDonglin Peng #endif
7021e92806SDonglin Peng #ifdef CONFIG_FUNCTION_GRAPH_RETADDR
7121e92806SDonglin Peng /* Display function return address ? */
7221e92806SDonglin Peng { TRACER_OPT(funcgraph-retaddr, TRACE_GRAPH_PRINT_RETADDR) },
7321e92806SDonglin Peng #endif
74ff5c9c57SSven Schnelle #ifdef CONFIG_FUNCTION_TRACE_ARGS
75ff5c9c57SSven Schnelle /* Display function arguments ? */
76ff5c9c57SSven Schnelle { TRACER_OPT(funcgraph-args, TRACE_GRAPH_ARGS) },
77ff5c9c57SSven Schnelle #endif
7855577204SSteven Rostedt (Red Hat) /* Include sleep time (scheduled out) between entry and return */
7955577204SSteven Rostedt (Red Hat) { TRACER_OPT(sleep-time, TRACE_GRAPH_SLEEP_TIME) },
80c8dd0f45SSteven Rostedt (VMware)
81c8dd0f45SSteven Rostedt (VMware) #ifdef CONFIG_FUNCTION_PROFILER
8255577204SSteven Rostedt (Red Hat) /* Include time within nested functions */
8355577204SSteven Rostedt (Red Hat) { TRACER_OPT(graph-time, TRACE_GRAPH_GRAPH_TIME) },
84c8dd0f45SSteven Rostedt (VMware) #endif
85c8dd0f45SSteven Rostedt (VMware)
86fb52607aSFrederic Weisbecker { } /* Empty entry */
87fb52607aSFrederic Weisbecker };
88fb52607aSFrederic Weisbecker
89fb52607aSFrederic Weisbecker static struct tracer_flags tracer_flags = {
90607e3a29SRobert Elliott /* Don't display overruns, proc, or tail by default */
919005f3ebSFrederic Weisbecker .val = TRACE_GRAPH_PRINT_CPU | TRACE_GRAPH_PRINT_OVERHEAD |
9255577204SSteven Rostedt (Red Hat) TRACE_GRAPH_PRINT_DURATION | TRACE_GRAPH_PRINT_IRQS |
9355577204SSteven Rostedt (Red Hat) TRACE_GRAPH_SLEEP_TIME | TRACE_GRAPH_GRAPH_TIME,
94fb52607aSFrederic Weisbecker .opts = trace_opts
95fb52607aSFrederic Weisbecker };
96fb52607aSFrederic Weisbecker
tracer_flags_is_set(u32 flags)970a6c61bcSMasami Hiramatsu (Google) static bool tracer_flags_is_set(u32 flags)
9821e92806SDonglin Peng {
990a6c61bcSMasami Hiramatsu (Google) return (tracer_flags.val & flags) == flags;
10021e92806SDonglin Peng }
10121e92806SDonglin Peng
102ffeb80fcSJiri Olsa /*
103ffeb80fcSJiri Olsa * DURATION column is being also used to display IRQ signs,
104ffeb80fcSJiri Olsa * following values are used by print_graph_irq and others
105ffeb80fcSJiri Olsa * to fill in space into DURATION column.
106ffeb80fcSJiri Olsa */
107ffeb80fcSJiri Olsa enum {
1086fc84ea7SSteven Rostedt (Red Hat) FLAGS_FILL_FULL = 1 << TRACE_GRAPH_PRINT_FILL_SHIFT,
1096fc84ea7SSteven Rostedt (Red Hat) FLAGS_FILL_START = 2 << TRACE_GRAPH_PRINT_FILL_SHIFT,
1106fc84ea7SSteven Rostedt (Red Hat) FLAGS_FILL_END = 3 << TRACE_GRAPH_PRINT_FILL_SHIFT,
111ffeb80fcSJiri Olsa };
112ffeb80fcSJiri Olsa
1139d9add34SSteven Rostedt (Red Hat) static void
114983f938aSSteven Rostedt (Red Hat) print_graph_duration(struct trace_array *tr, unsigned long long duration,
115983f938aSSteven Rostedt (Red Hat) struct trace_seq *s, u32 flags);
116fb52607aSFrederic Weisbecker
__graph_entry(struct trace_array * tr,struct ftrace_graph_ent * trace,unsigned int trace_ctx,struct ftrace_regs * fregs)117ff5c9c57SSven Schnelle static int __graph_entry(struct trace_array *tr, struct ftrace_graph_ent *trace,
118ff5c9c57SSven Schnelle unsigned int trace_ctx, struct ftrace_regs *fregs)
1191a0799a8SFrederic Weisbecker {
1201a0799a8SFrederic Weisbecker struct ring_buffer_event *event;
12113292494SSteven Rostedt (VMware) struct trace_buffer *buffer = tr->array_buffer.buffer;
1221a0799a8SFrederic Weisbecker struct ftrace_graph_ent_entry *entry;
123ff5c9c57SSven Schnelle int size;
1241a0799a8SFrederic Weisbecker
125ff5c9c57SSven Schnelle /* If fregs is defined, add FTRACE_REGS_MAX_ARGS long size words */
126ff5c9c57SSven Schnelle size = sizeof(*entry) + (FTRACE_REGS_MAX_ARGS * !!fregs * sizeof(long));
127ff5c9c57SSven Schnelle
128ff5c9c57SSven Schnelle event = trace_buffer_lock_reserve(buffer, TRACE_GRAPH_ENT, size, trace_ctx);
1291a0799a8SFrederic Weisbecker if (!event)
1301a0799a8SFrederic Weisbecker return 0;
131ff5c9c57SSven Schnelle
1321a0799a8SFrederic Weisbecker entry = ring_buffer_event_data(event);
1331a0799a8SFrederic Weisbecker entry->graph_ent = *trace;
134ff5c9c57SSven Schnelle
135ff5c9c57SSven Schnelle #ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
136ff5c9c57SSven Schnelle if (fregs) {
137ff5c9c57SSven Schnelle for (int i = 0; i < FTRACE_REGS_MAX_ARGS; i++)
138ff5c9c57SSven Schnelle entry->args[i] = ftrace_regs_get_argument(fregs, i);
139ff5c9c57SSven Schnelle }
140ff5c9c57SSven Schnelle #endif
141ff5c9c57SSven Schnelle
14252ffabe3SSteven Rostedt (Red Hat) trace_buffer_unlock_commit_nostack(buffer, event);
1431a0799a8SFrederic Weisbecker
1441a0799a8SFrederic Weisbecker return 1;
1451a0799a8SFrederic Weisbecker }
1461a0799a8SFrederic Weisbecker
__trace_graph_entry(struct trace_array * tr,struct ftrace_graph_ent * trace,unsigned int trace_ctx)147ff5c9c57SSven Schnelle int __trace_graph_entry(struct trace_array *tr,
148ff5c9c57SSven Schnelle struct ftrace_graph_ent *trace,
149ff5c9c57SSven Schnelle unsigned int trace_ctx)
150ff5c9c57SSven Schnelle {
151ff5c9c57SSven Schnelle return __graph_entry(tr, trace, trace_ctx, NULL);
152ff5c9c57SSven Schnelle }
153ff5c9c57SSven Schnelle
15421e92806SDonglin Peng #ifdef CONFIG_FUNCTION_GRAPH_RETADDR
__trace_graph_retaddr_entry(struct trace_array * tr,struct ftrace_graph_ent * trace,unsigned int trace_ctx,unsigned long retaddr)15521e92806SDonglin Peng int __trace_graph_retaddr_entry(struct trace_array *tr,
15621e92806SDonglin Peng struct ftrace_graph_ent *trace,
15721e92806SDonglin Peng unsigned int trace_ctx,
15821e92806SDonglin Peng unsigned long retaddr)
15921e92806SDonglin Peng {
16021e92806SDonglin Peng struct ring_buffer_event *event;
16121e92806SDonglin Peng struct trace_buffer *buffer = tr->array_buffer.buffer;
16221e92806SDonglin Peng struct fgraph_retaddr_ent_entry *entry;
16321e92806SDonglin Peng
16421e92806SDonglin Peng event = trace_buffer_lock_reserve(buffer, TRACE_GRAPH_RETADDR_ENT,
16521e92806SDonglin Peng sizeof(*entry), trace_ctx);
16621e92806SDonglin Peng if (!event)
16721e92806SDonglin Peng return 0;
16821e92806SDonglin Peng entry = ring_buffer_event_data(event);
16921e92806SDonglin Peng entry->graph_ent.func = trace->func;
17021e92806SDonglin Peng entry->graph_ent.depth = trace->depth;
17121e92806SDonglin Peng entry->graph_ent.retaddr = retaddr;
17221e92806SDonglin Peng trace_buffer_unlock_commit_nostack(buffer, event);
17321e92806SDonglin Peng
17421e92806SDonglin Peng return 1;
17521e92806SDonglin Peng }
17621e92806SDonglin Peng #else
__trace_graph_retaddr_entry(struct trace_array * tr,struct ftrace_graph_ent * trace,unsigned int trace_ctx,unsigned long retaddr)17721e92806SDonglin Peng int __trace_graph_retaddr_entry(struct trace_array *tr,
17821e92806SDonglin Peng struct ftrace_graph_ent *trace,
17921e92806SDonglin Peng unsigned int trace_ctx,
18021e92806SDonglin Peng unsigned long retaddr)
18121e92806SDonglin Peng {
18221e92806SDonglin Peng return 1;
18321e92806SDonglin Peng }
18421e92806SDonglin Peng #endif
18521e92806SDonglin Peng
ftrace_graph_ignore_irqs(void)186b304d044SSteven Rostedt static inline int ftrace_graph_ignore_irqs(void)
187b304d044SSteven Rostedt {
188e4a3f541SSteven Rostedt if (!ftrace_graph_skip_irqs || trace_recursion_test(TRACE_IRQ_BIT))
189b304d044SSteven Rostedt return 0;
190b304d044SSteven Rostedt
191affc6592SChangbin Du return in_hardirq();
192b304d044SSteven Rostedt }
193b304d044SSteven Rostedt
194f1f36e22SSteven Rostedt struct fgraph_times {
195f1f36e22SSteven Rostedt unsigned long long calltime;
196f1f36e22SSteven Rostedt unsigned long long sleeptime; /* may be optional! */
197f1f36e22SSteven Rostedt };
198f1f36e22SSteven Rostedt
graph_entry(struct ftrace_graph_ent * trace,struct fgraph_ops * gops,struct ftrace_regs * fregs)199ff5c9c57SSven Schnelle static int graph_entry(struct ftrace_graph_ent *trace,
20041705c42SMasami Hiramatsu (Google) struct fgraph_ops *gops,
20141705c42SMasami Hiramatsu (Google) struct ftrace_regs *fregs)
2021a0799a8SFrederic Weisbecker {
203b8421489SSteven Rostedt (VMware) unsigned long *task_var = fgraph_get_task_var(gops);
20426dda563SSteven Rostedt (VMware) struct trace_array *tr = gops->private;
2051a0799a8SFrederic Weisbecker struct trace_array_cpu *data;
206f1f36e22SSteven Rostedt struct fgraph_times *ftimes;
20736590c50SSebastian Andrzej Siewior unsigned int trace_ctx;
2081a0799a8SFrederic Weisbecker long disabled;
2097d137e60SSteven Rostedt int ret = 0;
2101a0799a8SFrederic Weisbecker int cpu;
2111a0799a8SFrederic Weisbecker
212b8421489SSteven Rostedt (VMware) if (*task_var & TRACE_GRAPH_NOTRACE)
2139cd2992fSSteven Rostedt (VMware) return 0;
2149cd2992fSSteven Rostedt (VMware)
2156c77221dSChangbin Du /*
2166c77221dSChangbin Du * Do not trace a function if it's filtered by set_graph_notrace.
2176c77221dSChangbin Du * Make the index of ret stack negative to indicate that it should
2186c77221dSChangbin Du * ignore further functions. But it needs its own ret stack entry
2196c77221dSChangbin Du * to recover the original index in order to continue tracing after
2206c77221dSChangbin Du * returning from the function.
2216c77221dSChangbin Du */
2229cd2992fSSteven Rostedt (VMware) if (ftrace_graph_notrace_addr(trace->func)) {
223c8c9b1d2SSteven Rostedt *task_var |= TRACE_GRAPH_NOTRACE;
2249cd2992fSSteven Rostedt (VMware) /*
2259cd2992fSSteven Rostedt (VMware) * Need to return 1 to have the return called
2269cd2992fSSteven Rostedt (VMware) * that will clear the NOTRACE bit.
2279cd2992fSSteven Rostedt (VMware) */
2289cd2992fSSteven Rostedt (VMware) return 1;
2299cd2992fSSteven Rostedt (VMware) }
2309cd2992fSSteven Rostedt (VMware)
231345ddcc8SSteven Rostedt (Red Hat) if (!ftrace_trace_task(tr))
2321a0799a8SFrederic Weisbecker return 0;
2331a0799a8SFrederic Weisbecker
23412117f33SSteven Rostedt (VMware) if (ftrace_graph_ignore_func(gops, trace))
2351a414428SSteven Rostedt (Red Hat) return 0;
2361a414428SSteven Rostedt (Red Hat)
2371a414428SSteven Rostedt (Red Hat) if (ftrace_graph_ignore_irqs())
2381a0799a8SFrederic Weisbecker return 0;
2391a0799a8SFrederic Weisbecker
240f1f36e22SSteven Rostedt if (fgraph_sleep_time) {
241f1f36e22SSteven Rostedt /* Only need to record the calltime */
242f1f36e22SSteven Rostedt ftimes = fgraph_reserve_data(gops->idx, sizeof(ftimes->calltime));
243f1f36e22SSteven Rostedt } else {
244f1f36e22SSteven Rostedt ftimes = fgraph_reserve_data(gops->idx, sizeof(*ftimes));
245f1f36e22SSteven Rostedt if (ftimes)
246f1f36e22SSteven Rostedt ftimes->sleeptime = current->ftrace_sleeptime;
2473c9880f3SSteven Rostedt }
248f1f36e22SSteven Rostedt if (!ftimes)
249f1f36e22SSteven Rostedt return 0;
250f1f36e22SSteven Rostedt
251f1f36e22SSteven Rostedt ftimes->calltime = trace_clock_local();
2523c9880f3SSteven Rostedt
25329ad23b0SNamhyung Kim /*
2547fa8b717SJoel Fernandes * Stop here if tracing_threshold is set. We only write function return
2557fa8b717SJoel Fernandes * events to the ring buffer.
2567fa8b717SJoel Fernandes */
2577fa8b717SJoel Fernandes if (tracing_thresh)
2587fa8b717SJoel Fernandes return 1;
2597fa8b717SJoel Fernandes
2607d137e60SSteven Rostedt preempt_disable_notrace();
2611a0799a8SFrederic Weisbecker cpu = raw_smp_processor_id();
2621c5eb448SSteven Rostedt (VMware) data = per_cpu_ptr(tr->array_buffer.data, cpu);
2637d137e60SSteven Rostedt disabled = atomic_read(&data->disabled);
2647d137e60SSteven Rostedt if (likely(!disabled)) {
2657d137e60SSteven Rostedt trace_ctx = tracing_gen_ctx();
2667d137e60SSteven Rostedt if (IS_ENABLED(CONFIG_FUNCTION_GRAPH_RETADDR) &&
2677d137e60SSteven Rostedt tracer_flags_is_set(TRACE_GRAPH_PRINT_RETADDR)) {
2680a6c61bcSMasami Hiramatsu (Google) unsigned long retaddr = ftrace_graph_top_ret_addr(current);
2690a6c61bcSMasami Hiramatsu (Google) ret = __trace_graph_retaddr_entry(tr, trace, trace_ctx, retaddr);
2701a0799a8SFrederic Weisbecker } else {
271ff5c9c57SSven Schnelle ret = __graph_entry(tr, trace, trace_ctx, fregs);
2721a0799a8SFrederic Weisbecker }
2737d137e60SSteven Rostedt }
2747d137e60SSteven Rostedt preempt_enable_notrace();
2751a0799a8SFrederic Weisbecker
2761a0799a8SFrederic Weisbecker return ret;
2771a0799a8SFrederic Weisbecker }
2781a0799a8SFrederic Weisbecker
trace_graph_entry(struct ftrace_graph_ent * trace,struct fgraph_ops * gops,struct ftrace_regs * fregs)279ff5c9c57SSven Schnelle int trace_graph_entry(struct ftrace_graph_ent *trace,
280ff5c9c57SSven Schnelle struct fgraph_ops *gops,
281ff5c9c57SSven Schnelle struct ftrace_regs *fregs)
282ff5c9c57SSven Schnelle {
283ff5c9c57SSven Schnelle return graph_entry(trace, gops, NULL);
284ff5c9c57SSven Schnelle }
285ff5c9c57SSven Schnelle
trace_graph_entry_args(struct ftrace_graph_ent * trace,struct fgraph_ops * gops,struct ftrace_regs * fregs)286ff5c9c57SSven Schnelle static int trace_graph_entry_args(struct ftrace_graph_ent *trace,
287ff5c9c57SSven Schnelle struct fgraph_ops *gops,
288ff5c9c57SSven Schnelle struct ftrace_regs *fregs)
289ff5c9c57SSven Schnelle {
290ff5c9c57SSven Schnelle return graph_entry(trace, gops, fregs);
291ff5c9c57SSven Schnelle }
292ff5c9c57SSven Schnelle
2930a772620SJiri Olsa static void
__trace_graph_function(struct trace_array * tr,unsigned long ip,unsigned int trace_ctx)2940a772620SJiri Olsa __trace_graph_function(struct trace_array *tr,
29536590c50SSebastian Andrzej Siewior unsigned long ip, unsigned int trace_ctx)
2960a772620SJiri Olsa {
2970a772620SJiri Olsa u64 time = trace_clock_local();
2980a772620SJiri Olsa struct ftrace_graph_ent ent = {
2990a772620SJiri Olsa .func = ip,
3000a772620SJiri Olsa .depth = 0,
3010a772620SJiri Olsa };
3020a772620SJiri Olsa struct ftrace_graph_ret ret = {
3030a772620SJiri Olsa .func = ip,
3040a772620SJiri Olsa .depth = 0,
3050a772620SJiri Olsa };
3060a772620SJiri Olsa
30736590c50SSebastian Andrzej Siewior __trace_graph_entry(tr, &ent, trace_ctx);
30866611c04SSteven Rostedt __trace_graph_return(tr, &ret, trace_ctx, time, time);
3090a772620SJiri Olsa }
3100a772620SJiri Olsa
3110a772620SJiri Olsa void
trace_graph_function(struct trace_array * tr,unsigned long ip,unsigned long parent_ip,unsigned int trace_ctx)3120a772620SJiri Olsa trace_graph_function(struct trace_array *tr,
3130a772620SJiri Olsa unsigned long ip, unsigned long parent_ip,
31436590c50SSebastian Andrzej Siewior unsigned int trace_ctx)
3150a772620SJiri Olsa {
31636590c50SSebastian Andrzej Siewior __trace_graph_function(tr, ip, trace_ctx);
3170a772620SJiri Olsa }
3180a772620SJiri Olsa
__trace_graph_return(struct trace_array * tr,struct ftrace_graph_ret * trace,unsigned int trace_ctx,u64 calltime,u64 rettime)31962b915f1SJiri Olsa void __trace_graph_return(struct trace_array *tr,
3201a0799a8SFrederic Weisbecker struct ftrace_graph_ret *trace,
32166611c04SSteven Rostedt unsigned int trace_ctx,
32266611c04SSteven Rostedt u64 calltime, u64 rettime)
3231a0799a8SFrederic Weisbecker {
3241a0799a8SFrederic Weisbecker struct ring_buffer_event *event;
32513292494SSteven Rostedt (VMware) struct trace_buffer *buffer = tr->array_buffer.buffer;
3261a0799a8SFrederic Weisbecker struct ftrace_graph_ret_entry *entry;
3271a0799a8SFrederic Weisbecker
328e77405adSSteven Rostedt event = trace_buffer_lock_reserve(buffer, TRACE_GRAPH_RET,
32936590c50SSebastian Andrzej Siewior sizeof(*entry), trace_ctx);
3301a0799a8SFrederic Weisbecker if (!event)
3311a0799a8SFrederic Weisbecker return;
3321a0799a8SFrederic Weisbecker entry = ring_buffer_event_data(event);
3331a0799a8SFrederic Weisbecker entry->ret = *trace;
33466611c04SSteven Rostedt entry->calltime = calltime;
33566611c04SSteven Rostedt entry->rettime = rettime;
33652ffabe3SSteven Rostedt (Red Hat) trace_buffer_unlock_commit_nostack(buffer, event);
3371a0799a8SFrederic Weisbecker }
3381a0799a8SFrederic Weisbecker
handle_nosleeptime(struct ftrace_graph_ret * trace,struct fgraph_times * ftimes,int size)3393c9880f3SSteven Rostedt static void handle_nosleeptime(struct ftrace_graph_ret *trace,
340f1f36e22SSteven Rostedt struct fgraph_times *ftimes,
341f1f36e22SSteven Rostedt int size)
3423c9880f3SSteven Rostedt {
343f1f36e22SSteven Rostedt if (fgraph_sleep_time || size < sizeof(*ftimes))
3443c9880f3SSteven Rostedt return;
3453c9880f3SSteven Rostedt
346f1f36e22SSteven Rostedt ftimes->calltime += current->ftrace_sleeptime - ftimes->sleeptime;
3473c9880f3SSteven Rostedt }
3483c9880f3SSteven Rostedt
trace_graph_return(struct ftrace_graph_ret * trace,struct fgraph_ops * gops,struct ftrace_regs * fregs)34937238abeSSteven Rostedt (VMware) void trace_graph_return(struct ftrace_graph_ret *trace,
3502ca8c112SMasami Hiramatsu (Google) struct fgraph_ops *gops, struct ftrace_regs *fregs)
3511a0799a8SFrederic Weisbecker {
352b8421489SSteven Rostedt (VMware) unsigned long *task_var = fgraph_get_task_var(gops);
35326dda563SSteven Rostedt (VMware) struct trace_array *tr = gops->private;
3541a0799a8SFrederic Weisbecker struct trace_array_cpu *data;
355f1f36e22SSteven Rostedt struct fgraph_times *ftimes;
35636590c50SSebastian Andrzej Siewior unsigned int trace_ctx;
35766611c04SSteven Rostedt u64 calltime, rettime;
3581a0799a8SFrederic Weisbecker long disabled;
359f1f36e22SSteven Rostedt int size;
3601a0799a8SFrederic Weisbecker int cpu;
3611a0799a8SFrederic Weisbecker
36266611c04SSteven Rostedt rettime = trace_clock_local();
36366611c04SSteven Rostedt
36412117f33SSteven Rostedt (VMware) ftrace_graph_addr_finish(gops, trace);
3655cf99a0fSSteven Rostedt (VMware)
366b8421489SSteven Rostedt (VMware) if (*task_var & TRACE_GRAPH_NOTRACE) {
367b8421489SSteven Rostedt (VMware) *task_var &= ~TRACE_GRAPH_NOTRACE;
3689cd2992fSSteven Rostedt (VMware) return;
3699cd2992fSSteven Rostedt (VMware) }
3709cd2992fSSteven Rostedt (VMware)
371f1f36e22SSteven Rostedt ftimes = fgraph_retrieve_data(gops->idx, &size);
372f1f36e22SSteven Rostedt if (!ftimes)
373f1f36e22SSteven Rostedt return;
374f1f36e22SSteven Rostedt
375f1f36e22SSteven Rostedt handle_nosleeptime(trace, ftimes, size);
376f1f36e22SSteven Rostedt
37766611c04SSteven Rostedt calltime = ftimes->calltime;
3783c9880f3SSteven Rostedt
3797d137e60SSteven Rostedt preempt_disable_notrace();
3801a0799a8SFrederic Weisbecker cpu = raw_smp_processor_id();
3811c5eb448SSteven Rostedt (VMware) data = per_cpu_ptr(tr->array_buffer.data, cpu);
3827d137e60SSteven Rostedt disabled = atomic_read(&data->disabled);
3837d137e60SSteven Rostedt if (likely(!disabled)) {
3847d137e60SSteven Rostedt trace_ctx = tracing_gen_ctx();
38566611c04SSteven Rostedt __trace_graph_return(tr, trace, trace_ctx, calltime, rettime);
3861a0799a8SFrederic Weisbecker }
3877d137e60SSteven Rostedt preempt_enable_notrace();
3881a0799a8SFrederic Weisbecker }
3891a0799a8SFrederic Weisbecker
trace_graph_thresh_return(struct ftrace_graph_ret * trace,struct fgraph_ops * gops,struct ftrace_regs * fregs)39037238abeSSteven Rostedt (VMware) static void trace_graph_thresh_return(struct ftrace_graph_ret *trace,
3912ca8c112SMasami Hiramatsu (Google) struct fgraph_ops *gops,
3922ca8c112SMasami Hiramatsu (Google) struct ftrace_regs *fregs)
3930e950173STim Bird {
394f1f36e22SSteven Rostedt struct fgraph_times *ftimes;
395f1f36e22SSteven Rostedt int size;
396f1f36e22SSteven Rostedt
39712117f33SSteven Rostedt (VMware) ftrace_graph_addr_finish(gops, trace);
3985cf99a0fSSteven Rostedt (VMware)
3999cd2992fSSteven Rostedt (VMware) if (trace_recursion_test(TRACE_GRAPH_NOTRACE_BIT)) {
4009cd2992fSSteven Rostedt (VMware) trace_recursion_clear(TRACE_GRAPH_NOTRACE_BIT);
4019cd2992fSSteven Rostedt (VMware) return;
4029cd2992fSSteven Rostedt (VMware) }
4039cd2992fSSteven Rostedt (VMware)
404f1f36e22SSteven Rostedt ftimes = fgraph_retrieve_data(gops->idx, &size);
405f1f36e22SSteven Rostedt if (!ftimes)
406f1f36e22SSteven Rostedt return;
407f1f36e22SSteven Rostedt
408f1f36e22SSteven Rostedt handle_nosleeptime(trace, ftimes, size);
409f1f36e22SSteven Rostedt
4100e950173STim Bird if (tracing_thresh &&
41166611c04SSteven Rostedt (trace_clock_local() - ftimes->calltime < tracing_thresh))
4120e950173STim Bird return;
4130e950173STim Bird else
4142ca8c112SMasami Hiramatsu (Google) trace_graph_return(trace, gops, fregs);
4150e950173STim Bird }
4160e950173STim Bird
417688f7089SSteven Rostedt (VMware) static struct fgraph_ops funcgraph_ops = {
418688f7089SSteven Rostedt (VMware) .entryfunc = &trace_graph_entry,
419688f7089SSteven Rostedt (VMware) .retfunc = &trace_graph_return,
420688f7089SSteven Rostedt (VMware) };
421688f7089SSteven Rostedt (VMware)
allocate_fgraph_ops(struct trace_array * tr,struct ftrace_ops * ops)422c132be2cSSteven Rostedt (VMware) int allocate_fgraph_ops(struct trace_array *tr, struct ftrace_ops *ops)
42326dda563SSteven Rostedt (VMware) {
42426dda563SSteven Rostedt (VMware) struct fgraph_ops *gops;
42526dda563SSteven Rostedt (VMware)
42626dda563SSteven Rostedt (VMware) gops = kzalloc(sizeof(*gops), GFP_KERNEL);
42726dda563SSteven Rostedt (VMware) if (!gops)
42826dda563SSteven Rostedt (VMware) return -ENOMEM;
42926dda563SSteven Rostedt (VMware)
43026dda563SSteven Rostedt (VMware) gops->entryfunc = &trace_graph_entry;
43126dda563SSteven Rostedt (VMware) gops->retfunc = &trace_graph_return;
43226dda563SSteven Rostedt (VMware)
43326dda563SSteven Rostedt (VMware) tr->gops = gops;
43426dda563SSteven Rostedt (VMware) gops->private = tr;
435c132be2cSSteven Rostedt (VMware)
436c132be2cSSteven Rostedt (VMware) fgraph_init_ops(&gops->ops, ops);
437c132be2cSSteven Rostedt (VMware)
43826dda563SSteven Rostedt (VMware) return 0;
43926dda563SSteven Rostedt (VMware) }
44026dda563SSteven Rostedt (VMware)
free_fgraph_ops(struct trace_array * tr)44126dda563SSteven Rostedt (VMware) void free_fgraph_ops(struct trace_array *tr)
44226dda563SSteven Rostedt (VMware) {
44326dda563SSteven Rostedt (VMware) kfree(tr->gops);
44426dda563SSteven Rostedt (VMware) }
44526dda563SSteven Rostedt (VMware)
init_array_fgraph_ops(struct trace_array * tr,struct ftrace_ops * ops)446c132be2cSSteven Rostedt (VMware) __init void init_array_fgraph_ops(struct trace_array *tr, struct ftrace_ops *ops)
44726dda563SSteven Rostedt (VMware) {
44826dda563SSteven Rostedt (VMware) tr->gops = &funcgraph_ops;
44926dda563SSteven Rostedt (VMware) funcgraph_ops.private = tr;
450c132be2cSSteven Rostedt (VMware) fgraph_init_ops(&tr->gops->ops, ops);
45126dda563SSteven Rostedt (VMware) }
45226dda563SSteven Rostedt (VMware)
graph_trace_init(struct trace_array * tr)453fb52607aSFrederic Weisbecker static int graph_trace_init(struct trace_array *tr)
454fb52607aSFrederic Weisbecker {
4551a0799a8SFrederic Weisbecker int ret;
4561a0799a8SFrederic Weisbecker
457ff5c9c57SSven Schnelle if (tracer_flags_is_set(TRACE_GRAPH_ARGS))
458ff5c9c57SSven Schnelle tr->gops->entryfunc = trace_graph_entry_args;
459ff5c9c57SSven Schnelle else
46026dda563SSteven Rostedt (VMware) tr->gops->entryfunc = trace_graph_entry;
46126dda563SSteven Rostedt (VMware)
4620e950173STim Bird if (tracing_thresh)
46326dda563SSteven Rostedt (VMware) tr->gops->retfunc = trace_graph_thresh_return;
4640e950173STim Bird else
46526dda563SSteven Rostedt (VMware) tr->gops->retfunc = trace_graph_return;
46626dda563SSteven Rostedt (VMware)
467c7a60a73SSteven Rostedt /* Make gops functions visible before we start tracing */
46826dda563SSteven Rostedt (VMware) smp_mb();
46926dda563SSteven Rostedt (VMware)
47026dda563SSteven Rostedt (VMware) ret = register_ftrace_graph(tr->gops);
471660c7f9bSSteven Rostedt if (ret)
472660c7f9bSSteven Rostedt return ret;
473660c7f9bSSteven Rostedt tracing_start_cmdline_record();
474660c7f9bSSteven Rostedt
475660c7f9bSSteven Rostedt return 0;
476fb52607aSFrederic Weisbecker }
477fb52607aSFrederic Weisbecker
ftrace_graph_trace_args(struct trace_array * tr,int set)478c7a60a73SSteven Rostedt static int ftrace_graph_trace_args(struct trace_array *tr, int set)
479c7a60a73SSteven Rostedt {
480c7a60a73SSteven Rostedt trace_func_graph_ent_t entry;
481c7a60a73SSteven Rostedt
482c7a60a73SSteven Rostedt if (set)
483c7a60a73SSteven Rostedt entry = trace_graph_entry_args;
484c7a60a73SSteven Rostedt else
485c7a60a73SSteven Rostedt entry = trace_graph_entry;
486c7a60a73SSteven Rostedt
487c7a60a73SSteven Rostedt /* See if there's any changes */
488c7a60a73SSteven Rostedt if (tr->gops->entryfunc == entry)
489c7a60a73SSteven Rostedt return 0;
490c7a60a73SSteven Rostedt
491c7a60a73SSteven Rostedt unregister_ftrace_graph(tr->gops);
492c7a60a73SSteven Rostedt
493c7a60a73SSteven Rostedt tr->gops->entryfunc = entry;
494c7a60a73SSteven Rostedt
495c7a60a73SSteven Rostedt /* Make gops functions visible before we start tracing */
496c7a60a73SSteven Rostedt smp_mb();
497c7a60a73SSteven Rostedt return register_ftrace_graph(tr->gops);
498c7a60a73SSteven Rostedt }
499c7a60a73SSteven Rostedt
graph_trace_reset(struct trace_array * tr)500fb52607aSFrederic Weisbecker static void graph_trace_reset(struct trace_array *tr)
501fb52607aSFrederic Weisbecker {
502660c7f9bSSteven Rostedt tracing_stop_cmdline_record();
50326dda563SSteven Rostedt (VMware) unregister_ftrace_graph(tr->gops);
504fb52607aSFrederic Weisbecker }
505fb52607aSFrederic Weisbecker
graph_trace_update_thresh(struct trace_array * tr)506ba1afef6SSteven Rostedt (Red Hat) static int graph_trace_update_thresh(struct trace_array *tr)
5076508fa76SStanislav Fomichev {
5086508fa76SStanislav Fomichev graph_trace_reset(tr);
5096508fa76SStanislav Fomichev return graph_trace_init(tr);
5106508fa76SStanislav Fomichev }
5116508fa76SStanislav Fomichev
5120c9e6f63SLai Jiangshan static int max_bytes_for_cpu;
5131a056155SFrederic Weisbecker
print_graph_cpu(struct trace_seq * s,int cpu)5149d9add34SSteven Rostedt (Red Hat) static void print_graph_cpu(struct trace_seq *s, int cpu)
5151a056155SFrederic Weisbecker {
516d51090b3SIngo Molnar /*
517d51090b3SIngo Molnar * Start with a space character - to make it stand out
518d51090b3SIngo Molnar * to the right a bit when trace output is pasted into
519d51090b3SIngo Molnar * email:
520d51090b3SIngo Molnar */
5219d9add34SSteven Rostedt (Red Hat) trace_seq_printf(s, " %*d) ", max_bytes_for_cpu, cpu);
5221a056155SFrederic Weisbecker }
5231a056155SFrederic Weisbecker
52411e84accSFrederic Weisbecker #define TRACE_GRAPH_PROCINFO_LENGTH 14
52511e84accSFrederic Weisbecker
print_graph_proc(struct trace_seq * s,pid_t pid)5269d9add34SSteven Rostedt (Red Hat) static void print_graph_proc(struct trace_seq *s, pid_t pid)
52711e84accSFrederic Weisbecker {
5284ca53085SSteven Rostedt char comm[TASK_COMM_LEN];
52911e84accSFrederic Weisbecker /* sign + log10(MAX_INT) + '\0' */
53011e84accSFrederic Weisbecker char pid_str[11];
5314ca53085SSteven Rostedt int spaces = 0;
5324ca53085SSteven Rostedt int len;
5334ca53085SSteven Rostedt int i;
53411e84accSFrederic Weisbecker
5354ca53085SSteven Rostedt trace_find_cmdline(pid, comm);
53611e84accSFrederic Weisbecker comm[7] = '\0';
53711e84accSFrederic Weisbecker sprintf(pid_str, "%d", pid);
53811e84accSFrederic Weisbecker
53911e84accSFrederic Weisbecker /* 1 stands for the "-" character */
54011e84accSFrederic Weisbecker len = strlen(comm) + strlen(pid_str) + 1;
54111e84accSFrederic Weisbecker
54211e84accSFrederic Weisbecker if (len < TRACE_GRAPH_PROCINFO_LENGTH)
54311e84accSFrederic Weisbecker spaces = TRACE_GRAPH_PROCINFO_LENGTH - len;
54411e84accSFrederic Weisbecker
54511e84accSFrederic Weisbecker /* First spaces to align center */
5469d9add34SSteven Rostedt (Red Hat) for (i = 0; i < spaces / 2; i++)
5479d9add34SSteven Rostedt (Red Hat) trace_seq_putc(s, ' ');
54811e84accSFrederic Weisbecker
5499d9add34SSteven Rostedt (Red Hat) trace_seq_printf(s, "%s-%s", comm, pid_str);
55011e84accSFrederic Weisbecker
55111e84accSFrederic Weisbecker /* Last spaces to align center */
5529d9add34SSteven Rostedt (Red Hat) for (i = 0; i < spaces - (spaces / 2); i++)
5539d9add34SSteven Rostedt (Red Hat) trace_seq_putc(s, ' ');
55411e84accSFrederic Weisbecker }
55511e84accSFrederic Weisbecker
5561a056155SFrederic Weisbecker
print_graph_lat_fmt(struct trace_seq * s,struct trace_entry * entry)5579d9add34SSteven Rostedt (Red Hat) static void print_graph_lat_fmt(struct trace_seq *s, struct trace_entry *entry)
55849ff5903SSteven Rostedt {
5599d9add34SSteven Rostedt (Red Hat) trace_seq_putc(s, ' ');
5609d9add34SSteven Rostedt (Red Hat) trace_print_lat_fmt(s, entry);
561afbab501SChangbin Du trace_seq_puts(s, " | ");
56249ff5903SSteven Rostedt }
56349ff5903SSteven Rostedt
564287b6e68SFrederic Weisbecker /* If the pid changed since the last trace, output this event */
5659d9add34SSteven Rostedt (Red Hat) static void
verif_pid(struct trace_seq * s,pid_t pid,int cpu,struct fgraph_data * data)5662fbcdb35SSteven Rostedt verif_pid(struct trace_seq *s, pid_t pid, int cpu, struct fgraph_data *data)
567287b6e68SFrederic Weisbecker {
568d51090b3SIngo Molnar pid_t prev_pid;
5699005f3ebSFrederic Weisbecker pid_t *last_pid;
570660c7f9bSSteven Rostedt
5712fbcdb35SSteven Rostedt if (!data)
5729d9add34SSteven Rostedt (Red Hat) return;
573287b6e68SFrederic Weisbecker
574be1eca39SJiri Olsa last_pid = &(per_cpu_ptr(data->cpu_data, cpu)->last_pid);
575660c7f9bSSteven Rostedt
5769005f3ebSFrederic Weisbecker if (*last_pid == pid)
5779d9add34SSteven Rostedt (Red Hat) return;
5789005f3ebSFrederic Weisbecker
5799005f3ebSFrederic Weisbecker prev_pid = *last_pid;
5809005f3ebSFrederic Weisbecker *last_pid = pid;
5819005f3ebSFrederic Weisbecker
5829005f3ebSFrederic Weisbecker if (prev_pid == -1)
5839d9add34SSteven Rostedt (Red Hat) return;
584d51090b3SIngo Molnar /*
585d51090b3SIngo Molnar * Context-switch trace line:
586d51090b3SIngo Molnar
587d51090b3SIngo Molnar ------------------------------------------
588d51090b3SIngo Molnar | 1) migration/0--1 => sshd-1755
589d51090b3SIngo Molnar ------------------------------------------
590d51090b3SIngo Molnar
591d51090b3SIngo Molnar */
5929d9add34SSteven Rostedt (Red Hat) trace_seq_puts(s, " ------------------------------------------\n");
5939d9add34SSteven Rostedt (Red Hat) print_graph_cpu(s, cpu);
5949d9add34SSteven Rostedt (Red Hat) print_graph_proc(s, prev_pid);
5959d9add34SSteven Rostedt (Red Hat) trace_seq_puts(s, " => ");
5969d9add34SSteven Rostedt (Red Hat) print_graph_proc(s, pid);
5979d9add34SSteven Rostedt (Red Hat) trace_seq_puts(s, "\n ------------------------------------------\n\n");
598287b6e68SFrederic Weisbecker }
599287b6e68SFrederic Weisbecker
600b91facc3SFrederic Weisbecker static struct ftrace_graph_ret_entry *
get_return_for_leaf(struct trace_iterator * iter,struct ftrace_graph_ent_entry * curr)601b91facc3SFrederic Weisbecker get_return_for_leaf(struct trace_iterator *iter,
60283a8df61SFrederic Weisbecker struct ftrace_graph_ent_entry *curr)
603287b6e68SFrederic Weisbecker {
604be1eca39SJiri Olsa struct fgraph_data *data = iter->private;
605be1eca39SJiri Olsa struct ring_buffer_iter *ring_iter = NULL;
60683a8df61SFrederic Weisbecker struct ring_buffer_event *event;
60783a8df61SFrederic Weisbecker struct ftrace_graph_ret_entry *next;
60883a8df61SFrederic Weisbecker
609be1eca39SJiri Olsa /*
610be1eca39SJiri Olsa * If the previous output failed to write to the seq buffer,
611be1eca39SJiri Olsa * then we just reuse the data from before.
612be1eca39SJiri Olsa */
613be1eca39SJiri Olsa if (data && data->failed) {
61421e92806SDonglin Peng curr = &data->ent.ent;
615be1eca39SJiri Olsa next = &data->ret;
616be1eca39SJiri Olsa } else {
617be1eca39SJiri Olsa
6186d158a81SSteven Rostedt ring_iter = trace_buffer_iter(iter, iter->cpu);
61983a8df61SFrederic Weisbecker
620b91facc3SFrederic Weisbecker /* First peek to compare current entry and the next one */
621b91facc3SFrederic Weisbecker if (ring_iter)
6221a056155SFrederic Weisbecker event = ring_buffer_iter_peek(ring_iter, NULL);
623b91facc3SFrederic Weisbecker else {
624be1eca39SJiri Olsa /*
625be1eca39SJiri Olsa * We need to consume the current entry to see
626be1eca39SJiri Olsa * the next one.
627be1eca39SJiri Olsa */
6281c5eb448SSteven Rostedt (VMware) ring_buffer_consume(iter->array_buffer->buffer, iter->cpu,
62966a8cb95SSteven Rostedt NULL, NULL);
6301c5eb448SSteven Rostedt (VMware) event = ring_buffer_peek(iter->array_buffer->buffer, iter->cpu,
63166a8cb95SSteven Rostedt NULL, NULL);
632b91facc3SFrederic Weisbecker }
63383a8df61SFrederic Weisbecker
63483a8df61SFrederic Weisbecker if (!event)
635b91facc3SFrederic Weisbecker return NULL;
63683a8df61SFrederic Weisbecker
63783a8df61SFrederic Weisbecker next = ring_buffer_event_data(event);
63883a8df61SFrederic Weisbecker
639be1eca39SJiri Olsa if (data) {
640be1eca39SJiri Olsa /*
641be1eca39SJiri Olsa * Save current and next entries for later reference
642be1eca39SJiri Olsa * if the output fails.
643be1eca39SJiri Olsa */
64421e92806SDonglin Peng if (unlikely(curr->ent.type == TRACE_GRAPH_RETADDR_ENT))
64521e92806SDonglin Peng data->ent.rent = *(struct fgraph_retaddr_ent_entry *)curr;
64621e92806SDonglin Peng else
64721e92806SDonglin Peng data->ent.ent = *curr;
648575570f0SShaohua Li /*
649575570f0SShaohua Li * If the next event is not a return type, then
650575570f0SShaohua Li * we only care about what type it is. Otherwise we can
651575570f0SShaohua Li * safely copy the entire event.
652575570f0SShaohua Li */
653575570f0SShaohua Li if (next->ent.type == TRACE_GRAPH_RET)
654be1eca39SJiri Olsa data->ret = *next;
655575570f0SShaohua Li else
656575570f0SShaohua Li data->ret.ent.type = next->ent.type;
657be1eca39SJiri Olsa }
658be1eca39SJiri Olsa }
659be1eca39SJiri Olsa
66083a8df61SFrederic Weisbecker if (next->ent.type != TRACE_GRAPH_RET)
661b91facc3SFrederic Weisbecker return NULL;
66283a8df61SFrederic Weisbecker
66383a8df61SFrederic Weisbecker if (curr->ent.pid != next->ent.pid ||
66483a8df61SFrederic Weisbecker curr->graph_ent.func != next->ret.func)
665b91facc3SFrederic Weisbecker return NULL;
66683a8df61SFrederic Weisbecker
667b91facc3SFrederic Weisbecker /* this is a leaf, now advance the iterator */
668b91facc3SFrederic Weisbecker if (ring_iter)
669bc1a72afSSteven Rostedt (VMware) ring_buffer_iter_advance(ring_iter);
670b91facc3SFrederic Weisbecker
671b91facc3SFrederic Weisbecker return next;
67283a8df61SFrederic Weisbecker }
67383a8df61SFrederic Weisbecker
print_graph_abs_time(u64 t,struct trace_seq * s)6749d9add34SSteven Rostedt (Red Hat) static void print_graph_abs_time(u64 t, struct trace_seq *s)
675d1f9cbd7SFrederic Weisbecker {
676d1f9cbd7SFrederic Weisbecker unsigned long usecs_rem;
677d1f9cbd7SFrederic Weisbecker
678d1f9cbd7SFrederic Weisbecker usecs_rem = do_div(t, NSEC_PER_SEC);
679d1f9cbd7SFrederic Weisbecker usecs_rem /= 1000;
680d1f9cbd7SFrederic Weisbecker
6819d9add34SSteven Rostedt (Red Hat) trace_seq_printf(s, "%5lu.%06lu | ",
682d1f9cbd7SFrederic Weisbecker (unsigned long)t, usecs_rem);
683d1f9cbd7SFrederic Weisbecker }
684d1f9cbd7SFrederic Weisbecker
6859d9add34SSteven Rostedt (Red Hat) static void
print_graph_rel_time(struct trace_iterator * iter,struct trace_seq * s)6869acd8de6SChangbin Du print_graph_rel_time(struct trace_iterator *iter, struct trace_seq *s)
6879acd8de6SChangbin Du {
6889acd8de6SChangbin Du unsigned long long usecs;
6899acd8de6SChangbin Du
6901c5eb448SSteven Rostedt (VMware) usecs = iter->ts - iter->array_buffer->time_start;
6919acd8de6SChangbin Du do_div(usecs, NSEC_PER_USEC);
6929acd8de6SChangbin Du
6939acd8de6SChangbin Du trace_seq_printf(s, "%9llu us | ", usecs);
6949acd8de6SChangbin Du }
6959acd8de6SChangbin Du
6969acd8de6SChangbin Du static void
print_graph_irq(struct trace_iterator * iter,unsigned long addr,enum trace_type type,int cpu,pid_t pid,u32 flags)697d1f9cbd7SFrederic Weisbecker print_graph_irq(struct trace_iterator *iter, unsigned long addr,
698d7a8d9e9SJiri Olsa enum trace_type type, int cpu, pid_t pid, u32 flags)
699f8b755acSFrederic Weisbecker {
700983f938aSSteven Rostedt (Red Hat) struct trace_array *tr = iter->tr;
701d1f9cbd7SFrederic Weisbecker struct trace_seq *s = &iter->seq;
702678f845eSDaniel Bristot de Oliveira struct trace_entry *ent = iter->ent;
703f8b755acSFrederic Weisbecker
7044c57d0beSSteven Rostedt addr += iter->tr->text_delta;
7054c57d0beSSteven Rostedt
706f8b755acSFrederic Weisbecker if (addr < (unsigned long)__irqentry_text_start ||
707f8b755acSFrederic Weisbecker addr >= (unsigned long)__irqentry_text_end)
7089d9add34SSteven Rostedt (Red Hat) return;
709f8b755acSFrederic Weisbecker
710983f938aSSteven Rostedt (Red Hat) if (tr->trace_flags & TRACE_ITER_CONTEXT_INFO) {
711d1f9cbd7SFrederic Weisbecker /* Absolute time */
7129d9add34SSteven Rostedt (Red Hat) if (flags & TRACE_GRAPH_PRINT_ABS_TIME)
7139d9add34SSteven Rostedt (Red Hat) print_graph_abs_time(iter->ts, s);
714d1f9cbd7SFrederic Weisbecker
7159acd8de6SChangbin Du /* Relative time */
7169acd8de6SChangbin Du if (flags & TRACE_GRAPH_PRINT_REL_TIME)
7179acd8de6SChangbin Du print_graph_rel_time(iter, s);
7189acd8de6SChangbin Du
719f8b755acSFrederic Weisbecker /* Cpu */
7209d9add34SSteven Rostedt (Red Hat) if (flags & TRACE_GRAPH_PRINT_CPU)
7219d9add34SSteven Rostedt (Red Hat) print_graph_cpu(s, cpu);
72249ff5903SSteven Rostedt
723f8b755acSFrederic Weisbecker /* Proc */
724d7a8d9e9SJiri Olsa if (flags & TRACE_GRAPH_PRINT_PROC) {
7259d9add34SSteven Rostedt (Red Hat) print_graph_proc(s, pid);
7269d9add34SSteven Rostedt (Red Hat) trace_seq_puts(s, " | ");
727f8b755acSFrederic Weisbecker }
728678f845eSDaniel Bristot de Oliveira
729678f845eSDaniel Bristot de Oliveira /* Latency format */
730983f938aSSteven Rostedt (Red Hat) if (tr->trace_flags & TRACE_ITER_LATENCY_FMT)
7319d9add34SSteven Rostedt (Red Hat) print_graph_lat_fmt(s, ent);
732749230b0SJiri Olsa }
733f8b755acSFrederic Weisbecker
734f8b755acSFrederic Weisbecker /* No overhead */
735983f938aSSteven Rostedt (Red Hat) print_graph_duration(tr, 0, s, flags | FLAGS_FILL_START);
736f8b755acSFrederic Weisbecker
7379005f3ebSFrederic Weisbecker if (type == TRACE_GRAPH_ENT)
7389d9add34SSteven Rostedt (Red Hat) trace_seq_puts(s, "==========>");
7399005f3ebSFrederic Weisbecker else
7409d9add34SSteven Rostedt (Red Hat) trace_seq_puts(s, "<==========");
7419005f3ebSFrederic Weisbecker
742983f938aSSteven Rostedt (Red Hat) print_graph_duration(tr, 0, s, flags | FLAGS_FILL_END);
7439d9add34SSteven Rostedt (Red Hat) trace_seq_putc(s, '\n');
744f8b755acSFrederic Weisbecker }
74583a8df61SFrederic Weisbecker
7469d9add34SSteven Rostedt (Red Hat) void
trace_print_graph_duration(unsigned long long duration,struct trace_seq * s)7470706f1c4SSteven Rostedt trace_print_graph_duration(unsigned long long duration, struct trace_seq *s)
74883a8df61SFrederic Weisbecker {
74983a8df61SFrederic Weisbecker unsigned long nsecs_rem = do_div(duration, 1000);
750166d3c79SFrederic Weisbecker /* log10(ULONG_MAX) + '\0' */
7514526d067SByungchul Park char usecs_str[21];
752166d3c79SFrederic Weisbecker char nsecs_str[5];
7539d9add34SSteven Rostedt (Red Hat) int len;
754166d3c79SFrederic Weisbecker int i;
755166d3c79SFrederic Weisbecker
7564526d067SByungchul Park sprintf(usecs_str, "%lu", (unsigned long) duration);
757166d3c79SFrederic Weisbecker
758166d3c79SFrederic Weisbecker /* Print msecs */
7599d9add34SSteven Rostedt (Red Hat) trace_seq_printf(s, "%s", usecs_str);
760166d3c79SFrederic Weisbecker
7614526d067SByungchul Park len = strlen(usecs_str);
762166d3c79SFrederic Weisbecker
763166d3c79SFrederic Weisbecker /* Print nsecs (we don't want to exceed 7 numbers) */
764166d3c79SFrederic Weisbecker if (len < 7) {
76514cae9bdSBorislav Petkov size_t slen = min_t(size_t, sizeof(nsecs_str), 8UL - len);
76614cae9bdSBorislav Petkov
76714cae9bdSBorislav Petkov snprintf(nsecs_str, slen, "%03lu", nsecs_rem);
7689d9add34SSteven Rostedt (Red Hat) trace_seq_printf(s, ".%s", nsecs_str);
76982c355e8SSteven Rostedt (Red Hat) len += strlen(nsecs_str) + 1;
770166d3c79SFrederic Weisbecker }
771166d3c79SFrederic Weisbecker
7729d9add34SSteven Rostedt (Red Hat) trace_seq_puts(s, " us ");
773166d3c79SFrederic Weisbecker
774166d3c79SFrederic Weisbecker /* Print remaining spaces to fit the row's width */
77582c355e8SSteven Rostedt (Red Hat) for (i = len; i < 8; i++)
7769d9add34SSteven Rostedt (Red Hat) trace_seq_putc(s, ' ');
7770706f1c4SSteven Rostedt }
7780706f1c4SSteven Rostedt
7799d9add34SSteven Rostedt (Red Hat) static void
print_graph_duration(struct trace_array * tr,unsigned long long duration,struct trace_seq * s,u32 flags)780983f938aSSteven Rostedt (Red Hat) print_graph_duration(struct trace_array *tr, unsigned long long duration,
781983f938aSSteven Rostedt (Red Hat) struct trace_seq *s, u32 flags)
7820706f1c4SSteven Rostedt {
783749230b0SJiri Olsa if (!(flags & TRACE_GRAPH_PRINT_DURATION) ||
784983f938aSSteven Rostedt (Red Hat) !(tr->trace_flags & TRACE_ITER_CONTEXT_INFO))
7859d9add34SSteven Rostedt (Red Hat) return;
786ffeb80fcSJiri Olsa
787ffeb80fcSJiri Olsa /* No real adata, just filling the column with spaces */
7886fc84ea7SSteven Rostedt (Red Hat) switch (flags & TRACE_GRAPH_PRINT_FILL_MASK) {
7896fc84ea7SSteven Rostedt (Red Hat) case FLAGS_FILL_FULL:
7909d9add34SSteven Rostedt (Red Hat) trace_seq_puts(s, " | ");
7919d9add34SSteven Rostedt (Red Hat) return;
7926fc84ea7SSteven Rostedt (Red Hat) case FLAGS_FILL_START:
7939d9add34SSteven Rostedt (Red Hat) trace_seq_puts(s, " ");
7949d9add34SSteven Rostedt (Red Hat) return;
7956fc84ea7SSteven Rostedt (Red Hat) case FLAGS_FILL_END:
7969d9add34SSteven Rostedt (Red Hat) trace_seq_puts(s, " |");
7979d9add34SSteven Rostedt (Red Hat) return;
798ffeb80fcSJiri Olsa }
799ffeb80fcSJiri Olsa
800ffeb80fcSJiri Olsa /* Signal a overhead of time execution to the output */
8018e1e1df2SByungchul Park if (flags & TRACE_GRAPH_PRINT_OVERHEAD)
8028e1e1df2SByungchul Park trace_seq_printf(s, "%c ", trace_find_mark(duration));
8038e1e1df2SByungchul Park else
8049d9add34SSteven Rostedt (Red Hat) trace_seq_puts(s, " ");
805ffeb80fcSJiri Olsa
8069d9add34SSteven Rostedt (Red Hat) trace_print_graph_duration(duration, s);
8079d9add34SSteven Rostedt (Red Hat) trace_seq_puts(s, "| ");
80883a8df61SFrederic Weisbecker }
80983a8df61SFrederic Weisbecker
810a1be9cccSDonglin Peng #ifdef CONFIG_FUNCTION_GRAPH_RETVAL
811a1be9cccSDonglin Peng #define __TRACE_GRAPH_PRINT_RETVAL TRACE_GRAPH_PRINT_RETVAL
81221e92806SDonglin Peng #else
81321e92806SDonglin Peng #define __TRACE_GRAPH_PRINT_RETVAL 0
81421e92806SDonglin Peng #endif
815a1be9cccSDonglin Peng
81621e92806SDonglin Peng #ifdef CONFIG_FUNCTION_GRAPH_RETADDR
81721e92806SDonglin Peng #define __TRACE_GRAPH_PRINT_RETADDR TRACE_GRAPH_PRINT_RETADDR
print_graph_retaddr(struct trace_seq * s,struct fgraph_retaddr_ent_entry * entry,u32 trace_flags,bool comment)81821e92806SDonglin Peng static void print_graph_retaddr(struct trace_seq *s, struct fgraph_retaddr_ent_entry *entry,
81921e92806SDonglin Peng u32 trace_flags, bool comment)
82021e92806SDonglin Peng {
82121e92806SDonglin Peng if (comment)
82221e92806SDonglin Peng trace_seq_puts(s, " /*");
82321e92806SDonglin Peng
82421e92806SDonglin Peng trace_seq_puts(s, " <-");
82521e92806SDonglin Peng seq_print_ip_sym(s, entry->graph_ent.retaddr, trace_flags | TRACE_ITER_SYM_OFFSET);
82621e92806SDonglin Peng
82721e92806SDonglin Peng if (comment)
82821e92806SDonglin Peng trace_seq_puts(s, " */");
82921e92806SDonglin Peng }
83021e92806SDonglin Peng #else
83121e92806SDonglin Peng #define __TRACE_GRAPH_PRINT_RETADDR 0
83221e92806SDonglin Peng #define print_graph_retaddr(_seq, _entry, _tflags, _comment) do { } while (0)
83321e92806SDonglin Peng #endif
83421e92806SDonglin Peng
83521e92806SDonglin Peng #if defined(CONFIG_FUNCTION_GRAPH_RETVAL) || defined(CONFIG_FUNCTION_GRAPH_RETADDR)
83621e92806SDonglin Peng
print_graph_retval(struct trace_seq * s,struct ftrace_graph_ent_entry * entry,struct ftrace_graph_ret * graph_ret,void * func,u32 opt_flags,u32 trace_flags,int args_size)83721e92806SDonglin Peng static void print_graph_retval(struct trace_seq *s, struct ftrace_graph_ent_entry *entry,
83821e92806SDonglin Peng struct ftrace_graph_ret *graph_ret, void *func,
839ff5c9c57SSven Schnelle u32 opt_flags, u32 trace_flags, int args_size)
840a1be9cccSDonglin Peng {
841a1be9cccSDonglin Peng unsigned long err_code = 0;
84221e92806SDonglin Peng unsigned long retval = 0;
84321e92806SDonglin Peng bool print_retaddr = false;
84421e92806SDonglin Peng bool print_retval = false;
84521e92806SDonglin Peng bool hex_format = !!(opt_flags & TRACE_GRAPH_PRINT_RETVAL_HEX);
846a1be9cccSDonglin Peng
84721e92806SDonglin Peng #ifdef CONFIG_FUNCTION_GRAPH_RETVAL
84821e92806SDonglin Peng retval = graph_ret->retval;
84921e92806SDonglin Peng print_retval = !!(opt_flags & TRACE_GRAPH_PRINT_RETVAL);
85021e92806SDonglin Peng #endif
851a1be9cccSDonglin Peng
85221e92806SDonglin Peng #ifdef CONFIG_FUNCTION_GRAPH_RETADDR
85321e92806SDonglin Peng print_retaddr = !!(opt_flags & TRACE_GRAPH_PRINT_RETADDR);
85421e92806SDonglin Peng #endif
85521e92806SDonglin Peng
85621e92806SDonglin Peng if (print_retval && retval && !hex_format) {
857a1be9cccSDonglin Peng /* Check if the return value matches the negative format */
858a1be9cccSDonglin Peng if (IS_ENABLED(CONFIG_64BIT) && (retval & BIT(31)) &&
859a1be9cccSDonglin Peng (((u64)retval) >> 32) == 0) {
86021e92806SDonglin Peng err_code = sign_extend64(retval, 31);
861a1be9cccSDonglin Peng } else {
862a1be9cccSDonglin Peng err_code = retval;
863a1be9cccSDonglin Peng }
864a1be9cccSDonglin Peng
865a1be9cccSDonglin Peng if (!IS_ERR_VALUE(err_code))
866a1be9cccSDonglin Peng err_code = 0;
867a1be9cccSDonglin Peng }
86821e92806SDonglin Peng
86921e92806SDonglin Peng if (entry) {
87021e92806SDonglin Peng if (entry->ent.type != TRACE_GRAPH_RETADDR_ENT)
87121e92806SDonglin Peng print_retaddr = false;
87221e92806SDonglin Peng
873ff5c9c57SSven Schnelle trace_seq_printf(s, "%ps", func);
874ff5c9c57SSven Schnelle
875ff5c9c57SSven Schnelle if (args_size >= FTRACE_REGS_MAX_ARGS * sizeof(long)) {
876ff5c9c57SSven Schnelle print_function_args(s, entry->args, (unsigned long)func);
877ff5c9c57SSven Schnelle trace_seq_putc(s, ';');
878ff5c9c57SSven Schnelle } else
879ff5c9c57SSven Schnelle trace_seq_puts(s, "();");
880ff5c9c57SSven Schnelle
88121e92806SDonglin Peng if (print_retval || print_retaddr)
88221e92806SDonglin Peng trace_seq_puts(s, " /*");
88321e92806SDonglin Peng } else {
88421e92806SDonglin Peng print_retaddr = false;
88521e92806SDonglin Peng trace_seq_printf(s, "} /* %ps", func);
88621e92806SDonglin Peng }
88721e92806SDonglin Peng
88821e92806SDonglin Peng if (print_retaddr)
88921e92806SDonglin Peng print_graph_retaddr(s, (struct fgraph_retaddr_ent_entry *)entry,
89021e92806SDonglin Peng trace_flags, false);
89121e92806SDonglin Peng
89221e92806SDonglin Peng if (print_retval) {
89321e92806SDonglin Peng if (hex_format || (err_code == 0))
89421e92806SDonglin Peng trace_seq_printf(s, " ret=0x%lx", retval);
89521e92806SDonglin Peng else
89621e92806SDonglin Peng trace_seq_printf(s, " ret=%ld", err_code);
89721e92806SDonglin Peng }
89821e92806SDonglin Peng
89921e92806SDonglin Peng if (!entry || print_retval || print_retaddr)
900*485acd20SSteven Rostedt trace_seq_puts(s, " */");
901a1be9cccSDonglin Peng }
902a1be9cccSDonglin Peng
903a1be9cccSDonglin Peng #else
904a1be9cccSDonglin Peng
905ff5c9c57SSven Schnelle #define print_graph_retval(_seq, _ent, _ret, _func, _opt_flags, _trace_flags, args_size) \
906ff5c9c57SSven Schnelle do {} while (0)
907a1be9cccSDonglin Peng
908a1be9cccSDonglin Peng #endif
909a1be9cccSDonglin Peng
91083a8df61SFrederic Weisbecker /* Case of a leaf function on its call entry */
91183a8df61SFrederic Weisbecker static enum print_line_t
print_graph_entry_leaf(struct trace_iterator * iter,struct ftrace_graph_ent_entry * entry,struct ftrace_graph_ret_entry * ret_entry,struct trace_seq * s,u32 flags)91283a8df61SFrederic Weisbecker print_graph_entry_leaf(struct trace_iterator *iter,
913b91facc3SFrederic Weisbecker struct ftrace_graph_ent_entry *entry,
914d7a8d9e9SJiri Olsa struct ftrace_graph_ret_entry *ret_entry,
915d7a8d9e9SJiri Olsa struct trace_seq *s, u32 flags)
91683a8df61SFrederic Weisbecker {
9172fbcdb35SSteven Rostedt struct fgraph_data *data = iter->private;
918983f938aSSteven Rostedt (Red Hat) struct trace_array *tr = iter->tr;
91983a8df61SFrederic Weisbecker struct ftrace_graph_ret *graph_ret;
92083a8df61SFrederic Weisbecker struct ftrace_graph_ent *call;
92183a8df61SFrederic Weisbecker unsigned long long duration;
922ff5c9c57SSven Schnelle unsigned long ret_func;
923ff5c9c57SSven Schnelle int args_size;
9241fe4293fSChangbin Du int cpu = iter->cpu;
9251a056155SFrederic Weisbecker int i;
926287b6e68SFrederic Weisbecker
927ff5c9c57SSven Schnelle args_size = iter->ent_size - offsetof(struct ftrace_graph_ent_entry, args);
928ff5c9c57SSven Schnelle
92983a8df61SFrederic Weisbecker graph_ret = &ret_entry->ret;
93083a8df61SFrederic Weisbecker call = &entry->graph_ent;
93166611c04SSteven Rostedt duration = ret_entry->rettime - ret_entry->calltime;
932437f24fbSSteven Rostedt
9332fbcdb35SSteven Rostedt if (data) {
934f1c7f517SSteven Rostedt struct fgraph_cpu_data *cpu_data;
935f1c7f517SSteven Rostedt
936f1c7f517SSteven Rostedt cpu_data = per_cpu_ptr(data->cpu_data, cpu);
9372fbcdb35SSteven Rostedt
9382fbcdb35SSteven Rostedt /*
9392fbcdb35SSteven Rostedt * Comments display at + 1 to depth. Since
9402fbcdb35SSteven Rostedt * this is a leaf function, keep the comments
9412fbcdb35SSteven Rostedt * equal to this depth.
9422fbcdb35SSteven Rostedt */
943f1c7f517SSteven Rostedt cpu_data->depth = call->depth - 1;
944f1c7f517SSteven Rostedt
945f1c7f517SSteven Rostedt /* No need to keep this function around for this depth */
946794de08aSSteven Rostedt (Red Hat) if (call->depth < FTRACE_RETFUNC_DEPTH &&
947794de08aSSteven Rostedt (Red Hat) !WARN_ON_ONCE(call->depth < 0))
948f1c7f517SSteven Rostedt cpu_data->enter_funcs[call->depth] = 0;
9492fbcdb35SSteven Rostedt }
9502fbcdb35SSteven Rostedt
951ffeb80fcSJiri Olsa /* Overhead and duration */
952983f938aSSteven Rostedt (Red Hat) print_graph_duration(tr, duration, s, flags);
953287b6e68SFrederic Weisbecker
95483a8df61SFrederic Weisbecker /* Function */
9559d9add34SSteven Rostedt (Red Hat) for (i = 0; i < call->depth * TRACE_GRAPH_INDENT; i++)
9569d9add34SSteven Rostedt (Red Hat) trace_seq_putc(s, ' ');
957287b6e68SFrederic Weisbecker
958ff5c9c57SSven Schnelle ret_func = graph_ret->func + iter->tr->text_delta;
959ff5c9c57SSven Schnelle
960a1be9cccSDonglin Peng /*
96121e92806SDonglin Peng * Write out the function return value or return address
962a1be9cccSDonglin Peng */
96321e92806SDonglin Peng if (flags & (__TRACE_GRAPH_PRINT_RETVAL | __TRACE_GRAPH_PRINT_RETADDR)) {
96421e92806SDonglin Peng print_graph_retval(s, entry, graph_ret,
96521e92806SDonglin Peng (void *)graph_ret->func + iter->tr->text_delta,
966ff5c9c57SSven Schnelle flags, tr->trace_flags, args_size);
96721e92806SDonglin Peng } else {
968ff5c9c57SSven Schnelle trace_seq_printf(s, "%ps", (void *)ret_func);
969ff5c9c57SSven Schnelle
970ff5c9c57SSven Schnelle if (args_size >= FTRACE_REGS_MAX_ARGS * sizeof(long)) {
971ff5c9c57SSven Schnelle print_function_args(s, entry->args, ret_func);
972ff5c9c57SSven Schnelle trace_seq_putc(s, ';');
973ff5c9c57SSven Schnelle } else
974ff5c9c57SSven Schnelle trace_seq_puts(s, "();");
97521e92806SDonglin Peng }
976*485acd20SSteven Rostedt trace_seq_putc(s, '\n');
97783a8df61SFrederic Weisbecker
9781fe4293fSChangbin Du print_graph_irq(iter, graph_ret->func, TRACE_GRAPH_RET,
9791fe4293fSChangbin Du cpu, iter->ent->pid, flags);
9801fe4293fSChangbin Du
9819d9add34SSteven Rostedt (Red Hat) return trace_handle_return(s);
982287b6e68SFrederic Weisbecker }
983287b6e68SFrederic Weisbecker
984287b6e68SFrederic Weisbecker static enum print_line_t
print_graph_entry_nested(struct trace_iterator * iter,struct ftrace_graph_ent_entry * entry,struct trace_seq * s,int cpu,u32 flags)9852fbcdb35SSteven Rostedt print_graph_entry_nested(struct trace_iterator *iter,
9862fbcdb35SSteven Rostedt struct ftrace_graph_ent_entry *entry,
987d7a8d9e9SJiri Olsa struct trace_seq *s, int cpu, u32 flags)
988287b6e68SFrederic Weisbecker {
98983a8df61SFrederic Weisbecker struct ftrace_graph_ent *call = &entry->graph_ent;
9902fbcdb35SSteven Rostedt struct fgraph_data *data = iter->private;
991983f938aSSteven Rostedt (Red Hat) struct trace_array *tr = iter->tr;
9924c57d0beSSteven Rostedt unsigned long func;
993ff5c9c57SSven Schnelle int args_size;
9942fbcdb35SSteven Rostedt int i;
9952fbcdb35SSteven Rostedt
9962fbcdb35SSteven Rostedt if (data) {
997f1c7f517SSteven Rostedt struct fgraph_cpu_data *cpu_data;
9982fbcdb35SSteven Rostedt int cpu = iter->cpu;
9992fbcdb35SSteven Rostedt
1000f1c7f517SSteven Rostedt cpu_data = per_cpu_ptr(data->cpu_data, cpu);
1001f1c7f517SSteven Rostedt cpu_data->depth = call->depth;
1002f1c7f517SSteven Rostedt
1003f1c7f517SSteven Rostedt /* Save this function pointer to see if the exit matches */
1004794de08aSSteven Rostedt (Red Hat) if (call->depth < FTRACE_RETFUNC_DEPTH &&
1005794de08aSSteven Rostedt (Red Hat) !WARN_ON_ONCE(call->depth < 0))
1006f1c7f517SSteven Rostedt cpu_data->enter_funcs[call->depth] = call->func;
10072fbcdb35SSteven Rostedt }
100883a8df61SFrederic Weisbecker
10091a056155SFrederic Weisbecker /* No time */
1010983f938aSSteven Rostedt (Red Hat) print_graph_duration(tr, 0, s, flags | FLAGS_FILL_FULL);
1011f8b755acSFrederic Weisbecker
101283a8df61SFrederic Weisbecker /* Function */
10139d9add34SSteven Rostedt (Red Hat) for (i = 0; i < call->depth * TRACE_GRAPH_INDENT; i++)
10149d9add34SSteven Rostedt (Red Hat) trace_seq_putc(s, ' ');
101583a8df61SFrederic Weisbecker
10164c57d0beSSteven Rostedt func = call->func + iter->tr->text_delta;
10174c57d0beSSteven Rostedt
1018ff5c9c57SSven Schnelle trace_seq_printf(s, "%ps", (void *)func);
1019ff5c9c57SSven Schnelle
1020ff5c9c57SSven Schnelle args_size = iter->ent_size - offsetof(struct ftrace_graph_ent_entry, args);
1021ff5c9c57SSven Schnelle
1022ff5c9c57SSven Schnelle if (args_size >= FTRACE_REGS_MAX_ARGS * sizeof(long))
1023ff5c9c57SSven Schnelle print_function_args(s, entry->args, func);
1024ff5c9c57SSven Schnelle else
1025ff5c9c57SSven Schnelle trace_seq_puts(s, "()");
1026ff5c9c57SSven Schnelle
1027ff5c9c57SSven Schnelle trace_seq_puts(s, " {");
1028ff5c9c57SSven Schnelle
102921e92806SDonglin Peng if (flags & __TRACE_GRAPH_PRINT_RETADDR &&
103021e92806SDonglin Peng entry->ent.type == TRACE_GRAPH_RETADDR_ENT)
103121e92806SDonglin Peng print_graph_retaddr(s, (struct fgraph_retaddr_ent_entry *)entry,
103221e92806SDonglin Peng tr->trace_flags, true);
103321e92806SDonglin Peng trace_seq_putc(s, '\n');
10349d9add34SSteven Rostedt (Red Hat)
10359d9add34SSteven Rostedt (Red Hat) if (trace_seq_has_overflowed(s))
103683a8df61SFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE;
103783a8df61SFrederic Weisbecker
1038b91facc3SFrederic Weisbecker /*
1039b91facc3SFrederic Weisbecker * we already consumed the current entry to check the next one
1040b91facc3SFrederic Weisbecker * and see if this is a leaf.
1041b91facc3SFrederic Weisbecker */
1042b91facc3SFrederic Weisbecker return TRACE_TYPE_NO_CONSUME;
104383a8df61SFrederic Weisbecker }
104483a8df61SFrederic Weisbecker
10459d9add34SSteven Rostedt (Red Hat) static void
print_graph_prologue(struct trace_iterator * iter,struct trace_seq * s,int type,unsigned long addr,u32 flags)1046ac5f6c96SSteven Rostedt print_graph_prologue(struct trace_iterator *iter, struct trace_seq *s,
1047d7a8d9e9SJiri Olsa int type, unsigned long addr, u32 flags)
104883a8df61SFrederic Weisbecker {
10492fbcdb35SSteven Rostedt struct fgraph_data *data = iter->private;
105083a8df61SFrederic Weisbecker struct trace_entry *ent = iter->ent;
1051983f938aSSteven Rostedt (Red Hat) struct trace_array *tr = iter->tr;
1052ac5f6c96SSteven Rostedt int cpu = iter->cpu;
1053287b6e68SFrederic Weisbecker
10541a056155SFrederic Weisbecker /* Pid */
10559d9add34SSteven Rostedt (Red Hat) verif_pid(s, ent->pid, cpu, data);
1056437f24fbSSteven Rostedt
10579d9add34SSteven Rostedt (Red Hat) if (type)
10589005f3ebSFrederic Weisbecker /* Interrupt */
10599d9add34SSteven Rostedt (Red Hat) print_graph_irq(iter, addr, type, cpu, ent->pid, flags);
10609005f3ebSFrederic Weisbecker
1061983f938aSSteven Rostedt (Red Hat) if (!(tr->trace_flags & TRACE_ITER_CONTEXT_INFO))
10629d9add34SSteven Rostedt (Red Hat) return;
1063749230b0SJiri Olsa
10649005f3ebSFrederic Weisbecker /* Absolute time */
10659d9add34SSteven Rostedt (Red Hat) if (flags & TRACE_GRAPH_PRINT_ABS_TIME)
10669d9add34SSteven Rostedt (Red Hat) print_graph_abs_time(iter->ts, s);
10679005f3ebSFrederic Weisbecker
10689acd8de6SChangbin Du /* Relative time */
10699acd8de6SChangbin Du if (flags & TRACE_GRAPH_PRINT_REL_TIME)
10709acd8de6SChangbin Du print_graph_rel_time(iter, s);
10719acd8de6SChangbin Du
10721a056155SFrederic Weisbecker /* Cpu */
10739d9add34SSteven Rostedt (Red Hat) if (flags & TRACE_GRAPH_PRINT_CPU)
10749d9add34SSteven Rostedt (Red Hat) print_graph_cpu(s, cpu);
107511e84accSFrederic Weisbecker
107611e84accSFrederic Weisbecker /* Proc */
1077d7a8d9e9SJiri Olsa if (flags & TRACE_GRAPH_PRINT_PROC) {
10789d9add34SSteven Rostedt (Red Hat) print_graph_proc(s, ent->pid);
10799d9add34SSteven Rostedt (Red Hat) trace_seq_puts(s, " | ");
10801a056155SFrederic Weisbecker }
1081287b6e68SFrederic Weisbecker
108249ff5903SSteven Rostedt /* Latency format */
1083983f938aSSteven Rostedt (Red Hat) if (tr->trace_flags & TRACE_ITER_LATENCY_FMT)
10849d9add34SSteven Rostedt (Red Hat) print_graph_lat_fmt(s, ent);
108549ff5903SSteven Rostedt
10869d9add34SSteven Rostedt (Red Hat) return;
1087ac5f6c96SSteven Rostedt }
1088ac5f6c96SSteven Rostedt
10892bd16212SJiri Olsa /*
10902bd16212SJiri Olsa * Entry check for irq code
10912bd16212SJiri Olsa *
10922bd16212SJiri Olsa * returns 1 if
10932bd16212SJiri Olsa * - we are inside irq code
109425985edcSLucas De Marchi * - we just entered irq code
10952bd16212SJiri Olsa *
1096f2cc020dSIngo Molnar * returns 0 if
10972bd16212SJiri Olsa * - funcgraph-interrupts option is set
10982bd16212SJiri Olsa * - we are not inside irq code
10992bd16212SJiri Olsa */
11002bd16212SJiri Olsa static int
check_irq_entry(struct trace_iterator * iter,u32 flags,unsigned long addr,int depth)11012bd16212SJiri Olsa check_irq_entry(struct trace_iterator *iter, u32 flags,
11022bd16212SJiri Olsa unsigned long addr, int depth)
11032bd16212SJiri Olsa {
11042bd16212SJiri Olsa int cpu = iter->cpu;
1105a9d61173SJiri Olsa int *depth_irq;
11062bd16212SJiri Olsa struct fgraph_data *data = iter->private;
11072bd16212SJiri Olsa
11084c57d0beSSteven Rostedt addr += iter->tr->text_delta;
11094c57d0beSSteven Rostedt
1110a9d61173SJiri Olsa /*
1111a9d61173SJiri Olsa * If we are either displaying irqs, or we got called as
1112a9d61173SJiri Olsa * a graph event and private data does not exist,
1113a9d61173SJiri Olsa * then we bypass the irq check.
1114a9d61173SJiri Olsa */
1115a9d61173SJiri Olsa if ((flags & TRACE_GRAPH_PRINT_IRQS) ||
1116a9d61173SJiri Olsa (!data))
11172bd16212SJiri Olsa return 0;
11182bd16212SJiri Olsa
1119a9d61173SJiri Olsa depth_irq = &(per_cpu_ptr(data->cpu_data, cpu)->depth_irq);
1120a9d61173SJiri Olsa
11212bd16212SJiri Olsa /*
11222bd16212SJiri Olsa * We are inside the irq code
11232bd16212SJiri Olsa */
11242bd16212SJiri Olsa if (*depth_irq >= 0)
11252bd16212SJiri Olsa return 1;
11262bd16212SJiri Olsa
11272bd16212SJiri Olsa if ((addr < (unsigned long)__irqentry_text_start) ||
11282bd16212SJiri Olsa (addr >= (unsigned long)__irqentry_text_end))
11292bd16212SJiri Olsa return 0;
11302bd16212SJiri Olsa
11312bd16212SJiri Olsa /*
11322bd16212SJiri Olsa * We are entering irq code.
11332bd16212SJiri Olsa */
11342bd16212SJiri Olsa *depth_irq = depth;
11352bd16212SJiri Olsa return 1;
11362bd16212SJiri Olsa }
11372bd16212SJiri Olsa
11382bd16212SJiri Olsa /*
11392bd16212SJiri Olsa * Return check for irq code
11402bd16212SJiri Olsa *
11412bd16212SJiri Olsa * returns 1 if
11422bd16212SJiri Olsa * - we are inside irq code
11432bd16212SJiri Olsa * - we just left irq code
11442bd16212SJiri Olsa *
11452bd16212SJiri Olsa * returns 0 if
11462bd16212SJiri Olsa * - funcgraph-interrupts option is set
11472bd16212SJiri Olsa * - we are not inside irq code
11482bd16212SJiri Olsa */
11492bd16212SJiri Olsa static int
check_irq_return(struct trace_iterator * iter,u32 flags,int depth)11502bd16212SJiri Olsa check_irq_return(struct trace_iterator *iter, u32 flags, int depth)
11512bd16212SJiri Olsa {
11522bd16212SJiri Olsa int cpu = iter->cpu;
1153a9d61173SJiri Olsa int *depth_irq;
11542bd16212SJiri Olsa struct fgraph_data *data = iter->private;
11552bd16212SJiri Olsa
1156a9d61173SJiri Olsa /*
1157a9d61173SJiri Olsa * If we are either displaying irqs, or we got called as
1158a9d61173SJiri Olsa * a graph event and private data does not exist,
1159a9d61173SJiri Olsa * then we bypass the irq check.
1160a9d61173SJiri Olsa */
1161a9d61173SJiri Olsa if ((flags & TRACE_GRAPH_PRINT_IRQS) ||
1162a9d61173SJiri Olsa (!data))
11632bd16212SJiri Olsa return 0;
11642bd16212SJiri Olsa
1165a9d61173SJiri Olsa depth_irq = &(per_cpu_ptr(data->cpu_data, cpu)->depth_irq);
1166a9d61173SJiri Olsa
11672bd16212SJiri Olsa /*
11682bd16212SJiri Olsa * We are not inside the irq code.
11692bd16212SJiri Olsa */
11702bd16212SJiri Olsa if (*depth_irq == -1)
11712bd16212SJiri Olsa return 0;
11722bd16212SJiri Olsa
11732bd16212SJiri Olsa /*
11742bd16212SJiri Olsa * We are inside the irq code, and this is returning entry.
11752bd16212SJiri Olsa * Let's not trace it and clear the entry depth, since
11762bd16212SJiri Olsa * we are out of irq code.
11772bd16212SJiri Olsa *
11782bd16212SJiri Olsa * This condition ensures that we 'leave the irq code' once
11792bd16212SJiri Olsa * we are out of the entry depth. Thus protecting us from
11802bd16212SJiri Olsa * the RETURN entry loss.
11812bd16212SJiri Olsa */
11822bd16212SJiri Olsa if (*depth_irq >= depth) {
11832bd16212SJiri Olsa *depth_irq = -1;
11842bd16212SJiri Olsa return 1;
11852bd16212SJiri Olsa }
11862bd16212SJiri Olsa
11872bd16212SJiri Olsa /*
11882bd16212SJiri Olsa * We are inside the irq code, and this is not the entry.
11892bd16212SJiri Olsa */
11902bd16212SJiri Olsa return 1;
11912bd16212SJiri Olsa }
11922bd16212SJiri Olsa
1193ac5f6c96SSteven Rostedt static enum print_line_t
print_graph_entry(struct ftrace_graph_ent_entry * field,struct trace_seq * s,struct trace_iterator * iter,u32 flags)1194ac5f6c96SSteven Rostedt print_graph_entry(struct ftrace_graph_ent_entry *field, struct trace_seq *s,
1195d7a8d9e9SJiri Olsa struct trace_iterator *iter, u32 flags)
1196ac5f6c96SSteven Rostedt {
1197be1eca39SJiri Olsa struct fgraph_data *data = iter->private;
1198ff5c9c57SSven Schnelle struct ftrace_graph_ent *call;
1199ac5f6c96SSteven Rostedt struct ftrace_graph_ret_entry *leaf_ret;
1200be1eca39SJiri Olsa static enum print_line_t ret;
1201be1eca39SJiri Olsa int cpu = iter->cpu;
1202ff5c9c57SSven Schnelle /*
1203ff5c9c57SSven Schnelle * print_graph_entry() may consume the current event,
1204ff5c9c57SSven Schnelle * thus @field may become invalid, so we need to save it.
1205ff5c9c57SSven Schnelle * sizeof(struct ftrace_graph_ent_entry) is very small,
1206ff5c9c57SSven Schnelle * it can be safely saved at the stack.
1207ff5c9c57SSven Schnelle */
1208ff5c9c57SSven Schnelle struct ftrace_graph_ent_entry *entry;
1209ff5c9c57SSven Schnelle u8 save_buf[sizeof(*entry) + FTRACE_REGS_MAX_ARGS * sizeof(long)];
1210ff5c9c57SSven Schnelle
1211ff5c9c57SSven Schnelle /* The ent_size is expected to be as big as the entry */
1212ff5c9c57SSven Schnelle if (iter->ent_size > sizeof(save_buf))
1213ff5c9c57SSven Schnelle iter->ent_size = sizeof(save_buf);
1214ff5c9c57SSven Schnelle
1215ff5c9c57SSven Schnelle entry = (void *)save_buf;
1216ff5c9c57SSven Schnelle memcpy(entry, field, iter->ent_size);
1217ff5c9c57SSven Schnelle
1218ff5c9c57SSven Schnelle call = &entry->graph_ent;
1219ac5f6c96SSteven Rostedt
12202bd16212SJiri Olsa if (check_irq_entry(iter, flags, call->func, call->depth))
12212bd16212SJiri Olsa return TRACE_TYPE_HANDLED;
12222bd16212SJiri Olsa
12239d9add34SSteven Rostedt (Red Hat) print_graph_prologue(iter, s, TRACE_GRAPH_ENT, call->func, flags);
1224ac5f6c96SSteven Rostedt
1225ff5c9c57SSven Schnelle leaf_ret = get_return_for_leaf(iter, entry);
1226b91facc3SFrederic Weisbecker if (leaf_ret)
1227ff5c9c57SSven Schnelle ret = print_graph_entry_leaf(iter, entry, leaf_ret, s, flags);
122883a8df61SFrederic Weisbecker else
1229ff5c9c57SSven Schnelle ret = print_graph_entry_nested(iter, entry, s, cpu, flags);
123083a8df61SFrederic Weisbecker
1231be1eca39SJiri Olsa if (data) {
1232be1eca39SJiri Olsa /*
1233be1eca39SJiri Olsa * If we failed to write our output, then we need to make
1234be1eca39SJiri Olsa * note of it. Because we already consumed our entry.
1235be1eca39SJiri Olsa */
1236be1eca39SJiri Olsa if (s->full) {
1237be1eca39SJiri Olsa data->failed = 1;
1238be1eca39SJiri Olsa data->cpu = cpu;
1239be1eca39SJiri Olsa } else
1240be1eca39SJiri Olsa data->failed = 0;
1241be1eca39SJiri Olsa }
1242be1eca39SJiri Olsa
1243be1eca39SJiri Olsa return ret;
124483a8df61SFrederic Weisbecker }
124583a8df61SFrederic Weisbecker
124683a8df61SFrederic Weisbecker static enum print_line_t
print_graph_return(struct ftrace_graph_ret_entry * retentry,struct trace_seq * s,struct trace_entry * ent,struct trace_iterator * iter,u32 flags)124766611c04SSteven Rostedt print_graph_return(struct ftrace_graph_ret_entry *retentry, struct trace_seq *s,
1248d7a8d9e9SJiri Olsa struct trace_entry *ent, struct trace_iterator *iter,
1249d7a8d9e9SJiri Olsa u32 flags)
125083a8df61SFrederic Weisbecker {
125166611c04SSteven Rostedt struct ftrace_graph_ret *trace = &retentry->ret;
125266611c04SSteven Rostedt u64 calltime = retentry->calltime;
125366611c04SSteven Rostedt u64 rettime = retentry->rettime;
125466611c04SSteven Rostedt unsigned long long duration = rettime - calltime;
12552fbcdb35SSteven Rostedt struct fgraph_data *data = iter->private;
1256983f938aSSteven Rostedt (Red Hat) struct trace_array *tr = iter->tr;
12574c57d0beSSteven Rostedt unsigned long func;
12582fbcdb35SSteven Rostedt pid_t pid = ent->pid;
12592fbcdb35SSteven Rostedt int cpu = iter->cpu;
1260f1c7f517SSteven Rostedt int func_match = 1;
12612fbcdb35SSteven Rostedt int i;
12622fbcdb35SSteven Rostedt
12634c57d0beSSteven Rostedt func = trace->func + iter->tr->text_delta;
12644c57d0beSSteven Rostedt
12652bd16212SJiri Olsa if (check_irq_return(iter, flags, trace->depth))
12662bd16212SJiri Olsa return TRACE_TYPE_HANDLED;
12672bd16212SJiri Olsa
12682fbcdb35SSteven Rostedt if (data) {
1269f1c7f517SSteven Rostedt struct fgraph_cpu_data *cpu_data;
1270f1c7f517SSteven Rostedt int cpu = iter->cpu;
1271f1c7f517SSteven Rostedt
1272f1c7f517SSteven Rostedt cpu_data = per_cpu_ptr(data->cpu_data, cpu);
12732fbcdb35SSteven Rostedt
12742fbcdb35SSteven Rostedt /*
12752fbcdb35SSteven Rostedt * Comments display at + 1 to depth. This is the
12762fbcdb35SSteven Rostedt * return from a function, we now want the comments
12772fbcdb35SSteven Rostedt * to display at the same level of the bracket.
12782fbcdb35SSteven Rostedt */
1279f1c7f517SSteven Rostedt cpu_data->depth = trace->depth - 1;
1280f1c7f517SSteven Rostedt
1281794de08aSSteven Rostedt (Red Hat) if (trace->depth < FTRACE_RETFUNC_DEPTH &&
1282794de08aSSteven Rostedt (Red Hat) !WARN_ON_ONCE(trace->depth < 0)) {
1283f1c7f517SSteven Rostedt if (cpu_data->enter_funcs[trace->depth] != trace->func)
1284f1c7f517SSteven Rostedt func_match = 0;
1285f1c7f517SSteven Rostedt cpu_data->enter_funcs[trace->depth] = 0;
1286f1c7f517SSteven Rostedt }
12872fbcdb35SSteven Rostedt }
128883a8df61SFrederic Weisbecker
12899d9add34SSteven Rostedt (Red Hat) print_graph_prologue(iter, s, 0, 0, flags);
129083a8df61SFrederic Weisbecker
1291ffeb80fcSJiri Olsa /* Overhead and duration */
1292983f938aSSteven Rostedt (Red Hat) print_graph_duration(tr, duration, s, flags);
129383a8df61SFrederic Weisbecker
129483a8df61SFrederic Weisbecker /* Closing brace */
12959d9add34SSteven Rostedt (Red Hat) for (i = 0; i < trace->depth * TRACE_GRAPH_INDENT; i++)
12969d9add34SSteven Rostedt (Red Hat) trace_seq_putc(s, ' ');
1297287b6e68SFrederic Weisbecker
1298f1c7f517SSteven Rostedt /*
1299a1be9cccSDonglin Peng * Always write out the function name and its return value if the
130021e92806SDonglin Peng * funcgraph-retval option is enabled.
1301a1be9cccSDonglin Peng */
1302a1be9cccSDonglin Peng if (flags & __TRACE_GRAPH_PRINT_RETVAL) {
1303ff5c9c57SSven Schnelle print_graph_retval(s, NULL, trace, (void *)func, flags,
1304ff5c9c57SSven Schnelle tr->trace_flags, 0);
1305a1be9cccSDonglin Peng } else {
1306a1be9cccSDonglin Peng /*
1307f1c7f517SSteven Rostedt * If the return function does not have a matching entry,
1308f1c7f517SSteven Rostedt * then the entry was lost. Instead of just printing
1309f1c7f517SSteven Rostedt * the '}' and letting the user guess what function this
1310607e3a29SRobert Elliott * belongs to, write out the function name. Always do
1311607e3a29SRobert Elliott * that if the funcgraph-tail option is enabled.
1312f1c7f517SSteven Rostedt */
13139d9add34SSteven Rostedt (Red Hat) if (func_match && !(flags & TRACE_GRAPH_PRINT_TAIL))
1314*485acd20SSteven Rostedt trace_seq_puts(s, "}");
13159d9add34SSteven Rostedt (Red Hat) else
1316*485acd20SSteven Rostedt trace_seq_printf(s, "} /* %ps */", (void *)func);
1317a1be9cccSDonglin Peng }
1318*485acd20SSteven Rostedt trace_seq_putc(s, '\n');
131983a8df61SFrederic Weisbecker
132083a8df61SFrederic Weisbecker /* Overrun */
13219d9add34SSteven Rostedt (Red Hat) if (flags & TRACE_GRAPH_PRINT_OVERRUN)
132260602cb5SSteven Rostedt (VMware) trace_seq_printf(s, " (Overruns: %u)\n",
1323287b6e68SFrederic Weisbecker trace->overrun);
1324f8b755acSFrederic Weisbecker
13259d9add34SSteven Rostedt (Red Hat) print_graph_irq(iter, trace->func, TRACE_GRAPH_RET,
1326d7a8d9e9SJiri Olsa cpu, pid, flags);
1327f8b755acSFrederic Weisbecker
13289d9add34SSteven Rostedt (Red Hat) return trace_handle_return(s);
1329287b6e68SFrederic Weisbecker }
1330fb52607aSFrederic Weisbecker
13311fd8f2a3SFrederic Weisbecker static enum print_line_t
print_graph_comment(struct trace_seq * s,struct trace_entry * ent,struct trace_iterator * iter,u32 flags)13325087f8d2SSteven Rostedt print_graph_comment(struct trace_seq *s, struct trace_entry *ent,
1333d7a8d9e9SJiri Olsa struct trace_iterator *iter, u32 flags)
13341fd8f2a3SFrederic Weisbecker {
1335983f938aSSteven Rostedt (Red Hat) struct trace_array *tr = iter->tr;
1336983f938aSSteven Rostedt (Red Hat) unsigned long sym_flags = (tr->trace_flags & TRACE_ITER_SYM_MASK);
13372fbcdb35SSteven Rostedt struct fgraph_data *data = iter->private;
13385087f8d2SSteven Rostedt struct trace_event *event;
13392fbcdb35SSteven Rostedt int depth = 0;
13401fd8f2a3SFrederic Weisbecker int ret;
13412fbcdb35SSteven Rostedt int i;
13422fbcdb35SSteven Rostedt
13432fbcdb35SSteven Rostedt if (data)
1344be1eca39SJiri Olsa depth = per_cpu_ptr(data->cpu_data, iter->cpu)->depth;
13459005f3ebSFrederic Weisbecker
13469d9add34SSteven Rostedt (Red Hat) print_graph_prologue(iter, s, 0, 0, flags);
1347d1f9cbd7SFrederic Weisbecker
13481fd8f2a3SFrederic Weisbecker /* No time */
1349983f938aSSteven Rostedt (Red Hat) print_graph_duration(tr, 0, s, flags | FLAGS_FILL_FULL);
13501fd8f2a3SFrederic Weisbecker
13511fd8f2a3SFrederic Weisbecker /* Indentation */
13522fbcdb35SSteven Rostedt if (depth > 0)
13539d9add34SSteven Rostedt (Red Hat) for (i = 0; i < (depth + 1) * TRACE_GRAPH_INDENT; i++)
13549d9add34SSteven Rostedt (Red Hat) trace_seq_putc(s, ' ');
13551fd8f2a3SFrederic Weisbecker
13561fd8f2a3SFrederic Weisbecker /* The comment */
13579d9add34SSteven Rostedt (Red Hat) trace_seq_puts(s, "/* ");
1358769b0441SFrederic Weisbecker
13595087f8d2SSteven Rostedt switch (iter->ent->type) {
1360613dccdfSNamhyung Kim case TRACE_BPUTS:
1361613dccdfSNamhyung Kim ret = trace_print_bputs_msg_only(iter);
1362613dccdfSNamhyung Kim if (ret != TRACE_TYPE_HANDLED)
1363613dccdfSNamhyung Kim return ret;
1364613dccdfSNamhyung Kim break;
13655087f8d2SSteven Rostedt case TRACE_BPRINT:
13665087f8d2SSteven Rostedt ret = trace_print_bprintk_msg_only(iter);
13675087f8d2SSteven Rostedt if (ret != TRACE_TYPE_HANDLED)
13685087f8d2SSteven Rostedt return ret;
13695087f8d2SSteven Rostedt break;
13705087f8d2SSteven Rostedt case TRACE_PRINT:
13715087f8d2SSteven Rostedt ret = trace_print_printk_msg_only(iter);
13725087f8d2SSteven Rostedt if (ret != TRACE_TYPE_HANDLED)
13735087f8d2SSteven Rostedt return ret;
13745087f8d2SSteven Rostedt break;
13755087f8d2SSteven Rostedt default:
13765087f8d2SSteven Rostedt event = ftrace_find_event(ent->type);
13775087f8d2SSteven Rostedt if (!event)
13785087f8d2SSteven Rostedt return TRACE_TYPE_UNHANDLED;
13795087f8d2SSteven Rostedt
1380a9a57763SSteven Rostedt ret = event->funcs->trace(iter, sym_flags, event);
13815087f8d2SSteven Rostedt if (ret != TRACE_TYPE_HANDLED)
13825087f8d2SSteven Rostedt return ret;
13835087f8d2SSteven Rostedt }
13841fd8f2a3SFrederic Weisbecker
13855ac48378SSteven Rostedt (Red Hat) if (trace_seq_has_overflowed(s))
13865ac48378SSteven Rostedt (Red Hat) goto out;
13875ac48378SSteven Rostedt (Red Hat)
1388412d0bb5SFrederic Weisbecker /* Strip ending newline */
13893a161d99SSteven Rostedt (Red Hat) if (s->buffer[s->seq.len - 1] == '\n') {
13903a161d99SSteven Rostedt (Red Hat) s->buffer[s->seq.len - 1] = '\0';
13913a161d99SSteven Rostedt (Red Hat) s->seq.len--;
1392412d0bb5SFrederic Weisbecker }
1393412d0bb5SFrederic Weisbecker
13949d9add34SSteven Rostedt (Red Hat) trace_seq_puts(s, " */\n");
13955ac48378SSteven Rostedt (Red Hat) out:
13969d9add34SSteven Rostedt (Red Hat) return trace_handle_return(s);
13971fd8f2a3SFrederic Weisbecker }
13981fd8f2a3SFrederic Weisbecker
13991fd8f2a3SFrederic Weisbecker
1400fb52607aSFrederic Weisbecker enum print_line_t
print_graph_function_flags(struct trace_iterator * iter,u32 flags)1401321e68b0SJiri Olsa print_graph_function_flags(struct trace_iterator *iter, u32 flags)
1402fb52607aSFrederic Weisbecker {
1403be1eca39SJiri Olsa struct ftrace_graph_ent_entry *field;
1404be1eca39SJiri Olsa struct fgraph_data *data = iter->private;
1405fb52607aSFrederic Weisbecker struct trace_entry *entry = iter->ent;
14065087f8d2SSteven Rostedt struct trace_seq *s = &iter->seq;
1407be1eca39SJiri Olsa int cpu = iter->cpu;
1408be1eca39SJiri Olsa int ret;
1409be1eca39SJiri Olsa
1410be1eca39SJiri Olsa if (data && per_cpu_ptr(data->cpu_data, cpu)->ignore) {
1411be1eca39SJiri Olsa per_cpu_ptr(data->cpu_data, cpu)->ignore = 0;
1412be1eca39SJiri Olsa return TRACE_TYPE_HANDLED;
1413be1eca39SJiri Olsa }
1414be1eca39SJiri Olsa
1415be1eca39SJiri Olsa /*
1416be1eca39SJiri Olsa * If the last output failed, there's a possibility we need
1417be1eca39SJiri Olsa * to print out the missing entry which would never go out.
1418be1eca39SJiri Olsa */
1419be1eca39SJiri Olsa if (data && data->failed) {
142021e92806SDonglin Peng field = &data->ent.ent;
1421be1eca39SJiri Olsa iter->cpu = data->cpu;
1422d7a8d9e9SJiri Olsa ret = print_graph_entry(field, s, iter, flags);
1423be1eca39SJiri Olsa if (ret == TRACE_TYPE_HANDLED && iter->cpu != cpu) {
1424be1eca39SJiri Olsa per_cpu_ptr(data->cpu_data, iter->cpu)->ignore = 1;
1425be1eca39SJiri Olsa ret = TRACE_TYPE_NO_CONSUME;
1426be1eca39SJiri Olsa }
1427be1eca39SJiri Olsa iter->cpu = cpu;
1428be1eca39SJiri Olsa return ret;
1429be1eca39SJiri Olsa }
1430fb52607aSFrederic Weisbecker
1431287b6e68SFrederic Weisbecker switch (entry->type) {
1432287b6e68SFrederic Weisbecker case TRACE_GRAPH_ENT: {
1433fb52607aSFrederic Weisbecker trace_assign_type(field, entry);
1434ff5c9c57SSven Schnelle return print_graph_entry(field, s, iter, flags);
1435fb52607aSFrederic Weisbecker }
143621e92806SDonglin Peng #ifdef CONFIG_FUNCTION_GRAPH_RETADDR
143721e92806SDonglin Peng case TRACE_GRAPH_RETADDR_ENT: {
143821e92806SDonglin Peng struct fgraph_retaddr_ent_entry saved;
143921e92806SDonglin Peng struct fgraph_retaddr_ent_entry *rfield;
144021e92806SDonglin Peng
144121e92806SDonglin Peng trace_assign_type(rfield, entry);
144221e92806SDonglin Peng saved = *rfield;
144321e92806SDonglin Peng return print_graph_entry((struct ftrace_graph_ent_entry *)&saved, s, iter, flags);
144421e92806SDonglin Peng }
144521e92806SDonglin Peng #endif
1446287b6e68SFrederic Weisbecker case TRACE_GRAPH_RET: {
1447287b6e68SFrederic Weisbecker struct ftrace_graph_ret_entry *field;
1448287b6e68SFrederic Weisbecker trace_assign_type(field, entry);
144966611c04SSteven Rostedt return print_graph_return(field, s, entry, iter, flags);
1450fb52607aSFrederic Weisbecker }
145162b915f1SJiri Olsa case TRACE_STACK:
145262b915f1SJiri Olsa case TRACE_FN:
145362b915f1SJiri Olsa /* dont trace stack and functions as comments */
145462b915f1SJiri Olsa return TRACE_TYPE_UNHANDLED;
145562b915f1SJiri Olsa
1456287b6e68SFrederic Weisbecker default:
1457d7a8d9e9SJiri Olsa return print_graph_comment(s, entry, iter, flags);
1458fb52607aSFrederic Weisbecker }
14595087f8d2SSteven Rostedt
14605087f8d2SSteven Rostedt return TRACE_TYPE_HANDLED;
1461287b6e68SFrederic Weisbecker }
1462fb52607aSFrederic Weisbecker
14639106b693SJiri Olsa static enum print_line_t
print_graph_function(struct trace_iterator * iter)1464d7a8d9e9SJiri Olsa print_graph_function(struct trace_iterator *iter)
1465d7a8d9e9SJiri Olsa {
1466321e68b0SJiri Olsa return print_graph_function_flags(iter, tracer_flags.val);
1467d7a8d9e9SJiri Olsa }
1468d7a8d9e9SJiri Olsa
1469d7a8d9e9SJiri Olsa static enum print_line_t
print_graph_function_event(struct trace_iterator * iter,int flags,struct trace_event * event)1470a9a57763SSteven Rostedt print_graph_function_event(struct trace_iterator *iter, int flags,
1471a9a57763SSteven Rostedt struct trace_event *event)
14729106b693SJiri Olsa {
14739106b693SJiri Olsa return print_graph_function(iter);
14749106b693SJiri Olsa }
14759106b693SJiri Olsa
print_lat_header(struct seq_file * s,u32 flags)1476d7a8d9e9SJiri Olsa static void print_lat_header(struct seq_file *s, u32 flags)
147749ff5903SSteven Rostedt {
147849ff5903SSteven Rostedt static const char spaces[] = " " /* 16 spaces */
147949ff5903SSteven Rostedt " " /* 4 spaces */
148049ff5903SSteven Rostedt " "; /* 17 spaces */
148149ff5903SSteven Rostedt int size = 0;
148249ff5903SSteven Rostedt
1483d7a8d9e9SJiri Olsa if (flags & TRACE_GRAPH_PRINT_ABS_TIME)
148449ff5903SSteven Rostedt size += 16;
14859acd8de6SChangbin Du if (flags & TRACE_GRAPH_PRINT_REL_TIME)
14869acd8de6SChangbin Du size += 16;
1487d7a8d9e9SJiri Olsa if (flags & TRACE_GRAPH_PRINT_CPU)
148849ff5903SSteven Rostedt size += 4;
1489d7a8d9e9SJiri Olsa if (flags & TRACE_GRAPH_PRINT_PROC)
149049ff5903SSteven Rostedt size += 17;
149149ff5903SSteven Rostedt
149249ff5903SSteven Rostedt seq_printf(s, "#%.*s _-----=> irqs-off \n", size, spaces);
149349ff5903SSteven Rostedt seq_printf(s, "#%.*s / _----=> need-resched \n", size, spaces);
149449ff5903SSteven Rostedt seq_printf(s, "#%.*s| / _---=> hardirq/softirq \n", size, spaces);
149549ff5903SSteven Rostedt seq_printf(s, "#%.*s|| / _--=> preempt-depth \n", size, spaces);
1496199abfabSJiri Olsa seq_printf(s, "#%.*s||| / \n", size, spaces);
149749ff5903SSteven Rostedt }
149849ff5903SSteven Rostedt
__print_graph_headers_flags(struct trace_array * tr,struct seq_file * s,u32 flags)1499983f938aSSteven Rostedt (Red Hat) static void __print_graph_headers_flags(struct trace_array *tr,
1500983f938aSSteven Rostedt (Red Hat) struct seq_file *s, u32 flags)
1501decbec38SFrederic Weisbecker {
1502983f938aSSteven Rostedt (Red Hat) int lat = tr->trace_flags & TRACE_ITER_LATENCY_FMT;
150349ff5903SSteven Rostedt
150449ff5903SSteven Rostedt if (lat)
1505d7a8d9e9SJiri Olsa print_lat_header(s, flags);
150649ff5903SSteven Rostedt
1507decbec38SFrederic Weisbecker /* 1st line */
15081177e436SRasmus Villemoes seq_putc(s, '#');
1509d7a8d9e9SJiri Olsa if (flags & TRACE_GRAPH_PRINT_ABS_TIME)
1510fa6f0cc7SRasmus Villemoes seq_puts(s, " TIME ");
15119acd8de6SChangbin Du if (flags & TRACE_GRAPH_PRINT_REL_TIME)
15129acd8de6SChangbin Du seq_puts(s, " REL TIME ");
1513d7a8d9e9SJiri Olsa if (flags & TRACE_GRAPH_PRINT_CPU)
1514fa6f0cc7SRasmus Villemoes seq_puts(s, " CPU");
1515d7a8d9e9SJiri Olsa if (flags & TRACE_GRAPH_PRINT_PROC)
1516fa6f0cc7SRasmus Villemoes seq_puts(s, " TASK/PID ");
151749ff5903SSteven Rostedt if (lat)
1518fa6f0cc7SRasmus Villemoes seq_puts(s, "|||| ");
1519d7a8d9e9SJiri Olsa if (flags & TRACE_GRAPH_PRINT_DURATION)
1520fa6f0cc7SRasmus Villemoes seq_puts(s, " DURATION ");
1521fa6f0cc7SRasmus Villemoes seq_puts(s, " FUNCTION CALLS\n");
1522decbec38SFrederic Weisbecker
1523decbec38SFrederic Weisbecker /* 2nd line */
15241177e436SRasmus Villemoes seq_putc(s, '#');
1525d7a8d9e9SJiri Olsa if (flags & TRACE_GRAPH_PRINT_ABS_TIME)
1526fa6f0cc7SRasmus Villemoes seq_puts(s, " | ");
15279acd8de6SChangbin Du if (flags & TRACE_GRAPH_PRINT_REL_TIME)
15289acd8de6SChangbin Du seq_puts(s, " | ");
1529d7a8d9e9SJiri Olsa if (flags & TRACE_GRAPH_PRINT_CPU)
1530fa6f0cc7SRasmus Villemoes seq_puts(s, " | ");
1531d7a8d9e9SJiri Olsa if (flags & TRACE_GRAPH_PRINT_PROC)
1532fa6f0cc7SRasmus Villemoes seq_puts(s, " | | ");
153349ff5903SSteven Rostedt if (lat)
1534fa6f0cc7SRasmus Villemoes seq_puts(s, "|||| ");
1535d7a8d9e9SJiri Olsa if (flags & TRACE_GRAPH_PRINT_DURATION)
1536fa6f0cc7SRasmus Villemoes seq_puts(s, " | | ");
1537fa6f0cc7SRasmus Villemoes seq_puts(s, " | | | |\n");
1538decbec38SFrederic Weisbecker }
15399005f3ebSFrederic Weisbecker
print_graph_headers(struct seq_file * s)1540ba1afef6SSteven Rostedt (Red Hat) static void print_graph_headers(struct seq_file *s)
1541d7a8d9e9SJiri Olsa {
1542d7a8d9e9SJiri Olsa print_graph_headers_flags(s, tracer_flags.val);
1543d7a8d9e9SJiri Olsa }
1544d7a8d9e9SJiri Olsa
print_graph_headers_flags(struct seq_file * s,u32 flags)15450a772620SJiri Olsa void print_graph_headers_flags(struct seq_file *s, u32 flags)
15460a772620SJiri Olsa {
15470a772620SJiri Olsa struct trace_iterator *iter = s->private;
1548983f938aSSteven Rostedt (Red Hat) struct trace_array *tr = iter->tr;
15490a772620SJiri Olsa
1550983f938aSSteven Rostedt (Red Hat) if (!(tr->trace_flags & TRACE_ITER_CONTEXT_INFO))
1551749230b0SJiri Olsa return;
1552749230b0SJiri Olsa
1553983f938aSSteven Rostedt (Red Hat) if (tr->trace_flags & TRACE_ITER_LATENCY_FMT) {
15540a772620SJiri Olsa /* print nothing if the buffers are empty */
15550a772620SJiri Olsa if (trace_empty(iter))
15560a772620SJiri Olsa return;
15570a772620SJiri Olsa
15580a772620SJiri Olsa print_trace_header(s, iter);
1559321e68b0SJiri Olsa }
15600a772620SJiri Olsa
1561983f938aSSteven Rostedt (Red Hat) __print_graph_headers_flags(tr, s, flags);
15620a772620SJiri Olsa }
15630a772620SJiri Olsa
graph_trace_open(struct trace_iterator * iter)156462b915f1SJiri Olsa void graph_trace_open(struct trace_iterator *iter)
15659005f3ebSFrederic Weisbecker {
15662fbcdb35SSteven Rostedt /* pid and depth on the last trace processed */
1567be1eca39SJiri Olsa struct fgraph_data *data;
1568ef99b88bSRabin Vincent gfp_t gfpflags;
15699005f3ebSFrederic Weisbecker int cpu;
15709005f3ebSFrederic Weisbecker
1571be1eca39SJiri Olsa iter->private = NULL;
1572be1eca39SJiri Olsa
1573ef99b88bSRabin Vincent /* We can be called in atomic context via ftrace_dump() */
1574ef99b88bSRabin Vincent gfpflags = (in_atomic() || irqs_disabled()) ? GFP_ATOMIC : GFP_KERNEL;
1575ef99b88bSRabin Vincent
1576ef99b88bSRabin Vincent data = kzalloc(sizeof(*data), gfpflags);
15772fbcdb35SSteven Rostedt if (!data)
1578be1eca39SJiri Olsa goto out_err;
1579be1eca39SJiri Olsa
1580ef99b88bSRabin Vincent data->cpu_data = alloc_percpu_gfp(struct fgraph_cpu_data, gfpflags);
1581be1eca39SJiri Olsa if (!data->cpu_data)
1582be1eca39SJiri Olsa goto out_err_free;
1583be1eca39SJiri Olsa
15849005f3ebSFrederic Weisbecker for_each_possible_cpu(cpu) {
1585be1eca39SJiri Olsa pid_t *pid = &(per_cpu_ptr(data->cpu_data, cpu)->last_pid);
1586be1eca39SJiri Olsa int *depth = &(per_cpu_ptr(data->cpu_data, cpu)->depth);
1587be1eca39SJiri Olsa int *ignore = &(per_cpu_ptr(data->cpu_data, cpu)->ignore);
15882bd16212SJiri Olsa int *depth_irq = &(per_cpu_ptr(data->cpu_data, cpu)->depth_irq);
15892bd16212SJiri Olsa
15909005f3ebSFrederic Weisbecker *pid = -1;
15912fbcdb35SSteven Rostedt *depth = 0;
1592be1eca39SJiri Olsa *ignore = 0;
15932bd16212SJiri Olsa *depth_irq = -1;
15949005f3ebSFrederic Weisbecker }
15959005f3ebSFrederic Weisbecker
15962fbcdb35SSteven Rostedt iter->private = data;
1597be1eca39SJiri Olsa
1598be1eca39SJiri Olsa return;
1599be1eca39SJiri Olsa
1600be1eca39SJiri Olsa out_err_free:
1601be1eca39SJiri Olsa kfree(data);
1602be1eca39SJiri Olsa out_err:
1603a395d6a7SJoe Perches pr_warn("function graph tracer: not enough memory\n");
16049005f3ebSFrederic Weisbecker }
16059005f3ebSFrederic Weisbecker
graph_trace_close(struct trace_iterator * iter)160662b915f1SJiri Olsa void graph_trace_close(struct trace_iterator *iter)
16079005f3ebSFrederic Weisbecker {
1608be1eca39SJiri Olsa struct fgraph_data *data = iter->private;
1609be1eca39SJiri Olsa
1610be1eca39SJiri Olsa if (data) {
1611be1eca39SJiri Olsa free_percpu(data->cpu_data);
1612be1eca39SJiri Olsa kfree(data);
16137f81f27bSTengda Wu iter->private = NULL;
1614be1eca39SJiri Olsa }
16159005f3ebSFrederic Weisbecker }
16169005f3ebSFrederic Weisbecker
16178c1a49aeSSteven Rostedt (Red Hat) static int
func_graph_set_flag(struct trace_array * tr,u32 old_flags,u32 bit,int set)16188c1a49aeSSteven Rostedt (Red Hat) func_graph_set_flag(struct trace_array *tr, u32 old_flags, u32 bit, int set)
1619b304d044SSteven Rostedt {
1620b304d044SSteven Rostedt if (bit == TRACE_GRAPH_PRINT_IRQS)
1621b304d044SSteven Rostedt ftrace_graph_skip_irqs = !set;
1622b304d044SSteven Rostedt
162355577204SSteven Rostedt (Red Hat) if (bit == TRACE_GRAPH_SLEEP_TIME)
162455577204SSteven Rostedt (Red Hat) ftrace_graph_sleep_time_control(set);
162555577204SSteven Rostedt (Red Hat)
162655577204SSteven Rostedt (Red Hat) if (bit == TRACE_GRAPH_GRAPH_TIME)
162755577204SSteven Rostedt (Red Hat) ftrace_graph_graph_time_control(set);
162855577204SSteven Rostedt (Red Hat)
1629c7a60a73SSteven Rostedt if (bit == TRACE_GRAPH_ARGS)
1630c7a60a73SSteven Rostedt return ftrace_graph_trace_args(tr, set);
1631c7a60a73SSteven Rostedt
1632b304d044SSteven Rostedt return 0;
1633b304d044SSteven Rostedt }
1634b304d044SSteven Rostedt
1635a9a57763SSteven Rostedt static struct trace_event_functions graph_functions = {
1636a9a57763SSteven Rostedt .trace = print_graph_function_event,
1637a9a57763SSteven Rostedt };
1638a9a57763SSteven Rostedt
16399106b693SJiri Olsa static struct trace_event graph_trace_entry_event = {
16409106b693SJiri Olsa .type = TRACE_GRAPH_ENT,
1641a9a57763SSteven Rostedt .funcs = &graph_functions,
16429106b693SJiri Olsa };
16439106b693SJiri Olsa
164421e92806SDonglin Peng #ifdef CONFIG_FUNCTION_GRAPH_RETADDR
164521e92806SDonglin Peng static struct trace_event graph_trace_retaddr_entry_event = {
164621e92806SDonglin Peng .type = TRACE_GRAPH_RETADDR_ENT,
164721e92806SDonglin Peng .funcs = &graph_functions,
164821e92806SDonglin Peng };
164921e92806SDonglin Peng #endif
165021e92806SDonglin Peng
16519106b693SJiri Olsa static struct trace_event graph_trace_ret_event = {
16529106b693SJiri Olsa .type = TRACE_GRAPH_RET,
1653a9a57763SSteven Rostedt .funcs = &graph_functions
16549106b693SJiri Olsa };
16559106b693SJiri Olsa
16568f768993SSteven Rostedt (Red Hat) static struct tracer graph_trace __tracer_data = {
165783a8df61SFrederic Weisbecker .name = "function_graph",
16586508fa76SStanislav Fomichev .update_thresh = graph_trace_update_thresh,
16599005f3ebSFrederic Weisbecker .open = graph_trace_open,
1660be1eca39SJiri Olsa .pipe_open = graph_trace_open,
16619005f3ebSFrederic Weisbecker .close = graph_trace_close,
1662be1eca39SJiri Olsa .pipe_close = graph_trace_close,
1663fb52607aSFrederic Weisbecker .init = graph_trace_init,
1664fb52607aSFrederic Weisbecker .reset = graph_trace_reset,
1665fb52607aSFrederic Weisbecker .print_line = print_graph_function,
1666decbec38SFrederic Weisbecker .print_header = print_graph_headers,
1667fb52607aSFrederic Weisbecker .flags = &tracer_flags,
1668b304d044SSteven Rostedt .set_flag = func_graph_set_flag,
166926dda563SSteven Rostedt (VMware) .allow_instances = true,
16707447dce9SFrederic Weisbecker #ifdef CONFIG_FTRACE_SELFTEST
16717447dce9SFrederic Weisbecker .selftest = trace_selftest_startup_function_graph,
16727447dce9SFrederic Weisbecker #endif
1673fb52607aSFrederic Weisbecker };
1674fb52607aSFrederic Weisbecker
16758741db53SSteven Rostedt
16768741db53SSteven Rostedt static ssize_t
graph_depth_write(struct file * filp,const char __user * ubuf,size_t cnt,loff_t * ppos)16778741db53SSteven Rostedt graph_depth_write(struct file *filp, const char __user *ubuf, size_t cnt,
16788741db53SSteven Rostedt loff_t *ppos)
16798741db53SSteven Rostedt {
16808741db53SSteven Rostedt unsigned long val;
16818741db53SSteven Rostedt int ret;
16828741db53SSteven Rostedt
16838741db53SSteven Rostedt ret = kstrtoul_from_user(ubuf, cnt, 10, &val);
16848741db53SSteven Rostedt if (ret)
16858741db53SSteven Rostedt return ret;
16868741db53SSteven Rostedt
16871a414428SSteven Rostedt (Red Hat) fgraph_max_depth = val;
16888741db53SSteven Rostedt
16898741db53SSteven Rostedt *ppos += cnt;
16908741db53SSteven Rostedt
16918741db53SSteven Rostedt return cnt;
16928741db53SSteven Rostedt }
16938741db53SSteven Rostedt
16948741db53SSteven Rostedt static ssize_t
graph_depth_read(struct file * filp,char __user * ubuf,size_t cnt,loff_t * ppos)16958741db53SSteven Rostedt graph_depth_read(struct file *filp, char __user *ubuf, size_t cnt,
16968741db53SSteven Rostedt loff_t *ppos)
16978741db53SSteven Rostedt {
16988741db53SSteven Rostedt char buf[15]; /* More than enough to hold UINT_MAX + "\n"*/
16998741db53SSteven Rostedt int n;
17008741db53SSteven Rostedt
17011a414428SSteven Rostedt (Red Hat) n = sprintf(buf, "%d\n", fgraph_max_depth);
17028741db53SSteven Rostedt
17038741db53SSteven Rostedt return simple_read_from_buffer(ubuf, cnt, ppos, buf, n);
17048741db53SSteven Rostedt }
17058741db53SSteven Rostedt
17068741db53SSteven Rostedt static const struct file_operations graph_depth_fops = {
17078741db53SSteven Rostedt .open = tracing_open_generic,
17088741db53SSteven Rostedt .write = graph_depth_write,
17098741db53SSteven Rostedt .read = graph_depth_read,
17108741db53SSteven Rostedt .llseek = generic_file_llseek,
17118741db53SSteven Rostedt };
17128741db53SSteven Rostedt
init_graph_tracefs(void)17138434dc93SSteven Rostedt (Red Hat) static __init int init_graph_tracefs(void)
17148741db53SSteven Rostedt {
171522c36b18SWei Yang int ret;
17168741db53SSteven Rostedt
171722c36b18SWei Yang ret = tracing_init_dentry();
171822c36b18SWei Yang if (ret)
17198741db53SSteven Rostedt return 0;
17208741db53SSteven Rostedt
172121ccc9cdSSteven Rostedt (VMware) trace_create_file("max_graph_depth", TRACE_MODE_WRITE, NULL,
17228741db53SSteven Rostedt NULL, &graph_depth_fops);
17238741db53SSteven Rostedt
17248741db53SSteven Rostedt return 0;
17258741db53SSteven Rostedt }
17268434dc93SSteven Rostedt (Red Hat) fs_initcall(init_graph_tracefs);
17278741db53SSteven Rostedt
init_graph_trace(void)1728fb52607aSFrederic Weisbecker static __init int init_graph_trace(void)
1729fb52607aSFrederic Weisbecker {
17309b130ad5SAlexey Dobriyan max_bytes_for_cpu = snprintf(NULL, 0, "%u", nr_cpu_ids - 1);
17310c9e6f63SLai Jiangshan
17329023c930SSteven Rostedt (Red Hat) if (!register_trace_event(&graph_trace_entry_event)) {
1733a395d6a7SJoe Perches pr_warn("Warning: could not register graph trace events\n");
17349106b693SJiri Olsa return 1;
17359106b693SJiri Olsa }
17369106b693SJiri Olsa
173721e92806SDonglin Peng #ifdef CONFIG_FUNCTION_GRAPH_RETADDR
173821e92806SDonglin Peng if (!register_trace_event(&graph_trace_retaddr_entry_event)) {
173921e92806SDonglin Peng pr_warn("Warning: could not register graph trace retaddr events\n");
174021e92806SDonglin Peng return 1;
174121e92806SDonglin Peng }
174221e92806SDonglin Peng #endif
174321e92806SDonglin Peng
17449023c930SSteven Rostedt (Red Hat) if (!register_trace_event(&graph_trace_ret_event)) {
1745a395d6a7SJoe Perches pr_warn("Warning: could not register graph trace events\n");
17469106b693SJiri Olsa return 1;
17479106b693SJiri Olsa }
17489106b693SJiri Olsa
1749fb52607aSFrederic Weisbecker return register_tracer(&graph_trace);
1750fb52607aSFrederic Weisbecker }
1751fb52607aSFrederic Weisbecker
17526f415672SSteven Rostedt core_initcall(init_graph_trace);
1753