Skip to contentSkip to conversation

tracing/ftrace: implement a set_flag callback for tracers

PATCH In review Frederic Weisbecker 2 participants 3 messages Nov 17, 2008 Tracing

lore
Share & tools
lore
#0 lore raw

PATCH 1/3 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.

---
 kernel/trace/trace.c |   80 ++++++++++++++++++++++++++++++++++++++++++++++---
 kernel/trace/trace.h |   28 +++++++++++++++++
 2 files changed, 103 insertions(+), 5 deletions(-)
kernel/trace/trace.c +75 -5
index 396fda0..8c4eb80 100644
@@ -2432,6 +2432,9 @@ tracing_trace_options_read(struct file *filp, char __user *ubuf,
 	int r = 0;
 	int len = 0;
 	int i;
+	u32 tracer_flags = 0;
+	struct tracer_opt *trace_opts = NULL;
+
 
 	/* calulate max size */
 	for (i = 0; trace_options[i]; i++) {
@@ -2439,6 +2442,22 @@ tracing_trace_options_read(struct file *filp, char __user *ubuf,
 		len += 3; /* "no" and space */
 	}
 
+	if (current_trace->flags) {
+		trace_opts = current_trace->flags->opts;
+		tracer_flags = current_trace->flags->val;
+	}
+
+	/*
+	 * Increase the size with names of options specific
+	 * of the current tracer.
+	 */
+	if (trace_opts) {
+		for (i = 0; trace_opts[i].name; i++) {
+			len += strlen(trace_opts[i].name);
+			len += 3;
+		}
+	}
+
 	/* +2 for \n and \0 */
 	buf = kmalloc(len + 2, GFP_KERNEL);
 	if (!buf)
@@ -2451,6 +2470,16 @@ tracing_trace_options_read(struct file *filp, char __user *ubuf,
 			r += sprintf(buf + r, "no%s ", trace_options[i]);
 	}
 
+	if (trace_opts) {
+		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");
 	WARN_ON(r >= len + 2);
 
@@ -2461,6 +2490,44 @@ tracing_trace_options_read(struct file *filp, char __user *ubuf,
 	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;
+
+	if (!trace_flags || !trace_flags->opts)
+		return -EINVAL;
+
+	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) {
+			if (trace->set_flag)
+					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
 tracing_trace_options_write(struct file *filp, const char __user *ubuf,
 			size_t cnt, loff_t *ppos)
@@ -2469,6 +2536,7 @@ tracing_trace_options_write(struct file *filp, const char __user *ubuf,
 	char *cmp = buf;
 	int neg = 0;
 	int i;
+	int ret;
 
 	if (cnt >= sizeof(buf))
 		return -EINVAL;
@@ -2494,11 +2562,13 @@ tracing_trace_options_write(struct file *filp, const char __user *ubuf,
 			break;
 		}
 	}
-	/*
-	 * If no option could be set, return an error:
-	 */
-	if (!trace_options[i])
-		return -EINVAL;
+
+	/* If no option could be set, test the specific tracer options */
+	if (!trace_options[i]) {
+		ret = set_tracer_option(current_trace, cmp, neg);
+		if (ret)
+			return ret;
+	}
 
 	filp->f_pos += cnt;
 
kernel/trace/trace.h +28 -0
index cdbd5cc..fc06485 100644
@@ -259,6 +259,29 @@ enum print_line_t {
 	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; /* The 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:
  */
@@ -280,8 +303,12 @@ struct tracer {
 					    struct trace_array *tr);
 #endif
 	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;
 	int			print_max;
+	struct tracer_flags *flags;
 };
 
 struct trace_seq {
@@ -484,6 +511,7 @@ enum trace_iterator_flags {
 #define TRACE_ITER_SYM_MASK \
 	(TRACE_ITER_PRINT_PARENT|TRACE_ITER_SYM_OFFSET|TRACE_ITER_SYM_ADDR)
 
+
 extern struct tracer nop_trace;
 
 /**
Signature · 1 line
1.5.2.5
#1 lore raw

i've got some syntactic nits:

kernel/trace/trace.c tracing_trace_options_read(struct file *filp, char __user *ubuf, · line 2457

Quoted 8 lines
kernel/trace/trace.c +8 -0
+	/*
+	 * Increase the size with names of options specific
+	 * of the current tracer.
+	 */
+	if (trace_opts) {
+		for (i = 0; trace_opts[i].name; i++) {
+			len += strlen(trace_opts[i].name);
+			len += 3;

magic constant '3' needs a comment at least.

kernel/trace/trace.c tracing_trace_options_read(struct file *filp, char __user *ubuf, · line 2482

Quoted 10 lines
kernel/trace/trace.c +10 -0
+	if (trace_opts) {
+		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);
+		}
+	}

how about making a dummy empty trace_opts for all tracers (set by plugin init if the init function finds a NULL there) - that way the 'if (trace_opts)' can go away and you can just iterate away on that array of options.

kernel/trace/trace.c tracing_trace_options_read(struct file *filp, char __user *ubuf, · line 2511

Quoted 4 lines
kernel/trace/trace.c +4 -0
+		if (strncmp(cmp, opts->name, len) == 0) {
+			if (trace->set_flag)
+					ret = trace->set_flag(trace_flags->val,
+							opts->bit, !neg);

one tab too much. Also, the condition can be avoided by creating a dummy ->set_flag() that just returns 0.

kernel/trace/trace.c tracing_trace_options_write(struct file *filp, const char __user *ubuf, · line 2539

Quoted 5 lines
kernel/trace/trace.c +1 -0
@@ -2469,6 +2536,7 @@ tracing_trace_options_write(struct file *filp, const char __user *ubuf,
 	char *cmp = buf;
 	int neg = 0;
 	int i;
+	int ret;

variable should be one line higher, to not break the upside down christmas tree layout. [ :-) ]

Quoted 16 lines
kernel/trace/trace.h +10 -0
@@ -259,6 +259,29 @@ enum print_line_t {
 	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; /* The mask assigned in val field in tracer_flags */
+};

kernel/trace/trace.h enum print_line_t { · line 281

Quoted 10 lines
kernel/trace/trace.h +9 -0
 
+
+/*
+ * 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;
+};

please look at the file you modify and try to clone the micro-style it follows. For example in this file it would be more standard to use vertical alignment for structure definitions:

kernel/trace/trace.h enum print_line_t { · line 283

Quoted 8 lines
kernel/trace/trace.h +7 -0
+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

ditto.

kernel/trace/trace.h struct tracer { · line 311

Quoted 8 lines

#endif

kernel/trace/trace.h +4 -0
 	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;
 	int			print_max;
+	struct tracer_flags *flags;

ditto - here the new field deviates from he existing style.

also note how the 'int set);' line is unnecessarily broken to a separate line - it could be in the same line where the set_flag field begins.

kernel/trace/trace.h enum trace_iterator_flags { · line 515

Quoted 6 lines
kernel/trace/trace.h +1 -0
@@ -484,6 +511,7 @@ enum trace_iterator_flags {
 #define TRACE_ITER_SYM_MASK \
 	(TRACE_ITER_PRINT_PARENT|TRACE_ITER_SYM_OFFSET|TRACE_ITER_SYM_ADDR)
 
+
 extern struct tracer nop_trace;

spurious looking newline insertion.

Ingo

#2 lore raw
Quoted 38 lines · Ingo Molnar

i've got some syntactic nits:

magic constant '3' needs a comment at least.

how about making a dummy empty trace_opts for all tracers (set by plugin init if the init function finds a NULL there) - that way the 'if (trace_opts)' can go away and you can just iterate away on that array of options.

one tab too much. Also, the condition can be avoided by creating a dummy ->set_flag() that just returns 0.

variable should be one line higher, to not break the upside down christmas tree layout. [ :-) ]

please look at the file you modify and try to clone the micro-style it follows. For example in this file it would be more standard to use vertical alignment for structure definitions:

ditto - here the new field deviates from he existing style.

also note how the 'int set);' line is unnecessarily broken to a separate line - it could be in the same line where the set_flag field begins.

Quoted 3 lines

spurious looking newline insertion.

Ingo

Ok. That's fixed in the patch below. I hope I haven't missed something. --

Quoted 1 line

From 93a0793dad5bb7937ccba6f4f4a8cc13ad18d2f9 Mon Sep 17 00:00:00 2001

From: Frederic Weisbecker <[email protected]>
Date: Mon, 17 Nov 2008 18:28:19 +0100
Subject: [PATCH 1/3][V2] 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....

---
 kernel/trace/trace.c |   92 ++++++++++++++++++++++++++++++++++++++++++++++---
 kernel/trace/trace.h |   26 ++++++++++++++
 2 files changed, 112 insertions(+), 6 deletions(-)
kernel/trace/trace.c +86 -6
index 4a90462..cd123e2 100644
@@ -43,6 +43,20 @@
 unsigned long __read_mostly	tracing_max_latency = (cycle_t)ULONG_MAX;
 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).
@@ -531,6 +545,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
 	if (type->selftest) {
 		struct tracer *saved_tracer = current_trace;
@@ -2428,10 +2450,13 @@ static ssize_t
 tracing_trace_options_read(struct file *filp, char __user *ubuf,
 		       size_t cnt, loff_t *ppos)
 {
+	int i;
 	char *buf;
 	int r = 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 */
 	for (i = 0; trace_options[i]; i++) {
@@ -2439,6 +2464,15 @@ tracing_trace_options_read(struct file *filp, char __user *ubuf,
 		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 */
 	buf = kmalloc(len + 2, GFP_KERNEL);
 	if (!buf)
@@ -2451,6 +2485,15 @@ tracing_trace_options_read(struct file *filp, char __user *ubuf,
 			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");
 	WARN_ON(r >= len + 2);
 
@@ -2461,6 +2504,40 @@ tracing_trace_options_read(struct file *filp, char __user *ubuf,
 	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
 tracing_trace_options_write(struct file *filp, const char __user *ubuf,
 			size_t cnt, loff_t *ppos)
@@ -2468,6 +2545,7 @@ tracing_trace_options_write(struct file *filp, const char __user *ubuf,
 	char buf[64];
 	char *cmp = buf;
 	int neg = 0;
+	int ret;
 	int i;
 
 	if (cnt >= sizeof(buf))
@@ -2494,11 +2572,13 @@ tracing_trace_options_write(struct file *filp, const char __user *ubuf,
 			break;
 		}
 	}
-	/*
-	 * If no option could be set, return an error:
-	 */
-	if (!trace_options[i])
-		return -EINVAL;
+
+	/* If no option could be set, test the specific tracer options */
+	if (!trace_options[i]) {
+		ret = set_tracer_option(current_trace, cmp, neg);
+		if (ret)
+			return ret;
+	}
 
 	filp->f_pos += cnt;
 
kernel/trace/trace.h +26 -0
index 790ea8c..23c8b67 100644
@@ -259,6 +259,29 @@ enum print_line_t {
 	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:
  */
@@ -279,8 +302,11 @@ struct tracer {
 					    struct trace_array *tr);
 #endif
 	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;
 	int			print_max;
+	struct tracer_flags 	*flags;
 };
 
 struct trace_seq {
Signature · 1 line
1.5.2.5