diff options
author | Steven Rostedt (Red Hat) <rostedt@goodmis.org> | 2013-03-12 19:35:13 -0400 |
---|---|---|
committer | Steven Rostedt <rostedt@goodmis.org> | 2013-03-15 00:36:04 -0400 |
commit | 3cd715de261182413b3487abfffe1b6af41b81b3 (patch) | |
tree | 14d9a48d2aa31eb1ad387477376e41f25031238f /kernel/trace | |
parent | 417944c4c7a0f657158d0515f3b8e8c043fd788f (diff) |
tracing: Add function probe triggers to enable/disable events
Add triggers to function tracer that lets an event get enabled or
disabled when a function is called:
format is:
<function>:enable_event:<system>:<event>[:<count>]
<function>:disable_event:<system>:<event>[:<count>]
echo 'schedule:enable_event:sched:sched_switch' > /debug/tracing/set_ftrace_filter
Every time schedule is called, it will enable the sched_switch event.
echo 'schedule:disable_event:sched:sched_switch:2' > /debug/tracing/set_ftrace_filter
The first two times schedule is called while the sched_switch
event is enabled, it will disable it. It will not count for a time
that the event is already disabled (or enabled for enable_event).
[ fixed return without mutex_unlock() - thanks to Dan Carpenter and smatch ]
Cc: Dan Carpenter <dan.carpenter@oracle.com>
Cc: Tom Zanussi <tom.zanussi@linux.intel.com>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'kernel/trace')
-rw-r--r-- | kernel/trace/trace_events.c | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 106640b0df4a..c636523b1a59 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c | |||
@@ -1798,6 +1798,283 @@ __trace_add_event_dirs(struct trace_array *tr) | |||
1798 | } | 1798 | } |
1799 | } | 1799 | } |
1800 | 1800 | ||
1801 | #ifdef CONFIG_DYNAMIC_FTRACE | ||
1802 | |||
1803 | /* Avoid typos */ | ||
1804 | #define ENABLE_EVENT_STR "enable_event" | ||
1805 | #define DISABLE_EVENT_STR "disable_event" | ||
1806 | |||
1807 | struct event_probe_data { | ||
1808 | struct ftrace_event_file *file; | ||
1809 | unsigned long count; | ||
1810 | int ref; | ||
1811 | bool enable; | ||
1812 | }; | ||
1813 | |||
1814 | static struct ftrace_event_file * | ||
1815 | find_event_file(struct trace_array *tr, const char *system, const char *event) | ||
1816 | { | ||
1817 | struct ftrace_event_file *file; | ||
1818 | struct ftrace_event_call *call; | ||
1819 | |||
1820 | list_for_each_entry(file, &tr->events, list) { | ||
1821 | |||
1822 | call = file->event_call; | ||
1823 | |||
1824 | if (!call->name || !call->class || !call->class->reg) | ||
1825 | continue; | ||
1826 | |||
1827 | if (call->flags & TRACE_EVENT_FL_IGNORE_ENABLE) | ||
1828 | continue; | ||
1829 | |||
1830 | if (strcmp(event, call->name) == 0 && | ||
1831 | strcmp(system, call->class->system) == 0) | ||
1832 | return file; | ||
1833 | } | ||
1834 | return NULL; | ||
1835 | } | ||
1836 | |||
1837 | static void | ||
1838 | event_enable_probe(unsigned long ip, unsigned long parent_ip, void **_data) | ||
1839 | { | ||
1840 | struct event_probe_data **pdata = (struct event_probe_data **)_data; | ||
1841 | struct event_probe_data *data = *pdata; | ||
1842 | |||
1843 | if (!data) | ||
1844 | return; | ||
1845 | |||
1846 | if (data->enable) | ||
1847 | clear_bit(FTRACE_EVENT_FL_SOFT_DISABLED_BIT, &data->file->flags); | ||
1848 | else | ||
1849 | set_bit(FTRACE_EVENT_FL_SOFT_DISABLED_BIT, &data->file->flags); | ||
1850 | } | ||
1851 | |||
1852 | static void | ||
1853 | event_enable_count_probe(unsigned long ip, unsigned long parent_ip, void **_data) | ||
1854 | { | ||
1855 | struct event_probe_data **pdata = (struct event_probe_data **)_data; | ||
1856 | struct event_probe_data *data = *pdata; | ||
1857 | |||
1858 | if (!data) | ||
1859 | return; | ||
1860 | |||
1861 | if (!data->count) | ||
1862 | return; | ||
1863 | |||
1864 | /* Skip if the event is in a state we want to switch to */ | ||
1865 | if (data->enable == !(data->file->flags & FTRACE_EVENT_FL_SOFT_DISABLED)) | ||
1866 | return; | ||
1867 | |||
1868 | if (data->count != -1) | ||
1869 | (data->count)--; | ||
1870 | |||
1871 | event_enable_probe(ip, parent_ip, _data); | ||
1872 | } | ||
1873 | |||
1874 | static int | ||
1875 | event_enable_print(struct seq_file *m, unsigned long ip, | ||
1876 | struct ftrace_probe_ops *ops, void *_data) | ||
1877 | { | ||
1878 | struct event_probe_data *data = _data; | ||
1879 | |||
1880 | seq_printf(m, "%ps:", (void *)ip); | ||
1881 | |||
1882 | seq_printf(m, "%s:%s:%s", | ||
1883 | data->enable ? ENABLE_EVENT_STR : DISABLE_EVENT_STR, | ||
1884 | data->file->event_call->class->system, | ||
1885 | data->file->event_call->name); | ||
1886 | |||
1887 | if (data->count == -1) | ||
1888 | seq_printf(m, ":unlimited\n"); | ||
1889 | else | ||
1890 | seq_printf(m, ":count=%ld\n", data->count); | ||
1891 | |||
1892 | return 0; | ||
1893 | } | ||
1894 | |||
1895 | static int | ||
1896 | event_enable_init(struct ftrace_probe_ops *ops, unsigned long ip, | ||
1897 | void **_data) | ||
1898 | { | ||
1899 | struct event_probe_data **pdata = (struct event_probe_data **)_data; | ||
1900 | struct event_probe_data *data = *pdata; | ||
1901 | |||
1902 | data->ref++; | ||
1903 | return 0; | ||
1904 | } | ||
1905 | |||
1906 | static void | ||
1907 | event_enable_free(struct ftrace_probe_ops *ops, unsigned long ip, | ||
1908 | void **_data) | ||
1909 | { | ||
1910 | struct event_probe_data **pdata = (struct event_probe_data **)_data; | ||
1911 | struct event_probe_data *data = *pdata; | ||
1912 | |||
1913 | if (WARN_ON_ONCE(data->ref <= 0)) | ||
1914 | return; | ||
1915 | |||
1916 | data->ref--; | ||
1917 | if (!data->ref) { | ||
1918 | /* Remove the SOFT_MODE flag */ | ||
1919 | __ftrace_event_enable_disable(data->file, 0, 1); | ||
1920 | module_put(data->file->event_call->mod); | ||
1921 | kfree(data); | ||
1922 | } | ||
1923 | *pdata = NULL; | ||
1924 | } | ||
1925 | |||
1926 | static struct ftrace_probe_ops event_enable_probe_ops = { | ||
1927 | .func = event_enable_probe, | ||
1928 | .print = event_enable_print, | ||
1929 | .init = event_enable_init, | ||
1930 | .free = event_enable_free, | ||
1931 | }; | ||
1932 | |||
1933 | static struct ftrace_probe_ops event_enable_count_probe_ops = { | ||
1934 | .func = event_enable_count_probe, | ||
1935 | .print = event_enable_print, | ||
1936 | .init = event_enable_init, | ||
1937 | .free = event_enable_free, | ||
1938 | }; | ||
1939 | |||
1940 | static struct ftrace_probe_ops event_disable_probe_ops = { | ||
1941 | .func = event_enable_probe, | ||
1942 | .print = event_enable_print, | ||
1943 | .init = event_enable_init, | ||
1944 | .free = event_enable_free, | ||
1945 | }; | ||
1946 | |||
1947 | static struct ftrace_probe_ops event_disable_count_probe_ops = { | ||
1948 | .func = event_enable_count_probe, | ||
1949 | .print = event_enable_print, | ||
1950 | .init = event_enable_init, | ||
1951 | .free = event_enable_free, | ||
1952 | }; | ||
1953 | |||
1954 | static int | ||
1955 | event_enable_func(struct ftrace_hash *hash, | ||
1956 | char *glob, char *cmd, char *param, int enabled) | ||
1957 | { | ||
1958 | struct trace_array *tr = top_trace_array(); | ||
1959 | struct ftrace_event_file *file; | ||
1960 | struct ftrace_probe_ops *ops; | ||
1961 | struct event_probe_data *data; | ||
1962 | const char *system; | ||
1963 | const char *event; | ||
1964 | char *number; | ||
1965 | bool enable; | ||
1966 | int ret; | ||
1967 | |||
1968 | /* hash funcs only work with set_ftrace_filter */ | ||
1969 | if (!enabled) | ||
1970 | return -EINVAL; | ||
1971 | |||
1972 | if (!param) | ||
1973 | return -EINVAL; | ||
1974 | |||
1975 | system = strsep(¶m, ":"); | ||
1976 | if (!param) | ||
1977 | return -EINVAL; | ||
1978 | |||
1979 | event = strsep(¶m, ":"); | ||
1980 | |||
1981 | mutex_lock(&event_mutex); | ||
1982 | |||
1983 | ret = -EINVAL; | ||
1984 | file = find_event_file(tr, system, event); | ||
1985 | if (!file) | ||
1986 | goto out; | ||
1987 | |||
1988 | enable = strcmp(cmd, ENABLE_EVENT_STR) == 0; | ||
1989 | |||
1990 | if (enable) | ||
1991 | ops = param ? &event_enable_count_probe_ops : &event_enable_probe_ops; | ||
1992 | else | ||
1993 | ops = param ? &event_disable_count_probe_ops : &event_disable_probe_ops; | ||
1994 | |||
1995 | if (glob[0] == '!') { | ||
1996 | unregister_ftrace_function_probe_func(glob+1, ops); | ||
1997 | ret = 0; | ||
1998 | goto out; | ||
1999 | } | ||
2000 | |||
2001 | ret = -ENOMEM; | ||
2002 | data = kzalloc(sizeof(*data), GFP_KERNEL); | ||
2003 | if (!data) | ||
2004 | goto out; | ||
2005 | |||
2006 | data->enable = enable; | ||
2007 | data->count = -1; | ||
2008 | data->file = file; | ||
2009 | |||
2010 | if (!param) | ||
2011 | goto out_reg; | ||
2012 | |||
2013 | number = strsep(¶m, ":"); | ||
2014 | |||
2015 | ret = -EINVAL; | ||
2016 | if (!strlen(number)) | ||
2017 | goto out_free; | ||
2018 | |||
2019 | /* | ||
2020 | * We use the callback data field (which is a pointer) | ||
2021 | * as our counter. | ||
2022 | */ | ||
2023 | ret = kstrtoul(number, 0, &data->count); | ||
2024 | if (ret) | ||
2025 | goto out_free; | ||
2026 | |||
2027 | out_reg: | ||
2028 | /* Don't let event modules unload while probe registered */ | ||
2029 | ret = try_module_get(file->event_call->mod); | ||
2030 | if (!ret) | ||
2031 | goto out_free; | ||
2032 | |||
2033 | ret = __ftrace_event_enable_disable(file, 1, 1); | ||
2034 | if (ret < 0) | ||
2035 | goto out_put; | ||
2036 | ret = register_ftrace_function_probe(glob, ops, data); | ||
2037 | if (!ret) | ||
2038 | goto out_disable; | ||
2039 | out: | ||
2040 | mutex_unlock(&event_mutex); | ||
2041 | return ret; | ||
2042 | |||
2043 | out_disable: | ||
2044 | __ftrace_event_enable_disable(file, 0, 1); | ||
2045 | out_put: | ||
2046 | module_put(file->event_call->mod); | ||
2047 | out_free: | ||
2048 | kfree(data); | ||
2049 | goto out; | ||
2050 | } | ||
2051 | |||
2052 | static struct ftrace_func_command event_enable_cmd = { | ||
2053 | .name = ENABLE_EVENT_STR, | ||
2054 | .func = event_enable_func, | ||
2055 | }; | ||
2056 | |||
2057 | static struct ftrace_func_command event_disable_cmd = { | ||
2058 | .name = DISABLE_EVENT_STR, | ||
2059 | .func = event_enable_func, | ||
2060 | }; | ||
2061 | |||
2062 | static __init int register_event_cmds(void) | ||
2063 | { | ||
2064 | int ret; | ||
2065 | |||
2066 | ret = register_ftrace_command(&event_enable_cmd); | ||
2067 | if (WARN_ON(ret < 0)) | ||
2068 | return ret; | ||
2069 | ret = register_ftrace_command(&event_disable_cmd); | ||
2070 | if (WARN_ON(ret < 0)) | ||
2071 | unregister_ftrace_command(&event_enable_cmd); | ||
2072 | return ret; | ||
2073 | } | ||
2074 | #else | ||
2075 | static inline int register_event_cmds(void) { return 0; } | ||
2076 | #endif /* CONFIG_DYNAMIC_FTRACE */ | ||
2077 | |||
1801 | /* | 2078 | /* |
1802 | * The top level array has already had its ftrace_event_file | 2079 | * The top level array has already had its ftrace_event_file |
1803 | * descriptors created in order to allow for early events to | 2080 | * descriptors created in order to allow for early events to |
@@ -2058,6 +2335,8 @@ static __init int event_trace_enable(void) | |||
2058 | 2335 | ||
2059 | trace_printk_start_comm(); | 2336 | trace_printk_start_comm(); |
2060 | 2337 | ||
2338 | register_event_cmds(); | ||
2339 | |||
2061 | return 0; | 2340 | return 0; |
2062 | } | 2341 | } |
2063 | 2342 | ||