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