Re: [PATCH] Tracing events with GPIOs

From: Tom Zanussi
Date: Tue Dec 17 2013 - 13:29:52 EST


Hi Jean-Jacques,

On Tue, 2013-12-17 at 18:22 +0100, Jean-Jacques Hiblot wrote:
> 2013/12/17 Masami Hiramatsu <masami.hiramatsu.pt@xxxxxxxxxxx>:
> > (2013/12/17 9:22), Jean-Jacques Hiblot wrote:
> >> This patch implements a new tracing mechanism based on kprobes and using GPIO.
> >> Debugging with GPIO is very common in the embedded world. At least for those of us
> >> fortunate enough to have an oscilloscope or a logic analyzer on their bench...
> >> This is especially true if the issue results of a hardware/sofware interaction.
> >>
> >> Typical use cases are :
> >> * mixed software/hardware debugging. For example when the software detects a
> >> situation of interest (typically an error) it toggles a GPIO to trigger the
> >> oscilloscope acquisition.
> >> * direct latency/duration measurements.
> >
> > Ah, it's interesting to me :)
> >
> > And I think this feature should be built on the event triggers which
> > Tom is working on, instead of kprobes directly, because it allows
> > you to take gpio actions on normal tracepoints, and uprobes too :)
> >
>
> I'll make a version that uses this framework then. When it's ready
> I'll make some measurements to check the overhead of both solutions.
>

Yeah, this does look like an interesting application for event triggers
- looking forward to seeing what you come up with...

> Tom,
> is git://git.yoctoproject.org/linux-yocto-contrib
> tzanussi/event-triggers-v11 the right starting point ?
>

Yep, that's the last iteration - I haven't changed anything since then.

Tom

> Thanks,
>
> Jean-Jacques
>
> > Thank you!
> >
> >>
> >> examples:
> >> To trig the oscilloscope whenever a mmc command error:
> >> echo "p:my_mmc_blk_error mmc_blk_cmd_error gpiopulse@13" > /sys/kernel/debug/tracing/kprobe_events
> >> echo 1 > /sys/kernel/debug/tracing/events/kprobes/my_mmc_blk_error/enable
> >>
> >> Signed-off-by: Jean-Jacques Hiblot <jjhiblot@xxxxxxxxxxxxxxx>
> >> ---
> >> kernel/trace/Kconfig | 12 ++++
> >> kernel/trace/trace_kprobe.c | 167 +++++++++++++++++++++++++++++++++++++++++++-
> >> 2 files changed, 177 insertions(+), 2 deletions(-)
> >>
> >> diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
> >> index 015f85a..4228768 100644
> >> --- a/kernel/trace/Kconfig
> >> +++ b/kernel/trace/Kconfig
> >> @@ -420,6 +420,18 @@ config KPROBE_EVENT
> >> This option is also required by perf-probe subcommand of perf tools.
> >> If you want to use perf tools, this option is strongly recommended.
> >>
> >> +config KPROBE_EVENT_GPIO
> >> + depends on KPROBE_EVENT
> >> + depends on GPIOLIB
> >> + bool "Enable kprobes-based tracing of events via GPIO"
> >> + default n
> >> + help
> >> + This allows the user to use GPIOs as a tracing tool.The primary
> >> + purpose of this option is to allow the user to trigger an
> >> + oscilloscope on software events.
> >> + The GPIO can be set, cleared, toggled, or pulsed when the event
> >> + is hit.
> >> +
> >> config UPROBE_EVENT
> >> bool "Enable uprobes-based dynamic events"
> >> depends on ARCH_SUPPORTS_UPROBES
> >> diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
> >> index dae9541..5d297f5 100644
> >> --- a/kernel/trace/trace_kprobe.c
> >> +++ b/kernel/trace/trace_kprobe.c
> >> @@ -19,6 +19,7 @@
> >>
> >> #include <linux/module.h>
> >> #include <linux/uaccess.h>
> >> +#include <linux/gpio.h>
> >>
> >> #include "trace_probe.h"
> >>
> >> @@ -27,6 +28,33 @@
> >> /**
> >> * Kprobe event core functions
> >> */
> >> +#ifdef CONFIG_KPROBE_EVENT_GPIO
> >> +#define TP_GPIO_UNDEFINED 0
> >> +#define TP_GPIO_CMD_SET 1
> >> +#define TP_GPIO_CMD_CLEAR 2
> >> +#define TP_GPIO_CMD_TOGGLE 3
> >> +#define TP_GPIO_CMD_PULSE 4
> >> +#define TP_GPIO_CMD_PULSE_HIGH 5
> >> +#define TP_GPIO_CMD_PULSE_LOW 6
> >> +
> >> +static const struct {
> >> + const char *name;
> >> + int action;
> >> +} gpio_actions[] = {
> >> + {"gpioset", TP_GPIO_CMD_SET},
> >> + {"gpioclear", TP_GPIO_CMD_CLEAR},
> >> + {"gpiotoggle", TP_GPIO_CMD_TOGGLE},
> >> + {"gpiopulse", TP_GPIO_CMD_PULSE},
> >> + {"gpiopulsehigh", TP_GPIO_CMD_PULSE_HIGH},
> >> + {"gpiopulselow", TP_GPIO_CMD_PULSE_LOW},
> >> +};
> >> +
> >> +struct gpio_trace {
> >> + int gpio;
> >> + int action;
> >> +};
> >> +#endif
> >> +
> >> struct trace_probe {
> >> struct list_head list;
> >> struct kretprobe rp; /* Use rp.kp for kprobe use */
> >> @@ -38,6 +66,9 @@ struct trace_probe {
> >> struct list_head files;
> >> ssize_t size; /* trace entry size */
> >> unsigned int nr_args;
> >> +#ifdef CONFIG_KPROBE_EVENT_GPIO
> >> + struct gpio_trace gpio_trace;
> >> +#endif
> >> struct probe_arg args[];
> >> };
> >>
> >> @@ -164,10 +195,24 @@ error:
> >> return ERR_PTR(ret);
> >> }
> >>
> >> +static struct trace_probe *find_gpio_probe(int gpio)
> >> +{
> >> + struct trace_probe *tp;
> >> +
> >> + list_for_each_entry(tp, &probe_list, list)
> >> + if ((tp->gpio_trace.action) && (tp->gpio_trace.gpio == gpio))
> >> + return tp;
> >> + return NULL;
> >> +}
> >> +
> >> static void free_trace_probe(struct trace_probe *tp)
> >> {
> >> int i;
> >>
> >> +#ifdef CONFIG_KPROBE_EVENT_GPIO
> >> + if (tp->gpio_trace.action && !find_gpio_probe(tp->gpio_trace.gpio))
> >> + gpio_free(tp->gpio_trace.gpio);
> >> +#endif
> >> for (i = 0; i < tp->nr_args; i++)
> >> traceprobe_free_probe_arg(&tp->args[i]);
> >>
> >> @@ -435,8 +480,14 @@ static int create_trace_probe(int argc, char **argv)
> >> {
> >> /*
> >> * Argument syntax:
> >> - * - Add kprobe: p[:[GRP/]EVENT] [MOD:]KSYM[+OFFS]|KADDR [FETCHARGS]
> >> - * - Add kretprobe: r[:[GRP/]EVENT] [MOD:]KSYM[+0] [FETCHARGS]
> >> + * - Add kprobe: p[:[GRP/]EVENT] [MOD:]KSYM[+OFFS]|KADDR
> >> + * [GPIOACTION@GPIONUM] [FETCHARGS]
> >> + * - Add kretprobe: r[:[GRP/]EVENT] [MOD:]KSYM[+0]
> >> + * [GPIOACTION@GPIONUM] [FETCHARGS]
> >> + * Gpio tracing:
> >> + * gpio action can be : gpioset, gpioclear, gpiotoggle, gpiopulse,
> >> + * gpiopulsehigh and gpiopulselow
> >> + * gpionum is the id of the gpio
> >> * Fetch args:
> >> * $retval : fetch return value
> >> * $stack : fetch stack address
> >> @@ -459,6 +510,9 @@ static int create_trace_probe(int argc, char **argv)
> >> unsigned long offset = 0;
> >> void *addr = NULL;
> >> char buf[MAX_EVENT_NAME_LEN];
> >> +#ifdef CONFIG_KPROBE_EVENT_GPIO
> >> + int len;
> >> +#endif
> >>
> >> /* argc must be >= 1 */
> >> if (argv[0][0] == 'p')
> >> @@ -541,6 +595,7 @@ static int create_trace_probe(int argc, char **argv)
> >> return -EINVAL;
> >> }
> >> }
> >> +
> >> argc -= 2; argv += 2;
> >>
> >> /* setup a probe */
> >> @@ -562,6 +617,66 @@ static int create_trace_probe(int argc, char **argv)
> >> return PTR_ERR(tp);
> >> }
> >>
> >> +#ifdef CONFIG_KPROBE_EVENT_GPIO
> >> + /* parse the optionnal gpio action argument */
> >> + for (i = 0; argc && (i < ARRAY_SIZE(gpio_actions)); i++) {
> >> + len = strlen(gpio_actions[i].name);
> >> + if (strncmp(argv[0], gpio_actions[i].name, len) == 0)
> >> + break;
> >> + }
> >> +
> >> + if (argc && (i < ARRAY_SIZE(gpio_actions))) {
> >> + int gpio;
> >> + unsigned long ul;
> >> + int gpio_requested = 0;
> >> + ret = -EINVAL;
> >> + if (argv[0][len] != '@') {
> >> + pr_info("Syntax error. wrong gpio syntax\n");
> >> + goto error;
> >> + }
> >> +
> >> + if (kstrtoul(&argv[0][len+1], 0, &ul)) {
> >> + pr_info("Failed to parse gpio number.\n");
> >> + goto error;
> >> + }
> >> + gpio = ul;
> >> +
> >> + if (!gpio_is_valid(gpio)) {
> >> + pr_info("gpio %d is not valid.\n", gpio);
> >> + goto error;
> >> + }
> >> +
> >> + if (gpio_cansleep(gpio))
> >> + pr_info("gpio %d can sleep. This may break your"
> >> + "kernel!!!!!!\n", gpio);
> >> +
> >> + if (!find_gpio_probe(gpio)) {
> >> + ret = gpio_request(gpio, event);
> >> + if (ret) {
> >> + pr_info("can't request gpio %d.\n", gpio);
> >> + goto error;
> >> + }
> >> + gpio_requested = 1;
> >> + }
> >> +
> >> + if ((gpio_actions[i].action == TP_GPIO_CMD_CLEAR) ||
> >> + (gpio_actions[i].action == TP_GPIO_CMD_PULSE_LOW))
> >> + ret = gpio_direction_output(gpio, 1);
> >> + else
> >> + ret = gpio_direction_output(gpio, 0);
> >> + if (ret) {
> >> + pr_info("can't make gpio %d an output.\n", gpio);
> >> + if (gpio_requested)
> >> + gpio_free(gpio);
> >> + goto error;
> >> + }
> >> +
> >> + tp->gpio_trace.gpio = gpio;
> >> + tp->gpio_trace.action = gpio_actions[i].action;
> >> + argc -= 1; argv += 1;
> >> + }
> >> +#endif
> >> +
> >> /* parse arguments */
> >> ret = 0;
> >> for (i = 0; i < argc && i < MAX_TRACE_ARGS; i++) {
> >> @@ -1145,12 +1260,54 @@ kretprobe_perf_func(struct trace_probe *tp, struct kretprobe_instance *ri,
> >> }
> >> #endif /* CONFIG_PERF_EVENTS */
> >>
> >> +#ifdef CONFIG_KPROBE_EVENT_GPIO
> >> +static __kprobes void kprobe_gpio_take_action(int gpio, int action)
> >> +{
> >> + int val;
> >> + switch (action) {
> >> + case TP_GPIO_CMD_PULSE_HIGH:
> >> + gpio_set_value(gpio, 1);
> >> + /* intentionnaly don't break here */
> >> + case TP_GPIO_CMD_CLEAR:
> >> + gpio_set_value(gpio, 0);
> >> + break;
> >> +
> >> + case TP_GPIO_CMD_PULSE_LOW:
> >> + gpio_set_value(gpio, 0);
> >> + /* intentionnaly don't break here */
> >> + case TP_GPIO_CMD_SET:
> >> + gpio_set_value(gpio, 1);
> >> + break;
> >> +
> >> + case TP_GPIO_CMD_TOGGLE:
> >> + val = gpio_get_value(gpio);
> >> + gpio_set_value(gpio, val ? 0 : 1);
> >> + break;
> >> +
> >> + case TP_GPIO_CMD_PULSE:
> >> + val = gpio_get_value(gpio);
> >> + gpio_set_value(gpio, val ? 0 : 1);
> >> + gpio_set_value(gpio, val);
> >> + break;
> >> + }
> >> +}
> >> +
> >> /*
> >> * called by perf_trace_init() or __ftrace_set_clr_event() under event_mutex.
> >> *
> >> * kprobe_trace_self_tests_init() does enable_trace_probe/disable_trace_probe
> >> * lockless, but we can't race with this __init function.
> >> */
> >> +/* Kprobe gpio handler */
> >> +static inline __kprobes void kprobe_gpio_func(struct trace_probe *tp,
> >> + struct pt_regs *regs)
> >> +{
> >> + if (tp->gpio_trace.action)
> >> + kprobe_gpio_take_action(tp->gpio_trace.gpio,
> >> + tp->gpio_trace.action);
> >> +}
> >> +#endif /* CONFIG_KPROBE_EVENT_GPIO */
> >> +
> >> static __kprobes
> >> int kprobe_register(struct ftrace_event_call *event,
> >> enum trace_reg type, void *data)
> >> @@ -1186,6 +1343,9 @@ int kprobe_dispatcher(struct kprobe *kp, struct pt_regs *regs)
> >>
> >> tp->nhit++;
> >>
> >> +#ifdef CONFIG_KPROBE_EVENT_GPIO
> >> + kprobe_gpio_func(tp, regs);
> >> +#endif
> >> if (tp->flags & TP_FLAG_TRACE)
> >> kprobe_trace_func(tp, regs);
> >> #ifdef CONFIG_PERF_EVENTS
> >> @@ -1202,6 +1362,9 @@ int kretprobe_dispatcher(struct kretprobe_instance *ri, struct pt_regs *regs)
> >>
> >> tp->nhit++;
> >>
> >> +#ifdef CONFIG_KPROBE_EVENT_GPIO
> >> + kprobe_gpio_func(tp, regs);
> >> +#endif
> >> if (tp->flags & TP_FLAG_TRACE)
> >> kretprobe_trace_func(tp, ri, regs);
> >> #ifdef CONFIG_PERF_EVENTS
> >>
> >
> >
> > --
> > Masami HIRAMATSU
> > IT Management Research Dept. Linux Technology Center
> > Hitachi, Ltd., Yokohama Research Laboratory
> > E-mail: masami.hiramatsu.pt@xxxxxxxxxxx
> >
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-gpio" in
> > the body of a message to majordomo@xxxxxxxxxxxxxxx
> > More majordomo info at http://vger.kernel.org/majordomo-info.html


--
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/