diff options
| -rw-r--r-- | include/asm-generic/vmlinux.lds.h | 5 | ||||
| -rw-r--r-- | include/linux/ftrace_event.h | 2 | ||||
| -rw-r--r-- | include/linux/tracepoint.h | 8 | ||||
| -rw-r--r-- | include/trace/ftrace.h | 22 | ||||
| -rw-r--r-- | kernel/trace/trace.c | 26 | ||||
| -rw-r--r-- | kernel/trace/trace.h | 2 | ||||
| -rw-r--r-- | kernel/trace/trace_events.c | 119 |
7 files changed, 178 insertions, 6 deletions
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index ac78910d7416..f8e8b34dc427 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h | |||
| @@ -124,7 +124,10 @@ | |||
| 124 | #define FTRACE_EVENTS() . = ALIGN(8); \ | 124 | #define FTRACE_EVENTS() . = ALIGN(8); \ |
| 125 | VMLINUX_SYMBOL(__start_ftrace_events) = .; \ | 125 | VMLINUX_SYMBOL(__start_ftrace_events) = .; \ |
| 126 | *(_ftrace_events) \ | 126 | *(_ftrace_events) \ |
| 127 | VMLINUX_SYMBOL(__stop_ftrace_events) = .; | 127 | VMLINUX_SYMBOL(__stop_ftrace_events) = .; \ |
| 128 | VMLINUX_SYMBOL(__start_ftrace_enum_maps) = .; \ | ||
| 129 | *(_ftrace_enum_map) \ | ||
| 130 | VMLINUX_SYMBOL(__stop_ftrace_enum_maps) = .; | ||
| 128 | #else | 131 | #else |
| 129 | #define FTRACE_EVENTS() | 132 | #define FTRACE_EVENTS() |
| 130 | #endif | 133 | #endif |
diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h index 62b8fac7ded5..112cf49d9576 100644 --- a/include/linux/ftrace_event.h +++ b/include/linux/ftrace_event.h | |||
| @@ -285,7 +285,7 @@ struct ftrace_event_call { | |||
| 285 | struct tracepoint *tp; | 285 | struct tracepoint *tp; |
| 286 | }; | 286 | }; |
| 287 | struct trace_event event; | 287 | struct trace_event event; |
| 288 | const char *print_fmt; | 288 | char *print_fmt; |
| 289 | struct event_filter *filter; | 289 | struct event_filter *filter; |
| 290 | void *mod; | 290 | void *mod; |
| 291 | void *data; | 291 | void *data; |
diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h index c72851328ca9..a5f7f3ecafa3 100644 --- a/include/linux/tracepoint.h +++ b/include/linux/tracepoint.h | |||
| @@ -36,6 +36,12 @@ struct tracepoint { | |||
| 36 | struct tracepoint_func __rcu *funcs; | 36 | struct tracepoint_func __rcu *funcs; |
| 37 | }; | 37 | }; |
| 38 | 38 | ||
| 39 | struct trace_enum_map { | ||
| 40 | const char *system; | ||
| 41 | const char *enum_string; | ||
| 42 | unsigned long enum_value; | ||
| 43 | }; | ||
| 44 | |||
| 39 | extern int | 45 | extern int |
| 40 | tracepoint_probe_register(struct tracepoint *tp, void *probe, void *data); | 46 | tracepoint_probe_register(struct tracepoint *tp, void *probe, void *data); |
| 41 | extern int | 47 | extern int |
| @@ -87,6 +93,8 @@ extern void syscall_unregfunc(void); | |||
| 87 | 93 | ||
| 88 | #define PARAMS(args...) args | 94 | #define PARAMS(args...) args |
| 89 | 95 | ||
| 96 | #define TRACE_DEFINE_ENUM(x) | ||
| 97 | |||
| 90 | #endif /* _LINUX_TRACEPOINT_H */ | 98 | #endif /* _LINUX_TRACEPOINT_H */ |
| 91 | 99 | ||
| 92 | /* | 100 | /* |
diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h index 2f9b95b6d3fb..37d4b10b111d 100644 --- a/include/trace/ftrace.h +++ b/include/trace/ftrace.h | |||
| @@ -33,6 +33,19 @@ | |||
| 33 | 33 | ||
| 34 | TRACE_MAKE_SYSTEM_STR(); | 34 | TRACE_MAKE_SYSTEM_STR(); |
| 35 | 35 | ||
| 36 | #undef TRACE_DEFINE_ENUM | ||
| 37 | #define TRACE_DEFINE_ENUM(a) \ | ||
| 38 | static struct trace_enum_map __used __initdata \ | ||
| 39 | __##TRACE_SYSTEM##_##a = \ | ||
| 40 | { \ | ||
| 41 | .system = TRACE_SYSTEM_STRING, \ | ||
| 42 | .enum_string = #a, \ | ||
| 43 | .enum_value = a \ | ||
| 44 | }; \ | ||
| 45 | static struct trace_enum_map __used \ | ||
| 46 | __attribute__((section("_ftrace_enum_map"))) \ | ||
| 47 | *TRACE_SYSTEM##_##a = &__##TRACE_SYSTEM##_##a | ||
| 48 | |||
| 36 | /* | 49 | /* |
| 37 | * DECLARE_EVENT_CLASS can be used to add a generic function | 50 | * DECLARE_EVENT_CLASS can be used to add a generic function |
| 38 | * handlers for events. That is, if all events have the same | 51 | * handlers for events. That is, if all events have the same |
| @@ -136,6 +149,9 @@ TRACE_MAKE_SYSTEM_STR(); | |||
| 136 | * The size of an array is also encoded, in the higher 16 bits of <item>. | 149 | * The size of an array is also encoded, in the higher 16 bits of <item>. |
| 137 | */ | 150 | */ |
| 138 | 151 | ||
| 152 | #undef TRACE_DEFINE_ENUM | ||
| 153 | #define TRACE_DEFINE_ENUM(a) | ||
| 154 | |||
| 139 | #undef __field | 155 | #undef __field |
| 140 | #define __field(type, item) | 156 | #define __field(type, item) |
| 141 | 157 | ||
| @@ -553,7 +569,7 @@ static inline notrace int ftrace_get_offsets_##call( \ | |||
| 553 | * .trace = ftrace_raw_output_<call>, <-- stage 2 | 569 | * .trace = ftrace_raw_output_<call>, <-- stage 2 |
| 554 | * }; | 570 | * }; |
| 555 | * | 571 | * |
| 556 | * static const char print_fmt_<call>[] = <TP_printk>; | 572 | * static char print_fmt_<call>[] = <TP_printk>; |
| 557 | * | 573 | * |
| 558 | * static struct ftrace_event_class __used event_class_<template> = { | 574 | * static struct ftrace_event_class __used event_class_<template> = { |
| 559 | * .system = "<system>", | 575 | * .system = "<system>", |
| @@ -704,7 +720,7 @@ static inline void ftrace_test_probe_##call(void) \ | |||
| 704 | #undef DECLARE_EVENT_CLASS | 720 | #undef DECLARE_EVENT_CLASS |
| 705 | #define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \ | 721 | #define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) \ |
| 706 | _TRACE_PERF_PROTO(call, PARAMS(proto)); \ | 722 | _TRACE_PERF_PROTO(call, PARAMS(proto)); \ |
| 707 | static const char print_fmt_##call[] = print; \ | 723 | static char print_fmt_##call[] = print; \ |
| 708 | static struct ftrace_event_class __used __refdata event_class_##call = { \ | 724 | static struct ftrace_event_class __used __refdata event_class_##call = { \ |
| 709 | .system = TRACE_SYSTEM_STRING, \ | 725 | .system = TRACE_SYSTEM_STRING, \ |
| 710 | .define_fields = ftrace_define_fields_##call, \ | 726 | .define_fields = ftrace_define_fields_##call, \ |
| @@ -733,7 +749,7 @@ __attribute__((section("_ftrace_events"))) *__event_##call = &event_##call | |||
| 733 | #undef DEFINE_EVENT_PRINT | 749 | #undef DEFINE_EVENT_PRINT |
| 734 | #define DEFINE_EVENT_PRINT(template, call, proto, args, print) \ | 750 | #define DEFINE_EVENT_PRINT(template, call, proto, args, print) \ |
| 735 | \ | 751 | \ |
| 736 | static const char print_fmt_##call[] = print; \ | 752 | static char print_fmt_##call[] = print; \ |
| 737 | \ | 753 | \ |
| 738 | static struct ftrace_event_call __used event_##call = { \ | 754 | static struct ftrace_event_call __used event_##call = { \ |
| 739 | .class = &event_class_##template, \ | 755 | .class = &event_class_##template, \ |
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 62c6506d663f..ebf49649534c 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c | |||
| @@ -3908,6 +3908,20 @@ static const struct file_operations tracing_saved_cmdlines_size_fops = { | |||
| 3908 | .write = tracing_saved_cmdlines_size_write, | 3908 | .write = tracing_saved_cmdlines_size_write, |
| 3909 | }; | 3909 | }; |
| 3910 | 3910 | ||
| 3911 | static void | ||
| 3912 | trace_insert_enum_map(struct trace_enum_map **start, struct trace_enum_map **stop) | ||
| 3913 | { | ||
| 3914 | struct trace_enum_map **map; | ||
| 3915 | int len = stop - start; | ||
| 3916 | |||
| 3917 | if (len <= 0) | ||
| 3918 | return; | ||
| 3919 | |||
| 3920 | map = start; | ||
| 3921 | |||
| 3922 | trace_event_enum_update(map, len); | ||
| 3923 | } | ||
| 3924 | |||
| 3911 | static ssize_t | 3925 | static ssize_t |
| 3912 | tracing_set_trace_read(struct file *filp, char __user *ubuf, | 3926 | tracing_set_trace_read(struct file *filp, char __user *ubuf, |
| 3913 | size_t cnt, loff_t *ppos) | 3927 | size_t cnt, loff_t *ppos) |
| @@ -6542,6 +6556,14 @@ struct dentry *tracing_init_dentry(void) | |||
| 6542 | return tr->dir; | 6556 | return tr->dir; |
| 6543 | } | 6557 | } |
| 6544 | 6558 | ||
| 6559 | extern struct trace_enum_map *__start_ftrace_enum_maps[]; | ||
| 6560 | extern struct trace_enum_map *__stop_ftrace_enum_maps[]; | ||
| 6561 | |||
| 6562 | static void __init trace_enum_init(void) | ||
| 6563 | { | ||
| 6564 | trace_insert_enum_map(__start_ftrace_enum_maps, __stop_ftrace_enum_maps); | ||
| 6565 | } | ||
| 6566 | |||
| 6545 | static __init int tracer_init_debugfs(void) | 6567 | static __init int tracer_init_debugfs(void) |
| 6546 | { | 6568 | { |
| 6547 | struct dentry *d_tracer; | 6569 | struct dentry *d_tracer; |
| @@ -6566,6 +6588,8 @@ static __init int tracer_init_debugfs(void) | |||
| 6566 | trace_create_file("saved_cmdlines_size", 0644, d_tracer, | 6588 | trace_create_file("saved_cmdlines_size", 0644, d_tracer, |
| 6567 | NULL, &tracing_saved_cmdlines_size_fops); | 6589 | NULL, &tracing_saved_cmdlines_size_fops); |
| 6568 | 6590 | ||
| 6591 | trace_enum_init(); | ||
| 6592 | |||
| 6569 | #ifdef CONFIG_DYNAMIC_FTRACE | 6593 | #ifdef CONFIG_DYNAMIC_FTRACE |
| 6570 | trace_create_file("dyn_ftrace_total_info", 0444, d_tracer, | 6594 | trace_create_file("dyn_ftrace_total_info", 0444, d_tracer, |
| 6571 | &ftrace_update_tot_cnt, &tracing_dyn_info_fops); | 6595 | &ftrace_update_tot_cnt, &tracing_dyn_info_fops); |
| @@ -6888,7 +6912,7 @@ void __init trace_init(void) | |||
| 6888 | tracepoint_printk = 0; | 6912 | tracepoint_printk = 0; |
| 6889 | } | 6913 | } |
| 6890 | tracer_alloc_buffers(); | 6914 | tracer_alloc_buffers(); |
| 6891 | trace_event_init(); | 6915 | trace_event_init(); |
| 6892 | } | 6916 | } |
| 6893 | 6917 | ||
| 6894 | __init static int clear_boot_tracer(void) | 6918 | __init static int clear_boot_tracer(void) |
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index dd8205a35760..b48d4b08f691 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
| @@ -1309,8 +1309,10 @@ static inline void init_ftrace_syscalls(void) { } | |||
| 1309 | 1309 | ||
| 1310 | #ifdef CONFIG_EVENT_TRACING | 1310 | #ifdef CONFIG_EVENT_TRACING |
| 1311 | void trace_event_init(void); | 1311 | void trace_event_init(void); |
| 1312 | void trace_event_enum_update(struct trace_enum_map **map, int len); | ||
| 1312 | #else | 1313 | #else |
| 1313 | static inline void __init trace_event_init(void) { } | 1314 | static inline void __init trace_event_init(void) { } |
| 1315 | static inlin void trace_event_enum_update(struct trace_enum_map **map, int len) { } | ||
| 1314 | #endif | 1316 | #endif |
| 1315 | 1317 | ||
| 1316 | extern struct trace_iterator *tracepoint_print_iter; | 1318 | extern struct trace_iterator *tracepoint_print_iter; |
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index db54dda10ccc..fc58c50fbf01 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c | |||
| @@ -1704,6 +1704,125 @@ __register_event(struct ftrace_event_call *call, struct module *mod) | |||
| 1704 | return 0; | 1704 | return 0; |
| 1705 | } | 1705 | } |
| 1706 | 1706 | ||
| 1707 | static char *enum_replace(char *ptr, struct trace_enum_map *map, int len) | ||
| 1708 | { | ||
| 1709 | int rlen; | ||
| 1710 | int elen; | ||
| 1711 | |||
| 1712 | /* Find the length of the enum value as a string */ | ||
| 1713 | elen = snprintf(ptr, 0, "%ld", map->enum_value); | ||
| 1714 | /* Make sure there's enough room to replace the string with the value */ | ||
| 1715 | if (len < elen) | ||
| 1716 | return NULL; | ||
| 1717 | |||
| 1718 | snprintf(ptr, elen + 1, "%ld", map->enum_value); | ||
| 1719 | |||
| 1720 | /* Get the rest of the string of ptr */ | ||
| 1721 | rlen = strlen(ptr + len); | ||
| 1722 | memmove(ptr + elen, ptr + len, rlen); | ||
| 1723 | /* Make sure we end the new string */ | ||
| 1724 | ptr[elen + rlen] = 0; | ||
| 1725 | |||
| 1726 | return ptr + elen; | ||
| 1727 | } | ||
| 1728 | |||
| 1729 | static void update_event_printk(struct ftrace_event_call *call, | ||
| 1730 | struct trace_enum_map *map) | ||
| 1731 | { | ||
| 1732 | char *ptr; | ||
| 1733 | int quote = 0; | ||
| 1734 | int len = strlen(map->enum_string); | ||
| 1735 | |||
| 1736 | for (ptr = call->print_fmt; *ptr; ptr++) { | ||
| 1737 | if (*ptr == '\\') { | ||
| 1738 | ptr++; | ||
| 1739 | /* paranoid */ | ||
| 1740 | if (!*ptr) | ||
| 1741 | break; | ||
| 1742 | continue; | ||
| 1743 | } | ||
| 1744 | if (*ptr == '"') { | ||
| 1745 | quote ^= 1; | ||
| 1746 | continue; | ||
| 1747 | } | ||
| 1748 | if (quote) | ||
| 1749 | continue; | ||
| 1750 | if (isdigit(*ptr)) { | ||
| 1751 | /* skip numbers */ | ||
| 1752 | do { | ||
| 1753 | ptr++; | ||
| 1754 | /* Check for alpha chars like ULL */ | ||
| 1755 | } while (isalnum(*ptr)); | ||
| 1756 | /* | ||
| 1757 | * A number must have some kind of delimiter after | ||
| 1758 | * it, and we can ignore that too. | ||
| 1759 | */ | ||
| 1760 | continue; | ||
| 1761 | } | ||
| 1762 | if (isalpha(*ptr) || *ptr == '_') { | ||
| 1763 | if (strncmp(map->enum_string, ptr, len) == 0 && | ||
| 1764 | !isalnum(ptr[len]) && ptr[len] != '_') { | ||
| 1765 | ptr = enum_replace(ptr, map, len); | ||
| 1766 | /* Hmm, enum string smaller than value */ | ||
| 1767 | if (WARN_ON_ONCE(!ptr)) | ||
| 1768 | return; | ||
| 1769 | /* | ||
| 1770 | * No need to decrement here, as enum_replace() | ||
| 1771 | * returns the pointer to the character passed | ||
| 1772 | * the enum, and two enums can not be placed | ||
| 1773 | * back to back without something in between. | ||
| 1774 | * We can skip that something in between. | ||
| 1775 | */ | ||
| 1776 | continue; | ||
| 1777 | } | ||
| 1778 | skip_more: | ||
| 1779 | do { | ||
| 1780 | ptr++; | ||
| 1781 | } while (isalnum(*ptr) || *ptr == '_'); | ||
| 1782 | /* | ||
| 1783 | * If what comes after this variable is a '.' or | ||
| 1784 | * '->' then we can continue to ignore that string. | ||
| 1785 | */ | ||
| 1786 | if (*ptr == '.' || (ptr[0] == '-' && ptr[1] == '>')) { | ||
| 1787 | ptr += *ptr == '.' ? 1 : 2; | ||
| 1788 | goto skip_more; | ||
| 1789 | } | ||
| 1790 | /* | ||
| 1791 | * Once again, we can skip the delimiter that came | ||
| 1792 | * after the string. | ||
| 1793 | */ | ||
| 1794 | continue; | ||
| 1795 | } | ||
| 1796 | } | ||
| 1797 | } | ||
| 1798 | |||
| 1799 | void trace_event_enum_update(struct trace_enum_map **map, int len) | ||
| 1800 | { | ||
| 1801 | struct ftrace_event_call *call, *p; | ||
| 1802 | const char *last_system = NULL; | ||
| 1803 | int last_i; | ||
| 1804 | int i; | ||
| 1805 | |||
| 1806 | down_write(&trace_event_sem); | ||
| 1807 | list_for_each_entry_safe(call, p, &ftrace_events, list) { | ||
| 1808 | /* events are usually grouped together with systems */ | ||
| 1809 | if (!last_system || call->class->system != last_system) { | ||
| 1810 | last_i = 0; | ||
| 1811 | last_system = call->class->system; | ||
| 1812 | } | ||
| 1813 | |||
| 1814 | for (i = last_i; i < len; i++) { | ||
| 1815 | if (call->class->system == map[i]->system) { | ||
| 1816 | /* Save the first system if need be */ | ||
| 1817 | if (!last_i) | ||
| 1818 | last_i = i; | ||
| 1819 | update_event_printk(call, map[i]); | ||
| 1820 | } | ||
| 1821 | } | ||
| 1822 | } | ||
| 1823 | up_write(&trace_event_sem); | ||
| 1824 | } | ||
| 1825 | |||
| 1707 | static struct ftrace_event_file * | 1826 | static struct ftrace_event_file * |
| 1708 | trace_create_new_event(struct ftrace_event_call *call, | 1827 | trace_create_new_event(struct ftrace_event_call *call, |
| 1709 | struct trace_array *tr) | 1828 | struct trace_array *tr) |
