Commit adf9f195 authored by Frederic Weisbecker's avatar Frederic Weisbecker Committed by Ingo Molnar

tracing/ftrace: implement a set_flag callback for tracers

Impact: give a way to send specific messages to tracers

The current implementation of tracing uses some flags to control the
output of general tracers. But we have no way to implement custom
flags handling for a specific tracer. This patch proposes a new
callback for the struct tracer which called set_flag and a structure
that represents a 32 bits variable flag.

A tracer can implement a struct tracer_flags on which it puts the
initial value of the flag integer. Than it can place a range of flags
with their name and their flag mask on the flag integer. The structure
that implement a single flag is called struct tracer_opt.

These custom flags will be available through the trace_options file
like the general tracing flags. Changing their value is done like the
other general flags. For example if you have a flag that calls "foo",
you can activate it by writing "foo" or "nofoo" on trace_options.

Note that the set_flag callback is optional and is only needed if you
want the flags changing to be signaled to your tracer and let it to
accept or refuse their assignment.

V2: Some arrangements in coding style....
Signed-off-by: default avatarFrederic Weisbecker <fweisbec@gmail.com>
Acked-by: default avatarSteven Rostedt <rostedt@goodmis.org>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent 5a209c2d
...@@ -43,6 +43,20 @@ ...@@ -43,6 +43,20 @@
unsigned long __read_mostly tracing_max_latency = (cycle_t)ULONG_MAX; unsigned long __read_mostly tracing_max_latency = (cycle_t)ULONG_MAX;
unsigned long __read_mostly tracing_thresh; unsigned long __read_mostly tracing_thresh;
/* For tracers that don't implement custom flags */
static struct tracer_opt dummy_tracer_opt[] = {
{ }
};
static struct tracer_flags dummy_tracer_flags = {
.val = 0,
.opts = dummy_tracer_opt
};
static int dummy_set_flag(u32 old_flags, u32 bit, int set)
{
return 0;
}
/* /*
* Kill all tracing for good (never come back). * Kill all tracing for good (never come back).
...@@ -529,6 +543,14 @@ int register_tracer(struct tracer *type) ...@@ -529,6 +543,14 @@ int register_tracer(struct tracer *type)
} }
} }
if (!type->set_flag)
type->set_flag = &dummy_set_flag;
if (!type->flags)
type->flags = &dummy_tracer_flags;
else
if (!type->flags->opts)
type->flags->opts = dummy_tracer_opt;
#ifdef CONFIG_FTRACE_STARTUP_TEST #ifdef CONFIG_FTRACE_STARTUP_TEST
if (type->selftest) { if (type->selftest) {
struct tracer *saved_tracer = current_trace; struct tracer *saved_tracer = current_trace;
...@@ -2426,10 +2448,13 @@ static ssize_t ...@@ -2426,10 +2448,13 @@ static ssize_t
tracing_trace_options_read(struct file *filp, char __user *ubuf, tracing_trace_options_read(struct file *filp, char __user *ubuf,
size_t cnt, loff_t *ppos) size_t cnt, loff_t *ppos)
{ {
int i;
char *buf; char *buf;
int r = 0; int r = 0;
int len = 0; int len = 0;
int i; u32 tracer_flags = current_trace->flags->val;
struct tracer_opt *trace_opts = current_trace->flags->opts;
/* calulate max size */ /* calulate max size */
for (i = 0; trace_options[i]; i++) { for (i = 0; trace_options[i]; i++) {
...@@ -2437,6 +2462,15 @@ tracing_trace_options_read(struct file *filp, char __user *ubuf, ...@@ -2437,6 +2462,15 @@ tracing_trace_options_read(struct file *filp, char __user *ubuf,
len += 3; /* "no" and space */ len += 3; /* "no" and space */
} }
/*
* Increase the size with names of options specific
* of the current tracer.
*/
for (i = 0; trace_opts[i].name; i++) {
len += strlen(trace_opts[i].name);
len += 3; /* "no" and space */
}
/* +2 for \n and \0 */ /* +2 for \n and \0 */
buf = kmalloc(len + 2, GFP_KERNEL); buf = kmalloc(len + 2, GFP_KERNEL);
if (!buf) if (!buf)
...@@ -2449,6 +2483,15 @@ tracing_trace_options_read(struct file *filp, char __user *ubuf, ...@@ -2449,6 +2483,15 @@ tracing_trace_options_read(struct file *filp, char __user *ubuf,
r += sprintf(buf + r, "no%s ", trace_options[i]); r += sprintf(buf + r, "no%s ", trace_options[i]);
} }
for (i = 0; trace_opts[i].name; i++) {
if (tracer_flags & trace_opts[i].bit)
r += sprintf(buf + r, "%s ",
trace_opts[i].name);
else
r += sprintf(buf + r, "no%s ",
trace_opts[i].name);
}
r += sprintf(buf + r, "\n"); r += sprintf(buf + r, "\n");
WARN_ON(r >= len + 2); WARN_ON(r >= len + 2);
...@@ -2459,6 +2502,40 @@ tracing_trace_options_read(struct file *filp, char __user *ubuf, ...@@ -2459,6 +2502,40 @@ tracing_trace_options_read(struct file *filp, char __user *ubuf,
return r; return r;
} }
/* Try to assign a tracer specific option */
static int set_tracer_option(struct tracer *trace, char *cmp, int neg)
{
struct tracer_flags *trace_flags = trace->flags;
struct tracer_opt *opts = NULL;
int ret = 0, i = 0;
int len;
for (i = 0; trace_flags->opts[i].name; i++) {
opts = &trace_flags->opts[i];
len = strlen(opts->name);
if (strncmp(cmp, opts->name, len) == 0) {
ret = trace->set_flag(trace_flags->val,
opts->bit, !neg);
break;
}
}
/* Not found */
if (!trace_flags->opts[i].name)
return -EINVAL;
/* Refused to handle */
if (ret)
return ret;
if (neg)
trace_flags->val &= ~opts->bit;
else
trace_flags->val |= opts->bit;
return 0;
}
static ssize_t static ssize_t
tracing_trace_options_write(struct file *filp, const char __user *ubuf, tracing_trace_options_write(struct file *filp, const char __user *ubuf,
size_t cnt, loff_t *ppos) size_t cnt, loff_t *ppos)
...@@ -2466,6 +2543,7 @@ tracing_trace_options_write(struct file *filp, const char __user *ubuf, ...@@ -2466,6 +2543,7 @@ tracing_trace_options_write(struct file *filp, const char __user *ubuf,
char buf[64]; char buf[64];
char *cmp = buf; char *cmp = buf;
int neg = 0; int neg = 0;
int ret;
int i; int i;
if (cnt >= sizeof(buf)) if (cnt >= sizeof(buf))
...@@ -2492,11 +2570,13 @@ tracing_trace_options_write(struct file *filp, const char __user *ubuf, ...@@ -2492,11 +2570,13 @@ tracing_trace_options_write(struct file *filp, const char __user *ubuf,
break; break;
} }
} }
/*
* If no option could be set, return an error: /* If no option could be set, test the specific tracer options */
*/ if (!trace_options[i]) {
if (!trace_options[i]) ret = set_tracer_option(current_trace, cmp, neg);
return -EINVAL; if (ret)
return ret;
}
filp->f_pos += cnt; filp->f_pos += cnt;
......
...@@ -259,6 +259,29 @@ enum print_line_t { ...@@ -259,6 +259,29 @@ enum print_line_t {
TRACE_TYPE_UNHANDLED = 2 /* Relay to other output functions */ TRACE_TYPE_UNHANDLED = 2 /* Relay to other output functions */
}; };
/*
* An option specific to a tracer. This is a boolean value.
* The bit is the bit index that sets its value on the
* flags value in struct tracer_flags.
*/
struct tracer_opt {
const char *name; /* Will appear on the trace_options file */
u32 bit; /* Mask assigned in val field in tracer_flags */
};
/*
* The set of specific options for a tracer. Your tracer
* have to set the initial value of the flags val.
*/
struct tracer_flags {
u32 val;
struct tracer_opt *opts;
};
/* Makes more easy to define a tracer opt */
#define TRACER_OPT(s, b) .name = #s, .bit = b
/* /*
* A specific tracer, represented by methods that operate on a trace array: * A specific tracer, represented by methods that operate on a trace array:
*/ */
...@@ -280,8 +303,11 @@ struct tracer { ...@@ -280,8 +303,11 @@ struct tracer {
struct trace_array *tr); struct trace_array *tr);
#endif #endif
enum print_line_t (*print_line)(struct trace_iterator *iter); enum print_line_t (*print_line)(struct trace_iterator *iter);
/* If you handled the flag setting, return 0 */
int (*set_flag)(u32 old_flags, u32 bit, int set);
struct tracer *next; struct tracer *next;
int print_max; int print_max;
struct tracer_flags *flags;
}; };
struct trace_seq { struct trace_seq {
......
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