diff options
| author | Ingo Molnar <mingo@elte.hu> | 2009-12-10 02:18:41 -0500 |
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2009-12-10 02:18:41 -0500 |
| commit | 788d70dce0184eccc249ac6f05aa38b385b7497c (patch) | |
| tree | 607f2d23bab877a530bbd3b7da2b9fd9e6c24ce1 /kernel/trace/trace_functions_graph.c | |
| parent | 2b876f95d03e226394b5d360c86127cbefaf614b (diff) | |
| parent | f2942487ffb0c0a80b2312f667ea30dd55a24bb0 (diff) | |
Merge branch 'tip/tracing/core3' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-2.6-trace into tracing/core
Diffstat (limited to 'kernel/trace/trace_functions_graph.c')
| -rw-r--r-- | kernel/trace/trace_functions_graph.c | 165 |
1 files changed, 131 insertions, 34 deletions
diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c index 45e6c01b2e4d..a43d009c561a 100644 --- a/kernel/trace/trace_functions_graph.c +++ b/kernel/trace/trace_functions_graph.c | |||
| @@ -14,9 +14,20 @@ | |||
| 14 | #include "trace.h" | 14 | #include "trace.h" |
| 15 | #include "trace_output.h" | 15 | #include "trace_output.h" |
| 16 | 16 | ||
| 17 | struct fgraph_data { | 17 | struct fgraph_cpu_data { |
| 18 | pid_t last_pid; | 18 | pid_t last_pid; |
| 19 | int depth; | 19 | int depth; |
| 20 | int ignore; | ||
| 21 | }; | ||
| 22 | |||
| 23 | struct fgraph_data { | ||
| 24 | struct fgraph_cpu_data *cpu_data; | ||
| 25 | |||
| 26 | /* Place to preserve last processed entry. */ | ||
| 27 | struct ftrace_graph_ent_entry ent; | ||
| 28 | struct ftrace_graph_ret_entry ret; | ||
| 29 | int failed; | ||
| 30 | int cpu; | ||
| 20 | }; | 31 | }; |
| 21 | 32 | ||
| 22 | #define TRACE_GRAPH_INDENT 2 | 33 | #define TRACE_GRAPH_INDENT 2 |
| @@ -384,7 +395,7 @@ verif_pid(struct trace_seq *s, pid_t pid, int cpu, struct fgraph_data *data) | |||
| 384 | if (!data) | 395 | if (!data) |
| 385 | return TRACE_TYPE_HANDLED; | 396 | return TRACE_TYPE_HANDLED; |
| 386 | 397 | ||
| 387 | last_pid = &(per_cpu_ptr(data, cpu)->last_pid); | 398 | last_pid = &(per_cpu_ptr(data->cpu_data, cpu)->last_pid); |
| 388 | 399 | ||
| 389 | if (*last_pid == pid) | 400 | if (*last_pid == pid) |
| 390 | return TRACE_TYPE_HANDLED; | 401 | return TRACE_TYPE_HANDLED; |
| @@ -435,26 +446,49 @@ static struct ftrace_graph_ret_entry * | |||
| 435 | get_return_for_leaf(struct trace_iterator *iter, | 446 | get_return_for_leaf(struct trace_iterator *iter, |
| 436 | struct ftrace_graph_ent_entry *curr) | 447 | struct ftrace_graph_ent_entry *curr) |
| 437 | { | 448 | { |
| 438 | struct ring_buffer_iter *ring_iter; | 449 | struct fgraph_data *data = iter->private; |
| 450 | struct ring_buffer_iter *ring_iter = NULL; | ||
| 439 | struct ring_buffer_event *event; | 451 | struct ring_buffer_event *event; |
| 440 | struct ftrace_graph_ret_entry *next; | 452 | struct ftrace_graph_ret_entry *next; |
| 441 | 453 | ||
| 442 | ring_iter = iter->buffer_iter[iter->cpu]; | 454 | /* |
| 455 | * If the previous output failed to write to the seq buffer, | ||
| 456 | * then we just reuse the data from before. | ||
| 457 | */ | ||
| 458 | if (data && data->failed) { | ||
| 459 | curr = &data->ent; | ||
| 460 | next = &data->ret; | ||
| 461 | } else { | ||
| 443 | 462 | ||
| 444 | /* First peek to compare current entry and the next one */ | 463 | ring_iter = iter->buffer_iter[iter->cpu]; |
| 445 | if (ring_iter) | 464 | |
| 446 | event = ring_buffer_iter_peek(ring_iter, NULL); | 465 | /* First peek to compare current entry and the next one */ |
| 447 | else { | 466 | if (ring_iter) |
| 448 | /* We need to consume the current entry to see the next one */ | 467 | event = ring_buffer_iter_peek(ring_iter, NULL); |
| 449 | ring_buffer_consume(iter->tr->buffer, iter->cpu, NULL); | 468 | else { |
| 450 | event = ring_buffer_peek(iter->tr->buffer, iter->cpu, | 469 | /* |
| 451 | NULL); | 470 | * We need to consume the current entry to see |
| 452 | } | 471 | * the next one. |
| 472 | */ | ||
| 473 | ring_buffer_consume(iter->tr->buffer, iter->cpu, NULL); | ||
| 474 | event = ring_buffer_peek(iter->tr->buffer, iter->cpu, | ||
| 475 | NULL); | ||
| 476 | } | ||
| 453 | 477 | ||
| 454 | if (!event) | 478 | if (!event) |
| 455 | return NULL; | 479 | return NULL; |
| 480 | |||
| 481 | next = ring_buffer_event_data(event); | ||
| 456 | 482 | ||
| 457 | next = ring_buffer_event_data(event); | 483 | if (data) { |
| 484 | /* | ||
| 485 | * Save current and next entries for later reference | ||
| 486 | * if the output fails. | ||
| 487 | */ | ||
| 488 | data->ent = *curr; | ||
| 489 | data->ret = *next; | ||
| 490 | } | ||
| 491 | } | ||
| 458 | 492 | ||
| 459 | if (next->ent.type != TRACE_GRAPH_RET) | 493 | if (next->ent.type != TRACE_GRAPH_RET) |
| 460 | return NULL; | 494 | return NULL; |
| @@ -640,7 +674,7 @@ print_graph_entry_leaf(struct trace_iterator *iter, | |||
| 640 | 674 | ||
| 641 | if (data) { | 675 | if (data) { |
| 642 | int cpu = iter->cpu; | 676 | int cpu = iter->cpu; |
| 643 | int *depth = &(per_cpu_ptr(data, cpu)->depth); | 677 | int *depth = &(per_cpu_ptr(data->cpu_data, cpu)->depth); |
| 644 | 678 | ||
| 645 | /* | 679 | /* |
| 646 | * Comments display at + 1 to depth. Since | 680 | * Comments display at + 1 to depth. Since |
| @@ -688,7 +722,7 @@ print_graph_entry_nested(struct trace_iterator *iter, | |||
| 688 | 722 | ||
| 689 | if (data) { | 723 | if (data) { |
| 690 | int cpu = iter->cpu; | 724 | int cpu = iter->cpu; |
| 691 | int *depth = &(per_cpu_ptr(data, cpu)->depth); | 725 | int *depth = &(per_cpu_ptr(data->cpu_data, cpu)->depth); |
| 692 | 726 | ||
| 693 | *depth = call->depth; | 727 | *depth = call->depth; |
| 694 | } | 728 | } |
| @@ -782,19 +816,34 @@ static enum print_line_t | |||
| 782 | print_graph_entry(struct ftrace_graph_ent_entry *field, struct trace_seq *s, | 816 | print_graph_entry(struct ftrace_graph_ent_entry *field, struct trace_seq *s, |
| 783 | struct trace_iterator *iter) | 817 | struct trace_iterator *iter) |
| 784 | { | 818 | { |
| 785 | int cpu = iter->cpu; | 819 | struct fgraph_data *data = iter->private; |
| 786 | struct ftrace_graph_ent *call = &field->graph_ent; | 820 | struct ftrace_graph_ent *call = &field->graph_ent; |
| 787 | struct ftrace_graph_ret_entry *leaf_ret; | 821 | struct ftrace_graph_ret_entry *leaf_ret; |
| 822 | static enum print_line_t ret; | ||
| 823 | int cpu = iter->cpu; | ||
| 788 | 824 | ||
| 789 | if (print_graph_prologue(iter, s, TRACE_GRAPH_ENT, call->func)) | 825 | if (print_graph_prologue(iter, s, TRACE_GRAPH_ENT, call->func)) |
| 790 | return TRACE_TYPE_PARTIAL_LINE; | 826 | return TRACE_TYPE_PARTIAL_LINE; |
| 791 | 827 | ||
| 792 | leaf_ret = get_return_for_leaf(iter, field); | 828 | leaf_ret = get_return_for_leaf(iter, field); |
| 793 | if (leaf_ret) | 829 | if (leaf_ret) |
| 794 | return print_graph_entry_leaf(iter, field, leaf_ret, s); | 830 | ret = print_graph_entry_leaf(iter, field, leaf_ret, s); |
| 795 | else | 831 | else |
| 796 | return print_graph_entry_nested(iter, field, s, cpu); | 832 | ret = print_graph_entry_nested(iter, field, s, cpu); |
| 797 | 833 | ||
| 834 | if (data) { | ||
| 835 | /* | ||
| 836 | * If we failed to write our output, then we need to make | ||
| 837 | * note of it. Because we already consumed our entry. | ||
| 838 | */ | ||
| 839 | if (s->full) { | ||
| 840 | data->failed = 1; | ||
| 841 | data->cpu = cpu; | ||
| 842 | } else | ||
| 843 | data->failed = 0; | ||
| 844 | } | ||
| 845 | |||
| 846 | return ret; | ||
| 798 | } | 847 | } |
| 799 | 848 | ||
| 800 | static enum print_line_t | 849 | static enum print_line_t |
| @@ -810,7 +859,7 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s, | |||
| 810 | 859 | ||
| 811 | if (data) { | 860 | if (data) { |
| 812 | int cpu = iter->cpu; | 861 | int cpu = iter->cpu; |
| 813 | int *depth = &(per_cpu_ptr(data, cpu)->depth); | 862 | int *depth = &(per_cpu_ptr(data->cpu_data, cpu)->depth); |
| 814 | 863 | ||
| 815 | /* | 864 | /* |
| 816 | * Comments display at + 1 to depth. This is the | 865 | * Comments display at + 1 to depth. This is the |
| @@ -873,7 +922,7 @@ print_graph_comment(struct trace_seq *s, struct trace_entry *ent, | |||
| 873 | int i; | 922 | int i; |
| 874 | 923 | ||
| 875 | if (data) | 924 | if (data) |
| 876 | depth = per_cpu_ptr(data, iter->cpu)->depth; | 925 | depth = per_cpu_ptr(data->cpu_data, iter->cpu)->depth; |
| 877 | 926 | ||
| 878 | if (print_graph_prologue(iter, s, 0, 0)) | 927 | if (print_graph_prologue(iter, s, 0, 0)) |
| 879 | return TRACE_TYPE_PARTIAL_LINE; | 928 | return TRACE_TYPE_PARTIAL_LINE; |
| @@ -941,8 +990,33 @@ print_graph_comment(struct trace_seq *s, struct trace_entry *ent, | |||
| 941 | enum print_line_t | 990 | enum print_line_t |
| 942 | print_graph_function(struct trace_iterator *iter) | 991 | print_graph_function(struct trace_iterator *iter) |
| 943 | { | 992 | { |
| 993 | struct ftrace_graph_ent_entry *field; | ||
| 994 | struct fgraph_data *data = iter->private; | ||
| 944 | struct trace_entry *entry = iter->ent; | 995 | struct trace_entry *entry = iter->ent; |
| 945 | struct trace_seq *s = &iter->seq; | 996 | struct trace_seq *s = &iter->seq; |
| 997 | int cpu = iter->cpu; | ||
| 998 | int ret; | ||
| 999 | |||
| 1000 | if (data && per_cpu_ptr(data->cpu_data, cpu)->ignore) { | ||
| 1001 | per_cpu_ptr(data->cpu_data, cpu)->ignore = 0; | ||
| 1002 | return TRACE_TYPE_HANDLED; | ||
| 1003 | } | ||
| 1004 | |||
| 1005 | /* | ||
| 1006 | * If the last output failed, there's a possibility we need | ||
| 1007 | * to print out the missing entry which would never go out. | ||
| 1008 | */ | ||
| 1009 | if (data && data->failed) { | ||
| 1010 | field = &data->ent; | ||
| 1011 | iter->cpu = data->cpu; | ||
| 1012 | ret = print_graph_entry(field, s, iter); | ||
| 1013 | if (ret == TRACE_TYPE_HANDLED && iter->cpu != cpu) { | ||
| 1014 | per_cpu_ptr(data->cpu_data, iter->cpu)->ignore = 1; | ||
| 1015 | ret = TRACE_TYPE_NO_CONSUME; | ||
| 1016 | } | ||
| 1017 | iter->cpu = cpu; | ||
| 1018 | return ret; | ||
| 1019 | } | ||
| 946 | 1020 | ||
| 947 | switch (entry->type) { | 1021 | switch (entry->type) { |
| 948 | case TRACE_GRAPH_ENT: { | 1022 | case TRACE_GRAPH_ENT: { |
| @@ -952,7 +1026,7 @@ print_graph_function(struct trace_iterator *iter) | |||
| 952 | * sizeof(struct ftrace_graph_ent_entry) is very small, | 1026 | * sizeof(struct ftrace_graph_ent_entry) is very small, |
| 953 | * it can be safely saved at the stack. | 1027 | * it can be safely saved at the stack. |
| 954 | */ | 1028 | */ |
| 955 | struct ftrace_graph_ent_entry *field, saved; | 1029 | struct ftrace_graph_ent_entry saved; |
| 956 | trace_assign_type(field, entry); | 1030 | trace_assign_type(field, entry); |
| 957 | saved = *field; | 1031 | saved = *field; |
| 958 | return print_graph_entry(&saved, s, iter); | 1032 | return print_graph_entry(&saved, s, iter); |
| @@ -1030,31 +1104,54 @@ static void print_graph_headers(struct seq_file *s) | |||
| 1030 | static void graph_trace_open(struct trace_iterator *iter) | 1104 | static void graph_trace_open(struct trace_iterator *iter) |
| 1031 | { | 1105 | { |
| 1032 | /* pid and depth on the last trace processed */ | 1106 | /* pid and depth on the last trace processed */ |
| 1033 | struct fgraph_data *data = alloc_percpu(struct fgraph_data); | 1107 | struct fgraph_data *data; |
| 1034 | int cpu; | 1108 | int cpu; |
| 1035 | 1109 | ||
| 1110 | iter->private = NULL; | ||
| 1111 | |||
| 1112 | data = kzalloc(sizeof(*data), GFP_KERNEL); | ||
| 1036 | if (!data) | 1113 | if (!data) |
| 1037 | pr_warning("function graph tracer: not enough memory\n"); | 1114 | goto out_err; |
| 1038 | else | 1115 | |
| 1039 | for_each_possible_cpu(cpu) { | 1116 | data->cpu_data = alloc_percpu(struct fgraph_cpu_data); |
| 1040 | pid_t *pid = &(per_cpu_ptr(data, cpu)->last_pid); | 1117 | if (!data->cpu_data) |
| 1041 | int *depth = &(per_cpu_ptr(data, cpu)->depth); | 1118 | goto out_err_free; |
| 1042 | *pid = -1; | 1119 | |
| 1043 | *depth = 0; | 1120 | for_each_possible_cpu(cpu) { |
| 1044 | } | 1121 | pid_t *pid = &(per_cpu_ptr(data->cpu_data, cpu)->last_pid); |
| 1122 | int *depth = &(per_cpu_ptr(data->cpu_data, cpu)->depth); | ||
| 1123 | int *ignore = &(per_cpu_ptr(data->cpu_data, cpu)->ignore); | ||
| 1124 | *pid = -1; | ||
| 1125 | *depth = 0; | ||
| 1126 | *ignore = 0; | ||
| 1127 | } | ||
| 1045 | 1128 | ||
| 1046 | iter->private = data; | 1129 | iter->private = data; |
| 1130 | |||
| 1131 | return; | ||
| 1132 | |||
| 1133 | out_err_free: | ||
| 1134 | kfree(data); | ||
| 1135 | out_err: | ||
| 1136 | pr_warning("function graph tracer: not enough memory\n"); | ||
| 1047 | } | 1137 | } |
| 1048 | 1138 | ||
| 1049 | static void graph_trace_close(struct trace_iterator *iter) | 1139 | static void graph_trace_close(struct trace_iterator *iter) |
| 1050 | { | 1140 | { |
| 1051 | free_percpu(iter->private); | 1141 | struct fgraph_data *data = iter->private; |
| 1142 | |||
| 1143 | if (data) { | ||
| 1144 | free_percpu(data->cpu_data); | ||
| 1145 | kfree(data); | ||
| 1146 | } | ||
| 1052 | } | 1147 | } |
| 1053 | 1148 | ||
| 1054 | static struct tracer graph_trace __read_mostly = { | 1149 | static struct tracer graph_trace __read_mostly = { |
| 1055 | .name = "function_graph", | 1150 | .name = "function_graph", |
| 1056 | .open = graph_trace_open, | 1151 | .open = graph_trace_open, |
| 1152 | .pipe_open = graph_trace_open, | ||
| 1057 | .close = graph_trace_close, | 1153 | .close = graph_trace_close, |
| 1154 | .pipe_close = graph_trace_close, | ||
| 1058 | .wait_pipe = poll_wait_pipe, | 1155 | .wait_pipe = poll_wait_pipe, |
| 1059 | .init = graph_trace_init, | 1156 | .init = graph_trace_init, |
| 1060 | .reset = graph_trace_reset, | 1157 | .reset = graph_trace_reset, |
