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> 12fb52607aSFrederic Weisbecker #include <linux/fs.h> 13fb52607aSFrederic Weisbecker 14fb52607aSFrederic Weisbecker #include "trace.h" 15f0868d1eSSteven Rostedt #include "trace_output.h" 16fb52607aSFrederic Weisbecker 17*2fbcdb35SSteven Rostedt struct fgraph_data { 18*2fbcdb35SSteven Rostedt pid_t last_pid; 19*2fbcdb35SSteven Rostedt int depth; 20*2fbcdb35SSteven Rostedt }; 21*2fbcdb35SSteven Rostedt 22287b6e68SFrederic Weisbecker #define TRACE_GRAPH_INDENT 2 23fb52607aSFrederic Weisbecker 241a056155SFrederic Weisbecker /* Flag options */ 25fb52607aSFrederic Weisbecker #define TRACE_GRAPH_PRINT_OVERRUN 0x1 261a056155SFrederic Weisbecker #define TRACE_GRAPH_PRINT_CPU 0x2 271a056155SFrederic Weisbecker #define TRACE_GRAPH_PRINT_OVERHEAD 0x4 2811e84accSFrederic Weisbecker #define TRACE_GRAPH_PRINT_PROC 0x8 299005f3ebSFrederic Weisbecker #define TRACE_GRAPH_PRINT_DURATION 0x10 309005f3ebSFrederic Weisbecker #define TRACE_GRAPH_PRINT_ABS_TIME 0X20 311a056155SFrederic Weisbecker 32fb52607aSFrederic Weisbecker static struct tracer_opt trace_opts[] = { 339005f3ebSFrederic Weisbecker /* Display overruns? (for self-debug purpose) */ 341a056155SFrederic Weisbecker { TRACER_OPT(funcgraph-overrun, TRACE_GRAPH_PRINT_OVERRUN) }, 351a056155SFrederic Weisbecker /* Display CPU ? */ 361a056155SFrederic Weisbecker { TRACER_OPT(funcgraph-cpu, TRACE_GRAPH_PRINT_CPU) }, 371a056155SFrederic Weisbecker /* Display Overhead ? */ 381a056155SFrederic Weisbecker { TRACER_OPT(funcgraph-overhead, TRACE_GRAPH_PRINT_OVERHEAD) }, 3911e84accSFrederic Weisbecker /* Display proc name/pid */ 4011e84accSFrederic Weisbecker { TRACER_OPT(funcgraph-proc, TRACE_GRAPH_PRINT_PROC) }, 419005f3ebSFrederic Weisbecker /* Display duration of execution */ 429005f3ebSFrederic Weisbecker { TRACER_OPT(funcgraph-duration, TRACE_GRAPH_PRINT_DURATION) }, 439005f3ebSFrederic Weisbecker /* Display absolute time of an entry */ 449005f3ebSFrederic Weisbecker { TRACER_OPT(funcgraph-abstime, TRACE_GRAPH_PRINT_ABS_TIME) }, 45fb52607aSFrederic Weisbecker { } /* Empty entry */ 46fb52607aSFrederic Weisbecker }; 47fb52607aSFrederic Weisbecker 48fb52607aSFrederic Weisbecker static struct tracer_flags tracer_flags = { 4911e84accSFrederic Weisbecker /* Don't display overruns and proc by default */ 509005f3ebSFrederic Weisbecker .val = TRACE_GRAPH_PRINT_CPU | TRACE_GRAPH_PRINT_OVERHEAD | 519005f3ebSFrederic Weisbecker TRACE_GRAPH_PRINT_DURATION, 52fb52607aSFrederic Weisbecker .opts = trace_opts 53fb52607aSFrederic Weisbecker }; 54fb52607aSFrederic Weisbecker 55287b6e68SFrederic Weisbecker /* pid on the last trace processed */ 569005f3ebSFrederic Weisbecker 57fb52607aSFrederic Weisbecker 58712406a6SSteven Rostedt /* Add a function return address to the trace stack on thread info.*/ 59712406a6SSteven Rostedt int 60712406a6SSteven Rostedt ftrace_push_return_trace(unsigned long ret, unsigned long long time, 61712406a6SSteven Rostedt unsigned long func, int *depth) 62712406a6SSteven Rostedt { 63712406a6SSteven Rostedt int index; 64712406a6SSteven Rostedt 65712406a6SSteven Rostedt if (!current->ret_stack) 66712406a6SSteven Rostedt return -EBUSY; 67712406a6SSteven Rostedt 68712406a6SSteven Rostedt /* The return trace stack is full */ 69712406a6SSteven Rostedt if (current->curr_ret_stack == FTRACE_RETFUNC_DEPTH - 1) { 70712406a6SSteven Rostedt atomic_inc(¤t->trace_overrun); 71712406a6SSteven Rostedt return -EBUSY; 72712406a6SSteven Rostedt } 73712406a6SSteven Rostedt 74712406a6SSteven Rostedt index = ++current->curr_ret_stack; 75712406a6SSteven Rostedt barrier(); 76712406a6SSteven Rostedt current->ret_stack[index].ret = ret; 77712406a6SSteven Rostedt current->ret_stack[index].func = func; 78712406a6SSteven Rostedt current->ret_stack[index].calltime = time; 79712406a6SSteven Rostedt *depth = index; 80712406a6SSteven Rostedt 81712406a6SSteven Rostedt return 0; 82712406a6SSteven Rostedt } 83712406a6SSteven Rostedt 84712406a6SSteven Rostedt /* Retrieve a function return address to the trace stack on thread info.*/ 85712406a6SSteven Rostedt void 86712406a6SSteven Rostedt ftrace_pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret) 87712406a6SSteven Rostedt { 88712406a6SSteven Rostedt int index; 89712406a6SSteven Rostedt 90712406a6SSteven Rostedt index = current->curr_ret_stack; 91712406a6SSteven Rostedt 92712406a6SSteven Rostedt if (unlikely(index < 0)) { 93712406a6SSteven Rostedt ftrace_graph_stop(); 94712406a6SSteven Rostedt WARN_ON(1); 95712406a6SSteven Rostedt /* Might as well panic, otherwise we have no where to go */ 96712406a6SSteven Rostedt *ret = (unsigned long)panic; 97712406a6SSteven Rostedt return; 98712406a6SSteven Rostedt } 99712406a6SSteven Rostedt 100712406a6SSteven Rostedt *ret = current->ret_stack[index].ret; 101712406a6SSteven Rostedt trace->func = current->ret_stack[index].func; 102712406a6SSteven Rostedt trace->calltime = current->ret_stack[index].calltime; 103712406a6SSteven Rostedt trace->overrun = atomic_read(¤t->trace_overrun); 104712406a6SSteven Rostedt trace->depth = index; 105712406a6SSteven Rostedt barrier(); 106712406a6SSteven Rostedt current->curr_ret_stack--; 107712406a6SSteven Rostedt 108712406a6SSteven Rostedt } 109712406a6SSteven Rostedt 110712406a6SSteven Rostedt /* 111712406a6SSteven Rostedt * Send the trace to the ring-buffer. 112712406a6SSteven Rostedt * @return the original return address. 113712406a6SSteven Rostedt */ 114712406a6SSteven Rostedt unsigned long ftrace_return_to_handler(void) 115712406a6SSteven Rostedt { 116712406a6SSteven Rostedt struct ftrace_graph_ret trace; 117712406a6SSteven Rostedt unsigned long ret; 118712406a6SSteven Rostedt 119712406a6SSteven Rostedt ftrace_pop_return_trace(&trace, &ret); 1200012693aSFrederic Weisbecker trace.rettime = trace_clock_local(); 121712406a6SSteven Rostedt ftrace_graph_return(&trace); 122712406a6SSteven Rostedt 123712406a6SSteven Rostedt if (unlikely(!ret)) { 124712406a6SSteven Rostedt ftrace_graph_stop(); 125712406a6SSteven Rostedt WARN_ON(1); 126712406a6SSteven Rostedt /* Might as well panic. What else to do? */ 127712406a6SSteven Rostedt ret = (unsigned long)panic; 128712406a6SSteven Rostedt } 129712406a6SSteven Rostedt 130712406a6SSteven Rostedt return ret; 131712406a6SSteven Rostedt } 132712406a6SSteven Rostedt 133fb52607aSFrederic Weisbecker static int graph_trace_init(struct trace_array *tr) 134fb52607aSFrederic Weisbecker { 135f04109bfSArnaldo Carvalho de Melo int ret = register_ftrace_graph(&trace_graph_return, 136287b6e68SFrederic Weisbecker &trace_graph_entry); 137660c7f9bSSteven Rostedt if (ret) 138660c7f9bSSteven Rostedt return ret; 139660c7f9bSSteven Rostedt tracing_start_cmdline_record(); 140660c7f9bSSteven Rostedt 141660c7f9bSSteven Rostedt return 0; 142fb52607aSFrederic Weisbecker } 143fb52607aSFrederic Weisbecker 144fb52607aSFrederic Weisbecker static void graph_trace_reset(struct trace_array *tr) 145fb52607aSFrederic Weisbecker { 146660c7f9bSSteven Rostedt tracing_stop_cmdline_record(); 147fb52607aSFrederic Weisbecker unregister_ftrace_graph(); 148fb52607aSFrederic Weisbecker } 149fb52607aSFrederic Weisbecker 1501a056155SFrederic Weisbecker static inline int log10_cpu(int nb) 1511a056155SFrederic Weisbecker { 1521a056155SFrederic Weisbecker if (nb / 100) 1531a056155SFrederic Weisbecker return 3; 1541a056155SFrederic Weisbecker if (nb / 10) 1551a056155SFrederic Weisbecker return 2; 1561a056155SFrederic Weisbecker return 1; 1571a056155SFrederic Weisbecker } 1581a056155SFrederic Weisbecker 1591a056155SFrederic Weisbecker static enum print_line_t 1601a056155SFrederic Weisbecker print_graph_cpu(struct trace_seq *s, int cpu) 1611a056155SFrederic Weisbecker { 1621a056155SFrederic Weisbecker int i; 1631a056155SFrederic Weisbecker int ret; 1641a056155SFrederic Weisbecker int log10_this = log10_cpu(cpu); 1654462344eSRusty Russell int log10_all = log10_cpu(cpumask_weight(cpu_online_mask)); 1661a056155SFrederic Weisbecker 1671a056155SFrederic Weisbecker 168d51090b3SIngo Molnar /* 169d51090b3SIngo Molnar * Start with a space character - to make it stand out 170d51090b3SIngo Molnar * to the right a bit when trace output is pasted into 171d51090b3SIngo Molnar * email: 172d51090b3SIngo Molnar */ 173d51090b3SIngo Molnar ret = trace_seq_printf(s, " "); 174d51090b3SIngo Molnar 175d51090b3SIngo Molnar /* 176d51090b3SIngo Molnar * Tricky - we space the CPU field according to the max 177d51090b3SIngo Molnar * number of online CPUs. On a 2-cpu system it would take 178d51090b3SIngo Molnar * a maximum of 1 digit - on a 128 cpu system it would 179d51090b3SIngo Molnar * take up to 3 digits: 180d51090b3SIngo Molnar */ 1811a056155SFrederic Weisbecker for (i = 0; i < log10_all - log10_this; i++) { 1821a056155SFrederic Weisbecker ret = trace_seq_printf(s, " "); 1831a056155SFrederic Weisbecker if (!ret) 1841a056155SFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 1851a056155SFrederic Weisbecker } 1861a056155SFrederic Weisbecker ret = trace_seq_printf(s, "%d) ", cpu); 1871a056155SFrederic Weisbecker if (!ret) 1881a056155SFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 189d51090b3SIngo Molnar 1901a056155SFrederic Weisbecker return TRACE_TYPE_HANDLED; 1911a056155SFrederic Weisbecker } 1921a056155SFrederic Weisbecker 19311e84accSFrederic Weisbecker #define TRACE_GRAPH_PROCINFO_LENGTH 14 19411e84accSFrederic Weisbecker 19511e84accSFrederic Weisbecker static enum print_line_t 19611e84accSFrederic Weisbecker print_graph_proc(struct trace_seq *s, pid_t pid) 19711e84accSFrederic Weisbecker { 1984ca53085SSteven Rostedt char comm[TASK_COMM_LEN]; 19911e84accSFrederic Weisbecker /* sign + log10(MAX_INT) + '\0' */ 20011e84accSFrederic Weisbecker char pid_str[11]; 2014ca53085SSteven Rostedt int spaces = 0; 2024ca53085SSteven Rostedt int ret; 2034ca53085SSteven Rostedt int len; 2044ca53085SSteven Rostedt int i; 20511e84accSFrederic Weisbecker 2064ca53085SSteven Rostedt trace_find_cmdline(pid, comm); 20711e84accSFrederic Weisbecker comm[7] = '\0'; 20811e84accSFrederic Weisbecker sprintf(pid_str, "%d", pid); 20911e84accSFrederic Weisbecker 21011e84accSFrederic Weisbecker /* 1 stands for the "-" character */ 21111e84accSFrederic Weisbecker len = strlen(comm) + strlen(pid_str) + 1; 21211e84accSFrederic Weisbecker 21311e84accSFrederic Weisbecker if (len < TRACE_GRAPH_PROCINFO_LENGTH) 21411e84accSFrederic Weisbecker spaces = TRACE_GRAPH_PROCINFO_LENGTH - len; 21511e84accSFrederic Weisbecker 21611e84accSFrederic Weisbecker /* First spaces to align center */ 21711e84accSFrederic Weisbecker for (i = 0; i < spaces / 2; i++) { 21811e84accSFrederic Weisbecker ret = trace_seq_printf(s, " "); 21911e84accSFrederic Weisbecker if (!ret) 22011e84accSFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 22111e84accSFrederic Weisbecker } 22211e84accSFrederic Weisbecker 22311e84accSFrederic Weisbecker ret = trace_seq_printf(s, "%s-%s", comm, pid_str); 22411e84accSFrederic Weisbecker if (!ret) 22511e84accSFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 22611e84accSFrederic Weisbecker 22711e84accSFrederic Weisbecker /* Last spaces to align center */ 22811e84accSFrederic Weisbecker for (i = 0; i < spaces - (spaces / 2); i++) { 22911e84accSFrederic Weisbecker ret = trace_seq_printf(s, " "); 23011e84accSFrederic Weisbecker if (!ret) 23111e84accSFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 23211e84accSFrederic Weisbecker } 23311e84accSFrederic Weisbecker return TRACE_TYPE_HANDLED; 23411e84accSFrederic Weisbecker } 23511e84accSFrederic Weisbecker 2361a056155SFrederic Weisbecker 237287b6e68SFrederic Weisbecker /* If the pid changed since the last trace, output this event */ 23811e84accSFrederic Weisbecker static enum print_line_t 239*2fbcdb35SSteven Rostedt verif_pid(struct trace_seq *s, pid_t pid, int cpu, struct fgraph_data *data) 240287b6e68SFrederic Weisbecker { 241d51090b3SIngo Molnar pid_t prev_pid; 2429005f3ebSFrederic Weisbecker pid_t *last_pid; 243d51090b3SIngo Molnar int ret; 244660c7f9bSSteven Rostedt 245*2fbcdb35SSteven Rostedt if (!data) 24611e84accSFrederic Weisbecker return TRACE_TYPE_HANDLED; 247287b6e68SFrederic Weisbecker 248*2fbcdb35SSteven Rostedt last_pid = &(per_cpu_ptr(data, cpu)->last_pid); 249660c7f9bSSteven Rostedt 2509005f3ebSFrederic Weisbecker if (*last_pid == pid) 2519005f3ebSFrederic Weisbecker return TRACE_TYPE_HANDLED; 2529005f3ebSFrederic Weisbecker 2539005f3ebSFrederic Weisbecker prev_pid = *last_pid; 2549005f3ebSFrederic Weisbecker *last_pid = pid; 2559005f3ebSFrederic Weisbecker 2569005f3ebSFrederic Weisbecker if (prev_pid == -1) 2579005f3ebSFrederic Weisbecker return TRACE_TYPE_HANDLED; 258d51090b3SIngo Molnar /* 259d51090b3SIngo Molnar * Context-switch trace line: 260d51090b3SIngo Molnar 261d51090b3SIngo Molnar ------------------------------------------ 262d51090b3SIngo Molnar | 1) migration/0--1 => sshd-1755 263d51090b3SIngo Molnar ------------------------------------------ 264d51090b3SIngo Molnar 265d51090b3SIngo Molnar */ 266d51090b3SIngo Molnar ret = trace_seq_printf(s, 2671fd8f2a3SFrederic Weisbecker " ------------------------------------------\n"); 26811e84accSFrederic Weisbecker if (!ret) 269810dc732SWenji Huang return TRACE_TYPE_PARTIAL_LINE; 27011e84accSFrederic Weisbecker 27111e84accSFrederic Weisbecker ret = print_graph_cpu(s, cpu); 27211e84accSFrederic Weisbecker if (ret == TRACE_TYPE_PARTIAL_LINE) 273810dc732SWenji Huang return TRACE_TYPE_PARTIAL_LINE; 27411e84accSFrederic Weisbecker 27511e84accSFrederic Weisbecker ret = print_graph_proc(s, prev_pid); 27611e84accSFrederic Weisbecker if (ret == TRACE_TYPE_PARTIAL_LINE) 277810dc732SWenji Huang return TRACE_TYPE_PARTIAL_LINE; 27811e84accSFrederic Weisbecker 27911e84accSFrederic Weisbecker ret = trace_seq_printf(s, " => "); 28011e84accSFrederic Weisbecker if (!ret) 281810dc732SWenji Huang return TRACE_TYPE_PARTIAL_LINE; 28211e84accSFrederic Weisbecker 28311e84accSFrederic Weisbecker ret = print_graph_proc(s, pid); 28411e84accSFrederic Weisbecker if (ret == TRACE_TYPE_PARTIAL_LINE) 285810dc732SWenji Huang return TRACE_TYPE_PARTIAL_LINE; 28611e84accSFrederic Weisbecker 28711e84accSFrederic Weisbecker ret = trace_seq_printf(s, 28811e84accSFrederic Weisbecker "\n ------------------------------------------\n\n"); 28911e84accSFrederic Weisbecker if (!ret) 290810dc732SWenji Huang return TRACE_TYPE_PARTIAL_LINE; 29111e84accSFrederic Weisbecker 292810dc732SWenji Huang return TRACE_TYPE_HANDLED; 293287b6e68SFrederic Weisbecker } 294287b6e68SFrederic Weisbecker 295b91facc3SFrederic Weisbecker static struct ftrace_graph_ret_entry * 296b91facc3SFrederic Weisbecker get_return_for_leaf(struct trace_iterator *iter, 29783a8df61SFrederic Weisbecker struct ftrace_graph_ent_entry *curr) 298287b6e68SFrederic Weisbecker { 29983a8df61SFrederic Weisbecker struct ring_buffer_iter *ring_iter; 30083a8df61SFrederic Weisbecker struct ring_buffer_event *event; 30183a8df61SFrederic Weisbecker struct ftrace_graph_ret_entry *next; 30283a8df61SFrederic Weisbecker 30383a8df61SFrederic Weisbecker ring_iter = iter->buffer_iter[iter->cpu]; 30483a8df61SFrederic Weisbecker 305b91facc3SFrederic Weisbecker /* First peek to compare current entry and the next one */ 306b91facc3SFrederic Weisbecker if (ring_iter) 3071a056155SFrederic Weisbecker event = ring_buffer_iter_peek(ring_iter, NULL); 308b91facc3SFrederic Weisbecker else { 309b91facc3SFrederic Weisbecker /* We need to consume the current entry to see the next one */ 310b91facc3SFrederic Weisbecker ring_buffer_consume(iter->tr->buffer, iter->cpu, NULL); 311b91facc3SFrederic Weisbecker event = ring_buffer_peek(iter->tr->buffer, iter->cpu, 312b91facc3SFrederic Weisbecker NULL); 313b91facc3SFrederic Weisbecker } 31483a8df61SFrederic Weisbecker 31583a8df61SFrederic Weisbecker if (!event) 316b91facc3SFrederic Weisbecker return NULL; 31783a8df61SFrederic Weisbecker 31883a8df61SFrederic Weisbecker next = ring_buffer_event_data(event); 31983a8df61SFrederic Weisbecker 32083a8df61SFrederic Weisbecker if (next->ent.type != TRACE_GRAPH_RET) 321b91facc3SFrederic Weisbecker return NULL; 32283a8df61SFrederic Weisbecker 32383a8df61SFrederic Weisbecker if (curr->ent.pid != next->ent.pid || 32483a8df61SFrederic Weisbecker curr->graph_ent.func != next->ret.func) 325b91facc3SFrederic Weisbecker return NULL; 32683a8df61SFrederic Weisbecker 327b91facc3SFrederic Weisbecker /* this is a leaf, now advance the iterator */ 328b91facc3SFrederic Weisbecker if (ring_iter) 329b91facc3SFrederic Weisbecker ring_buffer_read(ring_iter, NULL); 330b91facc3SFrederic Weisbecker 331b91facc3SFrederic Weisbecker return next; 33283a8df61SFrederic Weisbecker } 33383a8df61SFrederic Weisbecker 3349005f3ebSFrederic Weisbecker /* Signal a overhead of time execution to the output */ 3359005f3ebSFrederic Weisbecker static int 3369005f3ebSFrederic Weisbecker print_graph_overhead(unsigned long long duration, struct trace_seq *s) 3379005f3ebSFrederic Weisbecker { 3389005f3ebSFrederic Weisbecker /* If duration disappear, we don't need anything */ 3399005f3ebSFrederic Weisbecker if (!(tracer_flags.val & TRACE_GRAPH_PRINT_DURATION)) 3409005f3ebSFrederic Weisbecker return 1; 3419005f3ebSFrederic Weisbecker 3429005f3ebSFrederic Weisbecker /* Non nested entry or return */ 3439005f3ebSFrederic Weisbecker if (duration == -1) 3449005f3ebSFrederic Weisbecker return trace_seq_printf(s, " "); 3459005f3ebSFrederic Weisbecker 3469005f3ebSFrederic Weisbecker if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) { 3479005f3ebSFrederic Weisbecker /* Duration exceeded 100 msecs */ 3489005f3ebSFrederic Weisbecker if (duration > 100000ULL) 3499005f3ebSFrederic Weisbecker return trace_seq_printf(s, "! "); 3509005f3ebSFrederic Weisbecker 3519005f3ebSFrederic Weisbecker /* Duration exceeded 10 msecs */ 3529005f3ebSFrederic Weisbecker if (duration > 10000ULL) 3539005f3ebSFrederic Weisbecker return trace_seq_printf(s, "+ "); 3549005f3ebSFrederic Weisbecker } 3559005f3ebSFrederic Weisbecker 3569005f3ebSFrederic Weisbecker return trace_seq_printf(s, " "); 3579005f3ebSFrederic Weisbecker } 3589005f3ebSFrederic Weisbecker 359d1f9cbd7SFrederic Weisbecker static int print_graph_abs_time(u64 t, struct trace_seq *s) 360d1f9cbd7SFrederic Weisbecker { 361d1f9cbd7SFrederic Weisbecker unsigned long usecs_rem; 362d1f9cbd7SFrederic Weisbecker 363d1f9cbd7SFrederic Weisbecker usecs_rem = do_div(t, NSEC_PER_SEC); 364d1f9cbd7SFrederic Weisbecker usecs_rem /= 1000; 365d1f9cbd7SFrederic Weisbecker 366d1f9cbd7SFrederic Weisbecker return trace_seq_printf(s, "%5lu.%06lu | ", 367d1f9cbd7SFrederic Weisbecker (unsigned long)t, usecs_rem); 368d1f9cbd7SFrederic Weisbecker } 369d1f9cbd7SFrederic Weisbecker 370f8b755acSFrederic Weisbecker static enum print_line_t 371d1f9cbd7SFrederic Weisbecker print_graph_irq(struct trace_iterator *iter, unsigned long addr, 372f8b755acSFrederic Weisbecker enum trace_type type, int cpu, pid_t pid) 373f8b755acSFrederic Weisbecker { 374f8b755acSFrederic Weisbecker int ret; 375d1f9cbd7SFrederic Weisbecker struct trace_seq *s = &iter->seq; 376f8b755acSFrederic Weisbecker 377f8b755acSFrederic Weisbecker if (addr < (unsigned long)__irqentry_text_start || 378f8b755acSFrederic Weisbecker addr >= (unsigned long)__irqentry_text_end) 379f8b755acSFrederic Weisbecker return TRACE_TYPE_UNHANDLED; 380f8b755acSFrederic Weisbecker 381d1f9cbd7SFrederic Weisbecker /* Absolute time */ 382d1f9cbd7SFrederic Weisbecker if (tracer_flags.val & TRACE_GRAPH_PRINT_ABS_TIME) { 383d1f9cbd7SFrederic Weisbecker ret = print_graph_abs_time(iter->ts, s); 384d1f9cbd7SFrederic Weisbecker if (!ret) 385d1f9cbd7SFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 386d1f9cbd7SFrederic Weisbecker } 387d1f9cbd7SFrederic Weisbecker 388f8b755acSFrederic Weisbecker /* Cpu */ 389f8b755acSFrederic Weisbecker if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) { 390f8b755acSFrederic Weisbecker ret = print_graph_cpu(s, cpu); 391f8b755acSFrederic Weisbecker if (ret == TRACE_TYPE_PARTIAL_LINE) 392f8b755acSFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 393f8b755acSFrederic Weisbecker } 394f8b755acSFrederic Weisbecker /* Proc */ 395f8b755acSFrederic Weisbecker if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC) { 396f8b755acSFrederic Weisbecker ret = print_graph_proc(s, pid); 397f8b755acSFrederic Weisbecker if (ret == TRACE_TYPE_PARTIAL_LINE) 398f8b755acSFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 399f8b755acSFrederic Weisbecker ret = trace_seq_printf(s, " | "); 400f8b755acSFrederic Weisbecker if (!ret) 401f8b755acSFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 402f8b755acSFrederic Weisbecker } 403f8b755acSFrederic Weisbecker 404f8b755acSFrederic Weisbecker /* No overhead */ 4059005f3ebSFrederic Weisbecker ret = print_graph_overhead(-1, s); 406f8b755acSFrederic Weisbecker if (!ret) 407f8b755acSFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 408f8b755acSFrederic Weisbecker 4099005f3ebSFrederic Weisbecker if (type == TRACE_GRAPH_ENT) 4109005f3ebSFrederic Weisbecker ret = trace_seq_printf(s, "==========>"); 4119005f3ebSFrederic Weisbecker else 4129005f3ebSFrederic Weisbecker ret = trace_seq_printf(s, "<=========="); 4139005f3ebSFrederic Weisbecker 4149005f3ebSFrederic Weisbecker if (!ret) 4159005f3ebSFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 4169005f3ebSFrederic Weisbecker 4179005f3ebSFrederic Weisbecker /* Don't close the duration column if haven't one */ 4189005f3ebSFrederic Weisbecker if (tracer_flags.val & TRACE_GRAPH_PRINT_DURATION) 4199005f3ebSFrederic Weisbecker trace_seq_printf(s, " |"); 4209005f3ebSFrederic Weisbecker ret = trace_seq_printf(s, "\n"); 4219005f3ebSFrederic Weisbecker 422f8b755acSFrederic Weisbecker if (!ret) 423f8b755acSFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 424f8b755acSFrederic Weisbecker return TRACE_TYPE_HANDLED; 425f8b755acSFrederic Weisbecker } 42683a8df61SFrederic Weisbecker 427166d3c79SFrederic Weisbecker static enum print_line_t 42883a8df61SFrederic Weisbecker print_graph_duration(unsigned long long duration, struct trace_seq *s) 42983a8df61SFrederic Weisbecker { 43083a8df61SFrederic Weisbecker unsigned long nsecs_rem = do_div(duration, 1000); 431166d3c79SFrederic Weisbecker /* log10(ULONG_MAX) + '\0' */ 432166d3c79SFrederic Weisbecker char msecs_str[21]; 433166d3c79SFrederic Weisbecker char nsecs_str[5]; 434166d3c79SFrederic Weisbecker int ret, len; 435166d3c79SFrederic Weisbecker int i; 436166d3c79SFrederic Weisbecker 437166d3c79SFrederic Weisbecker sprintf(msecs_str, "%lu", (unsigned long) duration); 438166d3c79SFrederic Weisbecker 439166d3c79SFrederic Weisbecker /* Print msecs */ 4409005f3ebSFrederic Weisbecker ret = trace_seq_printf(s, "%s", msecs_str); 441166d3c79SFrederic Weisbecker if (!ret) 442166d3c79SFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 443166d3c79SFrederic Weisbecker 444166d3c79SFrederic Weisbecker len = strlen(msecs_str); 445166d3c79SFrederic Weisbecker 446166d3c79SFrederic Weisbecker /* Print nsecs (we don't want to exceed 7 numbers) */ 447166d3c79SFrederic Weisbecker if (len < 7) { 448166d3c79SFrederic Weisbecker snprintf(nsecs_str, 8 - len, "%03lu", nsecs_rem); 449166d3c79SFrederic Weisbecker ret = trace_seq_printf(s, ".%s", nsecs_str); 450166d3c79SFrederic Weisbecker if (!ret) 451166d3c79SFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 452166d3c79SFrederic Weisbecker len += strlen(nsecs_str); 453166d3c79SFrederic Weisbecker } 454166d3c79SFrederic Weisbecker 455166d3c79SFrederic Weisbecker ret = trace_seq_printf(s, " us "); 456166d3c79SFrederic Weisbecker if (!ret) 457166d3c79SFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 458166d3c79SFrederic Weisbecker 459166d3c79SFrederic Weisbecker /* Print remaining spaces to fit the row's width */ 460166d3c79SFrederic Weisbecker for (i = len; i < 7; i++) { 461166d3c79SFrederic Weisbecker ret = trace_seq_printf(s, " "); 462166d3c79SFrederic Weisbecker if (!ret) 463166d3c79SFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 464166d3c79SFrederic Weisbecker } 465166d3c79SFrederic Weisbecker 466166d3c79SFrederic Weisbecker ret = trace_seq_printf(s, "| "); 467166d3c79SFrederic Weisbecker if (!ret) 468166d3c79SFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 469166d3c79SFrederic Weisbecker return TRACE_TYPE_HANDLED; 470166d3c79SFrederic Weisbecker 47183a8df61SFrederic Weisbecker } 47283a8df61SFrederic Weisbecker 47383a8df61SFrederic Weisbecker /* Case of a leaf function on its call entry */ 47483a8df61SFrederic Weisbecker static enum print_line_t 47583a8df61SFrederic Weisbecker print_graph_entry_leaf(struct trace_iterator *iter, 476b91facc3SFrederic Weisbecker struct ftrace_graph_ent_entry *entry, 477b91facc3SFrederic Weisbecker struct ftrace_graph_ret_entry *ret_entry, struct trace_seq *s) 47883a8df61SFrederic Weisbecker { 479*2fbcdb35SSteven Rostedt struct fgraph_data *data = iter->private; 48083a8df61SFrederic Weisbecker struct ftrace_graph_ret *graph_ret; 48183a8df61SFrederic Weisbecker struct ftrace_graph_ent *call; 48283a8df61SFrederic Weisbecker unsigned long long duration; 483287b6e68SFrederic Weisbecker int ret; 4841a056155SFrederic Weisbecker int i; 485287b6e68SFrederic Weisbecker 48683a8df61SFrederic Weisbecker graph_ret = &ret_entry->ret; 48783a8df61SFrederic Weisbecker call = &entry->graph_ent; 48883a8df61SFrederic Weisbecker duration = graph_ret->rettime - graph_ret->calltime; 489437f24fbSSteven Rostedt 490*2fbcdb35SSteven Rostedt if (data) { 491*2fbcdb35SSteven Rostedt int cpu = iter->cpu; 492*2fbcdb35SSteven Rostedt int *depth = &(per_cpu_ptr(data, cpu)->depth); 493*2fbcdb35SSteven Rostedt 494*2fbcdb35SSteven Rostedt /* 495*2fbcdb35SSteven Rostedt * Comments display at + 1 to depth. Since 496*2fbcdb35SSteven Rostedt * this is a leaf function, keep the comments 497*2fbcdb35SSteven Rostedt * equal to this depth. 498*2fbcdb35SSteven Rostedt */ 499*2fbcdb35SSteven Rostedt *depth = call->depth - 1; 500*2fbcdb35SSteven Rostedt } 501*2fbcdb35SSteven Rostedt 50283a8df61SFrederic Weisbecker /* Overhead */ 50383a8df61SFrederic Weisbecker ret = print_graph_overhead(duration, s); 504437f24fbSSteven Rostedt if (!ret) 505287b6e68SFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 5061a056155SFrederic Weisbecker 5071a056155SFrederic Weisbecker /* Duration */ 5089005f3ebSFrederic Weisbecker if (tracer_flags.val & TRACE_GRAPH_PRINT_DURATION) { 5091a056155SFrederic Weisbecker ret = print_graph_duration(duration, s); 510166d3c79SFrederic Weisbecker if (ret == TRACE_TYPE_PARTIAL_LINE) 5111a056155SFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 5129005f3ebSFrederic Weisbecker } 513287b6e68SFrederic Weisbecker 51483a8df61SFrederic Weisbecker /* Function */ 515287b6e68SFrederic Weisbecker for (i = 0; i < call->depth * TRACE_GRAPH_INDENT; i++) { 516287b6e68SFrederic Weisbecker ret = trace_seq_printf(s, " "); 517287b6e68SFrederic Weisbecker if (!ret) 518287b6e68SFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 519287b6e68SFrederic Weisbecker } 520287b6e68SFrederic Weisbecker 521287b6e68SFrederic Weisbecker ret = seq_print_ip_sym(s, call->func, 0); 522287b6e68SFrederic Weisbecker if (!ret) 523287b6e68SFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 524287b6e68SFrederic Weisbecker 5251a056155SFrederic Weisbecker ret = trace_seq_printf(s, "();\n"); 52683a8df61SFrederic Weisbecker if (!ret) 52783a8df61SFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 52883a8df61SFrederic Weisbecker 529287b6e68SFrederic Weisbecker return TRACE_TYPE_HANDLED; 530287b6e68SFrederic Weisbecker } 531287b6e68SFrederic Weisbecker 532287b6e68SFrederic Weisbecker static enum print_line_t 533*2fbcdb35SSteven Rostedt print_graph_entry_nested(struct trace_iterator *iter, 534*2fbcdb35SSteven Rostedt struct ftrace_graph_ent_entry *entry, 535*2fbcdb35SSteven Rostedt struct trace_seq *s, int cpu) 536287b6e68SFrederic Weisbecker { 53783a8df61SFrederic Weisbecker struct ftrace_graph_ent *call = &entry->graph_ent; 538*2fbcdb35SSteven Rostedt struct fgraph_data *data = iter->private; 539*2fbcdb35SSteven Rostedt int ret; 540*2fbcdb35SSteven Rostedt int i; 541*2fbcdb35SSteven Rostedt 542*2fbcdb35SSteven Rostedt if (data) { 543*2fbcdb35SSteven Rostedt int cpu = iter->cpu; 544*2fbcdb35SSteven Rostedt int *depth = &(per_cpu_ptr(data, cpu)->depth); 545*2fbcdb35SSteven Rostedt 546*2fbcdb35SSteven Rostedt *depth = call->depth; 547*2fbcdb35SSteven Rostedt } 54883a8df61SFrederic Weisbecker 54983a8df61SFrederic Weisbecker /* No overhead */ 5509005f3ebSFrederic Weisbecker ret = print_graph_overhead(-1, s); 55183a8df61SFrederic Weisbecker if (!ret) 55283a8df61SFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 5531a056155SFrederic Weisbecker 5541a056155SFrederic Weisbecker /* No time */ 5559005f3ebSFrederic Weisbecker if (tracer_flags.val & TRACE_GRAPH_PRINT_DURATION) { 5561a056155SFrederic Weisbecker ret = trace_seq_printf(s, " | "); 557f8b755acSFrederic Weisbecker if (!ret) 558f8b755acSFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 559f8b755acSFrederic Weisbecker } 560f8b755acSFrederic Weisbecker 56183a8df61SFrederic Weisbecker /* Function */ 56283a8df61SFrederic Weisbecker for (i = 0; i < call->depth * TRACE_GRAPH_INDENT; i++) { 56383a8df61SFrederic Weisbecker ret = trace_seq_printf(s, " "); 56483a8df61SFrederic Weisbecker if (!ret) 56583a8df61SFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 56683a8df61SFrederic Weisbecker } 56783a8df61SFrederic Weisbecker 56883a8df61SFrederic Weisbecker ret = seq_print_ip_sym(s, call->func, 0); 56983a8df61SFrederic Weisbecker if (!ret) 57083a8df61SFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 57183a8df61SFrederic Weisbecker 5721a056155SFrederic Weisbecker ret = trace_seq_printf(s, "() {\n"); 57383a8df61SFrederic Weisbecker if (!ret) 57483a8df61SFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 57583a8df61SFrederic Weisbecker 576b91facc3SFrederic Weisbecker /* 577b91facc3SFrederic Weisbecker * we already consumed the current entry to check the next one 578b91facc3SFrederic Weisbecker * and see if this is a leaf. 579b91facc3SFrederic Weisbecker */ 580b91facc3SFrederic Weisbecker return TRACE_TYPE_NO_CONSUME; 58183a8df61SFrederic Weisbecker } 58283a8df61SFrederic Weisbecker 58383a8df61SFrederic Weisbecker static enum print_line_t 584ac5f6c96SSteven Rostedt print_graph_prologue(struct trace_iterator *iter, struct trace_seq *s, 585ac5f6c96SSteven Rostedt int type, unsigned long addr) 58683a8df61SFrederic Weisbecker { 587*2fbcdb35SSteven Rostedt struct fgraph_data *data = iter->private; 58883a8df61SFrederic Weisbecker struct trace_entry *ent = iter->ent; 589ac5f6c96SSteven Rostedt int cpu = iter->cpu; 590ac5f6c96SSteven Rostedt int ret; 591287b6e68SFrederic Weisbecker 5921a056155SFrederic Weisbecker /* Pid */ 593*2fbcdb35SSteven Rostedt if (verif_pid(s, ent->pid, cpu, data) == TRACE_TYPE_PARTIAL_LINE) 594437f24fbSSteven Rostedt return TRACE_TYPE_PARTIAL_LINE; 595437f24fbSSteven Rostedt 596ac5f6c96SSteven Rostedt if (type) { 5979005f3ebSFrederic Weisbecker /* Interrupt */ 598ac5f6c96SSteven Rostedt ret = print_graph_irq(iter, addr, type, cpu, ent->pid); 5999005f3ebSFrederic Weisbecker if (ret == TRACE_TYPE_PARTIAL_LINE) 6009005f3ebSFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 601ac5f6c96SSteven Rostedt } 6029005f3ebSFrederic Weisbecker 6039005f3ebSFrederic Weisbecker /* Absolute time */ 6049005f3ebSFrederic Weisbecker if (tracer_flags.val & TRACE_GRAPH_PRINT_ABS_TIME) { 6059005f3ebSFrederic Weisbecker ret = print_graph_abs_time(iter->ts, s); 6069005f3ebSFrederic Weisbecker if (!ret) 6079005f3ebSFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 6089005f3ebSFrederic Weisbecker } 6099005f3ebSFrederic Weisbecker 6101a056155SFrederic Weisbecker /* Cpu */ 6111a056155SFrederic Weisbecker if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) { 6121a056155SFrederic Weisbecker ret = print_graph_cpu(s, cpu); 61311e84accSFrederic Weisbecker if (ret == TRACE_TYPE_PARTIAL_LINE) 61411e84accSFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 61511e84accSFrederic Weisbecker } 61611e84accSFrederic Weisbecker 61711e84accSFrederic Weisbecker /* Proc */ 61811e84accSFrederic Weisbecker if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC) { 61900a8bf85SIngo Molnar ret = print_graph_proc(s, ent->pid); 62011e84accSFrederic Weisbecker if (ret == TRACE_TYPE_PARTIAL_LINE) 62111e84accSFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 62211e84accSFrederic Weisbecker 62311e84accSFrederic Weisbecker ret = trace_seq_printf(s, " | "); 624437f24fbSSteven Rostedt if (!ret) 625287b6e68SFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 6261a056155SFrederic Weisbecker } 627287b6e68SFrederic Weisbecker 628ac5f6c96SSteven Rostedt return 0; 629ac5f6c96SSteven Rostedt } 630ac5f6c96SSteven Rostedt 631ac5f6c96SSteven Rostedt static enum print_line_t 632ac5f6c96SSteven Rostedt print_graph_entry(struct ftrace_graph_ent_entry *field, struct trace_seq *s, 633ac5f6c96SSteven Rostedt struct trace_iterator *iter) 634ac5f6c96SSteven Rostedt { 635ac5f6c96SSteven Rostedt int cpu = iter->cpu; 636ac5f6c96SSteven Rostedt struct ftrace_graph_ent *call = &field->graph_ent; 637ac5f6c96SSteven Rostedt struct ftrace_graph_ret_entry *leaf_ret; 638ac5f6c96SSteven Rostedt 639ac5f6c96SSteven Rostedt if (print_graph_prologue(iter, s, TRACE_GRAPH_ENT, call->func)) 640ac5f6c96SSteven Rostedt return TRACE_TYPE_PARTIAL_LINE; 641ac5f6c96SSteven Rostedt 642b91facc3SFrederic Weisbecker leaf_ret = get_return_for_leaf(iter, field); 643b91facc3SFrederic Weisbecker if (leaf_ret) 644b91facc3SFrederic Weisbecker return print_graph_entry_leaf(iter, field, leaf_ret, s); 64583a8df61SFrederic Weisbecker else 646*2fbcdb35SSteven Rostedt return print_graph_entry_nested(iter, field, s, cpu); 64783a8df61SFrederic Weisbecker 64883a8df61SFrederic Weisbecker } 64983a8df61SFrederic Weisbecker 65083a8df61SFrederic Weisbecker static enum print_line_t 65183a8df61SFrederic Weisbecker print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s, 6529005f3ebSFrederic Weisbecker struct trace_entry *ent, struct trace_iterator *iter) 65383a8df61SFrederic Weisbecker { 65483a8df61SFrederic Weisbecker unsigned long long duration = trace->rettime - trace->calltime; 655*2fbcdb35SSteven Rostedt struct fgraph_data *data = iter->private; 656*2fbcdb35SSteven Rostedt pid_t pid = ent->pid; 657*2fbcdb35SSteven Rostedt int cpu = iter->cpu; 658*2fbcdb35SSteven Rostedt int ret; 659*2fbcdb35SSteven Rostedt int i; 660*2fbcdb35SSteven Rostedt 661*2fbcdb35SSteven Rostedt if (data) { 662*2fbcdb35SSteven Rostedt int cpu = iter->cpu; 663*2fbcdb35SSteven Rostedt int *depth = &(per_cpu_ptr(data, cpu)->depth); 664*2fbcdb35SSteven Rostedt 665*2fbcdb35SSteven Rostedt /* 666*2fbcdb35SSteven Rostedt * Comments display at + 1 to depth. This is the 667*2fbcdb35SSteven Rostedt * return from a function, we now want the comments 668*2fbcdb35SSteven Rostedt * to display at the same level of the bracket. 669*2fbcdb35SSteven Rostedt */ 670*2fbcdb35SSteven Rostedt *depth = trace->depth - 1; 671*2fbcdb35SSteven Rostedt } 67283a8df61SFrederic Weisbecker 673ac5f6c96SSteven Rostedt if (print_graph_prologue(iter, s, 0, 0)) 67483a8df61SFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 67583a8df61SFrederic Weisbecker 67683a8df61SFrederic Weisbecker /* Overhead */ 67783a8df61SFrederic Weisbecker ret = print_graph_overhead(duration, s); 67883a8df61SFrederic Weisbecker if (!ret) 67983a8df61SFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 6801a056155SFrederic Weisbecker 6811a056155SFrederic Weisbecker /* Duration */ 6829005f3ebSFrederic Weisbecker if (tracer_flags.val & TRACE_GRAPH_PRINT_DURATION) { 6831a056155SFrederic Weisbecker ret = print_graph_duration(duration, s); 684166d3c79SFrederic Weisbecker if (ret == TRACE_TYPE_PARTIAL_LINE) 6851a056155SFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 6869005f3ebSFrederic Weisbecker } 68783a8df61SFrederic Weisbecker 68883a8df61SFrederic Weisbecker /* Closing brace */ 689287b6e68SFrederic Weisbecker for (i = 0; i < trace->depth * TRACE_GRAPH_INDENT; i++) { 690287b6e68SFrederic Weisbecker ret = trace_seq_printf(s, " "); 691287b6e68SFrederic Weisbecker if (!ret) 692287b6e68SFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 693287b6e68SFrederic Weisbecker } 694287b6e68SFrederic Weisbecker 6951a056155SFrederic Weisbecker ret = trace_seq_printf(s, "}\n"); 69683a8df61SFrederic Weisbecker if (!ret) 69783a8df61SFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 69883a8df61SFrederic Weisbecker 69983a8df61SFrederic Weisbecker /* Overrun */ 700287b6e68SFrederic Weisbecker if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERRUN) { 701287b6e68SFrederic Weisbecker ret = trace_seq_printf(s, " (Overruns: %lu)\n", 702287b6e68SFrederic Weisbecker trace->overrun); 703287b6e68SFrederic Weisbecker if (!ret) 704287b6e68SFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 705287b6e68SFrederic Weisbecker } 706f8b755acSFrederic Weisbecker 707d1f9cbd7SFrederic Weisbecker ret = print_graph_irq(iter, trace->func, TRACE_GRAPH_RET, cpu, pid); 708f8b755acSFrederic Weisbecker if (ret == TRACE_TYPE_PARTIAL_LINE) 709f8b755acSFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 710f8b755acSFrederic Weisbecker 711287b6e68SFrederic Weisbecker return TRACE_TYPE_HANDLED; 712287b6e68SFrederic Weisbecker } 713fb52607aSFrederic Weisbecker 7141fd8f2a3SFrederic Weisbecker static enum print_line_t 71548ead020SFrederic Weisbecker print_graph_comment(struct bprint_entry *trace, struct trace_seq *s, 7161fd8f2a3SFrederic Weisbecker struct trace_entry *ent, struct trace_iterator *iter) 7171fd8f2a3SFrederic Weisbecker { 718*2fbcdb35SSteven Rostedt struct fgraph_data *data = iter->private; 719*2fbcdb35SSteven Rostedt int depth = 0; 7201fd8f2a3SFrederic Weisbecker int ret; 721*2fbcdb35SSteven Rostedt int i; 722*2fbcdb35SSteven Rostedt 723*2fbcdb35SSteven Rostedt if (data) 724*2fbcdb35SSteven Rostedt depth = per_cpu_ptr(data, iter->cpu)->depth; 7259005f3ebSFrederic Weisbecker 726ac5f6c96SSteven Rostedt if (print_graph_prologue(iter, s, 0, 0)) 727d1f9cbd7SFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 728d1f9cbd7SFrederic Weisbecker 7291fd8f2a3SFrederic Weisbecker /* No overhead */ 7309005f3ebSFrederic Weisbecker ret = print_graph_overhead(-1, s); 7311fd8f2a3SFrederic Weisbecker if (!ret) 7321fd8f2a3SFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 7331fd8f2a3SFrederic Weisbecker 7341fd8f2a3SFrederic Weisbecker /* No time */ 7359005f3ebSFrederic Weisbecker if (tracer_flags.val & TRACE_GRAPH_PRINT_DURATION) { 7361fd8f2a3SFrederic Weisbecker ret = trace_seq_printf(s, " | "); 7371fd8f2a3SFrederic Weisbecker if (!ret) 7381fd8f2a3SFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 7399005f3ebSFrederic Weisbecker } 7401fd8f2a3SFrederic Weisbecker 7411fd8f2a3SFrederic Weisbecker /* Indentation */ 742*2fbcdb35SSteven Rostedt if (depth > 0) 743*2fbcdb35SSteven Rostedt for (i = 0; i < (depth + 1) * TRACE_GRAPH_INDENT; i++) { 7441fd8f2a3SFrederic Weisbecker ret = trace_seq_printf(s, " "); 7451fd8f2a3SFrederic Weisbecker if (!ret) 7461fd8f2a3SFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 7471fd8f2a3SFrederic Weisbecker } 7481fd8f2a3SFrederic Weisbecker 7491fd8f2a3SFrederic Weisbecker /* The comment */ 750769b0441SFrederic Weisbecker ret = trace_seq_printf(s, "/* "); 751769b0441SFrederic Weisbecker if (!ret) 752769b0441SFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 753769b0441SFrederic Weisbecker 754769b0441SFrederic Weisbecker ret = trace_seq_bprintf(s, trace->fmt, trace->buf); 7551fd8f2a3SFrederic Weisbecker if (!ret) 7561fd8f2a3SFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 7571fd8f2a3SFrederic Weisbecker 758412d0bb5SFrederic Weisbecker /* Strip ending newline */ 759412d0bb5SFrederic Weisbecker if (s->buffer[s->len - 1] == '\n') { 760412d0bb5SFrederic Weisbecker s->buffer[s->len - 1] = '\0'; 761412d0bb5SFrederic Weisbecker s->len--; 762412d0bb5SFrederic Weisbecker } 763412d0bb5SFrederic Weisbecker 7641fd8f2a3SFrederic Weisbecker ret = trace_seq_printf(s, " */\n"); 7651fd8f2a3SFrederic Weisbecker if (!ret) 7661fd8f2a3SFrederic Weisbecker return TRACE_TYPE_PARTIAL_LINE; 7671fd8f2a3SFrederic Weisbecker 7681fd8f2a3SFrederic Weisbecker return TRACE_TYPE_HANDLED; 7691fd8f2a3SFrederic Weisbecker } 7701fd8f2a3SFrederic Weisbecker 7711fd8f2a3SFrederic Weisbecker 772fb52607aSFrederic Weisbecker enum print_line_t 773fb52607aSFrederic Weisbecker print_graph_function(struct trace_iterator *iter) 774fb52607aSFrederic Weisbecker { 775fb52607aSFrederic Weisbecker struct trace_seq *s = &iter->seq; 776fb52607aSFrederic Weisbecker struct trace_entry *entry = iter->ent; 777fb52607aSFrederic Weisbecker 778287b6e68SFrederic Weisbecker switch (entry->type) { 779287b6e68SFrederic Weisbecker case TRACE_GRAPH_ENT: { 780287b6e68SFrederic Weisbecker struct ftrace_graph_ent_entry *field; 781fb52607aSFrederic Weisbecker trace_assign_type(field, entry); 7829005f3ebSFrederic Weisbecker return print_graph_entry(field, s, iter); 783fb52607aSFrederic Weisbecker } 784287b6e68SFrederic Weisbecker case TRACE_GRAPH_RET: { 785287b6e68SFrederic Weisbecker struct ftrace_graph_ret_entry *field; 786287b6e68SFrederic Weisbecker trace_assign_type(field, entry); 7879005f3ebSFrederic Weisbecker return print_graph_return(&field->ret, s, entry, iter); 788fb52607aSFrederic Weisbecker } 78948ead020SFrederic Weisbecker case TRACE_BPRINT: { 79048ead020SFrederic Weisbecker struct bprint_entry *field; 7911fd8f2a3SFrederic Weisbecker trace_assign_type(field, entry); 7921fd8f2a3SFrederic Weisbecker return print_graph_comment(field, s, entry, iter); 7931fd8f2a3SFrederic Weisbecker } 794287b6e68SFrederic Weisbecker default: 795fb52607aSFrederic Weisbecker return TRACE_TYPE_UNHANDLED; 796fb52607aSFrederic Weisbecker } 797287b6e68SFrederic Weisbecker } 798fb52607aSFrederic Weisbecker 799decbec38SFrederic Weisbecker static void print_graph_headers(struct seq_file *s) 800decbec38SFrederic Weisbecker { 801decbec38SFrederic Weisbecker /* 1st line */ 802decbec38SFrederic Weisbecker seq_printf(s, "# "); 8039005f3ebSFrederic Weisbecker if (tracer_flags.val & TRACE_GRAPH_PRINT_ABS_TIME) 8049005f3ebSFrederic Weisbecker seq_printf(s, " TIME "); 805decbec38SFrederic Weisbecker if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) 806decbec38SFrederic Weisbecker seq_printf(s, "CPU"); 807decbec38SFrederic Weisbecker if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC) 808decbec38SFrederic Weisbecker seq_printf(s, " TASK/PID "); 8099005f3ebSFrederic Weisbecker if (tracer_flags.val & TRACE_GRAPH_PRINT_DURATION) 8109005f3ebSFrederic Weisbecker seq_printf(s, " DURATION "); 8119005f3ebSFrederic Weisbecker seq_printf(s, " FUNCTION CALLS\n"); 812decbec38SFrederic Weisbecker 813decbec38SFrederic Weisbecker /* 2nd line */ 814decbec38SFrederic Weisbecker seq_printf(s, "# "); 8159005f3ebSFrederic Weisbecker if (tracer_flags.val & TRACE_GRAPH_PRINT_ABS_TIME) 8169005f3ebSFrederic Weisbecker seq_printf(s, " | "); 817decbec38SFrederic Weisbecker if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) 818decbec38SFrederic Weisbecker seq_printf(s, "| "); 819decbec38SFrederic Weisbecker if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC) 820decbec38SFrederic Weisbecker seq_printf(s, " | | "); 8219005f3ebSFrederic Weisbecker if (tracer_flags.val & TRACE_GRAPH_PRINT_DURATION) 8229005f3ebSFrederic Weisbecker seq_printf(s, " | | "); 8239005f3ebSFrederic Weisbecker seq_printf(s, " | | | |\n"); 824decbec38SFrederic Weisbecker } 8259005f3ebSFrederic Weisbecker 8269005f3ebSFrederic Weisbecker static void graph_trace_open(struct trace_iterator *iter) 8279005f3ebSFrederic Weisbecker { 828*2fbcdb35SSteven Rostedt /* pid and depth on the last trace processed */ 829*2fbcdb35SSteven Rostedt struct fgraph_data *data = alloc_percpu(struct fgraph_data); 8309005f3ebSFrederic Weisbecker int cpu; 8319005f3ebSFrederic Weisbecker 832*2fbcdb35SSteven Rostedt if (!data) 8339005f3ebSFrederic Weisbecker pr_warning("function graph tracer: not enough memory\n"); 8349005f3ebSFrederic Weisbecker else 8359005f3ebSFrederic Weisbecker for_each_possible_cpu(cpu) { 836*2fbcdb35SSteven Rostedt pid_t *pid = &(per_cpu_ptr(data, cpu)->last_pid); 837*2fbcdb35SSteven Rostedt int *depth = &(per_cpu_ptr(data, cpu)->depth); 8389005f3ebSFrederic Weisbecker *pid = -1; 839*2fbcdb35SSteven Rostedt *depth = 0; 8409005f3ebSFrederic Weisbecker } 8419005f3ebSFrederic Weisbecker 842*2fbcdb35SSteven Rostedt iter->private = data; 8439005f3ebSFrederic Weisbecker } 8449005f3ebSFrederic Weisbecker 8459005f3ebSFrederic Weisbecker static void graph_trace_close(struct trace_iterator *iter) 8469005f3ebSFrederic Weisbecker { 8478293dd6fSIngo Molnar free_percpu(iter->private); 8489005f3ebSFrederic Weisbecker } 8499005f3ebSFrederic Weisbecker 850fb52607aSFrederic Weisbecker static struct tracer graph_trace __read_mostly = { 85183a8df61SFrederic Weisbecker .name = "function_graph", 8529005f3ebSFrederic Weisbecker .open = graph_trace_open, 8539005f3ebSFrederic Weisbecker .close = graph_trace_close, 8546eaaa5d5SFrederic Weisbecker .wait_pipe = poll_wait_pipe, 855fb52607aSFrederic Weisbecker .init = graph_trace_init, 856fb52607aSFrederic Weisbecker .reset = graph_trace_reset, 857fb52607aSFrederic Weisbecker .print_line = print_graph_function, 858decbec38SFrederic Weisbecker .print_header = print_graph_headers, 859fb52607aSFrederic Weisbecker .flags = &tracer_flags, 8607447dce9SFrederic Weisbecker #ifdef CONFIG_FTRACE_SELFTEST 8617447dce9SFrederic Weisbecker .selftest = trace_selftest_startup_function_graph, 8627447dce9SFrederic Weisbecker #endif 863fb52607aSFrederic Weisbecker }; 864fb52607aSFrederic Weisbecker 865fb52607aSFrederic Weisbecker static __init int init_graph_trace(void) 866fb52607aSFrederic Weisbecker { 867fb52607aSFrederic Weisbecker return register_tracer(&graph_trace); 868fb52607aSFrederic Weisbecker } 869fb52607aSFrederic Weisbecker 870fb52607aSFrederic Weisbecker device_initcall(init_graph_trace); 871