Commit f492d3f8 authored by Ingo Molnar's avatar Ingo Molnar

Merge branch 'tip/tracing/ftrace' of...

Merge branch 'tip/tracing/ftrace' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-2.6-trace into tracing/ftrace
parents c4e2b432 e110e3d1
...@@ -95,10 +95,41 @@ stack_trace_sysctl(struct ctl_table *table, int write, ...@@ -95,10 +95,41 @@ stack_trace_sysctl(struct ctl_table *table, int write,
loff_t *ppos); loff_t *ppos);
#endif #endif
struct ftrace_func_command {
struct list_head list;
char *name;
int (*func)(char *func, char *cmd,
char *params, int enable);
};
#ifdef CONFIG_DYNAMIC_FTRACE #ifdef CONFIG_DYNAMIC_FTRACE
/* asm/ftrace.h must be defined for archs supporting dynamic ftrace */ /* asm/ftrace.h must be defined for archs supporting dynamic ftrace */
#include <asm/ftrace.h> #include <asm/ftrace.h>
struct seq_file;
struct ftrace_hook_ops {
void (*func)(unsigned long ip,
unsigned long parent_ip,
void **data);
int (*callback)(unsigned long ip, void **data);
void (*free)(void **data);
int (*print)(struct seq_file *m,
unsigned long ip,
struct ftrace_hook_ops *ops,
void *data);
};
extern int
register_ftrace_function_hook(char *glob, struct ftrace_hook_ops *ops,
void *data);
extern void
unregister_ftrace_function_hook(char *glob, struct ftrace_hook_ops *ops,
void *data);
extern void
unregister_ftrace_function_hook_func(char *glob, struct ftrace_hook_ops *ops);
extern void unregister_ftrace_function_hook_all(char *glob);
enum { enum {
FTRACE_FL_FREE = (1 << 0), FTRACE_FL_FREE = (1 << 0),
FTRACE_FL_FAILED = (1 << 1), FTRACE_FL_FAILED = (1 << 1),
...@@ -119,6 +150,9 @@ struct dyn_ftrace { ...@@ -119,6 +150,9 @@ struct dyn_ftrace {
int ftrace_force_update(void); int ftrace_force_update(void);
void ftrace_set_filter(unsigned char *buf, int len, int reset); void ftrace_set_filter(unsigned char *buf, int len, int reset);
int register_ftrace_command(struct ftrace_func_command *cmd);
int unregister_ftrace_command(struct ftrace_func_command *cmd);
/* defined in arch */ /* defined in arch */
extern int ftrace_ip_converted(unsigned long ip); extern int ftrace_ip_converted(unsigned long ip);
extern int ftrace_dyn_arch_init(void *data); extern int ftrace_dyn_arch_init(void *data);
...@@ -202,6 +236,12 @@ extern void ftrace_enable_daemon(void); ...@@ -202,6 +236,12 @@ extern void ftrace_enable_daemon(void);
# define ftrace_disable_daemon() do { } while (0) # define ftrace_disable_daemon() do { } while (0)
# define ftrace_enable_daemon() do { } while (0) # define ftrace_enable_daemon() do { } while (0)
static inline void ftrace_release(void *start, unsigned long size) { } static inline void ftrace_release(void *start, unsigned long size) { }
static inline int register_ftrace_command(struct ftrace_func_command *cmd)
{
}
static inline int unregister_ftrace_command(char *cmd_name)
{
}
#endif /* CONFIG_DYNAMIC_FTRACE */ #endif /* CONFIG_DYNAMIC_FTRACE */
/* totally disable ftrace - can not re-enable after this */ /* totally disable ftrace - can not re-enable after this */
......
...@@ -128,10 +128,12 @@ void ring_buffer_normalize_time_stamp(int cpu, u64 *ts); ...@@ -128,10 +128,12 @@ void ring_buffer_normalize_time_stamp(int cpu, u64 *ts);
void tracing_on(void); void tracing_on(void);
void tracing_off(void); void tracing_off(void);
void tracing_off_permanent(void); void tracing_off_permanent(void);
int tracing_is_on(void);
#else #else
static inline void tracing_on(void) { } static inline void tracing_on(void) { }
static inline void tracing_off(void) { } static inline void tracing_off(void) { }
static inline void tracing_off_permanent(void) { } static inline void tracing_off_permanent(void) { }
static inline int tracing_is_on(void) { return 0; }
#endif #endif
void *ring_buffer_alloc_read_page(struct ring_buffer *buffer); void *ring_buffer_alloc_read_page(struct ring_buffer *buffer);
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <linux/sysctl.h> #include <linux/sysctl.h>
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/hash.h>
#include <asm/ftrace.h> #include <asm/ftrace.h>
...@@ -44,14 +45,14 @@ ...@@ -44,14 +45,14 @@
ftrace_kill(); \ ftrace_kill(); \
} while (0) } while (0)
/* hash bits for specific function selection */
#define FTRACE_HASH_BITS 7
#define FTRACE_FUNC_HASHSIZE (1 << FTRACE_HASH_BITS)
/* ftrace_enabled is a method to turn ftrace on or off */ /* ftrace_enabled is a method to turn ftrace on or off */
int ftrace_enabled __read_mostly; int ftrace_enabled __read_mostly;
static int last_ftrace_enabled; static int last_ftrace_enabled;
/* set when tracing only a pid */
struct pid *ftrace_pid_trace;
static struct pid * const ftrace_swapper_pid = &init_struct_pid;
/* Quick disabling of function tracer. */ /* Quick disabling of function tracer. */
int function_trace_stop; int function_trace_stop;
...@@ -61,9 +62,7 @@ int function_trace_stop; ...@@ -61,9 +62,7 @@ int function_trace_stop;
*/ */
static int ftrace_disabled __read_mostly; static int ftrace_disabled __read_mostly;
static DEFINE_SPINLOCK(ftrace_lock); static DEFINE_MUTEX(ftrace_lock);
static DEFINE_MUTEX(ftrace_sysctl_lock);
static DEFINE_MUTEX(ftrace_start_lock);
static struct ftrace_ops ftrace_list_end __read_mostly = static struct ftrace_ops ftrace_list_end __read_mostly =
{ {
...@@ -134,9 +133,6 @@ static void ftrace_test_stop_func(unsigned long ip, unsigned long parent_ip) ...@@ -134,9 +133,6 @@ static void ftrace_test_stop_func(unsigned long ip, unsigned long parent_ip)
static int __register_ftrace_function(struct ftrace_ops *ops) static int __register_ftrace_function(struct ftrace_ops *ops)
{ {
/* should not be called from interrupt context */
spin_lock(&ftrace_lock);
ops->next = ftrace_list; ops->next = ftrace_list;
/* /*
* We are entering ops into the ftrace_list but another * We are entering ops into the ftrace_list but another
...@@ -172,18 +168,12 @@ static int __register_ftrace_function(struct ftrace_ops *ops) ...@@ -172,18 +168,12 @@ static int __register_ftrace_function(struct ftrace_ops *ops)
#endif #endif
} }
spin_unlock(&ftrace_lock);
return 0; return 0;
} }
static int __unregister_ftrace_function(struct ftrace_ops *ops) static int __unregister_ftrace_function(struct ftrace_ops *ops)
{ {
struct ftrace_ops **p; struct ftrace_ops **p;
int ret = 0;
/* should not be called from interrupt context */
spin_lock(&ftrace_lock);
/* /*
* If we are removing the last function, then simply point * If we are removing the last function, then simply point
...@@ -192,17 +182,15 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops) ...@@ -192,17 +182,15 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops)
if (ftrace_list == ops && ops->next == &ftrace_list_end) { if (ftrace_list == ops && ops->next == &ftrace_list_end) {
ftrace_trace_function = ftrace_stub; ftrace_trace_function = ftrace_stub;
ftrace_list = &ftrace_list_end; ftrace_list = &ftrace_list_end;
goto out; return 0;
} }
for (p = &ftrace_list; *p != &ftrace_list_end; p = &(*p)->next) for (p = &ftrace_list; *p != &ftrace_list_end; p = &(*p)->next)
if (*p == ops) if (*p == ops)
break; break;
if (*p != ops) { if (*p != ops)
ret = -1; return -1;
goto out;
}
*p = (*p)->next; *p = (*p)->next;
...@@ -223,18 +211,14 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops) ...@@ -223,18 +211,14 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops)
} }
} }
out: return 0;
spin_unlock(&ftrace_lock);
return ret;
} }
static void ftrace_update_pid_func(void) static void ftrace_update_pid_func(void)
{ {
ftrace_func_t func; ftrace_func_t func;
/* should not be called from interrupt context */ mutex_lock(&ftrace_lock);
spin_lock(&ftrace_lock);
if (ftrace_trace_function == ftrace_stub) if (ftrace_trace_function == ftrace_stub)
goto out; goto out;
...@@ -256,7 +240,7 @@ static void ftrace_update_pid_func(void) ...@@ -256,7 +240,7 @@ static void ftrace_update_pid_func(void)
#endif #endif
out: out:
spin_unlock(&ftrace_lock); mutex_unlock(&ftrace_lock);
} }
#ifdef CONFIG_DYNAMIC_FTRACE #ifdef CONFIG_DYNAMIC_FTRACE
...@@ -264,6 +248,21 @@ static void ftrace_update_pid_func(void) ...@@ -264,6 +248,21 @@ static void ftrace_update_pid_func(void)
# error Dynamic ftrace depends on MCOUNT_RECORD # error Dynamic ftrace depends on MCOUNT_RECORD
#endif #endif
/* set when tracing only a pid */
struct pid *ftrace_pid_trace;
static struct pid * const ftrace_swapper_pid = &init_struct_pid;
static struct hlist_head ftrace_func_hash[FTRACE_FUNC_HASHSIZE] __read_mostly;
struct ftrace_func_hook {
struct hlist_node node;
struct ftrace_hook_ops *ops;
unsigned long flags;
unsigned long ip;
void *data;
struct rcu_head rcu;
};
enum { enum {
FTRACE_ENABLE_CALLS = (1 << 0), FTRACE_ENABLE_CALLS = (1 << 0),
FTRACE_DISABLE_CALLS = (1 << 1), FTRACE_DISABLE_CALLS = (1 << 1),
...@@ -297,6 +296,19 @@ static struct ftrace_page *ftrace_pages; ...@@ -297,6 +296,19 @@ static struct ftrace_page *ftrace_pages;
static struct dyn_ftrace *ftrace_free_records; static struct dyn_ftrace *ftrace_free_records;
/*
* This is a double for. Do not use 'break' to break out of the loop,
* you must use a goto.
*/
#define do_for_each_ftrace_rec(pg, rec) \
for (pg = ftrace_pages_start; pg; pg = pg->next) { \
int _____i; \
for (_____i = 0; _____i < pg->index; _____i++) { \
rec = &pg->records[_____i];
#define while_for_each_ftrace_rec() \
} \
}
#ifdef CONFIG_KPROBES #ifdef CONFIG_KPROBES
...@@ -341,23 +353,16 @@ void ftrace_release(void *start, unsigned long size) ...@@ -341,23 +353,16 @@ void ftrace_release(void *start, unsigned long size)
struct ftrace_page *pg; struct ftrace_page *pg;
unsigned long s = (unsigned long)start; unsigned long s = (unsigned long)start;
unsigned long e = s + size; unsigned long e = s + size;
int i;
if (ftrace_disabled || !start) if (ftrace_disabled || !start)
return; return;
/* should not be called from interrupt context */ mutex_lock(&ftrace_lock);
spin_lock(&ftrace_lock); do_for_each_ftrace_rec(pg, rec) {
if ((rec->ip >= s) && (rec->ip < e))
for (pg = ftrace_pages_start; pg; pg = pg->next) { ftrace_free_rec(rec);
for (i = 0; i < pg->index; i++) { } while_for_each_ftrace_rec();
rec = &pg->records[i]; mutex_unlock(&ftrace_lock);
if ((rec->ip >= s) && (rec->ip < e))
ftrace_free_rec(rec);
}
}
spin_unlock(&ftrace_lock);
} }
static struct dyn_ftrace *ftrace_alloc_dyn_node(unsigned long ip) static struct dyn_ftrace *ftrace_alloc_dyn_node(unsigned long ip)
...@@ -523,41 +528,37 @@ __ftrace_replace_code(struct dyn_ftrace *rec, int enable) ...@@ -523,41 +528,37 @@ __ftrace_replace_code(struct dyn_ftrace *rec, int enable)
static void ftrace_replace_code(int enable) static void ftrace_replace_code(int enable)
{ {
int i, failed; int failed;
struct dyn_ftrace *rec; struct dyn_ftrace *rec;
struct ftrace_page *pg; struct ftrace_page *pg;
for (pg = ftrace_pages_start; pg; pg = pg->next) { do_for_each_ftrace_rec(pg, rec) {
for (i = 0; i < pg->index; i++) { /*
rec = &pg->records[i]; * Skip over free records and records that have
* failed.
/* */
* Skip over free records and records that have if (rec->flags & FTRACE_FL_FREE ||
* failed. rec->flags & FTRACE_FL_FAILED)
*/ continue;
if (rec->flags & FTRACE_FL_FREE ||
rec->flags & FTRACE_FL_FAILED)
continue;
/* ignore updates to this record's mcount site */ /* ignore updates to this record's mcount site */
if (get_kprobe((void *)rec->ip)) { if (get_kprobe((void *)rec->ip)) {
freeze_record(rec); freeze_record(rec);
continue; continue;
} else { } else {
unfreeze_record(rec); unfreeze_record(rec);
} }
failed = __ftrace_replace_code(rec, enable); failed = __ftrace_replace_code(rec, enable);
if (failed && (rec->flags & FTRACE_FL_CONVERTED)) { if (failed && (rec->flags & FTRACE_FL_CONVERTED)) {
rec->flags |= FTRACE_FL_FAILED; rec->flags |= FTRACE_FL_FAILED;
if ((system_state == SYSTEM_BOOTING) || if ((system_state == SYSTEM_BOOTING) ||
!core_kernel_text(rec->ip)) { !core_kernel_text(rec->ip)) {
ftrace_free_rec(rec); ftrace_free_rec(rec);
} else } else
ftrace_bug(failed, rec->ip); ftrace_bug(failed, rec->ip);
}
} }
} } while_for_each_ftrace_rec();
} }
static int static int
...@@ -623,13 +624,10 @@ static void ftrace_startup(int command) ...@@ -623,13 +624,10 @@ static void ftrace_startup(int command)
if (unlikely(ftrace_disabled)) if (unlikely(ftrace_disabled))
return; return;
mutex_lock(&ftrace_start_lock);
ftrace_start_up++; ftrace_start_up++;
command |= FTRACE_ENABLE_CALLS; command |= FTRACE_ENABLE_CALLS;
ftrace_startup_enable(command); ftrace_startup_enable(command);
mutex_unlock(&ftrace_start_lock);
} }
static void ftrace_shutdown(int command) static void ftrace_shutdown(int command)
...@@ -637,7 +635,6 @@ static void ftrace_shutdown(int command) ...@@ -637,7 +635,6 @@ static void ftrace_shutdown(int command)
if (unlikely(ftrace_disabled)) if (unlikely(ftrace_disabled))
return; return;
mutex_lock(&ftrace_start_lock);
ftrace_start_up--; ftrace_start_up--;
if (!ftrace_start_up) if (!ftrace_start_up)
command |= FTRACE_DISABLE_CALLS; command |= FTRACE_DISABLE_CALLS;
...@@ -648,11 +645,9 @@ static void ftrace_shutdown(int command) ...@@ -648,11 +645,9 @@ static void ftrace_shutdown(int command)
} }
if (!command || !ftrace_enabled) if (!command || !ftrace_enabled)
goto out; return;
ftrace_run_update_code(command); ftrace_run_update_code(command);
out:
mutex_unlock(&ftrace_start_lock);
} }
static void ftrace_startup_sysctl(void) static void ftrace_startup_sysctl(void)
...@@ -662,7 +657,6 @@ static void ftrace_startup_sysctl(void) ...@@ -662,7 +657,6 @@ static void ftrace_startup_sysctl(void)
if (unlikely(ftrace_disabled)) if (unlikely(ftrace_disabled))
return; return;
mutex_lock(&ftrace_start_lock);
/* Force update next time */ /* Force update next time */
saved_ftrace_func = NULL; saved_ftrace_func = NULL;
/* ftrace_start_up is true if we want ftrace running */ /* ftrace_start_up is true if we want ftrace running */
...@@ -670,7 +664,6 @@ static void ftrace_startup_sysctl(void) ...@@ -670,7 +664,6 @@ static void ftrace_startup_sysctl(void)
command |= FTRACE_ENABLE_CALLS; command |= FTRACE_ENABLE_CALLS;
ftrace_run_update_code(command); ftrace_run_update_code(command);
mutex_unlock(&ftrace_start_lock);
} }
static void ftrace_shutdown_sysctl(void) static void ftrace_shutdown_sysctl(void)
...@@ -680,13 +673,11 @@ static void ftrace_shutdown_sysctl(void) ...@@ -680,13 +673,11 @@ static void ftrace_shutdown_sysctl(void)
if (unlikely(ftrace_disabled)) if (unlikely(ftrace_disabled))
return; return;
mutex_lock(&ftrace_start_lock);
/* ftrace_start_up is true if ftrace is running */ /* ftrace_start_up is true if ftrace is running */
if (ftrace_start_up) if (ftrace_start_up)
command |= FTRACE_DISABLE_CALLS; command |= FTRACE_DISABLE_CALLS;
ftrace_run_update_code(command); ftrace_run_update_code(command);
mutex_unlock(&ftrace_start_lock);
} }
static cycle_t ftrace_update_time; static cycle_t ftrace_update_time;
...@@ -773,12 +764,15 @@ enum { ...@@ -773,12 +764,15 @@ enum {
FTRACE_ITER_CONT = (1 << 1), FTRACE_ITER_CONT = (1 << 1),
FTRACE_ITER_NOTRACE = (1 << 2), FTRACE_ITER_NOTRACE = (1 << 2),
FTRACE_ITER_FAILURES = (1 << 3), FTRACE_ITER_FAILURES = (1 << 3),
FTRACE_ITER_PRINTALL = (1 << 4),
FTRACE_ITER_HASH = (1 << 5),
}; };
#define FTRACE_BUFF_MAX (KSYM_SYMBOL_LEN+4) /* room for wildcards */ #define FTRACE_BUFF_MAX (KSYM_SYMBOL_LEN+4) /* room for wildcards */
struct ftrace_iterator { struct ftrace_iterator {
struct ftrace_page *pg; struct ftrace_page *pg;
int hidx;
int idx; int idx;
unsigned flags; unsigned flags;
unsigned char buffer[FTRACE_BUFF_MAX+1]; unsigned char buffer[FTRACE_BUFF_MAX+1];
...@@ -786,16 +780,90 @@ struct ftrace_iterator { ...@@ -786,16 +780,90 @@ struct ftrace_iterator {
unsigned filtered; unsigned filtered;
}; };
static void *
t_hash_next(struct seq_file *m, void *v, loff_t *pos)
{
struct ftrace_iterator *iter = m->private;
struct hlist_node *hnd = v;
struct hlist_head *hhd;
WARN_ON(!(iter->flags & FTRACE_ITER_HASH));
(*pos)++;
retry:
if (iter->hidx >= FTRACE_FUNC_HASHSIZE)
return NULL;
hhd = &ftrace_func_hash[iter->hidx];
if (hlist_empty(hhd)) {
iter->hidx++;
hnd = NULL;
goto retry;
}
if (!hnd)
hnd = hhd->first;
else {
hnd = hnd->next;
if (!hnd) {
iter->hidx++;
goto retry;
}
}
return hnd;
}
static void *t_hash_start(struct seq_file *m, loff_t *pos)
{
struct ftrace_iterator *iter = m->private;
void *p = NULL;
iter->flags |= FTRACE_ITER_HASH;
return t_hash_next(m, p, pos);
}
static int t_hash_show(struct seq_file *m, void *v)
{
struct ftrace_func_hook *rec;
struct hlist_node *hnd = v;
char str[KSYM_SYMBOL_LEN];
rec = hlist_entry(hnd, struct ftrace_func_hook, node);
if (rec->ops->print)
return rec->ops->print(m, rec->ip, rec->ops, rec->data);
kallsyms_lookup(rec->ip, NULL, NULL, NULL, str);
seq_printf(m, "%s:", str);
kallsyms_lookup((unsigned long)rec->ops->func, NULL, NULL, NULL, str);
seq_printf(m, "%s", str);
if (rec->data)
seq_printf(m, ":%p", rec->data);
seq_putc(m, '\n');
return 0;
}
static void * static void *
t_next(struct seq_file *m, void *v, loff_t *pos) t_next(struct seq_file *m, void *v, loff_t *pos)
{ {
struct ftrace_iterator *iter = m->private; struct ftrace_iterator *iter = m->private;
struct dyn_ftrace *rec = NULL; struct dyn_ftrace *rec = NULL;
if (iter->flags & FTRACE_ITER_HASH)
return t_hash_next(m, v, pos);
(*pos)++; (*pos)++;
/* should not be called from interrupt context */ if (iter->flags & FTRACE_ITER_PRINTALL)
spin_lock(&ftrace_lock); return NULL;
retry: retry:
if (iter->idx >= iter->pg->index) { if (iter->idx >= iter->pg->index) {
if (iter->pg->next) { if (iter->pg->next) {
...@@ -824,7 +892,6 @@ t_next(struct seq_file *m, void *v, loff_t *pos) ...@@ -824,7 +892,6 @@ t_next(struct seq_file *m, void *v, loff_t *pos)
goto retry; goto retry;
} }
} }
spin_unlock(&ftrace_lock);
return rec; return rec;
} }
...@@ -834,6 +901,23 @@ static void *t_start(struct seq_file *m, loff_t *pos) ...@@ -834,6 +901,23 @@ static void *t_start(struct seq_file *m, loff_t *pos)
struct ftrace_iterator *iter = m->private; struct ftrace_iterator *iter = m->private;
void *p = NULL; void *p = NULL;
mutex_lock(&ftrace_lock);
/*
* For set_ftrace_filter reading, if we have the filter
* off, we can short cut and just print out that all
* functions are enabled.
*/
if (iter->flags & FTRACE_ITER_FILTER && !ftrace_filtered) {
if (*pos > 0)
return t_hash_start(m, pos);
iter->flags |= FTRACE_ITER_PRINTALL;
(*pos)++;
return iter;
}
if (iter->flags & FTRACE_ITER_HASH)
return t_hash_start(m, pos);
if (*pos > 0) { if (*pos > 0) {
if (iter->idx < 0) if (iter->idx < 0)
return p; return p;
...@@ -843,18 +927,31 @@ static void *t_start(struct seq_file *m, loff_t *pos) ...@@ -843,18 +927,31 @@ static void *t_start(struct seq_file *m, loff_t *pos)
p = t_next(m, p, pos); p = t_next(m, p, pos);
if (!p)
return t_hash_start(m, pos);
return p; return p;
} }
static void t_stop(struct seq_file *m, void *p) static void t_stop(struct seq_file *m, void *p)
{ {
mutex_unlock(&ftrace_lock);
} }
static int t_show(struct seq_file *m, void *v) static int t_show(struct seq_file *m, void *v)
{ {
struct ftrace_iterator *iter = m->private;
struct dyn_ftrace *rec = v; struct dyn_ftrace *rec = v;
char str[KSYM_SYMBOL_LEN]; char str[KSYM_SYMBOL_LEN];
if (iter->flags & FTRACE_ITER_HASH)
return t_hash_show(m, v);
if (iter->flags & FTRACE_ITER_PRINTALL) {
seq_printf(m, "#### all functions enabled ####\n");
return 0;
}
if (!rec) if (!rec)
return 0; return 0;
...@@ -933,23 +1030,16 @@ static void ftrace_filter_reset(int enable) ...@@ -933,23 +1030,16 @@ static void ftrace_filter_reset(int enable)
struct ftrace_page *pg; struct ftrace_page *pg;
struct dyn_ftrace *rec; struct dyn_ftrace *rec;
unsigned long type = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE; unsigned long type = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE;
unsigned i;
/* should not be called from interrupt context */ mutex_lock(&ftrace_lock);
spin_lock(&ftrace_lock);
if (enable) if (enable)
ftrace_filtered = 0; ftrace_filtered = 0;
pg = ftrace_pages_start; do_for_each_ftrace_rec(pg, rec) {
while (pg) { if (rec->flags & FTRACE_FL_FAILED)
for (i = 0; i < pg->index; i++) { continue;
rec = &pg->records[i]; rec->flags &= ~type;
if (rec->flags & FTRACE_FL_FAILED) } while_for_each_ftrace_rec();
continue; mutex_unlock(&ftrace_lock);
rec->flags &= ~type;
}
pg = pg->next;
}
spin_unlock(&ftrace_lock);
} }
static int static int
...@@ -1030,86 +1120,533 @@ enum { ...@@ -1030,86 +1120,533 @@ enum {
MATCH_END_ONLY, MATCH_END_ONLY,
}; };
static void /*
ftrace_match(unsigned char *buff, int len, int enable) * (static function - no need for kernel doc)
*
* Pass in a buffer containing a glob and this function will
* set search to point to the search part of the buffer and
* return the type of search it is (see enum above).
* This does modify buff.
*
* Returns enum type.
* search returns the pointer to use for comparison.
* not returns 1 if buff started with a '!'
* 0 otherwise.
*/
static int
ftrace_setup_glob(char *buff, int len, char **search, int *not)
{ {
char str[KSYM_SYMBOL_LEN];
char *search = NULL;
struct ftrace_page *pg;
struct dyn_ftrace *rec;
int type = MATCH_FULL; int type = MATCH_FULL;
unsigned long flag = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE; int i;
unsigned i, match = 0, search_len = 0;
int not = 0;
if (buff[0] == '!') { if (buff[0] == '!') {
not = 1; *not = 1;
buff++; buff++;
len--; len--;
} } else
*not = 0;
*search = buff;
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
if (buff[i] == '*') { if (buff[i] == '*') {
if (!i) { if (!i) {
search = buff + i + 1; *search = buff + 1;
type = MATCH_END_ONLY; type = MATCH_END_ONLY;
search_len = len - (i + 1);
} else { } else {
if (type == MATCH_END_ONLY) { if (type == MATCH_END_ONLY)
type = MATCH_MIDDLE_ONLY; type = MATCH_MIDDLE_ONLY;
} else { else
match = i;
type = MATCH_FRONT_ONLY; type = MATCH_FRONT_ONLY;
}
buff[i] = 0; buff[i] = 0;
break; break;
} }
} }
} }
/* should not be called from interrupt context */ return type;
spin_lock(&ftrace_lock); }
if (enable)
ftrace_filtered = 1; static int ftrace_match(char *str, char *regex, int len, int type)
pg = ftrace_pages_start; {
while (pg) { int matched = 0;
for (i = 0; i < pg->index; i++) { char *ptr;
int matched = 0;
char *ptr; switch (type) {
case MATCH_FULL:
rec = &pg->records[i]; if (strcmp(str, regex) == 0)
if (rec->flags & FTRACE_FL_FAILED) matched = 1;
break;
case MATCH_FRONT_ONLY:
if (strncmp(str, regex, len) == 0)
matched = 1;
break;
case MATCH_MIDDLE_ONLY:
if (strstr(str, regex))
matched = 1;
break;
case MATCH_END_ONLY:
ptr = strstr(str, regex);
if (ptr && (ptr[len] == 0))
matched = 1;
break;
}
return matched;
}
static int
ftrace_match_record(struct dyn_ftrace *rec, char *regex, int len, int type)
{
char str[KSYM_SYMBOL_LEN];
kallsyms_lookup(rec->ip, NULL, NULL, NULL, str);
return ftrace_match(str, regex, len, type);
}
static void ftrace_match_records(char *buff, int len, int enable)
{
char *search;
struct ftrace_page *pg;
struct dyn_ftrace *rec;
int type;
unsigned long flag = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE;
unsigned search_len;
int not;
type = ftrace_setup_glob(buff, len, &search, &not);
search_len = strlen(search);
mutex_lock(&ftrace_lock);
do_for_each_ftrace_rec(pg, rec) {
if (rec->flags & FTRACE_FL_FAILED)
continue;
if (ftrace_match_record(rec, search, search_len, type)) {
if (not)
rec->flags &= ~flag;
else
rec->flags |= flag;
}
/*
* Only enable filtering if we have a function that
* is filtered on.
*/
if (enable && (rec->flags & FTRACE_FL_FILTER))
ftrace_filtered = 1;
} while_for_each_ftrace_rec();
mutex_unlock(&ftrace_lock);
}
static int
ftrace_match_module_record(struct dyn_ftrace *rec, char *mod,
char *regex, int len, int type)
{
char str[KSYM_SYMBOL_LEN];
char *modname;
kallsyms_lookup(rec->ip, NULL, NULL, &modname, str);
if (!modname || strcmp(modname, mod))
return 0;
/* blank search means to match all funcs in the mod */
if (len)
return ftrace_match(str, regex, len, type);
else
return 1;
}
static void ftrace_match_module_records(char *buff, char *mod, int enable)
{
char *search = buff;
struct ftrace_page *pg;
struct dyn_ftrace *rec;
int type = MATCH_FULL;
unsigned long flag = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE;
unsigned search_len = 0;
int not = 0;
/* blank or '*' mean the same */
if (strcmp(buff, "*") == 0)
buff[0] = 0;
/* handle the case of 'dont filter this module' */
if (strcmp(buff, "!") == 0 || strcmp(buff, "!*") == 0) {
buff[0] = 0;
not = 1;
}
if (strlen(buff)) {
type = ftrace_setup_glob(buff, strlen(buff), &search, &not);
search_len = strlen(search);
}
mutex_lock(&ftrace_lock);
do_for_each_ftrace_rec(pg, rec) {
if (rec->flags & FTRACE_FL_FAILED)
continue;
if (ftrace_match_module_record(rec, mod,
search, search_len, type)) {
if (not)
rec->flags &= ~flag;
else
rec->flags |= flag;
}
if (enable && (rec->flags & FTRACE_FL_FILTER))
ftrace_filtered = 1;
} while_for_each_ftrace_rec();
mutex_unlock(&ftrace_lock);
}
/*
* We register the module command as a template to show others how
* to register the a command as well.
*/
static int
ftrace_mod_callback(char *func, char *cmd, char *param, int enable)
{
char *mod;
/*
* cmd == 'mod' because we only registered this func
* for the 'mod' ftrace_func_command.
* But if you register one func with multiple commands,
* you can tell which command was used by the cmd
* parameter.
*/
/* we must have a module name */
if (!param)
return -EINVAL;
mod = strsep(&param, ":");
if (!strlen(mod))
return -EINVAL;
ftrace_match_module_records(func, mod, enable);
return 0;
}
static struct ftrace_func_command ftrace_mod_cmd = {
.name = "mod",
.func = ftrace_mod_callback,
};
static int __init ftrace_mod_cmd_init(void)
{
return register_ftrace_command(&ftrace_mod_cmd);
}
device_initcall(ftrace_mod_cmd_init);
static void
function_trace_hook_call(unsigned long ip, unsigned long parent_ip)
{
struct ftrace_func_hook *entry;
struct hlist_head *hhd;
struct hlist_node *n;
unsigned long key;
int resched;
key = hash_long(ip, FTRACE_HASH_BITS);
hhd = &ftrace_func_hash[key];
if (hlist_empty(hhd))
return;
/*
* Disable preemption for these calls to prevent a RCU grace
* period. This syncs the hash iteration and freeing of items
* on the hash. rcu_read_lock is too dangerous here.
*/
resched = ftrace_preempt_disable();
hlist_for_each_entry_rcu(entry, n, hhd, node) {
if (entry->ip == ip)
entry->ops->func(ip, parent_ip, &entry->data);
}
ftrace_preempt_enable(resched);
}
static struct ftrace_ops trace_hook_ops __read_mostly =
{
.func = function_trace_hook_call,
};
static int ftrace_hook_registered;
static void __enable_ftrace_function_hook(void)
{
int i;
if (ftrace_hook_registered)
return;
for (i = 0; i < FTRACE_FUNC_HASHSIZE; i++) {
struct hlist_head *hhd = &ftrace_func_hash[i];
if (hhd->first)
break;
}
/* Nothing registered? */
if (i == FTRACE_FUNC_HASHSIZE)
return;
__register_ftrace_function(&trace_hook_ops);
ftrace_startup(0);
ftrace_hook_registered = 1;
}
static void __disable_ftrace_function_hook(void)
{
int i;
if (!ftrace_hook_registered)
return;
for (i = 0; i < FTRACE_FUNC_HASHSIZE; i++) {
struct hlist_head *hhd = &ftrace_func_hash[i];
if (hhd->first)
return;
}
/* no more funcs left */
__unregister_ftrace_function(&trace_hook_ops);
ftrace_shutdown(0);
ftrace_hook_registered = 0;
}
static void ftrace_free_entry_rcu(struct rcu_head *rhp)
{
struct ftrace_func_hook *entry =
container_of(rhp, struct ftrace_func_hook, rcu);
if (entry->ops->free)
entry->ops->free(&entry->data);
kfree(entry);
}
int
register_ftrace_function_hook(char *glob, struct ftrace_hook_ops *ops,
void *data)
{
struct ftrace_func_hook *entry;
struct ftrace_page *pg;
struct dyn_ftrace *rec;
unsigned long key;
int type, len, not;
int count = 0;
char *search;
type = ftrace_setup_glob(glob, strlen(glob), &search, &not);
len = strlen(search);
/* we do not support '!' for function hooks */
if (WARN_ON(not))
return -EINVAL;
mutex_lock(&ftrace_lock);
do_for_each_ftrace_rec(pg, rec) {
if (rec->flags & FTRACE_FL_FAILED)
continue;
if (!ftrace_match_record(rec, search, len, type))
continue;
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
if (!entry) {
/* If we did not hook to any, then return error */
if (!count)
count = -ENOMEM;
goto out_unlock;
}
count++;
entry->data = data;
/*
* The caller might want to do something special
* for each function we find. We call the callback
* to give the caller an opportunity to do so.
*/
if (ops->callback) {
if (ops->callback(rec->ip, &entry->data) < 0) {
/* caller does not like this func */
kfree(entry);
continue; continue;
kallsyms_lookup(rec->ip, NULL, NULL, NULL, str);
switch (type) {
case MATCH_FULL:
if (strcmp(str, buff) == 0)
matched = 1;
break;
case MATCH_FRONT_ONLY:
if (memcmp(str, buff, match) == 0)
matched = 1;
break;
case MATCH_MIDDLE_ONLY:
if (strstr(str, search))
matched = 1;
break;
case MATCH_END_ONLY:
ptr = strstr(str, search);
if (ptr && (ptr[search_len] == 0))
matched = 1;
break;
} }
if (matched) { }
if (not)
rec->flags &= ~flag; entry->ops = ops;
else entry->ip = rec->ip;
rec->flags |= flag;
key = hash_long(entry->ip, FTRACE_HASH_BITS);
hlist_add_head_rcu(&entry->node, &ftrace_func_hash[key]);
} while_for_each_ftrace_rec();
__enable_ftrace_function_hook();
out_unlock:
mutex_unlock(&ftrace_lock);
return count;
}
enum {
HOOK_TEST_FUNC = 1,
HOOK_TEST_DATA = 2
};
static void
__unregister_ftrace_function_hook(char *glob, struct ftrace_hook_ops *ops,
void *data, int flags)
{
struct ftrace_func_hook *entry;
struct hlist_node *n, *tmp;
char str[KSYM_SYMBOL_LEN];
int type = MATCH_FULL;
int i, len = 0;
char *search;
if (glob && (strcmp(glob, "*") || !strlen(glob)))
glob = NULL;
else {
int not;
type = ftrace_setup_glob(glob, strlen(glob), &search, &not);
len = strlen(search);
/* we do not support '!' for function hooks */
if (WARN_ON(not))
return;
}
mutex_lock(&ftrace_lock);
for (i = 0; i < FTRACE_FUNC_HASHSIZE; i++) {
struct hlist_head *hhd = &ftrace_func_hash[i];
hlist_for_each_entry_safe(entry, n, tmp, hhd, node) {
/* break up if statements for readability */
if ((flags & HOOK_TEST_FUNC) && entry->ops != ops)
continue;
if ((flags & HOOK_TEST_DATA) && entry->data != data)
continue;
/* do this last, since it is the most expensive */
if (glob) {
kallsyms_lookup(entry->ip, NULL, NULL,
NULL, str);
if (!ftrace_match(str, glob, len, type))
continue;
} }
hlist_del(&entry->node);
call_rcu(&entry->rcu, ftrace_free_entry_rcu);
} }
pg = pg->next;
} }
spin_unlock(&ftrace_lock); __disable_ftrace_function_hook();
mutex_unlock(&ftrace_lock);
}
void
unregister_ftrace_function_hook(char *glob, struct ftrace_hook_ops *ops,
void *data)
{
__unregister_ftrace_function_hook(glob, ops, data,
HOOK_TEST_FUNC | HOOK_TEST_DATA);
}
void
unregister_ftrace_function_hook_func(char *glob, struct ftrace_hook_ops *ops)
{
__unregister_ftrace_function_hook(glob, ops, NULL, HOOK_TEST_FUNC);
}
void unregister_ftrace_function_hook_all(char *glob)
{
__unregister_ftrace_function_hook(glob, NULL, NULL, 0);
}
static LIST_HEAD(ftrace_commands);
static DEFINE_MUTEX(ftrace_cmd_mutex);
int register_ftrace_command(struct ftrace_func_command *cmd)
{
struct ftrace_func_command *p;
int ret = 0;
mutex_lock(&ftrace_cmd_mutex);
list_for_each_entry(p, &ftrace_commands, list) {
if (strcmp(cmd->name, p->name) == 0) {
ret = -EBUSY;
goto out_unlock;
}
}
list_add(&cmd->list, &ftrace_commands);
out_unlock:
mutex_unlock(&ftrace_cmd_mutex);
return ret;
}
int unregister_ftrace_command(struct ftrace_func_command *cmd)
{
struct ftrace_func_command *p, *n;
int ret = -ENODEV;
mutex_lock(&ftrace_cmd_mutex);
list_for_each_entry_safe(p, n, &ftrace_commands, list) {
if (strcmp(cmd->name, p->name) == 0) {
ret = 0;
list_del_init(&p->list);
goto out_unlock;
}
}
out_unlock:
mutex_unlock(&ftrace_cmd_mutex);
return ret;
}
static int ftrace_process_regex(char *buff, int len, int enable)
{
struct ftrace_func_command *p;
char *func, *command, *next = buff;
int ret = -EINVAL;
func = strsep(&next, ":");
if (!next) {
ftrace_match_records(func, len, enable);
return 0;
}
/* command found */
command = strsep(&next, ":");
mutex_lock(&ftrace_cmd_mutex);
list_for_each_entry(p, &ftrace_commands, list) {
if (strcmp(p->name, command) == 0) {
ret = p->func(func, command, next, enable);
goto out_unlock;
}
}
out_unlock:
mutex_unlock(&ftrace_cmd_mutex);
return ret;
} }
static ssize_t static ssize_t
...@@ -1179,7 +1716,10 @@ ftrace_regex_write(struct file *file, const char __user *ubuf, ...@@ -1179,7 +1716,10 @@ ftrace_regex_write(struct file *file, const char __user *ubuf,
if (isspace(ch)) { if (isspace(ch)) {
iter->filtered++; iter->filtered++;
iter->buffer[iter->buffer_idx] = 0; iter->buffer[iter->buffer_idx] = 0;
ftrace_match(iter->buffer, iter->buffer_idx, enable); ret = ftrace_process_regex(iter->buffer,
iter->buffer_idx, enable);
if (ret)
goto out;
iter->buffer_idx = 0; iter->buffer_idx = 0;
} else } else
iter->flags |= FTRACE_ITER_CONT; iter->flags |= FTRACE_ITER_CONT;
...@@ -1218,7 +1758,7 @@ ftrace_set_regex(unsigned char *buf, int len, int reset, int enable) ...@@ -1218,7 +1758,7 @@ ftrace_set_regex(unsigned char *buf, int len, int reset, int enable)
if (reset) if (reset)
ftrace_filter_reset(enable); ftrace_filter_reset(enable);
if (buf) if (buf)
ftrace_match(buf, len, enable); ftrace_match_records(buf, len, enable);
mutex_unlock(&ftrace_regex_lock); mutex_unlock(&ftrace_regex_lock);
} }
...@@ -1268,15 +1808,13 @@ ftrace_regex_release(struct inode *inode, struct file *file, int enable) ...@@ -1268,15 +1808,13 @@ ftrace_regex_release(struct inode *inode, struct file *file, int enable)
if (iter->buffer_idx) { if (iter->buffer_idx) {
iter->filtered++; iter->filtered++;
iter->buffer[iter->buffer_idx] = 0; iter->buffer[iter->buffer_idx] = 0;
ftrace_match(iter->buffer, iter->buffer_idx, enable); ftrace_match_records(iter->buffer, iter->buffer_idx, enable);
} }
mutex_lock(&ftrace_sysctl_lock); mutex_lock(&ftrace_lock);
mutex_lock(&ftrace_start_lock);
if (ftrace_start_up && ftrace_enabled) if (ftrace_start_up && ftrace_enabled)
ftrace_run_update_code(FTRACE_ENABLE_CALLS); ftrace_run_update_code(FTRACE_ENABLE_CALLS);
mutex_unlock(&ftrace_start_lock); mutex_unlock(&ftrace_lock);
mutex_unlock(&ftrace_sysctl_lock);
kfree(iter); kfree(iter);
mutex_unlock(&ftrace_regex_lock); mutex_unlock(&ftrace_regex_lock);
...@@ -1429,36 +1967,33 @@ ftrace_set_func(unsigned long *array, int idx, char *buffer) ...@@ -1429,36 +1967,33 @@ ftrace_set_func(unsigned long *array, int idx, char *buffer)
struct dyn_ftrace *rec; struct dyn_ftrace *rec;
struct ftrace_page *pg; struct ftrace_page *pg;
int found = 0; int found = 0;
int i, j; int j;
if (ftrace_disabled) if (ftrace_disabled)
return -ENODEV; return -ENODEV;
/* should not be called from interrupt context */ mutex_lock(&ftrace_lock);
spin_lock(&ftrace_lock); do_for_each_ftrace_rec(pg, rec) {
for (pg = ftrace_pages_start; pg; pg = pg->next) {
for (i = 0; i < pg->index; i++) {
rec = &pg->records[i];
if (rec->flags & (FTRACE_FL_FAILED | FTRACE_FL_FREE)) if (rec->flags & (FTRACE_FL_FAILED | FTRACE_FL_FREE))
continue; continue;
kallsyms_lookup(rec->ip, NULL, NULL, NULL, str); kallsyms_lookup(rec->ip, NULL, NULL, NULL, str);
if (strcmp(str, buffer) == 0) { if (strcmp(str, buffer) == 0) {
found = 1; /* Return 1 if we add it to the array */
for (j = 0; j < idx; j++) found = 1;
if (array[j] == rec->ip) { for (j = 0; j < idx; j++)
found = 0; if (array[j] == rec->ip) {
break; found = 0;
} break;
if (found) }
array[idx] = rec->ip; if (found)
break; array[idx] = rec->ip;
} goto out;
} }
} } while_for_each_ftrace_rec();
spin_unlock(&ftrace_lock); out:
mutex_unlock(&ftrace_lock);
return found ? 0 : -EINVAL; return found ? 0 : -EINVAL;
} }
...@@ -1596,7 +2131,7 @@ static int ftrace_convert_nops(struct module *mod, ...@@ -1596,7 +2131,7 @@ static int ftrace_convert_nops(struct module *mod,
unsigned long addr; unsigned long addr;
unsigned long flags; unsigned long flags;
mutex_lock(&ftrace_start_lock); mutex_lock(&ftrace_lock);
p = start; p = start;
while (p < end) { while (p < end) {
addr = ftrace_call_adjust(*p++); addr = ftrace_call_adjust(*p++);
...@@ -1615,7 +2150,7 @@ static int ftrace_convert_nops(struct module *mod, ...@@ -1615,7 +2150,7 @@ static int ftrace_convert_nops(struct module *mod,
local_irq_save(flags); local_irq_save(flags);
ftrace_update_code(mod); ftrace_update_code(mod);
local_irq_restore(flags); local_irq_restore(flags);
mutex_unlock(&ftrace_start_lock); mutex_unlock(&ftrace_lock);
return 0; return 0;
} }
...@@ -1788,7 +2323,7 @@ ftrace_pid_write(struct file *filp, const char __user *ubuf, ...@@ -1788,7 +2323,7 @@ ftrace_pid_write(struct file *filp, const char __user *ubuf,
if (ret < 0) if (ret < 0)
return ret; return ret;
mutex_lock(&ftrace_start_lock); mutex_lock(&ftrace_lock);
if (val < 0) { if (val < 0) {
/* disable pid tracing */ /* disable pid tracing */
if (!ftrace_pid_trace) if (!ftrace_pid_trace)
...@@ -1827,7 +2362,7 @@ ftrace_pid_write(struct file *filp, const char __user *ubuf, ...@@ -1827,7 +2362,7 @@ ftrace_pid_write(struct file *filp, const char __user *ubuf,
ftrace_startup_enable(0); ftrace_startup_enable(0);
out: out:
mutex_unlock(&ftrace_start_lock); mutex_unlock(&ftrace_lock);
return cnt; return cnt;
} }
...@@ -1890,12 +2425,12 @@ int register_ftrace_function(struct ftrace_ops *ops) ...@@ -1890,12 +2425,12 @@ int register_ftrace_function(struct ftrace_ops *ops)
if (unlikely(ftrace_disabled)) if (unlikely(ftrace_disabled))
return -1; return -1;
mutex_lock(&ftrace_sysctl_lock); mutex_lock(&ftrace_lock);
ret = __register_ftrace_function(ops); ret = __register_ftrace_function(ops);
ftrace_startup(0); ftrace_startup(0);
mutex_unlock(&ftrace_sysctl_lock); mutex_unlock(&ftrace_lock);
return ret; return ret;
} }
...@@ -1909,10 +2444,10 @@ int unregister_ftrace_function(struct ftrace_ops *ops) ...@@ -1909,10 +2444,10 @@ int unregister_ftrace_function(struct ftrace_ops *ops)
{ {
int ret; int ret;
mutex_lock(&ftrace_sysctl_lock); mutex_lock(&ftrace_lock);
ret = __unregister_ftrace_function(ops); ret = __unregister_ftrace_function(ops);
ftrace_shutdown(0); ftrace_shutdown(0);
mutex_unlock(&ftrace_sysctl_lock); mutex_unlock(&ftrace_lock);
return ret; return ret;
} }
...@@ -1927,7 +2462,7 @@ ftrace_enable_sysctl(struct ctl_table *table, int write, ...@@ -1927,7 +2462,7 @@ ftrace_enable_sysctl(struct ctl_table *table, int write,
if (unlikely(ftrace_disabled)) if (unlikely(ftrace_disabled))
return -ENODEV; return -ENODEV;
mutex_lock(&ftrace_sysctl_lock); mutex_lock(&ftrace_lock);
ret = proc_dointvec(table, write, file, buffer, lenp, ppos); ret = proc_dointvec(table, write, file, buffer, lenp, ppos);
...@@ -1956,7 +2491,7 @@ ftrace_enable_sysctl(struct ctl_table *table, int write, ...@@ -1956,7 +2491,7 @@ ftrace_enable_sysctl(struct ctl_table *table, int write,
} }
out: out:
mutex_unlock(&ftrace_sysctl_lock); mutex_unlock(&ftrace_lock);
return ret; return ret;
} }
...@@ -2068,7 +2603,7 @@ int register_ftrace_graph(trace_func_graph_ret_t retfunc, ...@@ -2068,7 +2603,7 @@ int register_ftrace_graph(trace_func_graph_ret_t retfunc,
{ {
int ret = 0; int ret = 0;
mutex_lock(&ftrace_sysctl_lock); mutex_lock(&ftrace_lock);
ftrace_suspend_notifier.notifier_call = ftrace_suspend_notifier_call; ftrace_suspend_notifier.notifier_call = ftrace_suspend_notifier_call;
register_pm_notifier(&ftrace_suspend_notifier); register_pm_notifier(&ftrace_suspend_notifier);
...@@ -2086,13 +2621,13 @@ int register_ftrace_graph(trace_func_graph_ret_t retfunc, ...@@ -2086,13 +2621,13 @@ int register_ftrace_graph(trace_func_graph_ret_t retfunc,
ftrace_startup(FTRACE_START_FUNC_RET); ftrace_startup(FTRACE_START_FUNC_RET);
out: out:
mutex_unlock(&ftrace_sysctl_lock); mutex_unlock(&ftrace_lock);
return ret; return ret;
} }
void unregister_ftrace_graph(void) void unregister_ftrace_graph(void)
{ {
mutex_lock(&ftrace_sysctl_lock); mutex_lock(&ftrace_lock);
atomic_dec(&ftrace_graph_active); atomic_dec(&ftrace_graph_active);
ftrace_graph_return = (trace_func_graph_ret_t)ftrace_stub; ftrace_graph_return = (trace_func_graph_ret_t)ftrace_stub;
...@@ -2100,7 +2635,7 @@ void unregister_ftrace_graph(void) ...@@ -2100,7 +2635,7 @@ void unregister_ftrace_graph(void)
ftrace_shutdown(FTRACE_STOP_FUNC_RET); ftrace_shutdown(FTRACE_STOP_FUNC_RET);
unregister_pm_notifier(&ftrace_suspend_notifier); unregister_pm_notifier(&ftrace_suspend_notifier);
mutex_unlock(&ftrace_sysctl_lock); mutex_unlock(&ftrace_lock);
} }
/* Allocate a return stack for newly created task */ /* Allocate a return stack for newly created task */
......
...@@ -98,6 +98,15 @@ void tracing_off_permanent(void) ...@@ -98,6 +98,15 @@ void tracing_off_permanent(void)
set_bit(RB_BUFFERS_DISABLED_BIT, &ring_buffer_flags); set_bit(RB_BUFFERS_DISABLED_BIT, &ring_buffer_flags);
} }
/**
* tracing_is_on - show state of ring buffers enabled
*/
int tracing_is_on(void)
{
return ring_buffer_flags == RB_BUFFERS_ON;
}
EXPORT_SYMBOL_GPL(tracing_is_on);
#include "trace.h" #include "trace.h"
/* Up this if you want to test the TIME_EXTENTS and normalization */ /* Up this if you want to test the TIME_EXTENTS and normalization */
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
* Copyright (C) 2004-2006 Ingo Molnar * Copyright (C) 2004-2006 Ingo Molnar
* Copyright (C) 2004 William Lee Irwin III * Copyright (C) 2004 William Lee Irwin III
*/ */
#include <linux/ring_buffer.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/ftrace.h> #include <linux/ftrace.h>
...@@ -231,9 +232,171 @@ static struct tracer function_trace __read_mostly = ...@@ -231,9 +232,171 @@ static struct tracer function_trace __read_mostly =
#endif #endif
}; };
#ifdef CONFIG_DYNAMIC_FTRACE
static void
ftrace_traceon(unsigned long ip, unsigned long parent_ip, void **data)
{
long *count = (long *)data;
if (tracing_is_on())
return;
if (!*count)
return;
if (*count != -1)
(*count)--;
tracing_on();
}
static void
ftrace_traceoff(unsigned long ip, unsigned long parent_ip, void **data)
{
long *count = (long *)data;
if (!tracing_is_on())
return;
if (!*count)
return;
if (*count != -1)
(*count)--;
tracing_off();
}
static int
ftrace_trace_onoff_print(struct seq_file *m, unsigned long ip,
struct ftrace_hook_ops *ops, void *data);
static struct ftrace_hook_ops traceon_hook_ops = {
.func = ftrace_traceon,
.print = ftrace_trace_onoff_print,
};
static struct ftrace_hook_ops traceoff_hook_ops = {
.func = ftrace_traceoff,
.print = ftrace_trace_onoff_print,
};
static int
ftrace_trace_onoff_print(struct seq_file *m, unsigned long ip,
struct ftrace_hook_ops *ops, void *data)
{
char str[KSYM_SYMBOL_LEN];
long count = (long)data;
kallsyms_lookup(ip, NULL, NULL, NULL, str);
seq_printf(m, "%s:", str);
if (ops == &traceon_hook_ops)
seq_printf(m, "traceon");
else
seq_printf(m, "traceoff");
if (count != -1)
seq_printf(m, ":count=%ld", count);
seq_putc(m, '\n');
return 0;
}
static int
ftrace_trace_onoff_unreg(char *glob, char *cmd, char *param)
{
struct ftrace_hook_ops *ops;
/* we register both traceon and traceoff to this callback */
if (strcmp(cmd, "traceon") == 0)
ops = &traceon_hook_ops;
else
ops = &traceoff_hook_ops;
unregister_ftrace_function_hook_func(glob, ops);
return 0;
}
static int
ftrace_trace_onoff_callback(char *glob, char *cmd, char *param, int enable)
{
struct ftrace_hook_ops *ops;
void *count = (void *)-1;
char *number;
int ret;
/* hash funcs only work with set_ftrace_filter */
if (!enable)
return -EINVAL;
if (glob[0] == '!')
return ftrace_trace_onoff_unreg(glob+1, cmd, param);
/* we register both traceon and traceoff to this callback */
if (strcmp(cmd, "traceon") == 0)
ops = &traceon_hook_ops;
else
ops = &traceoff_hook_ops;
if (!param)
goto out_reg;
number = strsep(&param, ":");
if (!strlen(number))
goto out_reg;
/*
* We use the callback data field (which is a pointer)
* as our counter.
*/
ret = strict_strtoul(number, 0, (unsigned long *)&count);
if (ret)
return ret;
out_reg:
ret = register_ftrace_function_hook(glob, ops, count);
return ret;
}
static struct ftrace_func_command ftrace_traceon_cmd = {
.name = "traceon",
.func = ftrace_trace_onoff_callback,
};
static struct ftrace_func_command ftrace_traceoff_cmd = {
.name = "traceoff",
.func = ftrace_trace_onoff_callback,
};
static int __init init_func_cmd_traceon(void)
{
int ret;
ret = register_ftrace_command(&ftrace_traceoff_cmd);
if (ret)
return ret;
ret = register_ftrace_command(&ftrace_traceon_cmd);
if (ret)
unregister_ftrace_command(&ftrace_traceoff_cmd);
return ret;
}
#else
static inline int init_func_cmd_traceon(void)
{
return 0;
}
#endif /* CONFIG_DYNAMIC_FTRACE */
static __init int init_function_trace(void) static __init int init_function_trace(void)
{ {
init_func_cmd_traceon();
return register_tracer(&function_trace); return register_tracer(&function_trace);
} }
device_initcall(init_function_trace); device_initcall(init_function_trace);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment