[RFC 3/3] tracing: set_ftrace_filter support

From: Jiri Olsa
Date: Fri Apr 29 2011 - 10:43:44 EST


adding support to use set_ftrace_filter for multiple tracers

---
arch/x86/kernel/ftrace.c | 24 +++--
include/linux/ftrace.h | 3 +
kernel/trace/ftrace.c | 269 ++++++++++++++++++++++++++++++++++------------
3 files changed, 219 insertions(+), 77 deletions(-)

diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c
index 5a8e679..5fdb40e 100644
--- a/arch/x86/kernel/ftrace.c
+++ b/arch/x86/kernel/ftrace.c
@@ -76,18 +76,20 @@ static int ftrace_calc_offset(long ip, long addr)
return (int)(addr - ip);
}

-static unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr)
+static unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr, int old)
{
- static union ftrace_code_union calc;
+ static union ftrace_code_union calc_new;
+ static union ftrace_code_union calc_old;
+ union ftrace_code_union *calc = old ? &calc_old : &calc_new;

- calc.e8 = 0xe8;
- calc.offset = ftrace_calc_offset(ip + MCOUNT_INSN_SIZE, addr);
+ calc->e8 = 0xe8;
+ calc->offset = ftrace_calc_offset(ip + MCOUNT_INSN_SIZE, addr);

/*
* No locking needed, this must be called via kstop_machine
* which in essence is like running on a uniprocessor machine.
*/
- return calc.code;
+ return calc->code;
}

/*
@@ -304,9 +306,10 @@ int ftrace_make_nop(struct module *mod,
unsigned char *new, *old;
unsigned long ip = rec->ip;

- old = ftrace_call_replace(ip, addr);
+ old = ftrace_call_replace(ip, addr, 1);
new = ftrace_nop_replace();

+ rec->addr = 0L;
return ftrace_modify_code(rec->ip, old, new);
}

@@ -315,9 +318,14 @@ int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
unsigned char *new, *old;
unsigned long ip = rec->ip;

- old = ftrace_nop_replace();
- new = ftrace_call_replace(ip, addr);
+ if (rec->addr)
+ old = ftrace_call_replace(ip, rec->addr, 1);
+ else
+ old = ftrace_nop_replace();

+ new = ftrace_call_replace(ip, addr, 0);
+
+ rec->addr = addr;
return ftrace_modify_code(rec->ip, old, new);
}

diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index b12f5df..9ce3484 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -164,6 +164,9 @@ struct dyn_ftrace {
unsigned long flags;
struct dyn_ftrace *newlist;
};
+ unsigned long filter;
+ unsigned long no_trace;
+ unsigned long addr;
struct dyn_arch_ftrace arch;
};

diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index b39dd52..bbffb05 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -80,6 +80,8 @@ static struct ftrace_ops *ftrace_tracers[FTRACE_TRACERS_MAX];
static DECLARE_BITMAP(ftrace_tracers_bm, FTRACE_TRACERS_MAX);
static int ftrace_tracers_cur = -1;

+static void __ftrace_tracer_clear(int id, int type);
+
#define for_each_tracer(id, bm) \
for_each_set_bit(id, bm, FTRACE_TRACERS_MAX)

@@ -141,6 +143,8 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops)
BUG_ON(ops != ftrace_tracers[id]);
rcu_assign_pointer(ftrace_tracers[id], NULL);

+ __ftrace_tracer_clear(id, FTRACE_FL_FILTER|FTRACE_FL_NOTRACE);
+
if (ftrace_tracers_cur != id)
return 0;

@@ -175,6 +179,11 @@ static int ftrace_tracer_default(char *name)
return ret;
}

+static int __ftrace_get_next_tracer(int idx)
+{
+ return find_next_bit(ftrace_tracers_bm, FTRACE_TRACERS_MAX, idx);
+}
+
#ifdef CONFIG_FUNCTION_PROFILER
struct ftrace_profile {
struct hlist_node node;
@@ -808,7 +817,7 @@ enum {
FTRACE_STOP_FUNC_RET = (1 << 4),
};

-static int ftrace_filtered;
+static unsigned long ftrace_tracer_filtered;

static struct dyn_ftrace *ftrace_new_addrs;

@@ -845,6 +854,20 @@ static struct dyn_ftrace *ftrace_free_records;
} \
}

+static void __ftrace_tracer_clear(int id, int type)
+{
+ struct ftrace_page *pg;
+ struct dyn_ftrace *rec;
+ unsigned long mask = ~(1 << id);
+
+ do_for_each_ftrace_rec(pg, rec) {
+ if (type & FTRACE_FL_FILTER)
+ rec->filter &= mask;
+ if (type & FTRACE_FL_NOTRACE)
+ rec->no_trace &= mask;
+ } while_for_each_ftrace_rec();
+}
+
static void ftrace_free_rec(struct dyn_ftrace *rec)
{
rec->freelist = ftrace_free_records;
@@ -956,31 +979,52 @@ int ftrace_text_reserved(void *start, void *end)
return 0;
}

+unsigned long ftrace_tracer_flag_addr(struct dyn_ftrace *rec,
+ unsigned long *ftrace_addr, int enable)
+{
+ unsigned long flag = 0L;
+ int id, mask = 0;
+
+ for_each_tracer(id, ftrace_tracers_bm) {
+#define SET(var) (var & (1 << id))
+ int ftrace_filtered = SET(ftrace_tracer_filtered);
+
+ /*
+ * If this record is not to be traced or we want to disable it,
+ * then disable it.
+ *
+ * If we want to enable it and filtering is off, then enable
+ * it.
+ *
+ * If we want to enable it and filtering is on, enable it
+ * only if it's filtered
+ */
+ if (enable && !(SET(rec->no_trace)))
+ if (!ftrace_filtered || (SET(rec->filter)))
+ mask |= (1 << id);
+#undef SET
+ }
+
+ if (mask)
+ flag = FTRACE_FL_ENABLED;
+
+ *ftrace_addr = ftrace_arch_tracer_addr(mask);
+ return flag;
+}

static int
__ftrace_replace_code(struct dyn_ftrace *rec, int enable)
{
+ unsigned long flag;
unsigned long ftrace_addr;
- unsigned long flag = 0UL;

- ftrace_addr = ftrace_arch_tracer_addr(FTRACE_CALLERS_MAX);
+ flag = ftrace_tracer_flag_addr(rec, &ftrace_addr, enable);

- /*
- * If this record is not to be traced or we want to disable it,
- * then disable it.
- *
- * If we want to enable it and filtering is off, then enable it.
- *
- * If we want to enable it and filtering is on, enable it only if
- * it's filtered
- */
- if (enable && !(rec->flags & FTRACE_FL_NOTRACE)) {
- if (!ftrace_filtered || (rec->flags & FTRACE_FL_FILTER))
- flag = FTRACE_FL_ENABLED;
- }
+ BUG_ON(flag && !ftrace_addr);

/* If the state of this record hasn't changed, then do nothing */
- if ((rec->flags & FTRACE_FL_ENABLED) == flag)
+ if (((rec->flags & FTRACE_FL_ENABLED) == flag) &&
+ (rec->addr == ftrace_addr))
return 0;

if (flag) {
@@ -989,7 +1033,7 @@ __ftrace_replace_code(struct dyn_ftrace *rec, int enable)
}

rec->flags &= ~FTRACE_FL_ENABLED;
- return ftrace_make_nop(NULL, rec, ftrace_addr);
+ return ftrace_make_nop(NULL, rec, rec->addr);
}

static void ftrace_replace_code(int enable)
@@ -1273,6 +1317,7 @@ struct ftrace_iterator {
struct trace_parser parser;
int hidx;
int idx;
+ int tracer;
unsigned flags;
};

@@ -1363,6 +1408,76 @@ t_hash_show(struct seq_file *m, struct ftrace_iterator *iter)
return 0;
}

+static void reset_iter_read(struct ftrace_iterator *iter)
+{
+ iter->pos = 0;
+ iter->func_pos = 0;
+ iter->flags &= ~(FTRACE_ITER_PRINTALL & FTRACE_ITER_HASH);
+}
+
+static void *t_start(struct seq_file *m, loff_t *pos);
+
+#define TRACER_ISSET(var) (var & (1 << iter->tracer))
+
+static struct dyn_ftrace*
+t_next_entry(struct ftrace_iterator *iter)
+{
+ struct dyn_ftrace *rec = NULL;
+
+ while(!rec) {
+ if (iter->idx >= iter->pg->index) {
+ if (!iter->pg->next)
+ break;
+
+ iter->pg = iter->pg->next;
+ iter->idx = 0;
+
+ } else {
+ rec = &iter->pg->records[iter->idx++];
+
+ if ((rec->flags & FTRACE_FL_FREE) ||
+
+ (!(iter->flags & FTRACE_ITER_FAILURES) &&
+ (rec->flags & FTRACE_FL_FAILED)) ||
+
+ ((iter->flags & FTRACE_ITER_FAILURES) &&
+ !(rec->flags & FTRACE_FL_FAILED)) ||
+
+ ((iter->flags & FTRACE_ITER_FILTER) &&
+ !(TRACER_ISSET(rec->filter))) ||
+
+ ((iter->flags & FTRACE_ITER_NOTRACE) &&
+ !(TRACER_ISSET(rec->no_trace)))) {
+ rec = NULL;
+ }
+ }
+ }
+
+ return rec;
+}
+
+static int t_next_new_tracer(struct ftrace_iterator *iter)
+{
+ int id = __ftrace_get_next_tracer(++iter->tracer);
+ if (id >= FTRACE_TRACERS_MAX)
+ return -1;
+
+ iter->tracer = id;
+ iter->pg = ftrace_pages_start;
+ iter->idx = 0;
+
+ if (iter->flags & FTRACE_ITER_FILTER) {
+ if (!TRACER_ISSET(ftrace_tracer_filtered))
+ iter->flags |= FTRACE_ITER_PRINTALL;
+ else
+ iter->flags &= ~FTRACE_ITER_PRINTALL;
+ /* reset in case of seek/pread */
+ iter->flags &= ~FTRACE_ITER_HASH;
+ }
+
+ return id;
+}
+
static void *
t_next(struct seq_file *m, void *v, loff_t *pos)
{
@@ -1375,32 +1490,21 @@ t_next(struct seq_file *m, void *v, loff_t *pos)
(*pos)++;
iter->pos = iter->func_pos = *pos;

+ if (iter->flags & FTRACE_ITER_PRINTALL) {
+ if (t_next_new_tracer(iter) > 0)
+ if (iter->flags & FTRACE_ITER_PRINTALL)
+ return iter;
+ }
+
if (iter->flags & FTRACE_ITER_PRINTALL)
return t_hash_start(m, pos);

- retry:
- if (iter->idx >= iter->pg->index) {
- if (iter->pg->next) {
- iter->pg = iter->pg->next;
- iter->idx = 0;
- goto retry;
- }
- } else {
- rec = &iter->pg->records[iter->idx++];
- if ((rec->flags & FTRACE_FL_FREE) ||
-
- (!(iter->flags & FTRACE_ITER_FAILURES) &&
- (rec->flags & FTRACE_FL_FAILED)) ||
-
- ((iter->flags & FTRACE_ITER_FAILURES) &&
- !(rec->flags & FTRACE_FL_FAILED)) ||
-
- ((iter->flags & FTRACE_ITER_FILTER) &&
- !(rec->flags & FTRACE_FL_FILTER)) ||
-
- ((iter->flags & FTRACE_ITER_NOTRACE) &&
- !(rec->flags & FTRACE_FL_NOTRACE))) {
- rec = NULL;
+retry:
+ rec = t_next_entry(iter);
+ if (!rec) {
+ if (t_next_new_tracer(iter) > 0) {
+ if (iter->flags & FTRACE_ITER_PRINTALL)
+ return iter;
goto retry;
}
}
@@ -1409,17 +1513,9 @@ t_next(struct seq_file *m, void *v, loff_t *pos)
return t_hash_start(m, pos);

iter->func = rec;
-
return iter;
}

-static void reset_iter_read(struct ftrace_iterator *iter)
-{
- iter->pos = 0;
- iter->func_pos = 0;
- iter->flags &= ~(FTRACE_ITER_PRINTALL & FTRACE_ITER_HASH);
-}
-
static void *t_start(struct seq_file *m, loff_t *pos)
{
struct ftrace_iterator *iter = m->private;
@@ -1430,15 +1526,17 @@ static void *t_start(struct seq_file *m, loff_t *pos)
/*
* If an lseek was done, then reset and start from beginning.
*/
- if (*pos < iter->pos)
+ if (*pos < iter->pos) {
reset_iter_read(iter);
+ iter->tracer = 0;
+ }

/*
* 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 (iter->flags & FTRACE_ITER_FILTER && !TRACER_ISSET(ftrace_tracer_filtered)) {
if (*pos > 0)
return t_hash_start(m, pos);
iter->flags |= FTRACE_ITER_PRINTALL;
@@ -1472,6 +1570,7 @@ static void *t_start(struct seq_file *m, loff_t *pos)

return iter;
}
+#undef TRACER_ISSET

static void t_stop(struct seq_file *m, void *p)
{
@@ -1482,12 +1581,14 @@ static int t_show(struct seq_file *m, void *v)
{
struct ftrace_iterator *iter = m->private;
struct dyn_ftrace *rec;
+ char *tracer = iter->tracer < FTRACE_TRACERS_MAX ?
+ ftrace_tracers[iter->tracer]->name : NULL;

if (iter->flags & FTRACE_ITER_HASH)
return t_hash_show(m, iter);

if (iter->flags & FTRACE_ITER_PRINTALL) {
- seq_printf(m, "#### all functions enabled ####\n");
+ seq_printf(m, "[%20s] #### all functions enabled ####\n", tracer);
return 0;
}

@@ -1496,7 +1597,7 @@ static int t_show(struct seq_file *m, void *v)
if (!rec)
return 0;

- seq_printf(m, "%ps\n", (void *)rec->ip);
+ seq_printf(m, "[%20s] %ps\n", tracer, (void *)rec->ip);

return 0;
}
@@ -1552,21 +1653,15 @@ ftrace_failures_open(struct inode *inode, struct file *file)
return ret;
}

-
static void ftrace_filter_reset(int enable)
{
- struct ftrace_page *pg;
- struct dyn_ftrace *rec;
unsigned long type = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE;

mutex_lock(&ftrace_lock);
if (enable)
- ftrace_filtered = 0;
- do_for_each_ftrace_rec(pg, rec) {
- if (rec->flags & FTRACE_FL_FAILED)
- continue;
- rec->flags &= ~type;
- } while_for_each_ftrace_rec();
+ ftrace_tracer_filtered &= ~(1 << ftrace_tracers_cur);
+
+ __ftrace_tracer_clear(ftrace_tracers_cur, type);
mutex_unlock(&ftrace_lock);
}

@@ -1722,6 +1817,22 @@ ftrace_match_record(struct dyn_ftrace *rec, char *regex, int len, int type)
return ftrace_match(str, regex, len, type);
}

+#define TRACER_SET(enable) \
+do { \
+ if (enable) \
+ rec->filter |= (1 << ftrace_tracers_cur); \
+ else \
+ rec->no_trace |= (1 << ftrace_tracers_cur); \
+} while (0)
+
+#define TRACER_UNSET(enable) \
+do { \
+ if (enable) \
+ rec->filter &= ~(1 << ftrace_tracers_cur); \
+ else \
+ rec->no_trace &= ~(1 << ftrace_tracers_cur); \
+} while (0)
+
static int ftrace_match_records(char *buff, int len, int enable)
{
unsigned int search_len;
@@ -1739,16 +1850,23 @@ static int ftrace_match_records(char *buff, int len, int enable)
search_len = strlen(search);

mutex_lock(&ftrace_lock);
+
+ if (ftrace_tracers_cur < 0)
+ goto out_unlock;
+
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)
+ if (not) {
rec->flags &= ~flag;
- else
+ TRACER_UNSET(enable);
+ } else {
rec->flags |= flag;
+ TRACER_SET(enable);
+ }
found = 1;
}
/*
@@ -1756,10 +1874,12 @@ static int ftrace_match_records(char *buff, int len, int enable)
* is filtered on.
*/
if (enable && (rec->flags & FTRACE_FL_FILTER))
- ftrace_filtered = 1;
+ ftrace_tracer_filtered |= (1 << ftrace_tracers_cur);
+
} while_for_each_ftrace_rec();
- mutex_unlock(&ftrace_lock);

+ out_unlock:
+ mutex_unlock(&ftrace_lock);
return found;
}

@@ -1811,6 +1931,10 @@ static int ftrace_match_module_records(char *buff, char *mod, int enable)
}

mutex_lock(&ftrace_lock);
+
+ if (ftrace_tracers_cur < 0)
+ goto out_unlock;
+
do_for_each_ftrace_rec(pg, rec) {

if (rec->flags & FTRACE_FL_FAILED)
@@ -1818,20 +1942,27 @@ static int ftrace_match_module_records(char *buff, char *mod, int enable)

if (ftrace_match_module_record(rec, mod,
search, search_len, type)) {
- if (not)
+ if (not) {
rec->flags &= ~flag;
- else
+ TRACER_UNSET(enable);
+ } else {
rec->flags |= flag;
+ TRACER_SET(enable);
+ }
found = 1;
}
if (enable && (rec->flags & FTRACE_FL_FILTER))
- ftrace_filtered = 1;
+ ftrace_tracer_filtered |= (1 << ftrace_tracers_cur);

} while_for_each_ftrace_rec();
+
+ out_unlock:
mutex_unlock(&ftrace_lock);

return found;
}
+#undef TRACER_SET
+#undef TRACER_UNSET

/*
* We register the module command as a template to show others how
--
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/