diff options
Diffstat (limited to 'kernel/trace/trace_functions_graph.c')
| -rw-r--r-- | kernel/trace/trace_functions_graph.c | 209 |
1 files changed, 201 insertions, 8 deletions
diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c index 6f233698518e..76b05980225c 100644 --- a/kernel/trace/trace_functions_graph.c +++ b/kernel/trace/trace_functions_graph.c | |||
| @@ -15,15 +15,19 @@ | |||
| 15 | #include "trace.h" | 15 | #include "trace.h" |
| 16 | #include "trace_output.h" | 16 | #include "trace_output.h" |
| 17 | 17 | ||
| 18 | /* When set, irq functions will be ignored */ | ||
| 19 | static int ftrace_graph_skip_irqs; | ||
| 20 | |||
| 18 | struct fgraph_cpu_data { | 21 | struct fgraph_cpu_data { |
| 19 | pid_t last_pid; | 22 | pid_t last_pid; |
| 20 | int depth; | 23 | int depth; |
| 24 | int depth_irq; | ||
| 21 | int ignore; | 25 | int ignore; |
| 22 | unsigned long enter_funcs[FTRACE_RETFUNC_DEPTH]; | 26 | unsigned long enter_funcs[FTRACE_RETFUNC_DEPTH]; |
| 23 | }; | 27 | }; |
| 24 | 28 | ||
| 25 | struct fgraph_data { | 29 | struct fgraph_data { |
| 26 | struct fgraph_cpu_data *cpu_data; | 30 | struct fgraph_cpu_data __percpu *cpu_data; |
| 27 | 31 | ||
| 28 | /* Place to preserve last processed entry. */ | 32 | /* Place to preserve last processed entry. */ |
| 29 | struct ftrace_graph_ent_entry ent; | 33 | struct ftrace_graph_ent_entry ent; |
| @@ -41,6 +45,7 @@ struct fgraph_data { | |||
| 41 | #define TRACE_GRAPH_PRINT_PROC 0x8 | 45 | #define TRACE_GRAPH_PRINT_PROC 0x8 |
| 42 | #define TRACE_GRAPH_PRINT_DURATION 0x10 | 46 | #define TRACE_GRAPH_PRINT_DURATION 0x10 |
| 43 | #define TRACE_GRAPH_PRINT_ABS_TIME 0x20 | 47 | #define TRACE_GRAPH_PRINT_ABS_TIME 0x20 |
| 48 | #define TRACE_GRAPH_PRINT_IRQS 0x40 | ||
| 44 | 49 | ||
| 45 | static struct tracer_opt trace_opts[] = { | 50 | static struct tracer_opt trace_opts[] = { |
| 46 | /* Display overruns? (for self-debug purpose) */ | 51 | /* Display overruns? (for self-debug purpose) */ |
| @@ -55,13 +60,15 @@ static struct tracer_opt trace_opts[] = { | |||
| 55 | { TRACER_OPT(funcgraph-duration, TRACE_GRAPH_PRINT_DURATION) }, | 60 | { TRACER_OPT(funcgraph-duration, TRACE_GRAPH_PRINT_DURATION) }, |
| 56 | /* Display absolute time of an entry */ | 61 | /* Display absolute time of an entry */ |
| 57 | { TRACER_OPT(funcgraph-abstime, TRACE_GRAPH_PRINT_ABS_TIME) }, | 62 | { TRACER_OPT(funcgraph-abstime, TRACE_GRAPH_PRINT_ABS_TIME) }, |
| 63 | /* Display interrupts */ | ||
| 64 | { TRACER_OPT(funcgraph-irqs, TRACE_GRAPH_PRINT_IRQS) }, | ||
| 58 | { } /* Empty entry */ | 65 | { } /* Empty entry */ |
| 59 | }; | 66 | }; |
| 60 | 67 | ||
| 61 | static struct tracer_flags tracer_flags = { | 68 | static struct tracer_flags tracer_flags = { |
| 62 | /* Don't display overruns and proc by default */ | 69 | /* Don't display overruns and proc by default */ |
| 63 | .val = TRACE_GRAPH_PRINT_CPU | TRACE_GRAPH_PRINT_OVERHEAD | | 70 | .val = TRACE_GRAPH_PRINT_CPU | TRACE_GRAPH_PRINT_OVERHEAD | |
| 64 | TRACE_GRAPH_PRINT_DURATION, | 71 | TRACE_GRAPH_PRINT_DURATION | TRACE_GRAPH_PRINT_IRQS, |
| 65 | .opts = trace_opts | 72 | .opts = trace_opts |
| 66 | }; | 73 | }; |
| 67 | 74 | ||
| @@ -204,6 +211,14 @@ int __trace_graph_entry(struct trace_array *tr, | |||
| 204 | return 1; | 211 | return 1; |
| 205 | } | 212 | } |
| 206 | 213 | ||
| 214 | static inline int ftrace_graph_ignore_irqs(void) | ||
| 215 | { | ||
| 216 | if (!ftrace_graph_skip_irqs) | ||
| 217 | return 0; | ||
| 218 | |||
| 219 | return in_irq(); | ||
| 220 | } | ||
| 221 | |||
| 207 | int trace_graph_entry(struct ftrace_graph_ent *trace) | 222 | int trace_graph_entry(struct ftrace_graph_ent *trace) |
| 208 | { | 223 | { |
| 209 | struct trace_array *tr = graph_array; | 224 | struct trace_array *tr = graph_array; |
| @@ -218,7 +233,8 @@ int trace_graph_entry(struct ftrace_graph_ent *trace) | |||
| 218 | return 0; | 233 | return 0; |
| 219 | 234 | ||
| 220 | /* trace it when it is-nested-in or is a function enabled. */ | 235 | /* trace it when it is-nested-in or is a function enabled. */ |
| 221 | if (!(trace->depth || ftrace_graph_addr(trace->func))) | 236 | if (!(trace->depth || ftrace_graph_addr(trace->func)) || |
| 237 | ftrace_graph_ignore_irqs()) | ||
| 222 | return 0; | 238 | return 0; |
| 223 | 239 | ||
| 224 | local_irq_save(flags); | 240 | local_irq_save(flags); |
| @@ -246,6 +262,34 @@ int trace_graph_thresh_entry(struct ftrace_graph_ent *trace) | |||
| 246 | return trace_graph_entry(trace); | 262 | return trace_graph_entry(trace); |
| 247 | } | 263 | } |
| 248 | 264 | ||
| 265 | static void | ||
| 266 | __trace_graph_function(struct trace_array *tr, | ||
| 267 | unsigned long ip, unsigned long flags, int pc) | ||
| 268 | { | ||
| 269 | u64 time = trace_clock_local(); | ||
| 270 | struct ftrace_graph_ent ent = { | ||
| 271 | .func = ip, | ||
| 272 | .depth = 0, | ||
| 273 | }; | ||
| 274 | struct ftrace_graph_ret ret = { | ||
| 275 | .func = ip, | ||
| 276 | .depth = 0, | ||
| 277 | .calltime = time, | ||
| 278 | .rettime = time, | ||
| 279 | }; | ||
| 280 | |||
| 281 | __trace_graph_entry(tr, &ent, flags, pc); | ||
| 282 | __trace_graph_return(tr, &ret, flags, pc); | ||
| 283 | } | ||
| 284 | |||
| 285 | void | ||
| 286 | trace_graph_function(struct trace_array *tr, | ||
| 287 | unsigned long ip, unsigned long parent_ip, | ||
| 288 | unsigned long flags, int pc) | ||
| 289 | { | ||
| 290 | __trace_graph_function(tr, ip, flags, pc); | ||
| 291 | } | ||
| 292 | |||
| 249 | void __trace_graph_return(struct trace_array *tr, | 293 | void __trace_graph_return(struct trace_array *tr, |
| 250 | struct ftrace_graph_ret *trace, | 294 | struct ftrace_graph_ret *trace, |
| 251 | unsigned long flags, | 295 | unsigned long flags, |
| @@ -649,8 +693,9 @@ trace_print_graph_duration(unsigned long long duration, struct trace_seq *s) | |||
| 649 | 693 | ||
| 650 | /* Print nsecs (we don't want to exceed 7 numbers) */ | 694 | /* Print nsecs (we don't want to exceed 7 numbers) */ |
| 651 | if (len < 7) { | 695 | if (len < 7) { |
| 652 | snprintf(nsecs_str, min(sizeof(nsecs_str), 8UL - len), "%03lu", | 696 | size_t slen = min_t(size_t, sizeof(nsecs_str), 8UL - len); |
| 653 | nsecs_rem); | 697 | |
| 698 | snprintf(nsecs_str, slen, "%03lu", nsecs_rem); | ||
| 654 | ret = trace_seq_printf(s, ".%s", nsecs_str); | 699 | ret = trace_seq_printf(s, ".%s", nsecs_str); |
| 655 | if (!ret) | 700 | if (!ret) |
| 656 | return TRACE_TYPE_PARTIAL_LINE; | 701 | return TRACE_TYPE_PARTIAL_LINE; |
| @@ -855,6 +900,108 @@ print_graph_prologue(struct trace_iterator *iter, struct trace_seq *s, | |||
| 855 | return 0; | 900 | return 0; |
| 856 | } | 901 | } |
| 857 | 902 | ||
| 903 | /* | ||
| 904 | * Entry check for irq code | ||
| 905 | * | ||
| 906 | * returns 1 if | ||
| 907 | * - we are inside irq code | ||
| 908 | * - we just extered irq code | ||
| 909 | * | ||
| 910 | * retunns 0 if | ||
| 911 | * - funcgraph-interrupts option is set | ||
| 912 | * - we are not inside irq code | ||
| 913 | */ | ||
| 914 | static int | ||
| 915 | check_irq_entry(struct trace_iterator *iter, u32 flags, | ||
| 916 | unsigned long addr, int depth) | ||
| 917 | { | ||
| 918 | int cpu = iter->cpu; | ||
| 919 | int *depth_irq; | ||
| 920 | struct fgraph_data *data = iter->private; | ||
| 921 | |||
| 922 | /* | ||
| 923 | * If we are either displaying irqs, or we got called as | ||
| 924 | * a graph event and private data does not exist, | ||
| 925 | * then we bypass the irq check. | ||
| 926 | */ | ||
| 927 | if ((flags & TRACE_GRAPH_PRINT_IRQS) || | ||
| 928 | (!data)) | ||
| 929 | return 0; | ||
| 930 | |||
| 931 | depth_irq = &(per_cpu_ptr(data->cpu_data, cpu)->depth_irq); | ||
| 932 | |||
| 933 | /* | ||
| 934 | * We are inside the irq code | ||
| 935 | */ | ||
| 936 | if (*depth_irq >= 0) | ||
| 937 | return 1; | ||
| 938 | |||
| 939 | if ((addr < (unsigned long)__irqentry_text_start) || | ||
| 940 | (addr >= (unsigned long)__irqentry_text_end)) | ||
| 941 | return 0; | ||
| 942 | |||
| 943 | /* | ||
| 944 | * We are entering irq code. | ||
| 945 | */ | ||
| 946 | *depth_irq = depth; | ||
| 947 | return 1; | ||
| 948 | } | ||
| 949 | |||
| 950 | /* | ||
| 951 | * Return check for irq code | ||
| 952 | * | ||
| 953 | * returns 1 if | ||
| 954 | * - we are inside irq code | ||
| 955 | * - we just left irq code | ||
| 956 | * | ||
| 957 | * returns 0 if | ||
| 958 | * - funcgraph-interrupts option is set | ||
| 959 | * - we are not inside irq code | ||
| 960 | */ | ||
| 961 | static int | ||
| 962 | check_irq_return(struct trace_iterator *iter, u32 flags, int depth) | ||
| 963 | { | ||
| 964 | int cpu = iter->cpu; | ||
| 965 | int *depth_irq; | ||
| 966 | struct fgraph_data *data = iter->private; | ||
| 967 | |||
| 968 | /* | ||
| 969 | * If we are either displaying irqs, or we got called as | ||
| 970 | * a graph event and private data does not exist, | ||
| 971 | * then we bypass the irq check. | ||
| 972 | */ | ||
| 973 | if ((flags & TRACE_GRAPH_PRINT_IRQS) || | ||
| 974 | (!data)) | ||
| 975 | return 0; | ||
| 976 | |||
| 977 | depth_irq = &(per_cpu_ptr(data->cpu_data, cpu)->depth_irq); | ||
| 978 | |||
| 979 | /* | ||
| 980 | * We are not inside the irq code. | ||
| 981 | */ | ||
| 982 | if (*depth_irq == -1) | ||
| 983 | return 0; | ||
| 984 | |||
| 985 | /* | ||
| 986 | * We are inside the irq code, and this is returning entry. | ||
| 987 | * Let's not trace it and clear the entry depth, since | ||
| 988 | * we are out of irq code. | ||
| 989 | * | ||
| 990 | * This condition ensures that we 'leave the irq code' once | ||
| 991 | * we are out of the entry depth. Thus protecting us from | ||
| 992 | * the RETURN entry loss. | ||
| 993 | */ | ||
| 994 | if (*depth_irq >= depth) { | ||
| 995 | *depth_irq = -1; | ||
| 996 | return 1; | ||
| 997 | } | ||
| 998 | |||
| 999 | /* | ||
| 1000 | * We are inside the irq code, and this is not the entry. | ||
| 1001 | */ | ||
| 1002 | return 1; | ||
| 1003 | } | ||
| 1004 | |||
| 858 | static enum print_line_t | 1005 | static enum print_line_t |
| 859 | print_graph_entry(struct ftrace_graph_ent_entry *field, struct trace_seq *s, | 1006 | print_graph_entry(struct ftrace_graph_ent_entry *field, struct trace_seq *s, |
| 860 | struct trace_iterator *iter, u32 flags) | 1007 | struct trace_iterator *iter, u32 flags) |
| @@ -865,6 +1012,9 @@ print_graph_entry(struct ftrace_graph_ent_entry *field, struct trace_seq *s, | |||
| 865 | static enum print_line_t ret; | 1012 | static enum print_line_t ret; |
| 866 | int cpu = iter->cpu; | 1013 | int cpu = iter->cpu; |
| 867 | 1014 | ||
| 1015 | if (check_irq_entry(iter, flags, call->func, call->depth)) | ||
| 1016 | return TRACE_TYPE_HANDLED; | ||
| 1017 | |||
| 868 | if (print_graph_prologue(iter, s, TRACE_GRAPH_ENT, call->func, flags)) | 1018 | if (print_graph_prologue(iter, s, TRACE_GRAPH_ENT, call->func, flags)) |
| 869 | return TRACE_TYPE_PARTIAL_LINE; | 1019 | return TRACE_TYPE_PARTIAL_LINE; |
| 870 | 1020 | ||
| @@ -902,6 +1052,9 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s, | |||
| 902 | int ret; | 1052 | int ret; |
| 903 | int i; | 1053 | int i; |
| 904 | 1054 | ||
| 1055 | if (check_irq_return(iter, flags, trace->depth)) | ||
| 1056 | return TRACE_TYPE_HANDLED; | ||
| 1057 | |||
| 905 | if (data) { | 1058 | if (data) { |
| 906 | struct fgraph_cpu_data *cpu_data; | 1059 | struct fgraph_cpu_data *cpu_data; |
| 907 | int cpu = iter->cpu; | 1060 | int cpu = iter->cpu; |
| @@ -1054,7 +1207,7 @@ print_graph_comment(struct trace_seq *s, struct trace_entry *ent, | |||
| 1054 | 1207 | ||
| 1055 | 1208 | ||
| 1056 | enum print_line_t | 1209 | enum print_line_t |
| 1057 | print_graph_function_flags(struct trace_iterator *iter, u32 flags) | 1210 | __print_graph_function_flags(struct trace_iterator *iter, u32 flags) |
| 1058 | { | 1211 | { |
| 1059 | struct ftrace_graph_ent_entry *field; | 1212 | struct ftrace_graph_ent_entry *field; |
| 1060 | struct fgraph_data *data = iter->private; | 1213 | struct fgraph_data *data = iter->private; |
| @@ -1117,7 +1270,18 @@ print_graph_function_flags(struct trace_iterator *iter, u32 flags) | |||
| 1117 | static enum print_line_t | 1270 | static enum print_line_t |
| 1118 | print_graph_function(struct trace_iterator *iter) | 1271 | print_graph_function(struct trace_iterator *iter) |
| 1119 | { | 1272 | { |
| 1120 | return print_graph_function_flags(iter, tracer_flags.val); | 1273 | return __print_graph_function_flags(iter, tracer_flags.val); |
| 1274 | } | ||
| 1275 | |||
| 1276 | enum print_line_t print_graph_function_flags(struct trace_iterator *iter, | ||
| 1277 | u32 flags) | ||
| 1278 | { | ||
| 1279 | if (trace_flags & TRACE_ITER_LATENCY_FMT) | ||
| 1280 | flags |= TRACE_GRAPH_PRINT_DURATION; | ||
| 1281 | else | ||
| 1282 | flags |= TRACE_GRAPH_PRINT_ABS_TIME; | ||
| 1283 | |||
| 1284 | return __print_graph_function_flags(iter, flags); | ||
| 1121 | } | 1285 | } |
| 1122 | 1286 | ||
| 1123 | static enum print_line_t | 1287 | static enum print_line_t |
| @@ -1149,7 +1313,7 @@ static void print_lat_header(struct seq_file *s, u32 flags) | |||
| 1149 | seq_printf(s, "#%.*s|||| / \n", size, spaces); | 1313 | seq_printf(s, "#%.*s|||| / \n", size, spaces); |
| 1150 | } | 1314 | } |
| 1151 | 1315 | ||
| 1152 | void print_graph_headers_flags(struct seq_file *s, u32 flags) | 1316 | static void __print_graph_headers_flags(struct seq_file *s, u32 flags) |
| 1153 | { | 1317 | { |
| 1154 | int lat = trace_flags & TRACE_ITER_LATENCY_FMT; | 1318 | int lat = trace_flags & TRACE_ITER_LATENCY_FMT; |
| 1155 | 1319 | ||
| @@ -1190,6 +1354,23 @@ void print_graph_headers(struct seq_file *s) | |||
| 1190 | print_graph_headers_flags(s, tracer_flags.val); | 1354 | print_graph_headers_flags(s, tracer_flags.val); |
| 1191 | } | 1355 | } |
| 1192 | 1356 | ||
| 1357 | void print_graph_headers_flags(struct seq_file *s, u32 flags) | ||
| 1358 | { | ||
| 1359 | struct trace_iterator *iter = s->private; | ||
| 1360 | |||
| 1361 | if (trace_flags & TRACE_ITER_LATENCY_FMT) { | ||
| 1362 | /* print nothing if the buffers are empty */ | ||
| 1363 | if (trace_empty(iter)) | ||
| 1364 | return; | ||
| 1365 | |||
| 1366 | print_trace_header(s, iter); | ||
| 1367 | flags |= TRACE_GRAPH_PRINT_DURATION; | ||
| 1368 | } else | ||
| 1369 | flags |= TRACE_GRAPH_PRINT_ABS_TIME; | ||
| 1370 | |||
| 1371 | __print_graph_headers_flags(s, flags); | ||
| 1372 | } | ||
| 1373 | |||
| 1193 | void graph_trace_open(struct trace_iterator *iter) | 1374 | void graph_trace_open(struct trace_iterator *iter) |
| 1194 | { | 1375 | { |
| 1195 | /* pid and depth on the last trace processed */ | 1376 | /* pid and depth on the last trace processed */ |
| @@ -1210,9 +1391,12 @@ void graph_trace_open(struct trace_iterator *iter) | |||
| 1210 | pid_t *pid = &(per_cpu_ptr(data->cpu_data, cpu)->last_pid); | 1391 | pid_t *pid = &(per_cpu_ptr(data->cpu_data, cpu)->last_pid); |
| 1211 | int *depth = &(per_cpu_ptr(data->cpu_data, cpu)->depth); | 1392 | int *depth = &(per_cpu_ptr(data->cpu_data, cpu)->depth); |
| 1212 | int *ignore = &(per_cpu_ptr(data->cpu_data, cpu)->ignore); | 1393 | int *ignore = &(per_cpu_ptr(data->cpu_data, cpu)->ignore); |
| 1394 | int *depth_irq = &(per_cpu_ptr(data->cpu_data, cpu)->depth_irq); | ||
| 1395 | |||
| 1213 | *pid = -1; | 1396 | *pid = -1; |
| 1214 | *depth = 0; | 1397 | *depth = 0; |
| 1215 | *ignore = 0; | 1398 | *ignore = 0; |
| 1399 | *depth_irq = -1; | ||
| 1216 | } | 1400 | } |
| 1217 | 1401 | ||
| 1218 | iter->private = data; | 1402 | iter->private = data; |
| @@ -1235,6 +1419,14 @@ void graph_trace_close(struct trace_iterator *iter) | |||
| 1235 | } | 1419 | } |
| 1236 | } | 1420 | } |
| 1237 | 1421 | ||
| 1422 | static int func_graph_set_flag(u32 old_flags, u32 bit, int set) | ||
| 1423 | { | ||
| 1424 | if (bit == TRACE_GRAPH_PRINT_IRQS) | ||
| 1425 | ftrace_graph_skip_irqs = !set; | ||
| 1426 | |||
| 1427 | return 0; | ||
| 1428 | } | ||
| 1429 | |||
| 1238 | static struct trace_event_functions graph_functions = { | 1430 | static struct trace_event_functions graph_functions = { |
| 1239 | .trace = print_graph_function_event, | 1431 | .trace = print_graph_function_event, |
| 1240 | }; | 1432 | }; |
| @@ -1261,6 +1453,7 @@ static struct tracer graph_trace __read_mostly = { | |||
| 1261 | .print_line = print_graph_function, | 1453 | .print_line = print_graph_function, |
| 1262 | .print_header = print_graph_headers, | 1454 | .print_header = print_graph_headers, |
| 1263 | .flags = &tracer_flags, | 1455 | .flags = &tracer_flags, |
| 1456 | .set_flag = func_graph_set_flag, | ||
| 1264 | #ifdef CONFIG_FTRACE_SELFTEST | 1457 | #ifdef CONFIG_FTRACE_SELFTEST |
| 1265 | .selftest = trace_selftest_startup_function_graph, | 1458 | .selftest = trace_selftest_startup_function_graph, |
| 1266 | #endif | 1459 | #endif |
