aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Rostedt (Red Hat) <rostedt@goodmis.org>2015-03-24 17:58:09 -0400
committerSteven Rostedt <rostedt@goodmis.org>2015-04-08 09:39:56 -0400
commit0c564a538aa934ad15b2145aaf8b64f3feb0be63 (patch)
tree136ee858e4d9b1c4e2d9d39d4614eb8af175afb0
parent889204278ccf98c5c097b1d1ae69babc6a3222fd (diff)
tracing: Add TRACE_DEFINE_ENUM() macro to map enums to their values
Several tracepoints use the helper functions __print_symbolic() or __print_flags() and pass in enums that do the mapping between the binary data stored and the value to print. This works well for reading the ASCII trace files, but when the data is read via userspace tools such as perf and trace-cmd, the conversion of the binary value to a human string format is lost if an enum is used, as userspace does not have access to what the ENUM is. For example, the tracepoint trace_tlb_flush() has: __print_symbolic(REC->reason, { TLB_FLUSH_ON_TASK_SWITCH, "flush on task switch" }, { TLB_REMOTE_SHOOTDOWN, "remote shootdown" }, { TLB_LOCAL_SHOOTDOWN, "local shootdown" }, { TLB_LOCAL_MM_SHOOTDOWN, "local mm shootdown" }) Which maps the enum values to the strings they represent. But perf and trace-cmd do no know what value TLB_LOCAL_MM_SHOOTDOWN is, and would not be able to map it. With TRACE_DEFINE_ENUM(), developers can place these in the event header files and ftrace will convert the enums to their values: By adding: TRACE_DEFINE_ENUM(TLB_FLUSH_ON_TASK_SWITCH); TRACE_DEFINE_ENUM(TLB_REMOTE_SHOOTDOWN); TRACE_DEFINE_ENUM(TLB_LOCAL_SHOOTDOWN); TRACE_DEFINE_ENUM(TLB_LOCAL_MM_SHOOTDOWN); $ cat /sys/kernel/debug/tracing/events/tlb/tlb_flush/format [...] __print_symbolic(REC->reason, { 0, "flush on task switch" }, { 1, "remote shootdown" }, { 2, "local shootdown" }, { 3, "local mm shootdown" }) The above is what userspace expects to see, and tools do not need to be modified to parse them. Link: http://lkml.kernel.org/r/20150403013802.220157513@goodmis.org Cc: Guilherme Cox <cox@computer.org> Cc: Tony Luck <tony.luck@gmail.com> Cc: Xie XiuQi <xiexiuqi@huawei.com> Acked-by: Namhyung Kim <namhyung@kernel.org> Reviewed-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> Tested-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
-rw-r--r--include/asm-generic/vmlinux.lds.h5
-rw-r--r--include/linux/ftrace_event.h2
-rw-r--r--include/linux/tracepoint.h8
-rw-r--r--include/trace/ftrace.h22
-rw-r--r--kernel/trace/trace.c26
-rw-r--r--kernel/trace/trace.h2
-rw-r--r--kernel/trace/trace_events.c119
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
39struct trace_enum_map {
40 const char *system;
41 const char *enum_string;
42 unsigned long enum_value;
43};
44
39extern int 45extern int
40tracepoint_probe_register(struct tracepoint *tp, void *probe, void *data); 46tracepoint_probe_register(struct tracepoint *tp, void *probe, void *data);
41extern int 47extern 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
34TRACE_MAKE_SYSTEM_STR(); 34TRACE_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)); \
707static const char print_fmt_##call[] = print; \ 723static char print_fmt_##call[] = print; \
708static struct ftrace_event_class __used __refdata event_class_##call = { \ 724static 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 \
736static const char print_fmt_##call[] = print; \ 752static char print_fmt_##call[] = print; \
737 \ 753 \
738static struct ftrace_event_call __used event_##call = { \ 754static 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
3911static void
3912trace_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
3911static ssize_t 3925static ssize_t
3912tracing_set_trace_read(struct file *filp, char __user *ubuf, 3926tracing_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
6559extern struct trace_enum_map *__start_ftrace_enum_maps[];
6560extern struct trace_enum_map *__stop_ftrace_enum_maps[];
6561
6562static void __init trace_enum_init(void)
6563{
6564 trace_insert_enum_map(__start_ftrace_enum_maps, __stop_ftrace_enum_maps);
6565}
6566
6545static __init int tracer_init_debugfs(void) 6567static __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
1311void trace_event_init(void); 1311void trace_event_init(void);
1312void trace_event_enum_update(struct trace_enum_map **map, int len);
1312#else 1313#else
1313static inline void __init trace_event_init(void) { } 1314static inline void __init trace_event_init(void) { }
1315static inlin void trace_event_enum_update(struct trace_enum_map **map, int len) { }
1314#endif 1316#endif
1315 1317
1316extern struct trace_iterator *tracepoint_print_iter; 1318extern 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
1707static 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
1729static 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
1799void 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
1707static struct ftrace_event_file * 1826static struct ftrace_event_file *
1708trace_create_new_event(struct ftrace_event_call *call, 1827trace_create_new_event(struct ftrace_event_call *call,
1709 struct trace_array *tr) 1828 struct trace_array *tr)