From 3cd1b55f321ff8dc68d340aab0d8d0f159cde094 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Wed, 21 Sep 2022 21:37:36 -0400 Subject: trace-cmd library: Add tracecmd_follow_event() Add the tracecmd_follow_event() to make it easier for applications to have a callback for specific events when calling either tracecmd_iterate_events() or tracecmd_iterate_events_multi() Link: https://lore.kernel.org/linux-trace-devel/20220922013737.3302850-2-rostedt@goodmis.org Signed-off-by: Steven Rostedt (Google) --- include/trace-cmd/trace-cmd.h | 8 +++ lib/trace-cmd/trace-input.c | 117 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 119 insertions(+), 6 deletions(-) diff --git a/include/trace-cmd/trace-cmd.h b/include/trace-cmd/trace-cmd.h index bcbf9d78..bc5edb4d 100644 --- a/include/trace-cmd/trace-cmd.h +++ b/include/trace-cmd/trace-cmd.h @@ -55,6 +55,14 @@ struct tracecmd_input *tracecmd_buffer_instance_handle(struct tracecmd_input *ha void tracecmd_set_private(struct tracecmd_input *handle, void *data); void *tracecmd_get_private(struct tracecmd_input *handle); +int tracecmd_follow_event(struct tracecmd_input *handle, + const char *system, const char *event_name, + int (*callback)(struct tracecmd_input *handle, + struct tep_event *, + struct tep_record *, + int, void *), + void *callback_data); + int tracecmd_iterate_events(struct tracecmd_input *handle, cpu_set_t *cpus, int cpu_size, int (*callback)(struct tracecmd_input *handle, diff --git a/lib/trace-cmd/trace-input.c b/lib/trace-cmd/trace-input.c index cdaa17bd..ce3584ee 100644 --- a/lib/trace-cmd/trace-input.c +++ b/lib/trace-cmd/trace-input.c @@ -43,6 +43,15 @@ struct page_map { int ref_count; }; +struct follow_event { + struct tep_event *event; + void *callback_data; + int (*callback)(struct tracecmd_input *handle, + struct tep_event *, + struct tep_record *, + int, void *); +}; + struct page { struct list_head list; off64_t offset; @@ -161,6 +170,7 @@ struct tracecmd_input { struct tep_plugin_list *plugin_list; struct tracecmd_input *parent; struct tracecmd_filter *filter; + struct follow_event *followers; unsigned long file_state; unsigned long long trace_id; unsigned long long next_offset; @@ -173,6 +183,7 @@ struct tracecmd_input { int cpus; int start_cpu; int ref; + int nr_followers; int nr_buffers; /* buffer instances */ bool use_trace_clock; bool read_page; @@ -2525,6 +2536,88 @@ tracecmd_read_next_data(struct tracecmd_input *handle, int *rec_cpu) return tracecmd_read_data(handle, next_cpu); } +/** + * tracecmd_follow_event - Add callback for specific events for iterators + * @handle: The handle to get a callback from + * @system: The system of the event to track + * @event_name: The name of the event to track + * @callback: The function to call when the event is hit in an iterator + * @callback_data: The data to pass to @callback + * + * This attaches a callback to @handle where if tracecmd_iterate_events() + * or tracecmd_iterate_events_multi() is called, that if the specified + * event is hit, it will call @callback, with the following parameters: + * @handle: Same handle as passed to this function. + * @event: The event pointer that was found by @system and @event_name. + * @record; The event instance of @event. + * @cpu: The cpu that the event happened on. + * @callback_data: The same as @callback_data passed to the function. + * + * Note that when used with tracecmd_iterate_events_multi() that @cpu + * may be the nth CPU of all handles it is processing, so if the CPU + * that the @record is on is desired, then use @record->cpu. + * + * Returns 0 on success and -1 on error. + */ +int tracecmd_follow_event(struct tracecmd_input *handle, + const char *system, const char *event_name, + int (*callback)(struct tracecmd_input *handle, + struct tep_event *, + struct tep_record *, + int, void *), + void *callback_data) +{ + struct tep_handle *tep = tracecmd_get_tep(handle); + struct follow_event *followers; + struct follow_event follow; + + if (!tep) { + errno = EINVAL; + return -1; + } + + follow.event = tep_find_event_by_name(tep, system, event_name); + if (!follow.event) { + errno = ENOENT; + return -1; + } + + follow.callback = callback; + follow.callback_data = callback_data; + + followers = realloc(handle->followers, sizeof(*followers) * + (handle->nr_followers + 1)); + if (!followers) + return -1; + + handle->followers = followers; + followers[handle->nr_followers++] = follow; + + return 0; +} + +static int call_followers(struct tracecmd_input *handle, + struct tep_record *record, int cpu) +{ + struct tep_handle *tep = tracecmd_get_tep(handle); + struct follow_event *followers = handle->followers; + struct tep_event *event; + int ret = 0; + int i; + + event = tep_find_event_by_record(tep, record); + if (!event) + return -1; + + for (i = 0; i < handle->nr_followers; i++) { + if (handle->followers[i].event == event) + ret |= followers[i].callback(handle, event, record, + cpu, followers[i].callback_data); + } + + return ret; +} + /** * tracecmd_iterate_events - iterate events over a given handle * @handle: The handle to iterate over @@ -2553,6 +2646,11 @@ int tracecmd_iterate_events(struct tracecmd_input *handle, int cpu; int ret = 0; + if (!callback && !handle->nr_followers) { + errno = EINVAL; + return -1; + } + records = calloc(handle->max_cpu, sizeof(*records)); if (!records) return -1; @@ -2582,12 +2680,14 @@ int tracecmd_iterate_events(struct tracecmd_input *handle, records[next_cpu] = tracecmd_peek_data(handle, next_cpu); if (!handle->filter || - tracecmd_filter_match(handle->filter, record) == TRACECMD_FILTER_MATCH) - ret = callback(handle, record, next_cpu, callback_data); - + tracecmd_filter_match(handle->filter, record) == TRACECMD_FILTER_MATCH) { + if (handle->nr_followers) + ret = call_followers(handle, record, next_cpu); + if (!ret && callback) + ret = callback(handle, record, next_cpu, callback_data); + } tracecmd_free_record(record); } - } while (next_cpu >= 0 && ret >= 0); for (cpu = 0; cpu < handle->max_cpu; cpu++) @@ -2674,8 +2774,13 @@ int tracecmd_iterate_events_multi(struct tracecmd_input **handles, records[next_cpu].record = tracecmd_peek_data(handle, cpu); if (!handle->filter || - tracecmd_filter_match(handle->filter, record) == TRACECMD_FILTER_MATCH) - ret = callback(handle, record, next_cpu, callback_data); + tracecmd_filter_match(handle->filter, record) == TRACECMD_FILTER_MATCH) { + if (handle->nr_followers) + ret = call_followers(handle, record, next_cpu); + + if (!ret && callback) + ret = callback(handle, record, next_cpu, callback_data); + } tracecmd_free_record(record); } -- cgit 1.2.3-korg