aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
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 /kernel
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>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/trace/trace.c26
-rw-r--r--kernel/trace/trace.h2
-rw-r--r--kernel/trace/trace_events.c119
3 files changed, 146 insertions, 1 deletions
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)