From patchwork Tue Nov 15 03:47:09 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13043216 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 15F73C4332F for ; Tue, 15 Nov 2022 03:46:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232358AbiKODqd (ORCPT ); Mon, 14 Nov 2022 22:46:33 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:39660 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230041AbiKODqc (ORCPT ); Mon, 14 Nov 2022 22:46:32 -0500 Received: from dfw.source.kernel.org (dfw.source.kernel.org [139.178.84.217]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A3E2118B2B for ; Mon, 14 Nov 2022 19:46:30 -0800 (PST) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 3ECB961502 for ; Tue, 15 Nov 2022 03:46:30 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id AB0F7C433B5; Tue, 15 Nov 2022 03:46:29 +0000 (UTC) Received: from rostedt by gandalf.local.home with local (Exim 4.96) (envelope-from ) id 1oumum-00AGhr-1X; Mon, 14 Nov 2022 22:47:12 -0500 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: "Steven Rostedt (Google)" Subject: [PATCH 1/4] libtracefs: Use tracefs_cpu_read() for tracefs_iterate_raw_events() Date: Mon, 14 Nov 2022 22:47:09 -0500 Message-Id: <20221115034712.2447458-2-rostedt@goodmis.org> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20221115034712.2447458-1-rostedt@goodmis.org> References: <20221115034712.2447458-1-rostedt@goodmis.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org From: "Steven Rostedt (Google)" Now that there's an API for reading the raw events, use that for tracefs_iterate_raw_events() instead of open coding the reading. It should also make it more efficient as it can use pipes as well. Signed-off-by: Steven Rostedt (Google) --- src/tracefs-events.c | 106 +++++++++++++++++++++---------------------- 1 file changed, 52 insertions(+), 54 deletions(-) diff --git a/src/tracefs-events.c b/src/tracefs-events.c index 57b22964f893..592b1a01fea4 100644 --- a/src/tracefs-events.c +++ b/src/tracefs-events.c @@ -21,14 +21,13 @@ #include "tracefs-local.h" struct cpu_iterate { + struct tracefs_cpu *tcpu; struct tep_record record; struct tep_event *event; struct kbuffer *kbuf; void *page; int psize; - int rsize; int cpu; - int fd; }; static int read_kbuf_record(struct cpu_iterate *cpu) @@ -60,9 +59,21 @@ int read_next_page(struct tep_handle *tep, struct cpu_iterate *cpu) { enum kbuffer_long_size long_size; enum kbuffer_endian endian; + int r; - cpu->rsize = read(cpu->fd, cpu->page, cpu->psize); - if (cpu->rsize <= 0) + if (!cpu->tcpu) + return -1; + + r = tracefs_cpu_buffered_read(cpu->tcpu, cpu->page, true); + /* + * tracefs_cpu_buffered_read() only reads in full subbuffer size, + * but this wants partial buffers as well. If the function returns + * empty (-1 for EAGAIN), try tracefs_cpu_read() next, as that can + * read partially filled buffers too, but isn't as efficient. + */ + if (r <= 0) + r = tracefs_cpu_read(cpu->tcpu, cpu->page, true); + if (r <= 0) return -1; if (!cpu->kbuf) { @@ -82,8 +93,8 @@ int read_next_page(struct tep_handle *tep, struct cpu_iterate *cpu) } kbuffer_load_subbuffer(cpu->kbuf, cpu->page); - if (kbuffer_subbuffer_size(cpu->kbuf) > cpu->rsize) { - tracefs_warning("%s: page_size > %d", __func__, cpu->rsize); + if (kbuffer_subbuffer_size(cpu->kbuf) > r) { + tracefs_warning("%s: page_size > %d", __func__, r); return -1; } @@ -147,64 +158,51 @@ static int read_cpu_pages(struct tep_handle *tep, struct cpu_iterate *cpus, int static int open_cpu_files(struct tracefs_instance *instance, cpu_set_t *cpus, int cpu_size, struct cpu_iterate **all_cpus, int *count) { + struct tracefs_cpu *tcpu; struct cpu_iterate *tmp; - unsigned int p_size; - struct dirent *dent; - char file[PATH_MAX]; - struct stat st; - int ret = -1; - int fd = -1; - char *path; - DIR *dir; + int nr_cpus; int cpu; int i = 0; - path = tracefs_instance_get_file(instance, "per_cpu"); - if (!path) - return -1; - dir = opendir(path); - if (!dir) - goto out; - p_size = getpagesize(); - while ((dent = readdir(dir))) { - const char *name = dent->d_name; + *all_cpus = NULL; - if (strlen(name) < 4 || strncmp(name, "cpu", 3) != 0) - continue; - cpu = atoi(name + 3); + nr_cpus = sysconf(_SC_NPROCESSORS_CONF); + for (cpu = 0; cpu < nr_cpus; cpu++) { if (cpus && !CPU_ISSET_S(cpu, cpu_size, cpus)) continue; - sprintf(file, "%s/%s", path, name); - if (stat(file, &st) < 0 || !S_ISDIR(st.st_mode)) - continue; - - sprintf(file, "%s/%s/trace_pipe_raw", path, name); - fd = open(file, O_RDONLY | O_NONBLOCK); - if (fd < 0) - continue; - tmp = realloc(*all_cpus, (i + 1) * sizeof(struct cpu_iterate)); + tcpu = tracefs_cpu_open(instance, cpu, true); + tmp = realloc(*all_cpus, (i + 1) * sizeof(*tmp)); if (!tmp) { - close(fd); - goto out; + i--; + goto error; } - memset(tmp + i, 0, sizeof(struct cpu_iterate)); - tmp[i].fd = fd; - tmp[i].cpu = cpu; - tmp[i].page = malloc(p_size); - tmp[i].psize = p_size; + *all_cpus = tmp; - *count = i + 1; - if (!tmp[i++].page) - goto out; - } - ret = 0; + memset(tmp + i, 0, sizeof(*tmp)); -out: - if (dir) - closedir(dir); - tracefs_put_tracing_file(path); - return ret; + if (!tcpu) + goto error; + + tmp[i].tcpu = tcpu; + tmp[i].cpu = cpu; + tmp[i].psize = tracefs_cpu_read_size(tcpu); + tmp[i].page = malloc(tmp[i].psize); + + if (!tmp[i++].page) + goto error; + } + *count = i; + return 0; + error: + tmp = *all_cpus; + for (; i >= 0; i--) { + tracefs_cpu_close(tmp[i].tcpu); + free(tmp[i].page); + } + free(tmp); + *all_cpus = NULL; + return -1; } static bool top_iterate_keep_going; @@ -236,7 +234,7 @@ int tracefs_iterate_raw_events(struct tep_handle *tep, { bool *keep_going = instance ? &instance->pipe_keep_going : &top_iterate_keep_going; - struct cpu_iterate *all_cpus = NULL; + struct cpu_iterate *all_cpus; int count = 0; int ret; int i; @@ -257,7 +255,7 @@ out: if (all_cpus) { for (i = 0; i < count; i++) { kbuffer_free(all_cpus[i].kbuf); - close(all_cpus[i].fd); + tracefs_cpu_close(all_cpus[i].tcpu); free(all_cpus[i].page); } free(all_cpus); From patchwork Tue Nov 15 03:47:10 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13043219 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6CC4DC4321E for ; Tue, 15 Nov 2022 03:46:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232093AbiKODqf (ORCPT ); Mon, 14 Nov 2022 22:46:35 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:39674 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232437AbiKODqe (ORCPT ); Mon, 14 Nov 2022 22:46:34 -0500 Received: from ams.source.kernel.org (ams.source.kernel.org [145.40.68.75]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8A34919024 for ; Mon, 14 Nov 2022 19:46:32 -0800 (PST) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ams.source.kernel.org (Postfix) with ESMTPS id 0BADFB81677 for ; Tue, 15 Nov 2022 03:46:31 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id A5F3BC433C1; Tue, 15 Nov 2022 03:46:29 +0000 (UTC) Received: from rostedt by gandalf.local.home with local (Exim 4.96) (envelope-from ) id 1oumum-00AGhu-1b; Mon, 14 Nov 2022 22:47:12 -0500 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: "Steven Rostedt (Google)" Subject: [PATCH 2/4] libtracefs: Split out iterator functions from man page Date: Mon, 14 Nov 2022 22:47:10 -0500 Message-Id: <20221115034712.2447458-3-rostedt@goodmis.org> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20221115034712.2447458-1-rostedt@goodmis.org> References: <20221115034712.2447458-1-rostedt@goodmis.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org From: "Steven Rostedt (Google)" With preparation to add more APIs that deals with tracefs_iterate_raw_events(), split it out of the generic event man pages so that it can have its own set without going over the max of 9 APIs per man page. Signed-off-by: Steven Rostedt (Google) --- Documentation/libtracefs-events.txt | 32 +----- Documentation/libtracefs-iterator.txt | 143 ++++++++++++++++++++++++++ 2 files changed, 144 insertions(+), 31 deletions(-) create mode 100644 Documentation/libtracefs-iterator.txt diff --git a/Documentation/libtracefs-events.txt b/Documentation/libtracefs-events.txt index d5bd779273e2..90c54b8c79da 100644 --- a/Documentation/libtracefs-events.txt +++ b/Documentation/libtracefs-events.txt @@ -4,7 +4,7 @@ libtracefs(3) NAME ---- tracefs_event_systems, tracefs_system_events, tracefs_event_enable, tracefs_event_disable, -tracefs_event_is_enabled, tracefs_iterate_raw_events, tracefs_iterate_stop - Work with trace systems and events. +tracefs_event_is_enabled - Work with trace systems and events. SYNOPSIS -------- @@ -27,13 +27,6 @@ int *tracefs_event_disable*(struct tracefs_instance pass:[*]_instance_, const ch const char pass:[*]_event_); enum tracefs_enable_state *tracefs_event_is_enabled*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_system_, const char pass:[*]_event_); - -int *tracefs_iterate_raw_events*(struct tep_handle pass:[*]_tep_, struct tracefs_instance pass:[*]_instance_, - cpu_set_t pass:[*]_cpus_, int _cpu_size_, - int (pass:[*]_callback_)(struct tep_event pass:[*], struct tep_record pass:[*], int, void pass:[*]), - void pass:[*]_callback_context_); -void *tracefs_iterate_stop*(struct tracefs_instance pass:[*]_instance_); - -- DESCRIPTION @@ -89,26 +82,6 @@ events. The enabled state is defined as: *TRACEFS_SOME_ENABLED* - Some matching events were enabled while others were not. -The *tracefs_iterate_raw_events()* function will read the tracefs raw -data buffers and call the specified _callback_ function for every event it -encounters. Events are iterated in sorted order: oldest first. An initialized -_tep_ handler is required (See *tracefs_local_events*(3)). If _instance_ is -NULL, then the toplevel tracefs buffer is used, otherwise the buffer for -the corresponding _instance_ is read. To filter only on a subset of CPUs, -_cpus_ and _cpu_size_ may be set to only call _callback_ with events that -occurred on the CPUs specified, otherwise if _cpus_ is NULL then the _callback_ -function will be called for all events, and _cpu_size_ is ignored. The -_callback_ function will be called with the following parameters: A -pointer to a struct tep_event that corresponds to the type of event the -record is; The record representing the event; The CPU that the event -occurred on; and a pointer to user specified _callback_context_. If the _callback_ -returns non-zero, the iteration stops. - -Use *tracefs_iterate_stop()* to force a executing *tracefs_iterate_raw_events()* -to halt. This can be called from either a callback that is called by -the iterator (even though a return of non-zero will stop it), or from another -thread. - RETURN VALUE ------------ The *tracefs_event_systems()* and *tracefs_system_events()* functions return @@ -126,9 +99,6 @@ and errno is not set. The *tracefs_event_is_enabled()* returns the enabled status of the matching events or TRACEFS_ERROR on error. -The *tracefs_iterate_raw_events()* function returns -1 in case of an error or -0 otherwise. - EXAMPLE ------- [source,c] diff --git a/Documentation/libtracefs-iterator.txt b/Documentation/libtracefs-iterator.txt new file mode 100644 index 000000000000..5cdf31e3c090 --- /dev/null +++ b/Documentation/libtracefs-iterator.txt @@ -0,0 +1,143 @@ +libtracefs(3) +============= + +NAME +tracefs_iterate_raw_events, tracefs_iterate_stop - Iterate over events in the ring buffer + +SYNOPSIS +-------- +[verse] +-- +*#include * + +int *tracefs_iterate_raw_events*(struct tep_handle pass:[*]_tep_, struct tracefs_instance pass:[*]_instance_, + cpu_set_t pass:[*]_cpus_, int _cpu_size_, + int (pass:[*]_callback_)(struct tep_event pass:[*], struct tep_record pass:[*], int, void pass:[*]), + void pass:[*]_callback_context_); +void *tracefs_iterate_stop*(struct tracefs_instance pass:[*]_instance_); + +-- + +DESCRIPTION +----------- +Trace iterator over raw events. + +The *tracefs_iterate_raw_events()* function will read the tracefs raw +data buffers and call the specified _callback_ function for every event it +encounters. Events are iterated in sorted order: oldest first. An initialized +_tep_ handler is required (See *tracefs_local_events*(3)). If _instance_ is +NULL, then the toplevel tracefs buffer is used, otherwise the buffer for +the corresponding _instance_ is read. To filter only on a subset of CPUs, +_cpus_ and _cpu_size_ may be set to only call _callback_ with events that +occurred on the CPUs specified, otherwise if _cpus_ is NULL then the _callback_ +function will be called for all events, and _cpu_size_ is ignored. The +_callback_ function will be called with the following parameters: A +pointer to a struct tep_event that corresponds to the type of event the +record is; The record representing the event; The CPU that the event +occurred on; and a pointer to user specified _callback_context_. If the _callback_ +returns non-zero, the iteration stops. + +Use *tracefs_iterate_stop()* to force a executing *tracefs_iterate_raw_events()* +to halt. This can be called from either a callback that is called by +the iterator (even though a return of non-zero will stop it), or from another +thread. + +RETURN VALUE +------------ +The *tracefs_iterate_raw_events()* function returns -1 in case of an error or +0 otherwise. + +EXAMPLE +------- +[source,c] +-- +#include + +char **systems = tracefs_event_systems(NULL); + + if (systems) { + int i = 0; + /* Got registered trace systems from the top trace instance */ + while (systems[i]) { + char **events = tracefs_system_events(NULL, systems[i]); + if (events) { + /* Got registered events in system[i] from the top trace instance */ + int j = 0; + + while (events[j]) { + /* Got event[j] in system[i] from the top trace instance */ + j++; + } + tracefs_list_free(events); + } + i++; + } + tracefs_list_free(systems); + } +.... +static int records_walk(struct tep_event *tep, struct tep_record *record, int cpu, void *context) +{ + /* Got recorded event on cpu */ + return 0; +} +... +struct tep_handle *tep = tracefs_local_events(NULL); + + if (!tep) { + /* Failed to initialise tep handler with local events */ + ... + } + + errno = 0; + ret = tracefs_event_enable(NULL, "sched", NULL); + if (ret < 0 && !errno) + printf("Could not find 'sched' events\n"); + tracefs_event_enable(NULL, "irq", "irq_handler_\(enter\|exit\)"); + + if (tracefs_iterate_raw_events(tep, NULL, NULL, 0, records_walk, NULL) < 0) { + /* Error walking through the recorded raw events */ + } + + /* Disable all events */ + tracefs_event_disable(NULL, NULL, NULL); + tep_free(tep); +-- +FILES +----- +[verse] +-- +*tracefs.h* + Header file to include in order to have access to the library APIs. +*-ltracefs* + Linker switch to add when building a program that uses the library. +-- + +SEE ALSO +-------- +*libtracefs*(3), +*libtraceevent*(3), +*trace-cmd*(1) + +AUTHOR +------ +[verse] +-- +*Steven Rostedt* +*Tzvetomir Stoyanov* +-- +REPORTING BUGS +-------------- +Report bugs to + +LICENSE +------- +libtracefs is Free Software licensed under the GNU LGPL 2.1 + +RESOURCES +--------- +https://git.kernel.org/pub/scm/libs/libtrace/libtracefs.git/ + +COPYING +------- +Copyright \(C) 2020 VMware, Inc. Free use of this software is granted under +the terms of the GNU Public License (GPL). From patchwork Tue Nov 15 03:47:11 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13043220 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id AA4D3C43217 for ; Tue, 15 Nov 2022 03:46:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230415AbiKODqe (ORCPT ); Mon, 14 Nov 2022 22:46:34 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:39664 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232093AbiKODqc (ORCPT ); Mon, 14 Nov 2022 22:46:32 -0500 Received: from dfw.source.kernel.org (dfw.source.kernel.org [139.178.84.217]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id CD59C1A3B2 for ; Mon, 14 Nov 2022 19:46:30 -0800 (PST) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 66A5961527 for ; Tue, 15 Nov 2022 03:46:30 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id BB3F5C43470; Tue, 15 Nov 2022 03:46:29 +0000 (UTC) Received: from rostedt by gandalf.local.home with local (Exim 4.96) (envelope-from ) id 1oumum-00AGhx-1f; Mon, 14 Nov 2022 22:47:12 -0500 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: "Steven Rostedt (Google)" Subject: [PATCH 3/4] libtracefs: Add tracefs_follow_event() API Date: Mon, 14 Nov 2022 22:47:11 -0500 Message-Id: <20221115034712.2447458-4-rostedt@goodmis.org> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20221115034712.2447458-1-rostedt@goodmis.org> References: <20221115034712.2447458-1-rostedt@goodmis.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org From: "Steven Rostedt (Google)" Add tracefs_follow_event() API that allows to only receive a callback for a specific event. Signed-off-by: Steven Rostedt (Google) --- Documentation/libtracefs-iterator.txt | 130 ++++++++++++++++++-------- include/tracefs-local.h | 10 ++ include/tracefs.h | 6 ++ src/tracefs-events.c | 101 +++++++++++++++++++- 4 files changed, 204 insertions(+), 43 deletions(-) diff --git a/Documentation/libtracefs-iterator.txt b/Documentation/libtracefs-iterator.txt index 5cdf31e3c090..95ff1894d074 100644 --- a/Documentation/libtracefs-iterator.txt +++ b/Documentation/libtracefs-iterator.txt @@ -2,7 +2,7 @@ libtracefs(3) ============= NAME -tracefs_iterate_raw_events, tracefs_iterate_stop - Iterate over events in the ring buffer +tracefs_iterate_raw_events, tracefs_iterate_stop, tracefs_follow_event - Iterate over events in the ring buffer SYNOPSIS -------- @@ -16,6 +16,13 @@ int *tracefs_iterate_raw_events*(struct tep_handle pass:[*]_tep_, struct tracefs void pass:[*]_callback_context_); void *tracefs_iterate_stop*(struct tracefs_instance pass:[*]_instance_); +int *tracefs_follow_event*(struct tep_handle pass:[*]_tep_, struct tracefs_instance pass:[*]_instance_, + const char pass:[*]_system_, const char pass:[*]_event_name_, + int (pass:[*]_callback_)(struct tep_event pass:[*], + struct tep_record pass:[*], + int, void pass:[*]), + void pass:[*]_callback_data_): + -- DESCRIPTION @@ -42,6 +49,14 @@ to halt. This can be called from either a callback that is called by the iterator (even though a return of non-zero will stop it), or from another thread. +The *tracefs_follow_event()* is used with *tracefs_iterate_raw_events()* but +intead of the callback being called for every event, it is only called for the +specified _system_ / _event_name_ given to the function. The _callback_ is the +same as for *tracefs_iterate_raw_events()*, and the passed in _callback_context_ +will be passed to the _callback_ as well. Note, if it returns something other +than 0, it will stop the loop before the _callback_ of *tracefs_iterate_raw_events()* +is called. + RETURN VALUE ------------ The *tracefs_iterate_raw_events()* function returns -1 in case of an error or @@ -51,56 +66,89 @@ EXAMPLE ------- [source,c] -- +#include #include +#include -char **systems = tracefs_event_systems(NULL); - - if (systems) { - int i = 0; - /* Got registered trace systems from the top trace instance */ - while (systems[i]) { - char **events = tracefs_system_events(NULL, systems[i]); - if (events) { - /* Got registered events in system[i] from the top trace instance */ - int j = 0; - - while (events[j]) { - /* Got event[j] in system[i] from the top trace instance */ - j++; - } - tracefs_list_free(events); - } - i++; - } - tracefs_list_free(systems); - } -.... -static int records_walk(struct tep_event *tep, struct tep_record *record, int cpu, void *context) +struct my_struct { + bool stopped; +}; + +static int callback(struct tep_event *event, struct tep_record *record, + int cpu, void *data) { - /* Got recorded event on cpu */ + struct my_struct *my_data = data; + static struct trace_seq seq; + static int counter; + + if (counter++ > 10000) { + my_data->stopped = true; + return 1; + } + + if (!seq.buffer) + trace_seq_init(&seq); + + tep_print_event(event->tep, &seq, record, "%16s-%-5d [%03d] %6.1000d %s: %s\n", + TEP_PRINT_COMM, TEP_PRINT_PID, TEP_PRINT_CPU, + TEP_PRINT_TIME, TEP_PRINT_NAME, TEP_PRINT_INFO); + trace_seq_terminate(&seq); + trace_seq_do_printf(&seq); + trace_seq_reset(&seq); return 0; } -... -struct tep_handle *tep = tracefs_local_events(NULL); - if (!tep) { - /* Failed to initialise tep handler with local events */ - ... +static int sched_callback(struct tep_event *event, struct tep_record *record, + int cpu, void *data) +{ + static struct tep_format_field *prev_pid; + static struct tep_format_field *next_pid; + unsigned long long pid; + int this_pid = *(int *)data; + + if (!prev_pid) { + prev_pid = tep_find_field(event, "prev_pid"); + next_pid = tep_find_field(event, "next_pid"); + if (!prev_pid || !next_pid) { + fprintf(stderr, "No pid fields??\n"); + return -1; + } } - errno = 0; - ret = tracefs_event_enable(NULL, "sched", NULL); - if (ret < 0 && !errno) - printf("Could not find 'sched' events\n"); - tracefs_event_enable(NULL, "irq", "irq_handler_\(enter\|exit\)"); + tep_read_number_field(prev_pid, record->data, &pid); + if (pid == this_pid) + printf("WE ARE LEAVING!\n"); + tep_read_number_field(next_pid, record->data, &pid); + if (pid == this_pid) + printf("WE ARE ARRIVING!\n"); + return 0; +} - if (tracefs_iterate_raw_events(tep, NULL, NULL, 0, records_walk, NULL) < 0) { - /* Error walking through the recorded raw events */ - } +int main (int argc, char **argv, char **env) +{ + struct tep_handle *tep; + struct tracefs_instance *instance; + struct my_struct my_data = { .stopped = false }; + int this_pid = getpid(); + + instance = tracefs_instance_create("my-buffer"); + if (!instance) + return -1; + + tracefs_event_enable(instance, "sched", NULL); + sleep(1); + tracefs_event_disable(instance, NULL, NULL); + tep = tracefs_local_events(NULL); + tep_load_plugins(tep); + tracefs_follow_event(tep, instance, "sched", "sched_switch", sched_callback, &this_pid); + tracefs_iterate_raw_events(tep, instance, NULL, 0, callback, &my_data); + tracefs_instance_destroy(instance); + + if (my_data.stopped) + printf("stopped!\n"); - /* Disable all events */ - tracefs_event_disable(NULL, NULL, NULL); - tep_free(tep); + return 0; +} -- FILES ----- diff --git a/include/tracefs-local.h b/include/tracefs-local.h index 1286cbf800a2..4c636be8a1fe 100644 --- a/include/tracefs-local.h +++ b/include/tracefs-local.h @@ -23,9 +23,18 @@ struct tracefs_options_mask { unsigned long long mask; }; +struct follow_event { + struct tep_event *event; + void *callback_data; + int (*callback)(struct tep_event *, + struct tep_record *, + int, void *); +}; + struct tracefs_instance { struct tracefs_options_mask supported_opts; struct tracefs_options_mask enabled_opts; + struct follow_event *followers; char *trace_dir; char *name; pthread_mutex_t lock; @@ -35,6 +44,7 @@ struct tracefs_instance { int ftrace_notrace_fd; int ftrace_marker_fd; int ftrace_marker_raw_fd; + int nr_followers; bool pipe_keep_going; bool iterate_keep_going; }; diff --git a/include/tracefs.h b/include/tracefs.h index 10f84a25c31e..cb64e098883a 100644 --- a/include/tracefs.h +++ b/include/tracefs.h @@ -126,6 +126,12 @@ int tracefs_iterate_raw_events(struct tep_handle *tep, int, void *), void *callback_context); void tracefs_iterate_stop(struct tracefs_instance *instance); +int tracefs_follow_event(struct tep_handle *tep, struct tracefs_instance *instance, + const char *system, const char *event_name, + int (*callback)(struct tep_event *, + struct tep_record *, + int, void *), + void *callback_data); char *tracefs_event_get_file(struct tracefs_instance *instance, const char *system, const char *event, diff --git a/src/tracefs-events.c b/src/tracefs-events.c index 592b1a01fea4..abd97da8abbd 100644 --- a/src/tracefs-events.c +++ b/src/tracefs-events.c @@ -20,6 +20,9 @@ #include "tracefs.h" #include "tracefs-local.h" +static struct follow_event *root_followers; +static int nr_root_followers; + struct cpu_iterate { struct tracefs_cpu *tcpu; struct tep_record record; @@ -117,7 +120,36 @@ int read_next_record(struct tep_handle *tep, struct cpu_iterate *cpu) return -1; } -static int read_cpu_pages(struct tep_handle *tep, struct cpu_iterate *cpus, int count, +static int call_followers(struct tracefs_instance *instance, + struct tep_event *event, struct tep_record *record, int cpu) +{ + struct follow_event *followers; + int nr_followers; + int ret = 0; + int i; + + if (instance) { + followers = instance->followers; + nr_followers = instance->nr_followers; + } else { + followers = root_followers; + nr_followers = nr_root_followers; + } + + if (!followers) + return 0; + + for (i = 0; i < nr_followers; i++) { + if (followers[i].event == event) + ret |= followers[i].callback(event, record, + cpu, followers[i].callback_data); + } + + return ret; +} + +static int read_cpu_pages(struct tep_handle *tep, struct tracefs_instance *instance, + struct cpu_iterate *cpus, int count, int (*callback)(struct tep_event *, struct tep_record *, int, void *), @@ -143,6 +175,8 @@ static int read_cpu_pages(struct tep_handle *tep, struct cpu_iterate *cpus, int j = i; } if (j < count) { + if (call_followers(instance, cpus[j].event, &cpus[j].record, cpus[j].cpu)) + break; if (callback(cpus[j].event, &cpus[j].record, cpus[j].cpu, callback_context)) break; cpus[j].event = NULL; @@ -205,6 +239,69 @@ static int open_cpu_files(struct tracefs_instance *instance, cpu_set_t *cpus, return -1; } +/** + * tracefs_follow_event - Add callback for specific events for iterators + * @tep: a handle to the trace event parser context + * @instance: The instance to follow + * @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 an @instance or the root instance if @instance + * is NULL, where if tracefs_iterate_raw_events() is called, that if the specified + * event is hit, it will call @callback, with the following parameters: + * @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. + * + * Returns 0 on success and -1 on error. + */ +int tracefs_follow_event(struct tep_handle *tep, struct tracefs_instance *instance, + const char *system, const char *event_name, + int (*callback)(struct tep_event *, + struct tep_record *, + int, void *), + void *callback_data) +{ + struct follow_event **followers; + struct follow_event *follower; + struct follow_event follow; + int *nr_followers; + + 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; + + if (instance) { + followers = &instance->followers; + nr_followers = &instance->nr_followers; + } else { + followers = &root_followers; + nr_followers = &nr_root_followers; + } + follower = realloc(*followers, sizeof(*follower) * + ((*nr_followers) + 1)); + if (!follower) + return -1; + + *followers = follower; + follower[(*nr_followers)++] = follow; + + return 0; +} + static bool top_iterate_keep_going; /* @@ -247,7 +344,7 @@ int tracefs_iterate_raw_events(struct tep_handle *tep, ret = open_cpu_files(instance, cpus, cpu_size, &all_cpus, &count); if (ret < 0) goto out; - ret = read_cpu_pages(tep, all_cpus, count, + ret = read_cpu_pages(tep, instance, all_cpus, count, callback, callback_context, keep_going); From patchwork Tue Nov 15 03:47:12 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13043217 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 53CABC433FE for ; Tue, 15 Nov 2022 03:46:34 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230041AbiKODqd (ORCPT ); Mon, 14 Nov 2022 22:46:33 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:39662 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230415AbiKODqc (ORCPT ); Mon, 14 Nov 2022 22:46:32 -0500 Received: from dfw.source.kernel.org (dfw.source.kernel.org [IPv6:2604:1380:4641:c500::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C2E1F19006 for ; Mon, 14 Nov 2022 19:46:30 -0800 (PST) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 5BFC961504 for ; Tue, 15 Nov 2022 03:46:30 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id A8A1FC433D7; Tue, 15 Nov 2022 03:46:29 +0000 (UTC) Received: from rostedt by gandalf.local.home with local (Exim 4.96) (envelope-from ) id 1oumum-00AGi0-1j; Mon, 14 Nov 2022 22:47:12 -0500 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: "Steven Rostedt (Google)" Subject: [PATCH 4/4] libtracefs: Add tracefs_follow_missed_events() API Date: Mon, 14 Nov 2022 22:47:12 -0500 Message-Id: <20221115034712.2447458-5-rostedt@goodmis.org> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20221115034712.2447458-1-rostedt@goodmis.org> References: <20221115034712.2447458-1-rostedt@goodmis.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org From: "Steven Rostedt (Google)" Add the function tracefs_follow_missed_events() to allow applications to have callback when events are dropped due to overrun of the ring buffer. Signed-off-by: Steven Rostedt (Google) --- Documentation/libtracefs-iterator.txt | 53 +++++++++++++--- include/tracefs-local.h | 2 + include/tracefs.h | 5 ++ src/tracefs-events.c | 89 +++++++++++++++++++++++++++ 4 files changed, 141 insertions(+), 8 deletions(-) diff --git a/Documentation/libtracefs-iterator.txt b/Documentation/libtracefs-iterator.txt index 95ff1894d074..0483cdb748c2 100644 --- a/Documentation/libtracefs-iterator.txt +++ b/Documentation/libtracefs-iterator.txt @@ -22,7 +22,11 @@ int *tracefs_follow_event*(struct tep_handle pass:[*]_tep_, struct tracefs_insta struct tep_record pass:[*], int, void pass:[*]), void pass:[*]_callback_data_): - +int *tracefs_follow_missed_event*(struct tracefs_instance pass:[*]_instance_, + int (pass:[*]_callback_)(struct tep_event pass:[*], + struct tep_record pass:[*], + int, void pass:[*]), + void pass:[*]_callback_data_): -- DESCRIPTION @@ -57,6 +61,12 @@ will be passed to the _callback_ as well. Note, if it returns something other than 0, it will stop the loop before the _callback_ of *tracefs_iterate_raw_events()* is called. +The *tracefs_follow_missed_events()* will call the _callback_ when missed +events are detected. It will set the _record_ parameter of the callback to the +record that came after the missed events and _event_ will be of the type of +event _record_ is. _cpu_ will be set to the CPU that missed the events, and +_callback_data_ will be the content that was passed in to the function. + RETURN VALUE ------------ The *tracefs_iterate_raw_events()* function returns -1 in case of an error or @@ -69,19 +79,22 @@ EXAMPLE #include #include #include +#include struct my_struct { bool stopped; }; +#define MAX_COUNT 500000 +static int counter; + static int callback(struct tep_event *event, struct tep_record *record, int cpu, void *data) { struct my_struct *my_data = data; static struct trace_seq seq; - static int counter; - if (counter++ > 10000) { + if (counter++ > MAX_COUNT) { my_data->stopped = true; return 1; } @@ -124,28 +137,52 @@ static int sched_callback(struct tep_event *event, struct tep_record *record, return 0; } +static int missed_callback(struct tep_event *event, struct tep_record *record, + int cpu, void *data) +{ + printf("OOPS! cpu %d dropped ", cpu); + if (record->missed_events > 0) + printf("%lld ", record->missed_events); + printf("events\n"); + return 0; +} + +static struct tracefs_instance *instance; +static struct my_struct my_data; + +static void sig(int s) +{ + tracefs_iterate_stop(instance); + my_data.stopped = true; +} + int main (int argc, char **argv, char **env) { struct tep_handle *tep; - struct tracefs_instance *instance; - struct my_struct my_data = { .stopped = false }; int this_pid = getpid(); instance = tracefs_instance_create("my-buffer"); if (!instance) return -1; - tracefs_event_enable(instance, "sched", NULL); + signal(SIGINT, sig); + + tracefs_event_enable(instance, NULL, NULL); sleep(1); tracefs_event_disable(instance, NULL, NULL); tep = tracefs_local_events(NULL); tep_load_plugins(tep); + tracefs_follow_missed_events(instance, missed_callback, NULL); tracefs_follow_event(tep, instance, "sched", "sched_switch", sched_callback, &this_pid); tracefs_iterate_raw_events(tep, instance, NULL, 0, callback, &my_data); tracefs_instance_destroy(instance); - if (my_data.stopped) - printf("stopped!\n"); + if (my_data.stopped) { + if (counter > MAX_COUNT) + printf("Finished max count\n"); + else + printf("Finished via signal\n"); + } return 0; } diff --git a/include/tracefs-local.h b/include/tracefs-local.h index 4c636be8a1fe..2007d26361cb 100644 --- a/include/tracefs-local.h +++ b/include/tracefs-local.h @@ -35,6 +35,7 @@ struct tracefs_instance { struct tracefs_options_mask supported_opts; struct tracefs_options_mask enabled_opts; struct follow_event *followers; + struct follow_event *missed_followers; char *trace_dir; char *name; pthread_mutex_t lock; @@ -45,6 +46,7 @@ struct tracefs_instance { int ftrace_marker_fd; int ftrace_marker_raw_fd; int nr_followers; + int nr_missed_followers; bool pipe_keep_going; bool iterate_keep_going; }; diff --git a/include/tracefs.h b/include/tracefs.h index cb64e098883a..3547b5a348aa 100644 --- a/include/tracefs.h +++ b/include/tracefs.h @@ -132,6 +132,11 @@ int tracefs_follow_event(struct tep_handle *tep, struct tracefs_instance *instan struct tep_record *, int, void *), void *callback_data); +int tracefs_follow_missed_events(struct tracefs_instance *instance, + int (*callback)(struct tep_event *, + struct tep_record *, + int, void *), + void *callback_data); char *tracefs_event_get_file(struct tracefs_instance *instance, const char *system, const char *event, diff --git a/src/tracefs-events.c b/src/tracefs-events.c index abd97da8abbd..e92663656688 100644 --- a/src/tracefs-events.c +++ b/src/tracefs-events.c @@ -23,6 +23,9 @@ static struct follow_event *root_followers; static int nr_root_followers; +static struct follow_event *root_missed_followers; +static int nr_root_missed_followers; + struct cpu_iterate { struct tracefs_cpu *tcpu; struct tep_record record; @@ -120,6 +123,87 @@ int read_next_record(struct tep_handle *tep, struct cpu_iterate *cpu) return -1; } +/** + * tracefs_follow_missed_events - Add callback for missed events for iterators + * @instance: The instance to follow + * @callback: The function to call when missed events is detected + * @callback_data: The data to pass to @callback + * + * This attaches a callback to an @instance or the root instance if @instance + * is NULL, where if tracefs_iterate_raw_events() is called, that if missed + * events are detected, it will call @callback, with the following parameters: + * @event: The event pointer of the record with the missing events + * @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. + * + * If the count of missing events is available, @record->missed_events + * will have a positive number holding the number of missed events since + * the last event on the same CPU, or just -1 if that number is unknown + * but missed events did happen. + * + * Returns 0 on success and -1 on error. + */ +int tracefs_follow_missed_events(struct tracefs_instance *instance, + int (*callback)(struct tep_event *, + struct tep_record *, + int, void *), + void *callback_data) +{ + struct follow_event **followers; + struct follow_event *follower; + struct follow_event follow; + int *nr_followers; + + follow.event = NULL; + follow.callback = callback; + follow.callback_data = callback_data; + + if (instance) { + followers = &instance->missed_followers; + nr_followers = &instance->nr_missed_followers; + } else { + followers = &root_missed_followers; + nr_followers = &nr_root_missed_followers; + } + follower = realloc(*followers, sizeof(*follower) * + ((*nr_followers) + 1)); + if (!follower) + return -1; + + *followers = follower; + follower[(*nr_followers)++] = follow; + + return 0; +} + +static int call_missed_events(struct tracefs_instance *instance, + struct tep_event *event, struct tep_record *record, int cpu) +{ + struct follow_event *followers; + int nr_followers; + int ret = 0; + int i; + + if (instance) { + followers = instance->missed_followers; + nr_followers = instance->nr_missed_followers; + } else { + followers = root_missed_followers; + nr_followers = nr_root_missed_followers; + } + + if (!followers) + return 0; + + for (i = 0; i < nr_followers; i++) { + ret |= followers[i].callback(event, record, + cpu, followers[i].callback_data); + } + + return ret; +} + static int call_followers(struct tracefs_instance *instance, struct tep_event *event, struct tep_record *record, int cpu) { @@ -128,6 +212,11 @@ static int call_followers(struct tracefs_instance *instance, int ret = 0; int i; + if (record->missed_events) + ret = call_missed_events(instance, event, record, cpu); + if (ret) + return ret; + if (instance) { followers = instance->followers; nr_followers = instance->nr_followers;