trace_functions_graph.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673
  1. /*
  2. *
  3. * Function graph tracer.
  4. * Copyright (c) 2008 Frederic Weisbecker <fweisbec@gmail.com>
  5. * Mostly borrowed from function tracer which
  6. * is Copyright (c) Steven Rostedt <srostedt@redhat.com>
  7. *
  8. */
  9. #include <linux/debugfs.h>
  10. #include <linux/uaccess.h>
  11. #include <linux/ftrace.h>
  12. #include <linux/fs.h>
  13. #include "trace.h"
  14. #include "trace_output.h"
  15. #define TRACE_GRAPH_INDENT 2
  16. /* Flag options */
  17. #define TRACE_GRAPH_PRINT_OVERRUN 0x1
  18. #define TRACE_GRAPH_PRINT_CPU 0x2
  19. #define TRACE_GRAPH_PRINT_OVERHEAD 0x4
  20. #define TRACE_GRAPH_PRINT_PROC 0x8
  21. static struct tracer_opt trace_opts[] = {
  22. /* Display overruns ? */
  23. { TRACER_OPT(funcgraph-overrun, TRACE_GRAPH_PRINT_OVERRUN) },
  24. /* Display CPU ? */
  25. { TRACER_OPT(funcgraph-cpu, TRACE_GRAPH_PRINT_CPU) },
  26. /* Display Overhead ? */
  27. { TRACER_OPT(funcgraph-overhead, TRACE_GRAPH_PRINT_OVERHEAD) },
  28. /* Display proc name/pid */
  29. { TRACER_OPT(funcgraph-proc, TRACE_GRAPH_PRINT_PROC) },
  30. { } /* Empty entry */
  31. };
  32. static struct tracer_flags tracer_flags = {
  33. /* Don't display overruns and proc by default */
  34. .val = TRACE_GRAPH_PRINT_CPU | TRACE_GRAPH_PRINT_OVERHEAD,
  35. .opts = trace_opts
  36. };
  37. /* pid on the last trace processed */
  38. static pid_t last_pid[NR_CPUS] = { [0 ... NR_CPUS-1] = -1 };
  39. static int graph_trace_init(struct trace_array *tr)
  40. {
  41. int cpu, ret;
  42. for_each_online_cpu(cpu)
  43. tracing_reset(tr, cpu);
  44. ret = register_ftrace_graph(&trace_graph_return,
  45. &trace_graph_entry);
  46. if (ret)
  47. return ret;
  48. tracing_start_cmdline_record();
  49. return 0;
  50. }
  51. static void graph_trace_reset(struct trace_array *tr)
  52. {
  53. tracing_stop_cmdline_record();
  54. unregister_ftrace_graph();
  55. }
  56. static inline int log10_cpu(int nb)
  57. {
  58. if (nb / 100)
  59. return 3;
  60. if (nb / 10)
  61. return 2;
  62. return 1;
  63. }
  64. static enum print_line_t
  65. print_graph_cpu(struct trace_seq *s, int cpu)
  66. {
  67. int i;
  68. int ret;
  69. int log10_this = log10_cpu(cpu);
  70. int log10_all = log10_cpu(cpumask_weight(cpu_online_mask));
  71. /*
  72. * Start with a space character - to make it stand out
  73. * to the right a bit when trace output is pasted into
  74. * email:
  75. */
  76. ret = trace_seq_printf(s, " ");
  77. /*
  78. * Tricky - we space the CPU field according to the max
  79. * number of online CPUs. On a 2-cpu system it would take
  80. * a maximum of 1 digit - on a 128 cpu system it would
  81. * take up to 3 digits:
  82. */
  83. for (i = 0; i < log10_all - log10_this; i++) {
  84. ret = trace_seq_printf(s, " ");
  85. if (!ret)
  86. return TRACE_TYPE_PARTIAL_LINE;
  87. }
  88. ret = trace_seq_printf(s, "%d) ", cpu);
  89. if (!ret)
  90. return TRACE_TYPE_PARTIAL_LINE;
  91. return TRACE_TYPE_HANDLED;
  92. }
  93. #define TRACE_GRAPH_PROCINFO_LENGTH 14
  94. static enum print_line_t
  95. print_graph_proc(struct trace_seq *s, pid_t pid)
  96. {
  97. int i;
  98. int ret;
  99. int len;
  100. char comm[8];
  101. int spaces = 0;
  102. /* sign + log10(MAX_INT) + '\0' */
  103. char pid_str[11];
  104. strncpy(comm, trace_find_cmdline(pid), 7);
  105. comm[7] = '\0';
  106. sprintf(pid_str, "%d", pid);
  107. /* 1 stands for the "-" character */
  108. len = strlen(comm) + strlen(pid_str) + 1;
  109. if (len < TRACE_GRAPH_PROCINFO_LENGTH)
  110. spaces = TRACE_GRAPH_PROCINFO_LENGTH - len;
  111. /* First spaces to align center */
  112. for (i = 0; i < spaces / 2; i++) {
  113. ret = trace_seq_printf(s, " ");
  114. if (!ret)
  115. return TRACE_TYPE_PARTIAL_LINE;
  116. }
  117. ret = trace_seq_printf(s, "%s-%s", comm, pid_str);
  118. if (!ret)
  119. return TRACE_TYPE_PARTIAL_LINE;
  120. /* Last spaces to align center */
  121. for (i = 0; i < spaces - (spaces / 2); i++) {
  122. ret = trace_seq_printf(s, " ");
  123. if (!ret)
  124. return TRACE_TYPE_PARTIAL_LINE;
  125. }
  126. return TRACE_TYPE_HANDLED;
  127. }
  128. /* If the pid changed since the last trace, output this event */
  129. static enum print_line_t
  130. verif_pid(struct trace_seq *s, pid_t pid, int cpu)
  131. {
  132. pid_t prev_pid;
  133. int ret;
  134. if (last_pid[cpu] != -1 && last_pid[cpu] == pid)
  135. return TRACE_TYPE_HANDLED;
  136. prev_pid = last_pid[cpu];
  137. last_pid[cpu] = pid;
  138. /*
  139. * Context-switch trace line:
  140. ------------------------------------------
  141. | 1) migration/0--1 => sshd-1755
  142. ------------------------------------------
  143. */
  144. ret = trace_seq_printf(s,
  145. " ------------------------------------------\n");
  146. if (!ret)
  147. TRACE_TYPE_PARTIAL_LINE;
  148. ret = print_graph_cpu(s, cpu);
  149. if (ret == TRACE_TYPE_PARTIAL_LINE)
  150. TRACE_TYPE_PARTIAL_LINE;
  151. ret = print_graph_proc(s, prev_pid);
  152. if (ret == TRACE_TYPE_PARTIAL_LINE)
  153. TRACE_TYPE_PARTIAL_LINE;
  154. ret = trace_seq_printf(s, " => ");
  155. if (!ret)
  156. TRACE_TYPE_PARTIAL_LINE;
  157. ret = print_graph_proc(s, pid);
  158. if (ret == TRACE_TYPE_PARTIAL_LINE)
  159. TRACE_TYPE_PARTIAL_LINE;
  160. ret = trace_seq_printf(s,
  161. "\n ------------------------------------------\n\n");
  162. if (!ret)
  163. TRACE_TYPE_PARTIAL_LINE;
  164. return ret;
  165. }
  166. static bool
  167. trace_branch_is_leaf(struct trace_iterator *iter,
  168. struct ftrace_graph_ent_entry *curr)
  169. {
  170. struct ring_buffer_iter *ring_iter;
  171. struct ring_buffer_event *event;
  172. struct ftrace_graph_ret_entry *next;
  173. ring_iter = iter->buffer_iter[iter->cpu];
  174. if (!ring_iter)
  175. return false;
  176. event = ring_buffer_iter_peek(ring_iter, NULL);
  177. if (!event)
  178. return false;
  179. next = ring_buffer_event_data(event);
  180. if (next->ent.type != TRACE_GRAPH_RET)
  181. return false;
  182. if (curr->ent.pid != next->ent.pid ||
  183. curr->graph_ent.func != next->ret.func)
  184. return false;
  185. return true;
  186. }
  187. static enum print_line_t
  188. print_graph_irq(struct trace_seq *s, unsigned long addr,
  189. enum trace_type type, int cpu, pid_t pid)
  190. {
  191. int ret;
  192. if (addr < (unsigned long)__irqentry_text_start ||
  193. addr >= (unsigned long)__irqentry_text_end)
  194. return TRACE_TYPE_UNHANDLED;
  195. if (type == TRACE_GRAPH_ENT) {
  196. ret = trace_seq_printf(s, "==========> | ");
  197. } else {
  198. /* Cpu */
  199. if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) {
  200. ret = print_graph_cpu(s, cpu);
  201. if (ret == TRACE_TYPE_PARTIAL_LINE)
  202. return TRACE_TYPE_PARTIAL_LINE;
  203. }
  204. /* Proc */
  205. if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC) {
  206. ret = print_graph_proc(s, pid);
  207. if (ret == TRACE_TYPE_PARTIAL_LINE)
  208. return TRACE_TYPE_PARTIAL_LINE;
  209. ret = trace_seq_printf(s, " | ");
  210. if (!ret)
  211. return TRACE_TYPE_PARTIAL_LINE;
  212. }
  213. /* No overhead */
  214. if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) {
  215. ret = trace_seq_printf(s, " ");
  216. if (!ret)
  217. return TRACE_TYPE_PARTIAL_LINE;
  218. }
  219. ret = trace_seq_printf(s, "<========== |\n");
  220. }
  221. if (!ret)
  222. return TRACE_TYPE_PARTIAL_LINE;
  223. return TRACE_TYPE_HANDLED;
  224. }
  225. static enum print_line_t
  226. print_graph_duration(unsigned long long duration, struct trace_seq *s)
  227. {
  228. unsigned long nsecs_rem = do_div(duration, 1000);
  229. /* log10(ULONG_MAX) + '\0' */
  230. char msecs_str[21];
  231. char nsecs_str[5];
  232. int ret, len;
  233. int i;
  234. sprintf(msecs_str, "%lu", (unsigned long) duration);
  235. /* Print msecs */
  236. ret = trace_seq_printf(s, msecs_str);
  237. if (!ret)
  238. return TRACE_TYPE_PARTIAL_LINE;
  239. len = strlen(msecs_str);
  240. /* Print nsecs (we don't want to exceed 7 numbers) */
  241. if (len < 7) {
  242. snprintf(nsecs_str, 8 - len, "%03lu", nsecs_rem);
  243. ret = trace_seq_printf(s, ".%s", nsecs_str);
  244. if (!ret)
  245. return TRACE_TYPE_PARTIAL_LINE;
  246. len += strlen(nsecs_str);
  247. }
  248. ret = trace_seq_printf(s, " us ");
  249. if (!ret)
  250. return TRACE_TYPE_PARTIAL_LINE;
  251. /* Print remaining spaces to fit the row's width */
  252. for (i = len; i < 7; i++) {
  253. ret = trace_seq_printf(s, " ");
  254. if (!ret)
  255. return TRACE_TYPE_PARTIAL_LINE;
  256. }
  257. ret = trace_seq_printf(s, "| ");
  258. if (!ret)
  259. return TRACE_TYPE_PARTIAL_LINE;
  260. return TRACE_TYPE_HANDLED;
  261. }
  262. /* Signal a overhead of time execution to the output */
  263. static int
  264. print_graph_overhead(unsigned long long duration, struct trace_seq *s)
  265. {
  266. /* Duration exceeded 100 msecs */
  267. if (duration > 100000ULL)
  268. return trace_seq_printf(s, "! ");
  269. /* Duration exceeded 10 msecs */
  270. if (duration > 10000ULL)
  271. return trace_seq_printf(s, "+ ");
  272. return trace_seq_printf(s, " ");
  273. }
  274. /* Case of a leaf function on its call entry */
  275. static enum print_line_t
  276. print_graph_entry_leaf(struct trace_iterator *iter,
  277. struct ftrace_graph_ent_entry *entry, struct trace_seq *s)
  278. {
  279. struct ftrace_graph_ret_entry *ret_entry;
  280. struct ftrace_graph_ret *graph_ret;
  281. struct ring_buffer_event *event;
  282. struct ftrace_graph_ent *call;
  283. unsigned long long duration;
  284. int ret;
  285. int i;
  286. event = ring_buffer_read(iter->buffer_iter[iter->cpu], NULL);
  287. ret_entry = ring_buffer_event_data(event);
  288. graph_ret = &ret_entry->ret;
  289. call = &entry->graph_ent;
  290. duration = graph_ret->rettime - graph_ret->calltime;
  291. /* Overhead */
  292. if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) {
  293. ret = print_graph_overhead(duration, s);
  294. if (!ret)
  295. return TRACE_TYPE_PARTIAL_LINE;
  296. }
  297. /* Duration */
  298. ret = print_graph_duration(duration, s);
  299. if (ret == TRACE_TYPE_PARTIAL_LINE)
  300. return TRACE_TYPE_PARTIAL_LINE;
  301. /* Function */
  302. for (i = 0; i < call->depth * TRACE_GRAPH_INDENT; i++) {
  303. ret = trace_seq_printf(s, " ");
  304. if (!ret)
  305. return TRACE_TYPE_PARTIAL_LINE;
  306. }
  307. ret = seq_print_ip_sym(s, call->func, 0);
  308. if (!ret)
  309. return TRACE_TYPE_PARTIAL_LINE;
  310. ret = trace_seq_printf(s, "();\n");
  311. if (!ret)
  312. return TRACE_TYPE_PARTIAL_LINE;
  313. return TRACE_TYPE_HANDLED;
  314. }
  315. static enum print_line_t
  316. print_graph_entry_nested(struct ftrace_graph_ent_entry *entry,
  317. struct trace_seq *s, pid_t pid, int cpu)
  318. {
  319. int i;
  320. int ret;
  321. struct ftrace_graph_ent *call = &entry->graph_ent;
  322. /* No overhead */
  323. if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) {
  324. ret = trace_seq_printf(s, " ");
  325. if (!ret)
  326. return TRACE_TYPE_PARTIAL_LINE;
  327. }
  328. /* Interrupt */
  329. ret = print_graph_irq(s, call->func, TRACE_GRAPH_ENT, cpu, pid);
  330. if (ret == TRACE_TYPE_UNHANDLED) {
  331. /* No time */
  332. ret = trace_seq_printf(s, " | ");
  333. if (!ret)
  334. return TRACE_TYPE_PARTIAL_LINE;
  335. } else {
  336. if (ret == TRACE_TYPE_PARTIAL_LINE)
  337. return TRACE_TYPE_PARTIAL_LINE;
  338. }
  339. /* Function */
  340. for (i = 0; i < call->depth * TRACE_GRAPH_INDENT; i++) {
  341. ret = trace_seq_printf(s, " ");
  342. if (!ret)
  343. return TRACE_TYPE_PARTIAL_LINE;
  344. }
  345. ret = seq_print_ip_sym(s, call->func, 0);
  346. if (!ret)
  347. return TRACE_TYPE_PARTIAL_LINE;
  348. ret = trace_seq_printf(s, "() {\n");
  349. if (!ret)
  350. return TRACE_TYPE_PARTIAL_LINE;
  351. return TRACE_TYPE_HANDLED;
  352. }
  353. static enum print_line_t
  354. print_graph_entry(struct ftrace_graph_ent_entry *field, struct trace_seq *s,
  355. struct trace_iterator *iter, int cpu)
  356. {
  357. int ret;
  358. struct trace_entry *ent = iter->ent;
  359. /* Pid */
  360. if (verif_pid(s, ent->pid, cpu) == TRACE_TYPE_PARTIAL_LINE)
  361. return TRACE_TYPE_PARTIAL_LINE;
  362. /* Cpu */
  363. if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) {
  364. ret = print_graph_cpu(s, cpu);
  365. if (ret == TRACE_TYPE_PARTIAL_LINE)
  366. return TRACE_TYPE_PARTIAL_LINE;
  367. }
  368. /* Proc */
  369. if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC) {
  370. ret = print_graph_proc(s, ent->pid);
  371. if (ret == TRACE_TYPE_PARTIAL_LINE)
  372. return TRACE_TYPE_PARTIAL_LINE;
  373. ret = trace_seq_printf(s, " | ");
  374. if (!ret)
  375. return TRACE_TYPE_PARTIAL_LINE;
  376. }
  377. if (trace_branch_is_leaf(iter, field))
  378. return print_graph_entry_leaf(iter, field, s);
  379. else
  380. return print_graph_entry_nested(field, s, iter->ent->pid, cpu);
  381. }
  382. static enum print_line_t
  383. print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s,
  384. struct trace_entry *ent, int cpu)
  385. {
  386. int i;
  387. int ret;
  388. unsigned long long duration = trace->rettime - trace->calltime;
  389. /* Pid */
  390. if (verif_pid(s, ent->pid, cpu) == TRACE_TYPE_PARTIAL_LINE)
  391. return TRACE_TYPE_PARTIAL_LINE;
  392. /* Cpu */
  393. if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) {
  394. ret = print_graph_cpu(s, cpu);
  395. if (ret == TRACE_TYPE_PARTIAL_LINE)
  396. return TRACE_TYPE_PARTIAL_LINE;
  397. }
  398. /* Proc */
  399. if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC) {
  400. ret = print_graph_proc(s, ent->pid);
  401. if (ret == TRACE_TYPE_PARTIAL_LINE)
  402. return TRACE_TYPE_PARTIAL_LINE;
  403. ret = trace_seq_printf(s, " | ");
  404. if (!ret)
  405. return TRACE_TYPE_PARTIAL_LINE;
  406. }
  407. /* Overhead */
  408. if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) {
  409. ret = print_graph_overhead(duration, s);
  410. if (!ret)
  411. return TRACE_TYPE_PARTIAL_LINE;
  412. }
  413. /* Duration */
  414. ret = print_graph_duration(duration, s);
  415. if (ret == TRACE_TYPE_PARTIAL_LINE)
  416. return TRACE_TYPE_PARTIAL_LINE;
  417. /* Closing brace */
  418. for (i = 0; i < trace->depth * TRACE_GRAPH_INDENT; i++) {
  419. ret = trace_seq_printf(s, " ");
  420. if (!ret)
  421. return TRACE_TYPE_PARTIAL_LINE;
  422. }
  423. ret = trace_seq_printf(s, "}\n");
  424. if (!ret)
  425. return TRACE_TYPE_PARTIAL_LINE;
  426. /* Overrun */
  427. if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERRUN) {
  428. ret = trace_seq_printf(s, " (Overruns: %lu)\n",
  429. trace->overrun);
  430. if (!ret)
  431. return TRACE_TYPE_PARTIAL_LINE;
  432. }
  433. ret = print_graph_irq(s, trace->func, TRACE_GRAPH_RET, cpu, ent->pid);
  434. if (ret == TRACE_TYPE_PARTIAL_LINE)
  435. return TRACE_TYPE_PARTIAL_LINE;
  436. return TRACE_TYPE_HANDLED;
  437. }
  438. static enum print_line_t
  439. print_graph_comment(struct print_entry *trace, struct trace_seq *s,
  440. struct trace_entry *ent, struct trace_iterator *iter)
  441. {
  442. int i;
  443. int ret;
  444. /* Pid */
  445. if (verif_pid(s, ent->pid, iter->cpu) == TRACE_TYPE_PARTIAL_LINE)
  446. return TRACE_TYPE_PARTIAL_LINE;
  447. /* Cpu */
  448. if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) {
  449. ret = print_graph_cpu(s, iter->cpu);
  450. if (ret == TRACE_TYPE_PARTIAL_LINE)
  451. return TRACE_TYPE_PARTIAL_LINE;
  452. }
  453. /* Proc */
  454. if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC) {
  455. ret = print_graph_proc(s, ent->pid);
  456. if (ret == TRACE_TYPE_PARTIAL_LINE)
  457. return TRACE_TYPE_PARTIAL_LINE;
  458. ret = trace_seq_printf(s, " | ");
  459. if (!ret)
  460. return TRACE_TYPE_PARTIAL_LINE;
  461. }
  462. /* No overhead */
  463. if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) {
  464. ret = trace_seq_printf(s, " ");
  465. if (!ret)
  466. return TRACE_TYPE_PARTIAL_LINE;
  467. }
  468. /* No time */
  469. ret = trace_seq_printf(s, " | ");
  470. if (!ret)
  471. return TRACE_TYPE_PARTIAL_LINE;
  472. /* Indentation */
  473. if (trace->depth > 0)
  474. for (i = 0; i < (trace->depth + 1) * TRACE_GRAPH_INDENT; i++) {
  475. ret = trace_seq_printf(s, " ");
  476. if (!ret)
  477. return TRACE_TYPE_PARTIAL_LINE;
  478. }
  479. /* The comment */
  480. ret = trace_seq_printf(s, "/* %s", trace->buf);
  481. if (!ret)
  482. return TRACE_TYPE_PARTIAL_LINE;
  483. /* Strip ending newline */
  484. if (s->buffer[s->len - 1] == '\n') {
  485. s->buffer[s->len - 1] = '\0';
  486. s->len--;
  487. }
  488. ret = trace_seq_printf(s, " */\n");
  489. if (!ret)
  490. return TRACE_TYPE_PARTIAL_LINE;
  491. return TRACE_TYPE_HANDLED;
  492. }
  493. enum print_line_t
  494. print_graph_function(struct trace_iterator *iter)
  495. {
  496. struct trace_seq *s = &iter->seq;
  497. struct trace_entry *entry = iter->ent;
  498. switch (entry->type) {
  499. case TRACE_GRAPH_ENT: {
  500. struct ftrace_graph_ent_entry *field;
  501. trace_assign_type(field, entry);
  502. return print_graph_entry(field, s, iter,
  503. iter->cpu);
  504. }
  505. case TRACE_GRAPH_RET: {
  506. struct ftrace_graph_ret_entry *field;
  507. trace_assign_type(field, entry);
  508. return print_graph_return(&field->ret, s, entry, iter->cpu);
  509. }
  510. case TRACE_PRINT: {
  511. struct print_entry *field;
  512. trace_assign_type(field, entry);
  513. return print_graph_comment(field, s, entry, iter);
  514. }
  515. default:
  516. return TRACE_TYPE_UNHANDLED;
  517. }
  518. }
  519. static void print_graph_headers(struct seq_file *s)
  520. {
  521. /* 1st line */
  522. seq_printf(s, "# ");
  523. if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU)
  524. seq_printf(s, "CPU ");
  525. if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC)
  526. seq_printf(s, "TASK/PID ");
  527. if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD)
  528. seq_printf(s, "OVERHEAD/");
  529. seq_printf(s, "DURATION FUNCTION CALLS\n");
  530. /* 2nd line */
  531. seq_printf(s, "# ");
  532. if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU)
  533. seq_printf(s, "| ");
  534. if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC)
  535. seq_printf(s, "| | ");
  536. if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) {
  537. seq_printf(s, "| ");
  538. seq_printf(s, "| | | | |\n");
  539. } else
  540. seq_printf(s, " | | | | |\n");
  541. }
  542. static struct tracer graph_trace __read_mostly = {
  543. .name = "function_graph",
  544. .init = graph_trace_init,
  545. .reset = graph_trace_reset,
  546. .print_line = print_graph_function,
  547. .print_header = print_graph_headers,
  548. .flags = &tracer_flags,
  549. };
  550. static __init int init_graph_trace(void)
  551. {
  552. return register_tracer(&graph_trace);
  553. }
  554. device_initcall(init_graph_trace);