1fb52607aSFrederic Weisbecker /* 2fb52607aSFrederic Weisbecker * 3fb52607aSFrederic Weisbecker * Function graph tracer. 49005f3ebSFrederic Weisbecker * Copyright (c) 2008-2009 Frederic Weisbecker <fweisbec@gmail.com> 5fb52607aSFrederic Weisbecker * Mostly borrowed from function tracer which 6fb52607aSFrederic Weisbecker * is Copyright (c) Steven Rostedt <srostedt@redhat.com> 7fb52607aSFrederic Weisbecker * 8fb52607aSFrederic Weisbecker */ 9fb52607aSFrederic Weisbecker #include <linux/debugfs.h> 10fb52607aSFrederic Weisbecker #include <linux/uaccess.h> 11fb52607aSFrederic Weisbecker #include <linux/ftrace.h> 125a0e3ad6STejun Heo #include <linux/slab.h> 13fb52607aSFrederic Weisbecker #include <linux/fs.h> 14fb52607aSFrederic Weisbecker 15fb52607aSFrederic Weisbecker #include "trace.h" 16f0868d1eSSteven Rostedt #include "trace_output.h" 17fb52607aSFrederic Weisbecker 181b2f121cSSteven Rostedt (Red Hat) static bool kill_ftrace_graph; 191b2f121cSSteven Rostedt (Red Hat) 201b2f121cSSteven Rostedt (Red Hat) /** 211b2f121cSSteven Rostedt (Red Hat) * ftrace_graph_is_dead - returns true if ftrace_graph_stop() was called 221b2f121cSSteven Rostedt (Red Hat) * 231b2f121cSSteven Rostedt (Red Hat) * ftrace_graph_stop() is called when a severe error is detected in 241b2f121cSSteven Rostedt (Red Hat) * the function graph tracing. This function is called by the critical 251b2f121cSSteven Rostedt (Red Hat) * paths of function graph to keep those paths from doing any more harm. 261b2f121cSSteven Rostedt (Red Hat) */ 271b2f121cSSteven Rostedt (Red Hat) bool ftrace_graph_is_dead(void) 281b2f121cSSteven Rostedt (Red Hat) { 291b2f121cSSteven Rostedt (Red Hat) return kill_ftrace_graph; 301b2f121cSSteven Rostedt (Red Hat) } 311b2f121cSSteven Rostedt (Red Hat) 321b2f121cSSteven Rostedt (Red Hat) /** 331b2f121cSSteven Rostedt (Red Hat) * ftrace_graph_stop - set to permanently disable function graph tracincg 341b2f121cSSteven Rostedt (Red Hat) * 351b2f121cSSteven Rostedt (Red Hat) * In case of an error int function graph tracing, this is called 361b2f121cSSteven Rostedt (Red Hat) * to try to keep function graph tracing from causing any more harm. 371b2f121cSSteven Rostedt (Red Hat) * Usually this is pretty severe and this is called to try to at least 381b2f121cSSteven Rostedt (Red Hat) * get a warning out to the user. 391b2f121cSSteven Rostedt (Red Hat) */ 401b2f121cSSteven Rostedt (Red Hat) void ftrace_graph_stop(void) 411b2f121cSSteven Rostedt (Red Hat) { 421b2f121cSSteven Rostedt (Red Hat) kill_ftrace_graph = true; 431b2f121cSSteven Rostedt (Red Hat) } 441b2f121cSSteven Rostedt (Red Hat) 45b304d044SSteven Rostedt /* When set, irq functions will be ignored */ 46b304d044SSteven Rostedt static int ftrace_graph_skip_irqs; 47b304d044SSteven Rostedt 48be1eca39SJiri Olsa struct fgraph_cpu_data { 492fbcdb35SSteven Rostedt pid_t last_pid; 502fbcdb35SSteven Rostedt int depth; 512bd16212SJiri Olsa int depth_irq; 52be1eca39SJiri Olsa int ignore; 53f1c7f517SSteven Rostedt unsigned long enter_funcs[FTRACE_RETFUNC_DEPTH]; 54be1eca39SJiri Olsa }; 55be1eca39SJiri Olsa 56be1eca39SJiri Olsa struct fgraph_data { 576016ee13SNamhyung Kim struct fgraph_cpu_data __percpu *cpu_data; 58be1eca39SJiri Olsa 59be1eca39SJiri Olsa /* Place to preserve last processed entry. */ 60be1eca39SJiri Olsa struct ftrace_graph_ent_entry ent; 61be1eca39SJiri Olsa struct ftrace_graph_ret_entry ret; 62be1eca39SJiri Olsa int failed; 63be1eca39SJiri Olsa int cpu; 642fbcdb35SSteven Rostedt }; 652fbcdb35SSteven Rostedt 66287b6e68SFrederic Weisbecker #define TRACE_GRAPH_INDENT 2 67fb52607aSFrederic Weisbecker 688741db53SSteven Rostedt static unsigned int max_depth; 698741db53SSteven Rostedt 70fb52607aSFrederic Weisbecker static struct tracer_opt trace_opts[] = { 719005f3ebSFrederic Weisbecker /* Display overruns? (for self-debug purpose) */ 721a056155SFrederic Weisbecker { TRACER_OPT(funcgraph-overrun, TRACE_GRAPH_PRINT_OVERRUN) }, 731a056155SFrederic Weisbecker /* Display CPU ? */ 741a056155SFrederic Weisbecker { TRACER_OPT(funcgraph-cpu, TRACE_GRAPH_PRINT_CPU) }, 751a056155SFrederic Weisbecker /* Display Overhead ? */ 761a056155SFrederic Weisbecker { TRACER_OPT(funcgraph-overhead, TRACE_GRAPH_PRINT_OVERHEAD) }, 7711e84accSFrederic Weisbecker /* Display proc name/pid */ 7811e84accSFrederic Weisbecker { TRACER_OPT(funcgraph-proc, TRACE_GRAPH_PRINT_PROC) }, 799005f3ebSFrederic Weisbecker /* Display duration of execution */ 809005f3ebSFrederic Weisbecker { TRACER_OPT(funcgraph-duration, TRACE_GRAPH_PRINT_DURATION) }, 819005f3ebSFrederic Weisbecker /* Display absolute time of an entry */ 829005f3ebSFrederic Weisbecker { TRACER_OPT(funcgraph-abstime, TRACE_GRAPH_PRINT_ABS_TIME) }, 832bd16212SJiri Olsa /* Display interrupts */ 842bd16212SJiri Olsa { TRACER_OPT(funcgraph-irqs, TRACE_GRAPH_PRINT_IRQS) }, 85607e3a29SRobert Elliott /* Display function name after trailing } */ 86607e3a29SRobert Elliott { TRACER_OPT(funcgraph-tail, TRACE_GRAPH_PRINT_TAIL) }, 87fb52607aSFrederic Weisbecker { } /* Empty entry */ 88fb52607aSFrederic Weisbecker }; 89fb52607aSFrederic Weisbecker 90fb52607aSFrederic Weisbecker static struct tracer_flags tracer_flags = { 91607e3a29SRobert Elliott /* Don't display overruns, proc, or tail by default */ 929005f3ebSFrederic Weisbecker .val = TRACE_GRAPH_PRINT_CPU | TRACE_GRAPH_PRINT_OVERHEAD | 932bd16212SJiri Olsa TRACE_GRAPH_PRINT_DURATION | TRACE_GRAPH_PRINT_IRQS, 94fb52607aSFrederic Weisbecker .opts = trace_opts 95fb52607aSFrederic Weisbecker }; 96fb52607aSFrederic Weisbecker 971a0799a8SFrederic Weisbecker static struct trace_array *graph_array; 989005f3ebSFrederic Weisbecker 99ffeb80fcSJiri Olsa /* 100ffeb80fcSJiri Olsa * DURATION column is being also used to display IRQ signs, 101ffeb80fcSJiri Olsa * following values are used by print_graph_irq and others 102ffeb80fcSJiri Olsa * to fill in space into DURATION column. 103ffeb80fcSJiri Olsa */ 104ffeb80fcSJiri Olsa enum { 1056fc84ea7SSteven Rostedt (Red Hat) FLAGS_FILL_FULL = 1 << TRACE_GRAPH_PRINT_FILL_SHIFT, 1066fc84ea7SSteven Rostedt (Red Hat) FLAGS_FILL_START = 2 << TRACE_GRAPH_PRINT_FILL_SHIFT, 1076fc84ea7SSteven Rostedt (Red Hat) FLAGS_FILL_END = 3 << TRACE_GRAPH_PRINT_FILL_SHIFT, 108ffeb80fcSJiri Olsa }; 109ffeb80fcSJiri Olsa 1109d9add34SSteven Rostedt (Red Hat) static void 111ffeb80fcSJiri Olsa print_graph_duration(unsigned long long duration, struct trace_seq *s, 112ffeb80fcSJiri Olsa u32 flags); 113fb52607aSFrederic Weisbecker 114712406a6SSteven Rostedt /* Add a function return address to the trace stack on thread info.*/ 115712406a6SSteven Rostedt int 11671e308a2SSteven Rostedt ftrace_push_return_trace(unsigned long ret, unsigned long func, int *depth, 11771e308a2SSteven Rostedt unsigned long frame_pointer) 118712406a6SSteven Rostedt { 1195d1a03dcSSteven Rostedt unsigned long long calltime; 120712406a6SSteven Rostedt int index; 121712406a6SSteven Rostedt 1221b2f121cSSteven Rostedt (Red Hat) if (unlikely(ftrace_graph_is_dead())) 1231b2f121cSSteven Rostedt (Red Hat) return -EBUSY; 1241b2f121cSSteven Rostedt (Red Hat) 125712406a6SSteven Rostedt if (!current->ret_stack) 126712406a6SSteven Rostedt return -EBUSY; 127712406a6SSteven Rostedt 12882310a32SSteven Rostedt /* 12982310a32SSteven Rostedt * We must make sure the ret_stack is tested before we read 13082310a32SSteven Rostedt * anything else. 13182310a32SSteven Rostedt */ 13282310a32SSteven Rostedt smp_rmb(); 13382310a32SSteven Rostedt 134712406a6SSteven Rostedt /* The return trace stack is full */ 135712406a6SSteven Rostedt if (current->curr_ret_stack == FTRACE_RETFUNC_DEPTH - 1) { 136712406a6SSteven Rostedt atomic_inc(¤t->trace_overrun); 137712406a6SSteven Rostedt return -EBUSY; 138712406a6SSteven Rostedt } 139712406a6SSteven Rostedt 14029ad23b0SNamhyung Kim /* 14129ad23b0SNamhyung Kim * The curr_ret_stack is an index to ftrace return stack of 14229ad23b0SNamhyung Kim * current task. Its value should be in [0, FTRACE_RETFUNC_ 14329ad23b0SNamhyung Kim * DEPTH) when the function graph tracer is used. To support 14429ad23b0SNamhyung Kim * filtering out specific functions, it makes the index 14529ad23b0SNamhyung Kim * negative by subtracting huge value (FTRACE_NOTRACE_DEPTH) 14629ad23b0SNamhyung Kim * so when it sees a negative index the ftrace will ignore 14729ad23b0SNamhyung Kim * the record. And the index gets recovered when returning 14829ad23b0SNamhyung Kim * from the filtered function by adding the FTRACE_NOTRACE_ 14929ad23b0SNamhyung Kim * DEPTH and then it'll continue to record functions normally. 15029ad23b0SNamhyung Kim * 15129ad23b0SNamhyung Kim * The curr_ret_stack is initialized to -1 and get increased 15229ad23b0SNamhyung Kim * in this function. So it can be less than -1 only if it was 15329ad23b0SNamhyung Kim * filtered out via ftrace_graph_notrace_addr() which can be 15429ad23b0SNamhyung Kim * set from set_graph_notrace file in debugfs by user. 15529ad23b0SNamhyung Kim */ 15629ad23b0SNamhyung Kim if (current->curr_ret_stack < -1) 15729ad23b0SNamhyung Kim return -EBUSY; 15829ad23b0SNamhyung Kim 1595d1a03dcSSteven Rostedt calltime = trace_clock_local(); 1605d1a03dcSSteven Rostedt 161712406a6SSteven Rostedt index = ++current->curr_ret_stack; 16229ad23b0SNamhyung Kim if (ftrace_graph_notrace_addr(func)) 16329ad23b0SNamhyung Kim current->curr_ret_stack -= FTRACE_NOTRACE_DEPTH; 164712406a6SSteven Rostedt barrier(); 165712406a6SSteven Rostedt current->ret_stack[index].ret = ret; 166712406a6SSteven Rostedt current->ret_stack[index].func = func; 1675d1a03dcSSteven Rostedt current->ret_stack[index].calltime = calltime; 168a2a16d6aSSteven Rostedt current->ret_stack[index].subtime = 0; 16971e308a2SSteven Rostedt current->ret_stack[index].fp = frame_pointer; 17029ad23b0SNamhyung Kim *depth = current->curr_ret_stack; 171712406a6SSteven Rostedt 172712406a6SSteven Rostedt return 0; 173712406a6SSteven Rostedt } 174712406a6SSteven Rostedt 175712406a6SSteven Rostedt /* Retrieve a function return address to the trace stack on thread info.*/ 176a2a16d6aSSteven Rostedt static void 17771e308a2SSteven Rostedt ftrace_pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret, 17871e308a2SSteven Rostedt unsigned long frame_pointer) 179712406a6SSteven Rostedt { 180712406a6SSteven Rostedt int index; 181712406a6SSteven Rostedt 182712406a6SSteven Rostedt index = current->curr_ret_stack; 183712406a6SSteven Rostedt 18429ad23b0SNamhyung Kim /* 18529ad23b0SNamhyung Kim * A negative index here means that it's just returned from a 18629ad23b0SNamhyung Kim * notrace'd function. Recover index to get an original 18729ad23b0SNamhyung Kim * return address. See ftrace_push_return_trace(). 18829ad23b0SNamhyung Kim * 18929ad23b0SNamhyung Kim * TODO: Need to check whether the stack gets corrupted. 19029ad23b0SNamhyung Kim */ 19129ad23b0SNamhyung Kim if (index < 0) 19229ad23b0SNamhyung Kim index += FTRACE_NOTRACE_DEPTH; 19329ad23b0SNamhyung Kim 19429ad23b0SNamhyung Kim if (unlikely(index < 0 || index >= FTRACE_RETFUNC_DEPTH)) { 195712406a6SSteven Rostedt ftrace_graph_stop(); 196712406a6SSteven Rostedt WARN_ON(1); 197712406a6SSteven Rostedt /* Might as well panic, otherwise we have no where to go */ 198712406a6SSteven Rostedt *ret = (unsigned long)panic; 199712406a6SSteven Rostedt return; 200712406a6SSteven Rostedt } 201712406a6SSteven Rostedt 202781d0624SSteven Rostedt #if defined(CONFIG_HAVE_FUNCTION_GRAPH_FP_TEST) && !defined(CC_USING_FENTRY) 20371e308a2SSteven Rostedt /* 20471e308a2SSteven Rostedt * The arch may choose to record the frame pointer used 20571e308a2SSteven Rostedt * and check it here to make sure that it is what we expect it 20671e308a2SSteven Rostedt * to be. If gcc does not set the place holder of the return 20771e308a2SSteven Rostedt * address in the frame pointer, and does a copy instead, then 20871e308a2SSteven Rostedt * the function graph trace will fail. This test detects this 20971e308a2SSteven Rostedt * case. 21071e308a2SSteven Rostedt * 21171e308a2SSteven Rostedt * Currently, x86_32 with optimize for size (-Os) makes the latest 21271e308a2SSteven Rostedt * gcc do the above. 213781d0624SSteven Rostedt * 214781d0624SSteven Rostedt * Note, -mfentry does not use frame pointers, and this test 215781d0624SSteven Rostedt * is not needed if CC_USING_FENTRY is set. 21671e308a2SSteven Rostedt */ 21771e308a2SSteven Rostedt if (unlikely(current->ret_stack[index].fp != frame_pointer)) { 21871e308a2SSteven Rostedt ftrace_graph_stop(); 21971e308a2SSteven Rostedt WARN(1, "Bad frame pointer: expected %lx, received %lx\n" 220b375a11aSSteven Rostedt " from func %ps return to %lx\n", 22171e308a2SSteven Rostedt current->ret_stack[index].fp, 22271e308a2SSteven Rostedt frame_pointer, 22371e308a2SSteven Rostedt (void *)current->ret_stack[index].func, 22471e308a2SSteven Rostedt current->ret_stack[index].ret); 22571e308a2SSteven Rostedt *ret = (unsigned long)panic; 22671e308a2SSteven Rostedt return; 22771e308a2SSteven Rostedt } 22871e308a2SSteven Rostedt #endif 22971e308a2SSteven Rostedt 230712406a6SSteven Rostedt *ret = current->ret_stack[index].ret; 231712406a6SSteven Rostedt trace->func = current->ret_stack[index].func; 232712406a6SSteven Rostedt trace->calltime = current->ret_stack[index].calltime; 233712406a6SSteven Rostedt trace->overrun = atomic_read(¤t->trace_overrun); 234712406a6SSteven Rostedt trace->depth = index; 235712406a6SSteven Rostedt } 236712406a6SSteven Rostedt 237712406a6SSteven Rostedt /* 238712406a6SSteven Rostedt * Send the trace to the ring-buffer. 239712406a6SSteven Rostedt * @return the original return address. 240712406a6SSteven Rostedt */ 24171e308a2SSteven Rostedt unsigned long ftrace_return_to_handler(unsigned long frame_pointer) 242712406a6SSteven Rostedt { 243712406a6SSteven Rostedt struct ftrace_graph_ret trace; 244712406a6SSteven Rostedt unsigned long ret; 245712406a6SSteven Rostedt 24671e308a2SSteven Rostedt ftrace_pop_return_trace(&trace, &ret, frame_pointer); 2470012693aSFrederic Weisbecker trace.rettime = trace_clock_local(); 248a2a16d6aSSteven Rostedt barrier(); 249a2a16d6aSSteven Rostedt current->curr_ret_stack--; 25029ad23b0SNamhyung Kim /* 25129ad23b0SNamhyung Kim * The curr_ret_stack can be less than -1 only if it was 25229ad23b0SNamhyung Kim * filtered out and it's about to return from the function. 25329ad23b0SNamhyung Kim * Recover the index and continue to trace normal functions. 25429ad23b0SNamhyung Kim */ 25529ad23b0SNamhyung Kim if (current->curr_ret_stack < -1) { 25629ad23b0SNamhyung Kim current->curr_ret_stack += FTRACE_NOTRACE_DEPTH; 25729ad23b0SNamhyung Kim return ret; 25829ad23b0SNamhyung Kim } 259712406a6SSteven Rostedt 26003274a3fSSteven Rostedt (Red Hat) /* 26103274a3fSSteven Rostedt (Red Hat) * The trace should run after decrementing the ret counter 26203274a3fSSteven Rostedt (Red Hat) * in case an interrupt were to come in. We don't want to 26303274a3fSSteven Rostedt (Red Hat) * lose the interrupt if max_depth is set. 26403274a3fSSteven Rostedt (Red Hat) */ 26503274a3fSSteven Rostedt (Red Hat) ftrace_graph_return(&trace); 26603274a3fSSteven Rostedt (Red Hat) 267712406a6SSteven Rostedt if (unlikely(!ret)) { 268712406a6SSteven Rostedt ftrace_graph_stop(); 269712406a6SSteven Rostedt WARN_ON(1); 270712406a6SSteven Rostedt /* Might as well panic. What else to do? */ 271712406a6SSteven Rostedt ret = (unsigned long)panic; 272712406a6SSteven Rostedt } 273712406a6SSteven Rostedt 274712406a6SSteven Rostedt return ret; 275712406a6SSteven Rostedt } 276712406a6SSteven Rostedt 27762b915f1SJiri Olsa int __trace_graph_entry(struct trace_array *tr, 2781a0799a8SFrederic Weisbecker struct ftrace_graph_ent *trace, 2791a0799a8SFrederic Weisbecker unsigned long flags, 2801a0799a8SFrederic Weisbecker int pc) 2811a0799a8SFrederic Weisbecker { 2821a0799a8SFrederic Weisbecker struct ftrace_event_call *call = &event_funcgraph_entry; 2831a0799a8SFrederic Weisbecker struct ring_buffer_event *event; 28412883efbSSteven Rostedt (Red Hat) struct ring_buffer *buffer = tr->trace_buffer.buffer; 2851a0799a8SFrederic Weisbecker struct ftrace_graph_ent_entry *entry; 2861a0799a8SFrederic Weisbecker 287dd17c8f7SRusty Russell if (unlikely(__this_cpu_read(ftrace_cpu_disabled))) 2881a0799a8SFrederic Weisbecker return 0; 2891a0799a8SFrederic Weisbecker 290e77405adSSteven Rostedt event = trace_buffer_lock_reserve(buffer, TRACE_GRAPH_ENT, 2911a0799a8SFrederic Weisbecker sizeof(*entry), flags, pc); 2921a0799a8SFrederic Weisbecker if (!event) 2931a0799a8SFrederic Weisbecker return 0; 2941a0799a8SFrederic Weisbecker entry = ring_buffer_event_data(event); 2951a0799a8SFrederic Weisbecker entry->graph_ent = *trace; 296f306cc82STom Zanussi if (!call_filter_check_discard(call, entry, buffer, event)) 2977ffbd48dSSteven Rostedt __buffer_unlock_commit(buffer, event); 2981a0799a8SFrederic Weisbecker 2991a0799a8SFrederic Weisbecker return 1; 3001a0799a8SFrederic Weisbecker } 3011a0799a8SFrederic Weisbecker 302b304d044SSteven Rostedt static inline int ftrace_graph_ignore_irqs(void) 303b304d044SSteven Rostedt { 304e4a3f541SSteven Rostedt if (!ftrace_graph_skip_irqs || trace_recursion_test(TRACE_IRQ_BIT)) 305b304d044SSteven Rostedt return 0; 306b304d044SSteven Rostedt 307b304d044SSteven Rostedt return in_irq(); 308b304d044SSteven Rostedt } 309b304d044SSteven Rostedt 3101a0799a8SFrederic Weisbecker int trace_graph_entry(struct ftrace_graph_ent *trace) 3111a0799a8SFrederic Weisbecker { 3121a0799a8SFrederic Weisbecker struct trace_array *tr = graph_array; 3131a0799a8SFrederic Weisbecker struct trace_array_cpu *data; 3141a0799a8SFrederic Weisbecker unsigned long flags; 3151a0799a8SFrederic Weisbecker long disabled; 3161a0799a8SFrederic Weisbecker int ret; 3171a0799a8SFrederic Weisbecker int cpu; 3181a0799a8SFrederic Weisbecker int pc; 3191a0799a8SFrederic Weisbecker 3201a0799a8SFrederic Weisbecker if (!ftrace_trace_task(current)) 3211a0799a8SFrederic Weisbecker return 0; 3221a0799a8SFrederic Weisbecker 323ea2c68a0SLai Jiangshan /* trace it when it is-nested-in or is a function enabled. */ 3248741db53SSteven Rostedt if ((!(trace->depth || ftrace_graph_addr(trace->func)) || 32529ad23b0SNamhyung Kim ftrace_graph_ignore_irqs()) || (trace->depth < 0) || 3268741db53SSteven Rostedt (max_depth && trace->depth >= max_depth)) 3271a0799a8SFrederic Weisbecker return 0; 3281a0799a8SFrederic Weisbecker 32929ad23b0SNamhyung Kim /* 33029ad23b0SNamhyung Kim * Do not trace a function if it's filtered by set_graph_notrace. 33129ad23b0SNamhyung Kim * Make the index of ret stack negative to indicate that it should 33229ad23b0SNamhyung Kim * ignore further functions. But it needs its own ret stack entry 33329ad23b0SNamhyung Kim * to recover the original index in order to continue tracing after 33429ad23b0SNamhyung Kim * returning from the function. 33529ad23b0SNamhyung Kim */ 33629ad23b0SNamhyung Kim if (ftrace_graph_notrace_addr(trace->func)) 33729ad23b0SNamhyung Kim return 1; 33829ad23b0SNamhyung Kim 3391a0799a8SFrederic Weisbecker local_irq_save(flags); 3401a0799a8SFrederic Weisbecker cpu = raw_smp_processor_id(); 34112883efbSSteven Rostedt (Red Hat) data = per_cpu_ptr(tr->trace_buffer.data, cpu); 3421a0799a8SFrederic Weisbecker disabled = atomic_inc_return(&data->disabled); 3431a0799a8SFrederic Weisbecker if (likely(disabled == 1)) { 3441a0799a8SFrederic Weisbecker pc = preempt_count(); 3451a0799a8SFrederic Weisbecker ret = __trace_graph_entry(tr, trace, flags, pc); 3461a0799a8SFrederic Weisbecker } else { 3471a0799a8SFrederic Weisbecker ret = 0; 3481a0799a8SFrederic Weisbecker } 3491a0799a8SFrederic Weisbecker 3501a0799a8SFrederic Weisbecker atomic_dec(&data->disabled); 3511a0799a8SFrederic Weisbecker local_irq_restore(flags); 3521a0799a8SFrederic Weisbecker 3531a0799a8SFrederic Weisbecker return ret; 3541a0799a8SFrederic Weisbecker } 3551a0799a8SFrederic Weisbecker 356ba1afef6SSteven Rostedt (Red Hat) static int trace_graph_thresh_entry(struct ftrace_graph_ent *trace) 3570e950173STim Bird { 3580e950173STim Bird if (tracing_thresh) 3590e950173STim Bird return 1; 3600e950173STim Bird else 3610e950173STim Bird return trace_graph_entry(trace); 3620e950173STim Bird } 3630e950173STim Bird 3640a772620SJiri Olsa static void 3650a772620SJiri Olsa __trace_graph_function(struct trace_array *tr, 3660a772620SJiri Olsa unsigned long ip, unsigned long flags, int pc) 3670a772620SJiri Olsa { 3680a772620SJiri Olsa u64 time = trace_clock_local(); 3690a772620SJiri Olsa struct ftrace_graph_ent ent = { 3700a772620SJiri Olsa .func = ip, 3710a772620SJiri Olsa .depth = 0, 3720a772620SJiri Olsa }; 3730a772620SJiri Olsa struct ftrace_graph_ret ret = { 3740a772620SJiri Olsa .func = ip, 3750a772620SJiri Olsa .depth = 0, 3760a772620SJiri Olsa .calltime = time, 3770a772620SJiri Olsa .rettime = time, 3780a772620SJiri Olsa }; 3790a772620SJiri Olsa 3800a772620SJiri Olsa __trace_graph_entry(tr, &ent, flags, pc); 3810a772620SJiri Olsa __trace_graph_return(tr, &ret, flags, pc); 3820a772620SJiri Olsa } 3830a772620SJiri Olsa 3840a772620SJiri Olsa void 3850a772620SJiri Olsa trace_graph_function(struct trace_array *tr, 3860a772620SJiri Olsa unsigned long ip, unsigned long parent_ip, 3870a772620SJiri Olsa unsigned long flags, int pc) 3880a772620SJiri Olsa { 3890a772620SJiri Olsa __trace_graph_function(tr, ip, flags, pc); 3900a772620SJiri Olsa } 3910a772620SJiri Olsa 39262b915f1SJiri Olsa void __trace_graph_return(struct trace_array *tr, 3931a0799a8SFrederic Weisbecker struct ftrace_graph_ret *trace, 3941a0799a8SFrederic Weisbecker unsigned long flags, 3951a0799a8SFrederic Weisbecker int pc) 3961a0799a8SFrederic Weisbecker { 3971a0799a8SFrederic Weisbecker struct ftrace_event_call *call = &event_funcgraph_exit; 3981a0799a8SFrederic Weisbecker struct ring_buffer_event *event; 39912883efbSSteven Rostedt (Red Hat) struct ring_buffer *buffer = tr->trace_buffer.buffer; 4001a0799a8SFrederic Weisbecker struct ftrace_graph_ret_entry *entry; 4011a0799a8SFrederic Weisbecker 402dd17c8f7SRusty Russell if (unlikely(__this_cpu_read(ftrace_cpu_disabled))) 4031a0799a8SFrederic Weisbecker return; 4041a0799a8SFrederic Weisbecker 405e77405adSSteven Rostedt event = trace_buffer_lock_reserve(buffer, TRACE_GRAPH_RET, 4061a0799a8SFrederic Weisbecker sizeof(*entry), flags, pc); 4071a0799a8SFrederic Weisbecker if (!event) 4081a0799a8SFrederic Weisbecker return; 4091a0799a8SFrederic Weisbecker entry = ring_buffer_event_data(event); 4101a0799a8SFrederic Weisbecker entry->ret = *trace; 411f306cc82STom Zanussi if (!call_filter_check_discard(call, entry, buffer, event)) 4127ffbd48dSSteven Rostedt __buffer_unlock_commit(buffer, event); 4131a0799a8SFrederic Weisbecker } 4141a0799a8SFrederic Weisbecker 4151a0799a8SFrederic Weisbecker void trace_graph_return(struct ftrace_graph_ret *trace) 4161a0799a8SFrederic Weisbecker { 4171a0799a8SFrederic Weisbecker struct trace_array *tr = graph_array; 4181a0799a8SFrederic Weisbecker struct trace_array_cpu *data; 4191a0799a8SFrederic Weisbecker unsigned long flags; 4201a0799a8SFrederic Weisbecker long disabled; 4211a0799a8SFrederic Weisbecker int cpu; 4221a0799a8SFrederic Weisbecker int pc; 4231a0799a8SFrederic Weisbecker 4241a0799a8SFrederic Weisbecker local_irq_save(flags); 4251a0799a8SFrederic Weisbecker cpu = raw_smp_processor_id(); 42612883efbSSteven Rostedt (Red Hat) data = per_cpu_ptr(tr->trace_buffer.data, cpu); 4271a0799a8SFrederic Weisbecker disabled = atomic_inc_return(&data->disabled); 4281a0799a8SFrederic Weisbecker if (likely(disabled == 1)) { 4291a0799a8SFrederic Weisbecker pc = preempt_count(); 4301a0799a8SFrederic Weisbecker __trace_graph_return(tr, trace, flags, pc); 4311a0799a8SFrederic Weisbecker } 4321a0799a8SFrederic Weisbecker atomic_dec(&data->disabled); 4331a0799a8SFrederic Weisbecker local_irq_restore(flags); 4341a0799a8SFrederic Weisbecker } 4351a0799a8SFrederic Weisbecker 43624a53652SFrederic Weisbecker void set_graph_array(struct trace_array *tr) 43724a53652SFrederic Weisbecker { 43824a53652SFrederic Weisbecker graph_array = tr; 43924a53652SFrederic Weisbecker 44024a53652SFrederic Weisbecker /* Make graph_array visible before we start tracing */ 44124a53652SFrederic Weisbecker 44224a53652SFrederic Weisbecker smp_mb(); 44324a53652SFrederic Weisbecker } 44424a53652SFrederic Weisbecker 445ba1afef6SSteven Rostedt (Red Hat) static void trace_graph_thresh_return(struct ftrace_graph_ret *trace) 4460e950173STim Bird { 4470e950173STim Bird if (tracing_thresh && 4480e950173STim Bird (trace->rettime - trace->calltime < tracing_thresh)) 4490e950173STim Bird return; 4500e950173STim Bird else 4510e950173STim Bird trace_graph_return(trace); 4520e950173STim Bird } 4530e950173STim Bird 454fb52607aSFrederic Weisbecker static int graph_trace_init(struct trace_array *tr) 455fb52607aSFrederic Weisbecker { 4561a0799a8SFrederic Weisbecker int ret; 4571a0799a8SFrederic Weisbecker 45824a53652SFrederic Weisbecker set_graph_array(tr); 4590e950173STim Bird if (tracing_thresh) 4600e950173STim Bird ret = register_ftrace_graph(&trace_graph_thresh_return, 4610e950173STim Bird &trace_graph_thresh_entry); 4620e950173STim Bird else 4631a0799a8SFrederic Weisbecker ret = register_ftrace_graph(&trace_graph_return, 464287b6e68SFrederic Weisbecker &trace_graph_entry); 465660c7f9bSSteven Rostedt if (ret) 466660c7f9bSSteven Rostedt return ret; 467660c7f9bSSteven Rostedt tracing_start_cmdline_record(); 468660c7f9bSSteven Rostedt 469660c7f9bSSteven Rostedt return 0; 470fb52607aSFrederic Weisbecker } 471fb52607aSFrederic Weisbecker 472fb52607aSFrederic Weisbecker static void graph_trace_reset(struct trace_array *tr) 473fb52607aSFrederic Weisbecker { 474660c7f9bSSteven Rostedt tracing_stop_cmdline_record(); 475fb52607aSFrederic Weisbecker unregister_ftrace_graph(); 476fb52607aSFrederic Weisbecker } 477fb52607aSFrederic Weisbecker 478ba1afef6SSteven Rostedt (Red Hat) static int graph_trace_update_thresh(struct trace_array *tr) 4796508fa76SStanislav Fomichev { 4806508fa76SStanislav Fomichev graph_trace_reset(tr); 4816508fa76SStanislav Fomichev return graph_trace_init(tr); 4826508fa76SStanislav Fomichev } 4836508fa76SStanislav Fomichev 4840c9e6f63SLai Jiangshan static int max_bytes_for_cpu; 4851a056155SFrederic Weisbecker 4869d9add34SSteven Rostedt (Red Hat) static void print_graph_cpu(struct trace_seq *s, int cpu) 4871a056155SFrederic Weisbecker { 488d51090b3SIngo Molnar /* 489d51090b3SIngo Molnar * Start with a space character - to make it stand out 490d51090b3SIngo Molnar * to the right a bit when trace output is pasted into 491d51090b3SIngo Molnar * email: 492d51090b3SIngo Molnar */ 4939d9add34SSteven Rostedt (Red Hat) trace_seq_printf(s, " %*d) ", max_bytes_for_cpu, cpu); 4941a056155SFrederic Weisbecker } 4951a056155SFrederic Weisbecker 49611e84accSFrederic Weisbecker #define TRACE_GRAPH_PROCINFO_LENGTH 14 49711e84accSFrederic Weisbecker 4989d9add34SSteven Rostedt (Red Hat) static void print_graph_proc(struct trace_seq *s, pid_t pid) 49911e84accSFrederic Weisbecker { 5004ca53085SSteven Rostedt char comm[TASK_COMM_LEN]; 50111e84accSFrederic Weisbecker /* sign + log10(MAX_INT) + '\0' */ 50211e84accSFrederic Weisbecker char pid_str[11]; 5034ca53085SSteven Rostedt int spaces = 0; 5044ca53085SSteven Rostedt int len; 5054ca53085SSteven Rostedt int i; 50611e84accSFrederic Weisbecker 5074ca53085SSteven Rostedt trace_find_cmdline(pid, comm); 50811e84accSFrederic Weisbecker comm[7] = '\0'; 50911e84accSFrederic Weisbecker sprintf(pid_str, "%d", pid); 51011e84accSFrederic Weisbecker 51111e84accSFrederic Weisbecker /* 1 stands for the "-" character */ 51211e84accSFrederic Weisbecker len = strlen(comm) + strlen(pid_str) + 1; 51311e84accSFrederic Weisbecker 51411e84accSFrederic Weisbecker if (len < TRACE_GRAPH_PROCINFO_LENGTH) 51511e84accSFrederic Weisbecker spaces = TRACE_GRAPH_PROCINFO_LENGTH - len; 51611e84accSFrederic Weisbecker 51711e84accSFrederic Weisbecker /* First spaces to align center */ 5189d9add34SSteven Rostedt (Red Hat) for (i = 0; i < spaces / 2; i++) 5199d9add34SSteven Rostedt (Red Hat) trace_seq_putc(s, ' '); 52011e84accSFrederic Weisbecker 5219d9add34SSteven Rostedt (Red Hat) trace_seq_printf(s, "%s-%s", comm, pid_str); 52211e84accSFrederic Weisbecker 52311e84accSFrederic Weisbecker /* Last spaces to align center */ 5249d9add34SSteven Rostedt (Red Hat) for (i = 0; i < spaces - (spaces / 2); i++) 5259d9add34SSteven Rostedt (Red Hat) trace_seq_putc(s, ' '); 52611e84accSFrederic Weisbecker } 52711e84accSFrederic Weisbecker 5281a056155SFrederic Weisbecker 5299d9add34SSteven Rostedt (Red Hat) static void print_graph_lat_fmt(struct trace_seq *s, struct trace_entry *entry) 53049ff5903SSteven Rostedt { 5319d9add34SSteven Rostedt (Red Hat) trace_seq_putc(s, ' '); 5329d9add34SSteven Rostedt (Red Hat) trace_print_lat_fmt(s, entry); 53349ff5903SSteven Rostedt } 53449ff5903SSteven Rostedt 535287b6e68SFrederic Weisbecker /* If the pid changed since the last trace, output this event */ 5369d9add34SSteven Rostedt (Red Hat) static void 5372fbcdb35SSteven Rostedt verif_pid(struct trace_seq *s, pid_t pid, int cpu, struct fgraph_data *data) 538287b6e68SFrederic Weisbecker { 539d51090b3SIngo Molnar pid_t prev_pid; 5409005f3ebSFrederic Weisbecker pid_t *last_pid; 541660c7f9bSSteven Rostedt 5422fbcdb35SSteven Rostedt if (!data) 5439d9add34SSteven Rostedt (Red Hat) return; 544287b6e68SFrederic Weisbecker 545be1eca39SJiri Olsa last_pid = &(per_cpu_ptr(data->cpu_data, cpu)->last_pid); 546660c7f9bSSteven Rostedt 5479005f3ebSFrederic Weisbecker if (*last_pid == pid) 5489d9add34SSteven Rostedt (Red Hat) return; 5499005f3ebSFrederic Weisbecker 5509005f3ebSFrederic Weisbecker prev_pid = *last_pid; 5519005f3ebSFrederic Weisbecker *last_pid = pid; 5529005f3ebSFrederic Weisbecker 5539005f3ebSFrederic Weisbecker if (prev_pid == -1) 5549d9add34SSteven Rostedt (Red Hat) return; 555d51090b3SIngo Molnar /* 556d51090b3SIngo Molnar * Context-switch trace line: 557d51090b3SIngo Molnar 558d51090b3SIngo Molnar ------------------------------------------ 559d51090b3SIngo Molnar | 1) migration/0--1 => sshd-1755 560d51090b3SIngo Molnar ------------------------------------------ 561d51090b3SIngo Molnar 562d51090b3SIngo Molnar */ 5639d9add34SSteven Rostedt (Red Hat) trace_seq_puts(s, " ------------------------------------------\n"); 5649d9add34SSteven Rostedt (Red Hat) print_graph_cpu(s, cpu); 5659d9add34SSteven Rostedt (Red Hat) print_graph_proc(s, prev_pid); 5669d9add34SSteven Rostedt (Red Hat) trace_seq_puts(s, " => "); 5679d9add34SSteven Rostedt (Red Hat) print_graph_proc(s, pid); 5689d9add34SSteven Rostedt (Red Hat) trace_seq_puts(s, "\n ------------------------------------------\n\n"); 569287b6e68SFrederic Weisbecker } 570287b6e68SFrederic Weisbecker 571b91facc3SFrederic Weisbecker static struct ftrace_graph_ret_entry * 572b91facc3SFrederic Weisbecker get_return_for_leaf(struct trace_iterator *iter, 57383a8df61SFrederic Weisbecker struct ftrace_graph_ent_entry *curr) 574287b6e68SFrederic Weisbecker { 575be1eca39SJiri Olsa struct fgraph_data *data = iter->private; 576be1eca39SJiri Olsa struct ring_buffer_iter *ring_iter = NULL; 57783a8df61SFrederic Weisbecker struct ring_buffer_event *event; 57883a8df61SFrederic Weisbecker struct ftrace_graph_ret_entry *next; 57983a8df61SFrederic Weisbecker 580be1eca39SJiri Olsa /* 581be1eca39SJiri Olsa * If the previous output failed to write to the seq buffer, 582be1eca39SJiri Olsa * then we just reuse the data from before. 583be1eca39SJiri Olsa */ 584be1eca39SJiri Olsa if (data && data->failed) { 585be1eca39SJiri Olsa curr = &data->ent; 586be1eca39SJiri Olsa next = &data->ret; 587be1eca39SJiri Olsa } else { 588be1eca39SJiri Olsa 5896d158a81SSteven Rostedt ring_iter = trace_buffer_iter(iter, iter->cpu); 59083a8df61SFrederic Weisbecker 591b91facc3SFrederic Weisbecker /* First peek to compare current entry and the next one */ 592b91facc3SFrederic Weisbecker if (ring_iter) 5931a056155SFrederic Weisbecker event = ring_buffer_iter_peek(ring_iter, NULL); 594b91facc3SFrederic Weisbecker else { 595be1eca39SJiri Olsa /* 596be1eca39SJiri Olsa * We need to consume the current entry to see 597be1eca39SJiri Olsa * the next one. 598be1eca39SJiri Olsa */ 59912883efbSSteven Rostedt (Red Hat) ring_buffer_consume(iter->trace_buffer->buffer, iter->cpu, 60066a8cb95SSteven Rostedt NULL, NULL); 60112883efbSSteven Rostedt (Red Hat) event = ring_buffer_peek(iter->trace_buffer->buffer, iter->cpu, 60266a8cb95SSteven Rostedt NULL, NULL); 603b91facc3SFrederic Weisbecker } 60483a8df61SFrederic Weisbecker 60583a8df61SFrederic Weisbecker if (!event) 606b91facc3SFrederic Weisbecker return NULL; 60783a8df61SFrederic Weisbecker 60883a8df61SFrederic Weisbecker next = ring_buffer_event_data(event); 60983a8df61SFrederic Weisbecker 610be1eca39SJiri Olsa if (data) { 611be1eca39SJiri Olsa /* 612be1eca39SJiri Olsa * Save current and next entries for later reference 613be1eca39SJiri Olsa * if the output fails. 614be1eca39SJiri Olsa */ 615be1eca39SJiri Olsa data->ent = *curr; 616575570f0SShaohua Li /* 617575570f0SShaohua Li * If the next event is not a return type, then 618575570f0SShaohua Li * we only care about what type it is. Otherwise we can 619575570f0SShaohua Li * safely copy the entire event. 620575570f0SShaohua Li */ 621575570f0SShaohua Li if (next->ent.type == TRACE_GRAPH_RET) 622be1eca39SJiri Olsa data->ret = *next; 623575570f0SShaohua Li else 624575570f0SShaohua Li data->ret.ent.type = next->ent.type; 625be1eca39SJiri Olsa } 626be1eca39SJiri Olsa } 627be1eca39SJiri Olsa 62883a8df61SFrederic Weisbecker if (next->ent.type != TRACE_GRAPH_RET) 629b91facc3SFrederic Weisbecker return NULL; 63083a8df61SFrederic Weisbecker 63183a8df61SFrederic Weisbecker if (curr->ent.pid != next->ent.pid || 63283a8df61SFrederic Weisbecker curr->graph_ent.func != next->ret.func) 633b91facc3SFrederic Weisbecker return NULL; 63483a8df61SFrederic Weisbecker 635b91facc3SFrederic Weisbecker /* this is a leaf, now advance the iterator */ 636b91facc3SFrederic Weisbecker if (ring_iter) 637b91facc3SFrederic Weisbecker ring_buffer_read(ring_iter, NULL); 638b91facc3SFrederic Weisbecker 639b91facc3SFrederic Weisbecker return next; 64083a8df61SFrederic Weisbecker } 64183a8df61SFrederic Weisbecker 6429d9add34SSteven Rostedt (Red Hat) static void print_graph_abs_time(u64 t, struct trace_seq *s) 643d1f9cbd7SFrederic Weisbecker { 644d1f9cbd7SFrederic Weisbecker unsigned long usecs_rem; 645d1f9cbd7SFrederic Weisbecker 646d1f9cbd7SFrederic Weisbecker usecs_rem = do_div(t, NSEC_PER_SEC); 647d1f9cbd7SFrederic Weisbecker usecs_rem /= 1000; 648d1f9cbd7SFrederic Weisbecker 6499d9add34SSteven Rostedt (Red Hat) trace_seq_printf(s, "%5lu.%06lu | ", 650d1f9cbd7SFrederic Weisbecker (unsigned long)t, usecs_rem); 651d1f9cbd7SFrederic Weisbecker } 652d1f9cbd7SFrederic Weisbecker 6539d9add34SSteven Rostedt (Red Hat) static void 654d1f9cbd7SFrederic Weisbecker print_graph_irq(struct trace_iterator *iter, unsigned long addr, 655d7a8d9e9SJiri Olsa enum trace_type type, int cpu, pid_t pid, u32 flags) 656f8b755acSFrederic Weisbecker { 657d1f9cbd7SFrederic Weisbecker struct trace_seq *s = &iter->seq; 658678f845eSDaniel Bristot de Oliveira struct trace_entry *ent = iter->ent; 659f8b755acSFrederic Weisbecker 660f8b755acSFrederic Weisbecker if (addr < (unsigned long)__irqentry_text_start || 661f8b755acSFrederic Weisbecker addr >= (unsigned long)__irqentry_text_end) 6629d9add34SSteven Rostedt (Red Hat) return; 663f8b755acSFrederic Weisbecker 664749230b0SJiri Olsa if (trace_flags & TRACE_ITER_CONTEXT_INFO) { 665d1f9cbd7SFrederic Weisbecker /* Absolute time */ 6669d9add34SSteven Rostedt (Red Hat) if (flags & TRACE_GRAPH_PRINT_ABS_TIME) 6679d9add34SSteven Rostedt (Red Hat) print_graph_abs_time(iter->ts, s); 668d1f9cbd7SFrederic Weisbecker 669f8b755acSFrederic Weisbecker /* Cpu */ 6709d9add34SSteven Rostedt (Red Hat) if (flags & TRACE_GRAPH_PRINT_CPU) 6719d9add34SSteven Rostedt (Red Hat) print_graph_cpu(s, cpu); 67249ff5903SSteven Rostedt 673f8b755acSFrederic Weisbecker /* Proc */ 674d7a8d9e9SJiri Olsa if (flags & TRACE_GRAPH_PRINT_PROC) { 6759d9add34SSteven Rostedt (Red Hat) print_graph_proc(s, pid); 6769d9add34SSteven Rostedt (Red Hat) trace_seq_puts(s, " | "); 677f8b755acSFrederic Weisbecker } 678678f845eSDaniel Bristot de Oliveira 679678f845eSDaniel Bristot de Oliveira /* Latency format */ 6809d9add34SSteven Rostedt (Red Hat) if (trace_flags & TRACE_ITER_LATENCY_FMT) 6819d9add34SSteven Rostedt (Red Hat) print_graph_lat_fmt(s, ent); 682749230b0SJiri Olsa } 683f8b755acSFrederic Weisbecker 684f8b755acSFrederic Weisbecker /* No overhead */ 6859d9add34SSteven Rostedt (Red Hat) print_graph_duration(0, s, flags | FLAGS_FILL_START); 686f8b755acSFrederic Weisbecker 6879005f3ebSFrederic Weisbecker if (type == TRACE_GRAPH_ENT) 6889d9add34SSteven Rostedt (Red Hat) trace_seq_puts(s, "==========>"); 6899005f3ebSFrederic Weisbecker else 6909d9add34SSteven Rostedt (Red Hat) trace_seq_puts(s, "<=========="); 6919005f3ebSFrederic Weisbecker 6929d9add34SSteven Rostedt (Red Hat) print_graph_duration(0, s, flags | FLAGS_FILL_END); 6939d9add34SSteven Rostedt (Red Hat) trace_seq_putc(s, '\n'); 694f8b755acSFrederic Weisbecker } 69583a8df61SFrederic Weisbecker 6969d9add34SSteven Rostedt (Red Hat) void 6970706f1c4SSteven Rostedt trace_print_graph_duration(unsigned long long duration, struct trace_seq *s) 69883a8df61SFrederic Weisbecker { 69983a8df61SFrederic Weisbecker unsigned long nsecs_rem = do_div(duration, 1000); 700166d3c79SFrederic Weisbecker /* log10(ULONG_MAX) + '\0' */ 7014526d067SByungchul Park char usecs_str[21]; 702166d3c79SFrederic Weisbecker char nsecs_str[5]; 7039d9add34SSteven Rostedt (Red Hat) int len; 704166d3c79SFrederic Weisbecker int i; 705166d3c79SFrederic Weisbecker 7064526d067SByungchul Park sprintf(usecs_str, "%lu", (unsigned long) duration); 707166d3c79SFrederic Weisbecker 708166d3c79SFrederic Weisbecker /* Print msecs */ 7099d9add34SSteven Rostedt (Red Hat) trace_seq_printf(s, "%s", usecs_str); 710166d3c79SFrederic Weisbecker 7114526d067SByungchul Park len = strlen(usecs_str); 712166d3c79SFrederic Weisbecker 713166d3c79SFrederic Weisbecker /* Print nsecs (we don't want to exceed 7 numbers) */ 714166d3c79SFrederic Weisbecker if (len < 7) { 71514cae9bdSBorislav Petkov size_t slen = min_t(size_t, sizeof(nsecs_str), 8UL - len); 71614cae9bdSBorislav Petkov 71714cae9bdSBorislav Petkov snprintf(nsecs_str, slen, "%03lu", nsecs_rem); 7189d9add34SSteven Rostedt (Red Hat) trace_seq_printf(s, ".%s", nsecs_str); 719166d3c79SFrederic Weisbecker len += strlen(nsecs_str); 720166d3c79SFrederic Weisbecker } 721166d3c79SFrederic Weisbecker 7229d9add34SSteven Rostedt (Red Hat) trace_seq_puts(s, " us "); 723166d3c79SFrederic Weisbecker 724166d3c79SFrederic Weisbecker /* Print remaining spaces to fit the row's width */ 7259d9add34SSteven Rostedt (Red Hat) for (i = len; i < 7; i++) 7269d9add34SSteven Rostedt (Red Hat) trace_seq_putc(s, ' '); 7270706f1c4SSteven Rostedt } 7280706f1c4SSteven Rostedt 7299d9add34SSteven Rostedt (Red Hat) static void 730ffeb80fcSJiri Olsa print_graph_duration(unsigned long long duration, struct trace_seq *s, 731ffeb80fcSJiri Olsa u32 flags) 7320706f1c4SSteven Rostedt { 7339d9add34SSteven Rostedt (Red Hat) bool duration_printed = false; 734ffeb80fcSJiri Olsa 735749230b0SJiri Olsa if (!(flags & TRACE_GRAPH_PRINT_DURATION) || 736749230b0SJiri Olsa !(trace_flags & TRACE_ITER_CONTEXT_INFO)) 7379d9add34SSteven Rostedt (Red Hat) return; 738ffeb80fcSJiri Olsa 739ffeb80fcSJiri Olsa /* No real adata, just filling the column with spaces */ 7406fc84ea7SSteven Rostedt (Red Hat) switch (flags & TRACE_GRAPH_PRINT_FILL_MASK) { 7416fc84ea7SSteven Rostedt (Red Hat) case FLAGS_FILL_FULL: 7429d9add34SSteven Rostedt (Red Hat) trace_seq_puts(s, " | "); 7439d9add34SSteven Rostedt (Red Hat) return; 7446fc84ea7SSteven Rostedt (Red Hat) case FLAGS_FILL_START: 7459d9add34SSteven Rostedt (Red Hat) trace_seq_puts(s, " "); 7469d9add34SSteven Rostedt (Red Hat) return; 7476fc84ea7SSteven Rostedt (Red Hat) case FLAGS_FILL_END: 7489d9add34SSteven Rostedt (Red Hat) trace_seq_puts(s, " |"); 7499d9add34SSteven Rostedt (Red Hat) return; 750ffeb80fcSJiri Olsa } 751ffeb80fcSJiri Olsa 752ffeb80fcSJiri Olsa /* Signal a overhead of time execution to the output */ 753ffeb80fcSJiri Olsa if (flags & TRACE_GRAPH_PRINT_OVERHEAD) { 7544526d067SByungchul Park /* Duration exceeded 100 usecs */ 7559d9add34SSteven Rostedt (Red Hat) if (duration > 100000ULL) { 7569d9add34SSteven Rostedt (Red Hat) trace_seq_puts(s, "! "); 7579d9add34SSteven Rostedt (Red Hat) duration_printed = true; 7589d9add34SSteven Rostedt (Red Hat) 7594526d067SByungchul Park /* Duration exceeded 10 usecs */ 7609d9add34SSteven Rostedt (Red Hat) } else if (duration > 10000ULL) { 7619d9add34SSteven Rostedt (Red Hat) trace_seq_puts(s, "+ "); 7629d9add34SSteven Rostedt (Red Hat) duration_printed = true; 7639d9add34SSteven Rostedt (Red Hat) } 764ffeb80fcSJiri Olsa } 765ffeb80fcSJiri Olsa 766ffeb80fcSJiri Olsa /* 7679d9add34SSteven Rostedt (Red Hat) * If we did not exceed the duration tresholds or we dont want 7689d9add34SSteven Rostedt (Red Hat) * to print out the overhead. Either way we need to fill out the space. 769ffeb80fcSJiri Olsa */ 7709d9add34SSteven Rostedt (Red Hat) if (!duration_printed) 7719d9add34SSteven Rostedt (Red Hat) trace_seq_puts(s, " "); 772ffeb80fcSJiri Olsa 7739d9add34SSteven Rostedt (Red Hat) trace_print_graph_duration(duration, s); 7749d9add34SSteven Rostedt (Red Hat) trace_seq_puts(s, "| "); 77583a8df61SFrederic Weisbecker } 77683a8df61SFrederic Weisbecker 77783a8df61SFrederic Weisbecker /* Case of a leaf function on its call entry */ 77883a8df61SFrederic Weisbecker static enum print_line_t 77983a8df61SFrederic Weisbecker print_graph_entry_leaf(struct trace_iterator *iter, 780b91facc3SFrederic Weisbecker struct ftrace_graph_ent_entry *entry, 781d7a8d9e9SJiri Olsa struct ftrace_graph_ret_entry *ret_entry, 782d7a8d9e9SJiri Olsa struct trace_seq *s, u32 flags) 78383a8df61SFrederic Weisbecker { 7842fbcdb35SSteven Rostedt struct fgraph_data *data = iter->private; 78583a8df61SFrederic Weisbecker struct ftrace_graph_ret *graph_ret; 78683a8df61SFrederic Weisbecker struct ftrace_graph_ent *call; 78783a8df61SFrederic Weisbecker unsigned long long duration; 7881a056155SFrederic Weisbecker int i; 789287b6e68SFrederic Weisbecker 79083a8df61SFrederic Weisbecker graph_ret = &ret_entry->ret; 79183a8df61SFrederic Weisbecker call = &entry->graph_ent; 79283a8df61SFrederic Weisbecker duration = graph_ret->rettime - graph_ret->calltime; 793437f24fbSSteven Rostedt 7942fbcdb35SSteven Rostedt if (data) { 795f1c7f517SSteven Rostedt struct fgraph_cpu_data *cpu_data; 7962fbcdb35SSteven Rostedt int cpu = iter->cpu; 797f1c7f517SSteven Rostedt 798f1c7f517SSteven Rostedt cpu_data = per_cpu_ptr(data->cpu_data, cpu); 7992fbcdb35SSteven Rostedt 8002fbcdb35SSteven Rostedt /* 8012fbcdb35SSteven Rostedt * Comments display at + 1 to depth. Since 8022fbcdb35SSteven Rostedt * this is a leaf function, keep the comments 8032fbcdb35SSteven Rostedt * equal to this depth. 8042fbcdb35SSteven Rostedt */ 805f1c7f517SSteven Rostedt cpu_data->depth = call->depth - 1; 806f1c7f517SSteven Rostedt 807f1c7f517SSteven Rostedt /* No need to keep this function around for this depth */ 808f1c7f517SSteven Rostedt if (call->depth < FTRACE_RETFUNC_DEPTH) 809f1c7f517SSteven Rostedt cpu_data->enter_funcs[call->depth] = 0; 8102fbcdb35SSteven Rostedt } 8112fbcdb35SSteven Rostedt 812ffeb80fcSJiri Olsa /* Overhead and duration */ 8139d9add34SSteven Rostedt (Red Hat) print_graph_duration(duration, s, flags); 814287b6e68SFrederic Weisbecker 81583a8df61SFrederic Weisbecker /* Function */ 8169d9add34SSteven Rostedt (Red Hat) for (i = 0; i < call->depth * TRACE_GRAPH_INDENT; i++) 8179d9add34SSteven Rostedt (Red Hat) trace_seq_putc(s, ' '); 818287b6e68SFrederic Weisbecker 8199d9add34SSteven Rostedt (Red Hat) trace_seq_printf(s, "%ps();\n", (void *)call->func); 82083a8df61SFrederic Weisbecker 8219d9add34SSteven Rostedt (Red Hat) return trace_handle_return(s); 822287b6e68SFrederic Weisbecker } 823287b6e68SFrederic Weisbecker 824287b6e68SFrederic Weisbecker static enum print_line_t 8252fbcdb35SSteven Rostedt print_graph_entry_nested(struct trace_iterator *iter, 8262fbcdb35SSteven Rostedt struct ftrace_graph_ent_entry *entry, 827d7a8d9e9SJiri Olsa struct trace_seq *s, int cpu, u32 flags) 828287b6e68SFrederic Weisbecker { 82983a8df61SFrederic Weisbecker struct ftrace_graph_ent *call = &entry->graph_ent; 8302fbcdb35SSteven Rostedt struct fgraph_data *data = iter->private; 8312fbcdb35SSteven Rostedt int i; 8322fbcdb35SSteven Rostedt 8332fbcdb35SSteven Rostedt if (data) { 834f1c7f517SSteven Rostedt struct fgraph_cpu_data *cpu_data; 8352fbcdb35SSteven Rostedt int cpu = iter->cpu; 8362fbcdb35SSteven Rostedt 837f1c7f517SSteven Rostedt cpu_data = per_cpu_ptr(data->cpu_data, cpu); 838f1c7f517SSteven Rostedt cpu_data->depth = call->depth; 839f1c7f517SSteven Rostedt 840f1c7f517SSteven Rostedt /* Save this function pointer to see if the exit matches */ 841f1c7f517SSteven Rostedt if (call->depth < FTRACE_RETFUNC_DEPTH) 842f1c7f517SSteven Rostedt cpu_data->enter_funcs[call->depth] = call->func; 8432fbcdb35SSteven Rostedt } 84483a8df61SFrederic Weisbecker 8451a056155SFrederic Weisbecker /* No time */ 8469d9add34SSteven Rostedt (Red Hat) print_graph_duration(0, s, flags | FLAGS_FILL_FULL); 847f8b755acSFrederic Weisbecker 84883a8df61SFrederic Weisbecker /* Function */ 8499d9add34SSteven Rostedt (Red Hat) for (i = 0; i < call->depth * TRACE_GRAPH_INDENT; i++) 8509d9add34SSteven Rostedt (Red Hat) trace_seq_putc(s, ' '); 85183a8df61SFrederic Weisbecker 8529d9add34SSteven Rostedt (Red Hat) trace_seq_printf(s, "%ps() {\n", (void *)call->func); 8539d9add34SSteven Rostedt (Red Hat) 8549d9add34SSteven Rostedt (Red Hat) if (trace_seq_has_overflowed(s)) 85583a8df61SFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 85683a8df61SFrederic Weisbecker 857b91facc3SFrederic Weisbecker /* 858b91facc3SFrederic Weisbecker * we already consumed the current entry to check the next one 859b91facc3SFrederic Weisbecker * and see if this is a leaf. 860b91facc3SFrederic Weisbecker */ 861b91facc3SFrederic Weisbecker return TRACE_TYPE_NO_CONSUME; 86283a8df61SFrederic Weisbecker } 86383a8df61SFrederic Weisbecker 8649d9add34SSteven Rostedt (Red Hat) static void 865ac5f6c96SSteven Rostedt print_graph_prologue(struct trace_iterator *iter, struct trace_seq *s, 866d7a8d9e9SJiri Olsa int type, unsigned long addr, u32 flags) 86783a8df61SFrederic Weisbecker { 8682fbcdb35SSteven Rostedt struct fgraph_data *data = iter->private; 86983a8df61SFrederic Weisbecker struct trace_entry *ent = iter->ent; 870ac5f6c96SSteven Rostedt int cpu = iter->cpu; 871287b6e68SFrederic Weisbecker 8721a056155SFrederic Weisbecker /* Pid */ 8739d9add34SSteven Rostedt (Red Hat) verif_pid(s, ent->pid, cpu, data); 874437f24fbSSteven Rostedt 8759d9add34SSteven Rostedt (Red Hat) if (type) 8769005f3ebSFrederic Weisbecker /* Interrupt */ 8779d9add34SSteven Rostedt (Red Hat) print_graph_irq(iter, addr, type, cpu, ent->pid, flags); 8789005f3ebSFrederic Weisbecker 879749230b0SJiri Olsa if (!(trace_flags & TRACE_ITER_CONTEXT_INFO)) 8809d9add34SSteven Rostedt (Red Hat) return; 881749230b0SJiri Olsa 8829005f3ebSFrederic Weisbecker /* Absolute time */ 8839d9add34SSteven Rostedt (Red Hat) if (flags & TRACE_GRAPH_PRINT_ABS_TIME) 8849d9add34SSteven Rostedt (Red Hat) print_graph_abs_time(iter->ts, s); 8859005f3ebSFrederic Weisbecker 8861a056155SFrederic Weisbecker /* Cpu */ 8879d9add34SSteven Rostedt (Red Hat) if (flags & TRACE_GRAPH_PRINT_CPU) 8889d9add34SSteven Rostedt (Red Hat) print_graph_cpu(s, cpu); 88911e84accSFrederic Weisbecker 89011e84accSFrederic Weisbecker /* Proc */ 891d7a8d9e9SJiri Olsa if (flags & TRACE_GRAPH_PRINT_PROC) { 8929d9add34SSteven Rostedt (Red Hat) print_graph_proc(s, ent->pid); 8939d9add34SSteven Rostedt (Red Hat) trace_seq_puts(s, " | "); 8941a056155SFrederic Weisbecker } 895287b6e68SFrederic Weisbecker 89649ff5903SSteven Rostedt /* Latency format */ 8979d9add34SSteven Rostedt (Red Hat) if (trace_flags & TRACE_ITER_LATENCY_FMT) 8989d9add34SSteven Rostedt (Red Hat) print_graph_lat_fmt(s, ent); 89949ff5903SSteven Rostedt 9009d9add34SSteven Rostedt (Red Hat) return; 901ac5f6c96SSteven Rostedt } 902ac5f6c96SSteven Rostedt 9032bd16212SJiri Olsa /* 9042bd16212SJiri Olsa * Entry check for irq code 9052bd16212SJiri Olsa * 9062bd16212SJiri Olsa * returns 1 if 9072bd16212SJiri Olsa * - we are inside irq code 90825985edcSLucas De Marchi * - we just entered irq code 9092bd16212SJiri Olsa * 9102bd16212SJiri Olsa * retunns 0 if 9112bd16212SJiri Olsa * - funcgraph-interrupts option is set 9122bd16212SJiri Olsa * - we are not inside irq code 9132bd16212SJiri Olsa */ 9142bd16212SJiri Olsa static int 9152bd16212SJiri Olsa check_irq_entry(struct trace_iterator *iter, u32 flags, 9162bd16212SJiri Olsa unsigned long addr, int depth) 9172bd16212SJiri Olsa { 9182bd16212SJiri Olsa int cpu = iter->cpu; 919a9d61173SJiri Olsa int *depth_irq; 9202bd16212SJiri Olsa struct fgraph_data *data = iter->private; 9212bd16212SJiri Olsa 922a9d61173SJiri Olsa /* 923a9d61173SJiri Olsa * If we are either displaying irqs, or we got called as 924a9d61173SJiri Olsa * a graph event and private data does not exist, 925a9d61173SJiri Olsa * then we bypass the irq check. 926a9d61173SJiri Olsa */ 927a9d61173SJiri Olsa if ((flags & TRACE_GRAPH_PRINT_IRQS) || 928a9d61173SJiri Olsa (!data)) 9292bd16212SJiri Olsa return 0; 9302bd16212SJiri Olsa 931a9d61173SJiri Olsa depth_irq = &(per_cpu_ptr(data->cpu_data, cpu)->depth_irq); 932a9d61173SJiri Olsa 9332bd16212SJiri Olsa /* 9342bd16212SJiri Olsa * We are inside the irq code 9352bd16212SJiri Olsa */ 9362bd16212SJiri Olsa if (*depth_irq >= 0) 9372bd16212SJiri Olsa return 1; 9382bd16212SJiri Olsa 9392bd16212SJiri Olsa if ((addr < (unsigned long)__irqentry_text_start) || 9402bd16212SJiri Olsa (addr >= (unsigned long)__irqentry_text_end)) 9412bd16212SJiri Olsa return 0; 9422bd16212SJiri Olsa 9432bd16212SJiri Olsa /* 9442bd16212SJiri Olsa * We are entering irq code. 9452bd16212SJiri Olsa */ 9462bd16212SJiri Olsa *depth_irq = depth; 9472bd16212SJiri Olsa return 1; 9482bd16212SJiri Olsa } 9492bd16212SJiri Olsa 9502bd16212SJiri Olsa /* 9512bd16212SJiri Olsa * Return check for irq code 9522bd16212SJiri Olsa * 9532bd16212SJiri Olsa * returns 1 if 9542bd16212SJiri Olsa * - we are inside irq code 9552bd16212SJiri Olsa * - we just left irq code 9562bd16212SJiri Olsa * 9572bd16212SJiri Olsa * returns 0 if 9582bd16212SJiri Olsa * - funcgraph-interrupts option is set 9592bd16212SJiri Olsa * - we are not inside irq code 9602bd16212SJiri Olsa */ 9612bd16212SJiri Olsa static int 9622bd16212SJiri Olsa check_irq_return(struct trace_iterator *iter, u32 flags, int depth) 9632bd16212SJiri Olsa { 9642bd16212SJiri Olsa int cpu = iter->cpu; 965a9d61173SJiri Olsa int *depth_irq; 9662bd16212SJiri Olsa struct fgraph_data *data = iter->private; 9672bd16212SJiri Olsa 968a9d61173SJiri Olsa /* 969a9d61173SJiri Olsa * If we are either displaying irqs, or we got called as 970a9d61173SJiri Olsa * a graph event and private data does not exist, 971a9d61173SJiri Olsa * then we bypass the irq check. 972a9d61173SJiri Olsa */ 973a9d61173SJiri Olsa if ((flags & TRACE_GRAPH_PRINT_IRQS) || 974a9d61173SJiri Olsa (!data)) 9752bd16212SJiri Olsa return 0; 9762bd16212SJiri Olsa 977a9d61173SJiri Olsa depth_irq = &(per_cpu_ptr(data->cpu_data, cpu)->depth_irq); 978a9d61173SJiri Olsa 9792bd16212SJiri Olsa /* 9802bd16212SJiri Olsa * We are not inside the irq code. 9812bd16212SJiri Olsa */ 9822bd16212SJiri Olsa if (*depth_irq == -1) 9832bd16212SJiri Olsa return 0; 9842bd16212SJiri Olsa 9852bd16212SJiri Olsa /* 9862bd16212SJiri Olsa * We are inside the irq code, and this is returning entry. 9872bd16212SJiri Olsa * Let's not trace it and clear the entry depth, since 9882bd16212SJiri Olsa * we are out of irq code. 9892bd16212SJiri Olsa * 9902bd16212SJiri Olsa * This condition ensures that we 'leave the irq code' once 9912bd16212SJiri Olsa * we are out of the entry depth. Thus protecting us from 9922bd16212SJiri Olsa * the RETURN entry loss. 9932bd16212SJiri Olsa */ 9942bd16212SJiri Olsa if (*depth_irq >= depth) { 9952bd16212SJiri Olsa *depth_irq = -1; 9962bd16212SJiri Olsa return 1; 9972bd16212SJiri Olsa } 9982bd16212SJiri Olsa 9992bd16212SJiri Olsa /* 10002bd16212SJiri Olsa * We are inside the irq code, and this is not the entry. 10012bd16212SJiri Olsa */ 10022bd16212SJiri Olsa return 1; 10032bd16212SJiri Olsa } 10042bd16212SJiri Olsa 1005ac5f6c96SSteven Rostedt static enum print_line_t 1006ac5f6c96SSteven Rostedt print_graph_entry(struct ftrace_graph_ent_entry *field, struct trace_seq *s, 1007d7a8d9e9SJiri Olsa struct trace_iterator *iter, u32 flags) 1008ac5f6c96SSteven Rostedt { 1009be1eca39SJiri Olsa struct fgraph_data *data = iter->private; 1010ac5f6c96SSteven Rostedt struct ftrace_graph_ent *call = &field->graph_ent; 1011ac5f6c96SSteven Rostedt struct ftrace_graph_ret_entry *leaf_ret; 1012be1eca39SJiri Olsa static enum print_line_t ret; 1013be1eca39SJiri Olsa int cpu = iter->cpu; 1014ac5f6c96SSteven Rostedt 10152bd16212SJiri Olsa if (check_irq_entry(iter, flags, call->func, call->depth)) 10162bd16212SJiri Olsa return TRACE_TYPE_HANDLED; 10172bd16212SJiri Olsa 10189d9add34SSteven Rostedt (Red Hat) print_graph_prologue(iter, s, TRACE_GRAPH_ENT, call->func, flags); 1019ac5f6c96SSteven Rostedt 1020b91facc3SFrederic Weisbecker leaf_ret = get_return_for_leaf(iter, field); 1021b91facc3SFrederic Weisbecker if (leaf_ret) 1022d7a8d9e9SJiri Olsa ret = print_graph_entry_leaf(iter, field, leaf_ret, s, flags); 102383a8df61SFrederic Weisbecker else 1024d7a8d9e9SJiri Olsa ret = print_graph_entry_nested(iter, field, s, cpu, flags); 102583a8df61SFrederic Weisbecker 1026be1eca39SJiri Olsa if (data) { 1027be1eca39SJiri Olsa /* 1028be1eca39SJiri Olsa * If we failed to write our output, then we need to make 1029be1eca39SJiri Olsa * note of it. Because we already consumed our entry. 1030be1eca39SJiri Olsa */ 1031be1eca39SJiri Olsa if (s->full) { 1032be1eca39SJiri Olsa data->failed = 1; 1033be1eca39SJiri Olsa data->cpu = cpu; 1034be1eca39SJiri Olsa } else 1035be1eca39SJiri Olsa data->failed = 0; 1036be1eca39SJiri Olsa } 1037be1eca39SJiri Olsa 1038be1eca39SJiri Olsa return ret; 103983a8df61SFrederic Weisbecker } 104083a8df61SFrederic Weisbecker 104183a8df61SFrederic Weisbecker static enum print_line_t 104283a8df61SFrederic Weisbecker print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s, 1043d7a8d9e9SJiri Olsa struct trace_entry *ent, struct trace_iterator *iter, 1044d7a8d9e9SJiri Olsa u32 flags) 104583a8df61SFrederic Weisbecker { 104683a8df61SFrederic Weisbecker unsigned long long duration = trace->rettime - trace->calltime; 10472fbcdb35SSteven Rostedt struct fgraph_data *data = iter->private; 10482fbcdb35SSteven Rostedt pid_t pid = ent->pid; 10492fbcdb35SSteven Rostedt int cpu = iter->cpu; 1050f1c7f517SSteven Rostedt int func_match = 1; 10512fbcdb35SSteven Rostedt int i; 10522fbcdb35SSteven Rostedt 10532bd16212SJiri Olsa if (check_irq_return(iter, flags, trace->depth)) 10542bd16212SJiri Olsa return TRACE_TYPE_HANDLED; 10552bd16212SJiri Olsa 10562fbcdb35SSteven Rostedt if (data) { 1057f1c7f517SSteven Rostedt struct fgraph_cpu_data *cpu_data; 1058f1c7f517SSteven Rostedt int cpu = iter->cpu; 1059f1c7f517SSteven Rostedt 1060f1c7f517SSteven Rostedt cpu_data = per_cpu_ptr(data->cpu_data, cpu); 10612fbcdb35SSteven Rostedt 10622fbcdb35SSteven Rostedt /* 10632fbcdb35SSteven Rostedt * Comments display at + 1 to depth. This is the 10642fbcdb35SSteven Rostedt * return from a function, we now want the comments 10652fbcdb35SSteven Rostedt * to display at the same level of the bracket. 10662fbcdb35SSteven Rostedt */ 1067f1c7f517SSteven Rostedt cpu_data->depth = trace->depth - 1; 1068f1c7f517SSteven Rostedt 1069f1c7f517SSteven Rostedt if (trace->depth < FTRACE_RETFUNC_DEPTH) { 1070f1c7f517SSteven Rostedt if (cpu_data->enter_funcs[trace->depth] != trace->func) 1071f1c7f517SSteven Rostedt func_match = 0; 1072f1c7f517SSteven Rostedt cpu_data->enter_funcs[trace->depth] = 0; 1073f1c7f517SSteven Rostedt } 10742fbcdb35SSteven Rostedt } 107583a8df61SFrederic Weisbecker 10769d9add34SSteven Rostedt (Red Hat) print_graph_prologue(iter, s, 0, 0, flags); 107783a8df61SFrederic Weisbecker 1078ffeb80fcSJiri Olsa /* Overhead and duration */ 10799d9add34SSteven Rostedt (Red Hat) print_graph_duration(duration, s, flags); 108083a8df61SFrederic Weisbecker 108183a8df61SFrederic Weisbecker /* Closing brace */ 10829d9add34SSteven Rostedt (Red Hat) for (i = 0; i < trace->depth * TRACE_GRAPH_INDENT; i++) 10839d9add34SSteven Rostedt (Red Hat) trace_seq_putc(s, ' '); 1084287b6e68SFrederic Weisbecker 1085f1c7f517SSteven Rostedt /* 1086f1c7f517SSteven Rostedt * If the return function does not have a matching entry, 1087f1c7f517SSteven Rostedt * then the entry was lost. Instead of just printing 1088f1c7f517SSteven Rostedt * the '}' and letting the user guess what function this 1089607e3a29SRobert Elliott * belongs to, write out the function name. Always do 1090607e3a29SRobert Elliott * that if the funcgraph-tail option is enabled. 1091f1c7f517SSteven Rostedt */ 10929d9add34SSteven Rostedt (Red Hat) if (func_match && !(flags & TRACE_GRAPH_PRINT_TAIL)) 10939d9add34SSteven Rostedt (Red Hat) trace_seq_puts(s, "}\n"); 10949d9add34SSteven Rostedt (Red Hat) else 10959d9add34SSteven Rostedt (Red Hat) trace_seq_printf(s, "} /* %ps */\n", (void *)trace->func); 109683a8df61SFrederic Weisbecker 109783a8df61SFrederic Weisbecker /* Overrun */ 10989d9add34SSteven Rostedt (Red Hat) if (flags & TRACE_GRAPH_PRINT_OVERRUN) 10999d9add34SSteven Rostedt (Red Hat) trace_seq_printf(s, " (Overruns: %lu)\n", 1100287b6e68SFrederic Weisbecker trace->overrun); 1101f8b755acSFrederic Weisbecker 11029d9add34SSteven Rostedt (Red Hat) print_graph_irq(iter, trace->func, TRACE_GRAPH_RET, 1103d7a8d9e9SJiri Olsa cpu, pid, flags); 1104f8b755acSFrederic Weisbecker 11059d9add34SSteven Rostedt (Red Hat) return trace_handle_return(s); 1106287b6e68SFrederic Weisbecker } 1107fb52607aSFrederic Weisbecker 11081fd8f2a3SFrederic Weisbecker static enum print_line_t 11095087f8d2SSteven Rostedt print_graph_comment(struct trace_seq *s, struct trace_entry *ent, 1110d7a8d9e9SJiri Olsa struct trace_iterator *iter, u32 flags) 11111fd8f2a3SFrederic Weisbecker { 11125087f8d2SSteven Rostedt unsigned long sym_flags = (trace_flags & TRACE_ITER_SYM_MASK); 11132fbcdb35SSteven Rostedt struct fgraph_data *data = iter->private; 11145087f8d2SSteven Rostedt struct trace_event *event; 11152fbcdb35SSteven Rostedt int depth = 0; 11161fd8f2a3SFrederic Weisbecker int ret; 11172fbcdb35SSteven Rostedt int i; 11182fbcdb35SSteven Rostedt 11192fbcdb35SSteven Rostedt if (data) 1120be1eca39SJiri Olsa depth = per_cpu_ptr(data->cpu_data, iter->cpu)->depth; 11219005f3ebSFrederic Weisbecker 11229d9add34SSteven Rostedt (Red Hat) print_graph_prologue(iter, s, 0, 0, flags); 1123d1f9cbd7SFrederic Weisbecker 11241fd8f2a3SFrederic Weisbecker /* No time */ 11259d9add34SSteven Rostedt (Red Hat) print_graph_duration(0, s, flags | FLAGS_FILL_FULL); 11261fd8f2a3SFrederic Weisbecker 11271fd8f2a3SFrederic Weisbecker /* Indentation */ 11282fbcdb35SSteven Rostedt if (depth > 0) 11299d9add34SSteven Rostedt (Red Hat) for (i = 0; i < (depth + 1) * TRACE_GRAPH_INDENT; i++) 11309d9add34SSteven Rostedt (Red Hat) trace_seq_putc(s, ' '); 11311fd8f2a3SFrederic Weisbecker 11321fd8f2a3SFrederic Weisbecker /* The comment */ 11339d9add34SSteven Rostedt (Red Hat) trace_seq_puts(s, "/* "); 1134769b0441SFrederic Weisbecker 11355087f8d2SSteven Rostedt switch (iter->ent->type) { 11365087f8d2SSteven Rostedt case TRACE_BPRINT: 11375087f8d2SSteven Rostedt ret = trace_print_bprintk_msg_only(iter); 11385087f8d2SSteven Rostedt if (ret != TRACE_TYPE_HANDLED) 11395087f8d2SSteven Rostedt return ret; 11405087f8d2SSteven Rostedt break; 11415087f8d2SSteven Rostedt case TRACE_PRINT: 11425087f8d2SSteven Rostedt ret = trace_print_printk_msg_only(iter); 11435087f8d2SSteven Rostedt if (ret != TRACE_TYPE_HANDLED) 11445087f8d2SSteven Rostedt return ret; 11455087f8d2SSteven Rostedt break; 11465087f8d2SSteven Rostedt default: 11475087f8d2SSteven Rostedt event = ftrace_find_event(ent->type); 11485087f8d2SSteven Rostedt if (!event) 11495087f8d2SSteven Rostedt return TRACE_TYPE_UNHANDLED; 11505087f8d2SSteven Rostedt 1151a9a57763SSteven Rostedt ret = event->funcs->trace(iter, sym_flags, event); 11525087f8d2SSteven Rostedt if (ret != TRACE_TYPE_HANDLED) 11535087f8d2SSteven Rostedt return ret; 11545087f8d2SSteven Rostedt } 11551fd8f2a3SFrederic Weisbecker 1156412d0bb5SFrederic Weisbecker /* Strip ending newline */ 1157*3a161d99SSteven Rostedt (Red Hat) if (s->buffer[s->seq.len - 1] == '\n') { 1158*3a161d99SSteven Rostedt (Red Hat) s->buffer[s->seq.len - 1] = '\0'; 1159*3a161d99SSteven Rostedt (Red Hat) s->seq.len--; 1160412d0bb5SFrederic Weisbecker } 1161412d0bb5SFrederic Weisbecker 11629d9add34SSteven Rostedt (Red Hat) trace_seq_puts(s, " */\n"); 11631fd8f2a3SFrederic Weisbecker 11649d9add34SSteven Rostedt (Red Hat) return trace_handle_return(s); 11651fd8f2a3SFrederic Weisbecker } 11661fd8f2a3SFrederic Weisbecker 11671fd8f2a3SFrederic Weisbecker 1168fb52607aSFrederic Weisbecker enum print_line_t 1169321e68b0SJiri Olsa print_graph_function_flags(struct trace_iterator *iter, u32 flags) 1170fb52607aSFrederic Weisbecker { 1171be1eca39SJiri Olsa struct ftrace_graph_ent_entry *field; 1172be1eca39SJiri Olsa struct fgraph_data *data = iter->private; 1173fb52607aSFrederic Weisbecker struct trace_entry *entry = iter->ent; 11745087f8d2SSteven Rostedt struct trace_seq *s = &iter->seq; 1175be1eca39SJiri Olsa int cpu = iter->cpu; 1176be1eca39SJiri Olsa int ret; 1177be1eca39SJiri Olsa 1178be1eca39SJiri Olsa if (data && per_cpu_ptr(data->cpu_data, cpu)->ignore) { 1179be1eca39SJiri Olsa per_cpu_ptr(data->cpu_data, cpu)->ignore = 0; 1180be1eca39SJiri Olsa return TRACE_TYPE_HANDLED; 1181be1eca39SJiri Olsa } 1182be1eca39SJiri Olsa 1183be1eca39SJiri Olsa /* 1184be1eca39SJiri Olsa * If the last output failed, there's a possibility we need 1185be1eca39SJiri Olsa * to print out the missing entry which would never go out. 1186be1eca39SJiri Olsa */ 1187be1eca39SJiri Olsa if (data && data->failed) { 1188be1eca39SJiri Olsa field = &data->ent; 1189be1eca39SJiri Olsa iter->cpu = data->cpu; 1190d7a8d9e9SJiri Olsa ret = print_graph_entry(field, s, iter, flags); 1191be1eca39SJiri Olsa if (ret == TRACE_TYPE_HANDLED && iter->cpu != cpu) { 1192be1eca39SJiri Olsa per_cpu_ptr(data->cpu_data, iter->cpu)->ignore = 1; 1193be1eca39SJiri Olsa ret = TRACE_TYPE_NO_CONSUME; 1194be1eca39SJiri Olsa } 1195be1eca39SJiri Olsa iter->cpu = cpu; 1196be1eca39SJiri Olsa return ret; 1197be1eca39SJiri Olsa } 1198fb52607aSFrederic Weisbecker 1199287b6e68SFrederic Weisbecker switch (entry->type) { 1200287b6e68SFrederic Weisbecker case TRACE_GRAPH_ENT: { 120138ceb592SLai Jiangshan /* 120238ceb592SLai Jiangshan * print_graph_entry() may consume the current event, 120338ceb592SLai Jiangshan * thus @field may become invalid, so we need to save it. 120438ceb592SLai Jiangshan * sizeof(struct ftrace_graph_ent_entry) is very small, 120538ceb592SLai Jiangshan * it can be safely saved at the stack. 120638ceb592SLai Jiangshan */ 1207be1eca39SJiri Olsa struct ftrace_graph_ent_entry saved; 1208fb52607aSFrederic Weisbecker trace_assign_type(field, entry); 120938ceb592SLai Jiangshan saved = *field; 1210d7a8d9e9SJiri Olsa return print_graph_entry(&saved, s, iter, flags); 1211fb52607aSFrederic Weisbecker } 1212287b6e68SFrederic Weisbecker case TRACE_GRAPH_RET: { 1213287b6e68SFrederic Weisbecker struct ftrace_graph_ret_entry *field; 1214287b6e68SFrederic Weisbecker trace_assign_type(field, entry); 1215d7a8d9e9SJiri Olsa return print_graph_return(&field->ret, s, entry, iter, flags); 1216fb52607aSFrederic Weisbecker } 121762b915f1SJiri Olsa case TRACE_STACK: 121862b915f1SJiri Olsa case TRACE_FN: 121962b915f1SJiri Olsa /* dont trace stack and functions as comments */ 122062b915f1SJiri Olsa return TRACE_TYPE_UNHANDLED; 122162b915f1SJiri Olsa 1222287b6e68SFrederic Weisbecker default: 1223d7a8d9e9SJiri Olsa return print_graph_comment(s, entry, iter, flags); 1224fb52607aSFrederic Weisbecker } 12255087f8d2SSteven Rostedt 12265087f8d2SSteven Rostedt return TRACE_TYPE_HANDLED; 1227287b6e68SFrederic Weisbecker } 1228fb52607aSFrederic Weisbecker 12299106b693SJiri Olsa static enum print_line_t 1230d7a8d9e9SJiri Olsa print_graph_function(struct trace_iterator *iter) 1231d7a8d9e9SJiri Olsa { 1232321e68b0SJiri Olsa return print_graph_function_flags(iter, tracer_flags.val); 1233d7a8d9e9SJiri Olsa } 1234d7a8d9e9SJiri Olsa 1235d7a8d9e9SJiri Olsa static enum print_line_t 1236a9a57763SSteven Rostedt print_graph_function_event(struct trace_iterator *iter, int flags, 1237a9a57763SSteven Rostedt struct trace_event *event) 12389106b693SJiri Olsa { 12399106b693SJiri Olsa return print_graph_function(iter); 12409106b693SJiri Olsa } 12419106b693SJiri Olsa 1242d7a8d9e9SJiri Olsa static void print_lat_header(struct seq_file *s, u32 flags) 124349ff5903SSteven Rostedt { 124449ff5903SSteven Rostedt static const char spaces[] = " " /* 16 spaces */ 124549ff5903SSteven Rostedt " " /* 4 spaces */ 124649ff5903SSteven Rostedt " "; /* 17 spaces */ 124749ff5903SSteven Rostedt int size = 0; 124849ff5903SSteven Rostedt 1249d7a8d9e9SJiri Olsa if (flags & TRACE_GRAPH_PRINT_ABS_TIME) 125049ff5903SSteven Rostedt size += 16; 1251d7a8d9e9SJiri Olsa if (flags & TRACE_GRAPH_PRINT_CPU) 125249ff5903SSteven Rostedt size += 4; 1253d7a8d9e9SJiri Olsa if (flags & TRACE_GRAPH_PRINT_PROC) 125449ff5903SSteven Rostedt size += 17; 125549ff5903SSteven Rostedt 125649ff5903SSteven Rostedt seq_printf(s, "#%.*s _-----=> irqs-off \n", size, spaces); 125749ff5903SSteven Rostedt seq_printf(s, "#%.*s / _----=> need-resched \n", size, spaces); 125849ff5903SSteven Rostedt seq_printf(s, "#%.*s| / _---=> hardirq/softirq \n", size, spaces); 125949ff5903SSteven Rostedt seq_printf(s, "#%.*s|| / _--=> preempt-depth \n", size, spaces); 1260199abfabSJiri Olsa seq_printf(s, "#%.*s||| / \n", size, spaces); 126149ff5903SSteven Rostedt } 126249ff5903SSteven Rostedt 12630a772620SJiri Olsa static void __print_graph_headers_flags(struct seq_file *s, u32 flags) 1264decbec38SFrederic Weisbecker { 126549ff5903SSteven Rostedt int lat = trace_flags & TRACE_ITER_LATENCY_FMT; 126649ff5903SSteven Rostedt 126749ff5903SSteven Rostedt if (lat) 1268d7a8d9e9SJiri Olsa print_lat_header(s, flags); 126949ff5903SSteven Rostedt 1270decbec38SFrederic Weisbecker /* 1st line */ 12711177e436SRasmus Villemoes seq_putc(s, '#'); 1272d7a8d9e9SJiri Olsa if (flags & TRACE_GRAPH_PRINT_ABS_TIME) 1273fa6f0cc7SRasmus Villemoes seq_puts(s, " TIME "); 1274d7a8d9e9SJiri Olsa if (flags & TRACE_GRAPH_PRINT_CPU) 1275fa6f0cc7SRasmus Villemoes seq_puts(s, " CPU"); 1276d7a8d9e9SJiri Olsa if (flags & TRACE_GRAPH_PRINT_PROC) 1277fa6f0cc7SRasmus Villemoes seq_puts(s, " TASK/PID "); 127849ff5903SSteven Rostedt if (lat) 1279fa6f0cc7SRasmus Villemoes seq_puts(s, "||||"); 1280d7a8d9e9SJiri Olsa if (flags & TRACE_GRAPH_PRINT_DURATION) 1281fa6f0cc7SRasmus Villemoes seq_puts(s, " DURATION "); 1282fa6f0cc7SRasmus Villemoes seq_puts(s, " FUNCTION CALLS\n"); 1283decbec38SFrederic Weisbecker 1284decbec38SFrederic Weisbecker /* 2nd line */ 12851177e436SRasmus Villemoes seq_putc(s, '#'); 1286d7a8d9e9SJiri Olsa if (flags & TRACE_GRAPH_PRINT_ABS_TIME) 1287fa6f0cc7SRasmus Villemoes seq_puts(s, " | "); 1288d7a8d9e9SJiri Olsa if (flags & TRACE_GRAPH_PRINT_CPU) 1289fa6f0cc7SRasmus Villemoes seq_puts(s, " | "); 1290d7a8d9e9SJiri Olsa if (flags & TRACE_GRAPH_PRINT_PROC) 1291fa6f0cc7SRasmus Villemoes seq_puts(s, " | | "); 129249ff5903SSteven Rostedt if (lat) 1293fa6f0cc7SRasmus Villemoes seq_puts(s, "||||"); 1294d7a8d9e9SJiri Olsa if (flags & TRACE_GRAPH_PRINT_DURATION) 1295fa6f0cc7SRasmus Villemoes seq_puts(s, " | | "); 1296fa6f0cc7SRasmus Villemoes seq_puts(s, " | | | |\n"); 1297decbec38SFrederic Weisbecker } 12989005f3ebSFrederic Weisbecker 1299ba1afef6SSteven Rostedt (Red Hat) static void print_graph_headers(struct seq_file *s) 1300d7a8d9e9SJiri Olsa { 1301d7a8d9e9SJiri Olsa print_graph_headers_flags(s, tracer_flags.val); 1302d7a8d9e9SJiri Olsa } 1303d7a8d9e9SJiri Olsa 13040a772620SJiri Olsa void print_graph_headers_flags(struct seq_file *s, u32 flags) 13050a772620SJiri Olsa { 13060a772620SJiri Olsa struct trace_iterator *iter = s->private; 13070a772620SJiri Olsa 1308749230b0SJiri Olsa if (!(trace_flags & TRACE_ITER_CONTEXT_INFO)) 1309749230b0SJiri Olsa return; 1310749230b0SJiri Olsa 13110a772620SJiri Olsa if (trace_flags & TRACE_ITER_LATENCY_FMT) { 13120a772620SJiri Olsa /* print nothing if the buffers are empty */ 13130a772620SJiri Olsa if (trace_empty(iter)) 13140a772620SJiri Olsa return; 13150a772620SJiri Olsa 13160a772620SJiri Olsa print_trace_header(s, iter); 1317321e68b0SJiri Olsa } 13180a772620SJiri Olsa 13190a772620SJiri Olsa __print_graph_headers_flags(s, flags); 13200a772620SJiri Olsa } 13210a772620SJiri Olsa 132262b915f1SJiri Olsa void graph_trace_open(struct trace_iterator *iter) 13239005f3ebSFrederic Weisbecker { 13242fbcdb35SSteven Rostedt /* pid and depth on the last trace processed */ 1325be1eca39SJiri Olsa struct fgraph_data *data; 13269005f3ebSFrederic Weisbecker int cpu; 13279005f3ebSFrederic Weisbecker 1328be1eca39SJiri Olsa iter->private = NULL; 1329be1eca39SJiri Olsa 1330be1eca39SJiri Olsa data = kzalloc(sizeof(*data), GFP_KERNEL); 13312fbcdb35SSteven Rostedt if (!data) 1332be1eca39SJiri Olsa goto out_err; 1333be1eca39SJiri Olsa 1334be1eca39SJiri Olsa data->cpu_data = alloc_percpu(struct fgraph_cpu_data); 1335be1eca39SJiri Olsa if (!data->cpu_data) 1336be1eca39SJiri Olsa goto out_err_free; 1337be1eca39SJiri Olsa 13389005f3ebSFrederic Weisbecker for_each_possible_cpu(cpu) { 1339be1eca39SJiri Olsa pid_t *pid = &(per_cpu_ptr(data->cpu_data, cpu)->last_pid); 1340be1eca39SJiri Olsa int *depth = &(per_cpu_ptr(data->cpu_data, cpu)->depth); 1341be1eca39SJiri Olsa int *ignore = &(per_cpu_ptr(data->cpu_data, cpu)->ignore); 13422bd16212SJiri Olsa int *depth_irq = &(per_cpu_ptr(data->cpu_data, cpu)->depth_irq); 13432bd16212SJiri Olsa 13449005f3ebSFrederic Weisbecker *pid = -1; 13452fbcdb35SSteven Rostedt *depth = 0; 1346be1eca39SJiri Olsa *ignore = 0; 13472bd16212SJiri Olsa *depth_irq = -1; 13489005f3ebSFrederic Weisbecker } 13499005f3ebSFrederic Weisbecker 13502fbcdb35SSteven Rostedt iter->private = data; 1351be1eca39SJiri Olsa 1352be1eca39SJiri Olsa return; 1353be1eca39SJiri Olsa 1354be1eca39SJiri Olsa out_err_free: 1355be1eca39SJiri Olsa kfree(data); 1356be1eca39SJiri Olsa out_err: 1357be1eca39SJiri Olsa pr_warning("function graph tracer: not enough memory\n"); 13589005f3ebSFrederic Weisbecker } 13599005f3ebSFrederic Weisbecker 136062b915f1SJiri Olsa void graph_trace_close(struct trace_iterator *iter) 13619005f3ebSFrederic Weisbecker { 1362be1eca39SJiri Olsa struct fgraph_data *data = iter->private; 1363be1eca39SJiri Olsa 1364be1eca39SJiri Olsa if (data) { 1365be1eca39SJiri Olsa free_percpu(data->cpu_data); 1366be1eca39SJiri Olsa kfree(data); 1367be1eca39SJiri Olsa } 13689005f3ebSFrederic Weisbecker } 13699005f3ebSFrederic Weisbecker 13708c1a49aeSSteven Rostedt (Red Hat) static int 13718c1a49aeSSteven Rostedt (Red Hat) func_graph_set_flag(struct trace_array *tr, u32 old_flags, u32 bit, int set) 1372b304d044SSteven Rostedt { 1373b304d044SSteven Rostedt if (bit == TRACE_GRAPH_PRINT_IRQS) 1374b304d044SSteven Rostedt ftrace_graph_skip_irqs = !set; 1375b304d044SSteven Rostedt 1376b304d044SSteven Rostedt return 0; 1377b304d044SSteven Rostedt } 1378b304d044SSteven Rostedt 1379a9a57763SSteven Rostedt static struct trace_event_functions graph_functions = { 1380a9a57763SSteven Rostedt .trace = print_graph_function_event, 1381a9a57763SSteven Rostedt }; 1382a9a57763SSteven Rostedt 13839106b693SJiri Olsa static struct trace_event graph_trace_entry_event = { 13849106b693SJiri Olsa .type = TRACE_GRAPH_ENT, 1385a9a57763SSteven Rostedt .funcs = &graph_functions, 13869106b693SJiri Olsa }; 13879106b693SJiri Olsa 13889106b693SJiri Olsa static struct trace_event graph_trace_ret_event = { 13899106b693SJiri Olsa .type = TRACE_GRAPH_RET, 1390a9a57763SSteven Rostedt .funcs = &graph_functions 13919106b693SJiri Olsa }; 13929106b693SJiri Olsa 13938f768993SSteven Rostedt (Red Hat) static struct tracer graph_trace __tracer_data = { 139483a8df61SFrederic Weisbecker .name = "function_graph", 13956508fa76SStanislav Fomichev .update_thresh = graph_trace_update_thresh, 13969005f3ebSFrederic Weisbecker .open = graph_trace_open, 1397be1eca39SJiri Olsa .pipe_open = graph_trace_open, 13989005f3ebSFrederic Weisbecker .close = graph_trace_close, 1399be1eca39SJiri Olsa .pipe_close = graph_trace_close, 1400fb52607aSFrederic Weisbecker .init = graph_trace_init, 1401fb52607aSFrederic Weisbecker .reset = graph_trace_reset, 1402fb52607aSFrederic Weisbecker .print_line = print_graph_function, 1403decbec38SFrederic Weisbecker .print_header = print_graph_headers, 1404fb52607aSFrederic Weisbecker .flags = &tracer_flags, 1405b304d044SSteven Rostedt .set_flag = func_graph_set_flag, 14067447dce9SFrederic Weisbecker #ifdef CONFIG_FTRACE_SELFTEST 14077447dce9SFrederic Weisbecker .selftest = trace_selftest_startup_function_graph, 14087447dce9SFrederic Weisbecker #endif 1409fb52607aSFrederic Weisbecker }; 1410fb52607aSFrederic Weisbecker 14118741db53SSteven Rostedt 14128741db53SSteven Rostedt static ssize_t 14138741db53SSteven Rostedt graph_depth_write(struct file *filp, const char __user *ubuf, size_t cnt, 14148741db53SSteven Rostedt loff_t *ppos) 14158741db53SSteven Rostedt { 14168741db53SSteven Rostedt unsigned long val; 14178741db53SSteven Rostedt int ret; 14188741db53SSteven Rostedt 14198741db53SSteven Rostedt ret = kstrtoul_from_user(ubuf, cnt, 10, &val); 14208741db53SSteven Rostedt if (ret) 14218741db53SSteven Rostedt return ret; 14228741db53SSteven Rostedt 14238741db53SSteven Rostedt max_depth = val; 14248741db53SSteven Rostedt 14258741db53SSteven Rostedt *ppos += cnt; 14268741db53SSteven Rostedt 14278741db53SSteven Rostedt return cnt; 14288741db53SSteven Rostedt } 14298741db53SSteven Rostedt 14308741db53SSteven Rostedt static ssize_t 14318741db53SSteven Rostedt graph_depth_read(struct file *filp, char __user *ubuf, size_t cnt, 14328741db53SSteven Rostedt loff_t *ppos) 14338741db53SSteven Rostedt { 14348741db53SSteven Rostedt char buf[15]; /* More than enough to hold UINT_MAX + "\n"*/ 14358741db53SSteven Rostedt int n; 14368741db53SSteven Rostedt 14378741db53SSteven Rostedt n = sprintf(buf, "%d\n", max_depth); 14388741db53SSteven Rostedt 14398741db53SSteven Rostedt return simple_read_from_buffer(ubuf, cnt, ppos, buf, n); 14408741db53SSteven Rostedt } 14418741db53SSteven Rostedt 14428741db53SSteven Rostedt static const struct file_operations graph_depth_fops = { 14438741db53SSteven Rostedt .open = tracing_open_generic, 14448741db53SSteven Rostedt .write = graph_depth_write, 14458741db53SSteven Rostedt .read = graph_depth_read, 14468741db53SSteven Rostedt .llseek = generic_file_llseek, 14478741db53SSteven Rostedt }; 14488741db53SSteven Rostedt 14498741db53SSteven Rostedt static __init int init_graph_debugfs(void) 14508741db53SSteven Rostedt { 14518741db53SSteven Rostedt struct dentry *d_tracer; 14528741db53SSteven Rostedt 14538741db53SSteven Rostedt d_tracer = tracing_init_dentry(); 14548741db53SSteven Rostedt if (!d_tracer) 14558741db53SSteven Rostedt return 0; 14568741db53SSteven Rostedt 14578741db53SSteven Rostedt trace_create_file("max_graph_depth", 0644, d_tracer, 14588741db53SSteven Rostedt NULL, &graph_depth_fops); 14598741db53SSteven Rostedt 14608741db53SSteven Rostedt return 0; 14618741db53SSteven Rostedt } 14628741db53SSteven Rostedt fs_initcall(init_graph_debugfs); 14638741db53SSteven Rostedt 1464fb52607aSFrederic Weisbecker static __init int init_graph_trace(void) 1465fb52607aSFrederic Weisbecker { 14660c9e6f63SLai Jiangshan max_bytes_for_cpu = snprintf(NULL, 0, "%d", nr_cpu_ids - 1); 14670c9e6f63SLai Jiangshan 14689106b693SJiri Olsa if (!register_ftrace_event(&graph_trace_entry_event)) { 14699106b693SJiri Olsa pr_warning("Warning: could not register graph trace events\n"); 14709106b693SJiri Olsa return 1; 14719106b693SJiri Olsa } 14729106b693SJiri Olsa 14739106b693SJiri Olsa if (!register_ftrace_event(&graph_trace_ret_event)) { 14749106b693SJiri Olsa pr_warning("Warning: could not register graph trace events\n"); 14759106b693SJiri Olsa return 1; 14769106b693SJiri Olsa } 14779106b693SJiri Olsa 1478fb52607aSFrederic Weisbecker return register_tracer(&graph_trace); 1479fb52607aSFrederic Weisbecker } 1480fb52607aSFrederic Weisbecker 14816f415672SSteven Rostedt core_initcall(init_graph_trace); 1482