From 3a73333fb370f7b65de9d94c53df503642bda789 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Thu, 3 Mar 2022 17:05:34 -0500 Subject: tracing: Add TRACE_CUSTOM_EVENT() macro To make it really easy to add custom events from modules, add a TRACE_CUSTOM_EVENT() macro that acts just like the TRACE_EVENT() macro, but creates a custom event to an already existing tracepoint. The trace_custom_sched.[ch] has been updated to use this new macro to show how simple it is. Link: https://lkml.kernel.org/r/20220303220625.738622494@goodmis.org Cc: Ingo Molnar Cc: Andrew Morton Cc: Joel Fernandes Cc: Peter Zijlstra Cc: Masami Hiramatsu Cc: Tom Zanussi Signed-off-by: Steven Rostedt (Google) --- samples/trace_events/Makefile | 2 +- samples/trace_events/trace_custom_sched.c | 259 +++--------------------------- samples/trace_events/trace_custom_sched.h | 95 +++++++++++ 3 files changed, 120 insertions(+), 236 deletions(-) create mode 100644 samples/trace_events/trace_custom_sched.h (limited to 'samples/trace_events') diff --git a/samples/trace_events/Makefile b/samples/trace_events/Makefile index e98afc447fe1..b3808bb4cf8b 100644 --- a/samples/trace_events/Makefile +++ b/samples/trace_events/Makefile @@ -11,7 +11,7 @@ # Here trace-events-sample.c does the CREATE_TRACE_POINTS. # CFLAGS_trace-events-sample.o := -I$(src) +CFLAGS_trace_custom_sched.o := -I$(src) obj-$(CONFIG_SAMPLE_TRACE_EVENTS) += trace-events-sample.o - obj-$(CONFIG_SAMPLE_TRACE_CUSTOM_EVENTS) += trace_custom_sched.o diff --git a/samples/trace_events/trace_custom_sched.c b/samples/trace_events/trace_custom_sched.c index 70a12c32ff99..b99d9ab7db85 100644 --- a/samples/trace_events/trace_custom_sched.c +++ b/samples/trace_events/trace_custom_sched.c @@ -11,256 +11,45 @@ #include #include #include -#include - -#define THIS_SYSTEM "custom_sched" - -#define SCHED_PRINT_FMT \ - C("prev_prio=%d next_pid=%d next_prio=%d", REC->prev_prio, REC->next_pid, \ - REC->next_prio) - -#define SCHED_WAKING_FMT \ - C("pid=%d prio=%d", REC->pid, REC->prio) - -#undef C -#define C(a, b...) a, b - -static struct trace_event_fields sched_switch_fields[] = { - { - .type = "unsigned short", - .name = "prev_prio", - .size = sizeof(short), - .align = __alignof__(short), - .is_signed = 0, - .filter_type = FILTER_OTHER, - }, - { - .type = "unsigned short", - .name = "next_prio", - .size = sizeof(short), - .align = __alignof__(short), - .is_signed = 0, - .filter_type = FILTER_OTHER, - }, - { - .type = "unsigned int", - .name = "next_prio", - .size = sizeof(int), - .align = __alignof__(int), - .is_signed = 0, - .filter_type = FILTER_OTHER, - }, - {} -}; - -struct sched_event { - struct trace_entry ent; - unsigned short prev_prio; - unsigned short next_prio; - unsigned int next_pid; -}; - -static struct trace_event_fields sched_waking_fields[] = { - { - .type = "unsigned int", - .name = "pid", - .size = sizeof(int), - .align = __alignof__(int), - .is_signed = 0, - .filter_type = FILTER_OTHER, - }, - { - .type = "unsigned short", - .name = "prio", - .size = sizeof(short), - .align = __alignof__(short), - .is_signed = 0, - .filter_type = FILTER_OTHER, - }, - {} -}; - -struct wake_event { - struct trace_entry ent; - unsigned int pid; - unsigned short prio; -}; - -static void sched_switch_probe(void *data, bool preempt, struct task_struct *prev, - struct task_struct *next) -{ - struct trace_event_file *trace_file = data; - struct trace_event_buffer fbuffer; - struct sched_event *entry; - - if (trace_trigger_soft_disabled(trace_file)) - return; - - entry = trace_event_buffer_reserve(&fbuffer, trace_file, - sizeof(*entry)); - - if (!entry) - return; - - entry->prev_prio = prev->prio; - entry->next_prio = next->prio; - entry->next_pid = next->pid; - - trace_event_buffer_commit(&fbuffer); -} - -static struct trace_event_class sched_switch_class = { - .system = THIS_SYSTEM, - .reg = trace_event_reg, - .fields_array = sched_switch_fields, - .fields = LIST_HEAD_INIT(sched_switch_class.fields), - .probe = sched_switch_probe, -}; - -static void sched_waking_probe(void *data, struct task_struct *t) -{ - struct trace_event_file *trace_file = data; - struct trace_event_buffer fbuffer; - struct wake_event *entry; - - if (trace_trigger_soft_disabled(trace_file)) - return; - - entry = trace_event_buffer_reserve(&fbuffer, trace_file, - sizeof(*entry)); - - if (!entry) - return; - - entry->prio = t->prio; - entry->pid = t->pid; - - trace_event_buffer_commit(&fbuffer); -} - -static struct trace_event_class sched_waking_class = { - .system = THIS_SYSTEM, - .reg = trace_event_reg, - .fields_array = sched_waking_fields, - .fields = LIST_HEAD_INIT(sched_waking_class.fields), - .probe = sched_waking_probe, -}; - -static enum print_line_t sched_switch_output(struct trace_iterator *iter, int flags, - struct trace_event *trace_event) -{ - struct trace_seq *s = &iter->seq; - struct sched_event *REC = (struct sched_event *)iter->ent; - int ret; - - ret = trace_raw_output_prep(iter, trace_event); - if (ret != TRACE_TYPE_HANDLED) - return ret; - - trace_seq_printf(s, SCHED_PRINT_FMT); - trace_seq_putc(s, '\n'); - return trace_handle_return(s); -} - -static struct trace_event_functions sched_switch_funcs = { - .trace = sched_switch_output, -}; - -static enum print_line_t sched_waking_output(struct trace_iterator *iter, int flags, - struct trace_event *trace_event) -{ - struct trace_seq *s = &iter->seq; - struct wake_event *REC = (struct wake_event *)iter->ent; - int ret; - - ret = trace_raw_output_prep(iter, trace_event); - if (ret != TRACE_TYPE_HANDLED) - return ret; - - trace_seq_printf(s, SCHED_WAKING_FMT); - trace_seq_putc(s, '\n'); - - return trace_handle_return(s); -} - -static struct trace_event_functions sched_waking_funcs = { - .trace = sched_waking_output, -}; - -#undef C -#define C(a, b...) #a "," __stringify(b) +/* + * Must include the event header that the custom event will attach to, + * from the C file, and not in the custom header file. + */ +#include -static struct trace_event_call sched_switch_call = { - .class = &sched_switch_class, - .event = { - .funcs = &sched_switch_funcs, - }, - .print_fmt = SCHED_PRINT_FMT, - .module = THIS_MODULE, - .flags = TRACE_EVENT_FL_TRACEPOINT, -}; +/* Declare CREATE_CUSTOM_TRACE_EVENTS before including custom header */ +#define CREATE_CUSTOM_TRACE_EVENTS -static struct trace_event_call sched_waking_call = { - .class = &sched_waking_class, - .event = { - .funcs = &sched_waking_funcs, - }, - .print_fmt = SCHED_WAKING_FMT, - .module = THIS_MODULE, - .flags = TRACE_EVENT_FL_TRACEPOINT, -}; +#include "trace_custom_sched.h" +/* + * As the trace events are not exported to modules, the use of + * for_each_kernel_tracepoint() is needed to find the trace event + * to attach to. The fct() function below, is a callback that + * will be called for every event. + * + * Helper functions are created by the TRACE_CUSTOM_EVENT() macro + * update the event. Those are of the form: + * + * trace_custom_event__update() + * + * Where is the event to attach. + */ static void fct(struct tracepoint *tp, void *priv) { - if (tp->name && strcmp(tp->name, "sched_switch") == 0) - sched_switch_call.tp = tp; - else if (tp->name && strcmp(tp->name, "sched_waking") == 0) - sched_waking_call.tp = tp; -} - -static int add_event(struct trace_event_call *call) -{ - int ret; - - ret = register_trace_event(&call->event); - if (WARN_ON(!ret)) - return -ENODEV; - - ret = trace_add_event_call(call); - if (WARN_ON(ret)) - unregister_trace_event(&call->event); - - return ret; + trace_custom_event_sched_switch_update(tp); + trace_custom_event_sched_waking_update(tp); } static int __init trace_sched_init(void) { - int ret; - - check_trace_callback_type_sched_switch(sched_switch_probe); - check_trace_callback_type_sched_waking(sched_waking_probe); - for_each_kernel_tracepoint(fct, NULL); - - ret = add_event(&sched_switch_call); - if (ret) - return ret; - - ret = add_event(&sched_waking_call); - if (ret) - trace_remove_event_call(&sched_switch_call); - - return ret; + return 0; } static void __exit trace_sched_exit(void) { - trace_set_clr_event(THIS_SYSTEM, "sched_switch", 0); - trace_set_clr_event(THIS_SYSTEM, "sched_waking", 0); - - trace_remove_event_call(&sched_switch_call); - trace_remove_event_call(&sched_waking_call); } module_init(trace_sched_init); diff --git a/samples/trace_events/trace_custom_sched.h b/samples/trace_events/trace_custom_sched.h new file mode 100644 index 000000000000..a3d14de6a2e5 --- /dev/null +++ b/samples/trace_events/trace_custom_sched.h @@ -0,0 +1,95 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * Like the headers that use TRACE_EVENT(), the TRACE_CUSTOM_EVENT() + * needs a header that allows for multiple inclusions. + * + * Test for a unique name (here we have _TRACE_CUSTOM_SCHED_H), + * also allowing to continue if TRACE_CUSTOM_MULTI_READ is defined. + */ +#if !defined(_TRACE_CUSTOM_SCHED_H) || defined(TRACE_CUSTOM_MULTI_READ) +#define _TRACE_CUSTOM_SCHED_H + +/* Include linux/trace_events.h for initial defines of TRACE_CUSTOM_EVENT() */ +#include + +/* + * TRACE_CUSTOM_EVENT() is just like TRACE_EVENT(). The first parameter + * is the event name of an existing event where the TRACE_EVENT has been included + * in the C file before including this file. + */ +TRACE_CUSTOM_EVENT(sched_switch, + + /* + * The TP_PROTO() and TP_ARGS must match the trace event + * that the custom event is using. + */ + TP_PROTO(bool preempt, + struct task_struct *prev, + struct task_struct *next), + + TP_ARGS(preempt, prev, next), + + /* + * The next fields are where the customization happens. + * The TP_STRUCT__entry() defines what will be recorded + * in the ring buffer when the custom event triggers. + * + * The rest is just like the TRACE_EVENT() macro except that + * it uses the custom entry. + */ + TP_STRUCT__entry( + __field( unsigned short, prev_prio ) + __field( unsigned short, next_prio ) + __field( pid_t, next_pid ) + ), + + TP_fast_assign( + __entry->prev_prio = prev->prio; + __entry->next_pid = next->pid; + __entry->next_prio = next->prio; + ), + + TP_printk("prev_prio=%d next_pid=%d next_prio=%d", + __entry->prev_prio, __entry->next_pid, __entry->next_prio) +) + + +TRACE_CUSTOM_EVENT(sched_waking, + + TP_PROTO(struct task_struct *p), + + TP_ARGS(p), + + TP_STRUCT__entry( + __field( pid_t, pid ) + __field( unsigned short, prio ) + ), + + TP_fast_assign( + __entry->pid = p->pid; + __entry->prio = p->prio; + ), + + TP_printk("pid=%d prio=%d", __entry->pid, __entry->prio) +) +#endif +/* + * Just like the headers that create TRACE_EVENTs, the below must + * be outside the protection of the above #if block. + */ + +/* + * It is required that the Makefile includes: + * CFLAGS_.o := -I$(src) + */ +#undef TRACE_INCLUDE_PATH +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_PATH . + +/* + * It is requred that the TRACE_INCLUDE_FILE be the same + * as this file without the ".h". + */ +#define TRACE_INCLUDE_FILE trace_custom_sched +#include -- cgit