From patchwork Thu Dec 28 20:35:23 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13506188 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 59D6910787 for ; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id D459DC433C9; Thu, 28 Dec 2023 20:36:25 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.97) (envelope-from ) id 1rIx7z-00000000Dsa-2JUd; Thu, 28 Dec 2023 15:37:15 -0500 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: "Steven Rostedt (Google)" Subject: [PATCH 01/23] libtracefs Documentation: Fix tracefs_event_file_exists() issues Date: Thu, 28 Dec 2023 15:35:23 -0500 Message-ID: <20231228203714.53294-2-rostedt@goodmis.org> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231228203714.53294-1-rostedt@goodmis.org> References: <20231228203714.53294-1-rostedt@goodmis.org> Precedence: bulk X-Mailing-List: linux-trace-devel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: "Steven Rostedt (Google)" In the man pages, tracefs_event_file_exists() is missing a semicolon and in the main man page, it's missing it's last parameter too. Signed-off-by: Steven Rostedt (Google) --- Documentation/libtracefs-events-file.txt | 3 +-- Documentation/libtracefs.txt | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/libtracefs-events-file.txt b/Documentation/libtracefs-events-file.txt index 425eebd82e8d..1a298b3bbdaa 100644 --- a/Documentation/libtracefs-events-file.txt +++ b/Documentation/libtracefs-events-file.txt @@ -23,8 +23,7 @@ int *tracefs_event_file_append*(struct tracefs_instance pass:[*]_instance_, cons int *tracefs_event_file_clear*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_system_, const char pass:[*]_event_, const char pass:[*]_file_); bool *tracefs_event_file_exists*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_system_, const char pass:[*]_event_, - const char pass:[*]_file_) - + const char pass:[*]_file_); -- DESCRIPTION diff --git a/Documentation/libtracefs.txt b/Documentation/libtracefs.txt index 787636a32965..850118302ab2 100644 --- a/Documentation/libtracefs.txt +++ b/Documentation/libtracefs.txt @@ -87,6 +87,7 @@ Trace events: int *tracefs_event_file_clear*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_system_, const char pass:[*]_event_, const char pass:[*]_file_); bool *tracefs_event_file_exists*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_system_, const char pass:[*]_event_, + const char pass:[*]_file_); Event filters: int *tracefs_filter_string_append*(struct tep_event pass:[*]_event_, char pass:[**]_filter_, From patchwork Thu Dec 28 20:35:24 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13506190 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 77ACF101F8 for ; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id D6BE6C433CB; Thu, 28 Dec 2023 20:36:25 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.97) (envelope-from ) id 1rIx7z-00000000Dsd-2QlL; Thu, 28 Dec 2023 15:37:15 -0500 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: "Steven Rostedt (Google)" Subject: [PATCH 02/23] libtracefs testing: Use one tep handle for most tests Date: Thu, 28 Dec 2023 15:35:24 -0500 Message-ID: <20231228203714.53294-3-rostedt@goodmis.org> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231228203714.53294-1-rostedt@goodmis.org> References: <20231228203714.53294-1-rostedt@goodmis.org> Precedence: bulk X-Mailing-List: linux-trace-devel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: "Steven Rostedt (Google)" Creating a tep handle with all events takes a bit of time. Instead of recreating one for every test, have the main test_tep handle load all events and use that for all tests that do not need to add special events (like the dynamic event tests). Signed-off-by: Steven Rostedt (Google) --- utest/tracefs-utest.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/utest/tracefs-utest.c b/utest/tracefs-utest.c index 398c94368e66..5ce4bafbfda5 100644 --- a/utest/tracefs-utest.c +++ b/utest/tracefs-utest.c @@ -366,8 +366,7 @@ static void test_instance_trace_sql(struct tracefs_instance *instance) struct tep_event *event; int ret; - tep = tracefs_local_events(NULL); - CU_TEST(tep != NULL); + tep = test_tep; trace_seq_init(&seq); @@ -409,7 +408,6 @@ static void test_instance_trace_sql(struct tracefs_instance *instance) trace_seq_reset(&seq); } - tep_free(tep); trace_seq_destroy(&seq); } @@ -436,7 +434,6 @@ struct test_cpu_data { static void cleanup_trace_cpu(struct test_cpu_data *data) { close(data->fd); - tep_free(data->tep); tracefs_cpu_close(data->tcpu); free(data->buf); kbuffer_free(data->kbuf); @@ -467,10 +464,7 @@ static int setup_trace_cpu(struct tracefs_instance *instance, struct test_cpu_da if (data->fd < 0) return -1; - data->tep = tracefs_local_events(NULL); - CU_TEST(data->tep != NULL); - if (!data->tep) - goto fail; + data->tep = test_tep; data->tcpu = tracefs_cpu_open(instance, 0, true); CU_TEST(data->tcpu != NULL); @@ -685,10 +679,7 @@ static void test_instance_follow_events(struct tracefs_instance *instance) memset(&fdata, 0, sizeof(fdata)); - tep = tracefs_local_events(NULL); - CU_TEST(tep != NULL); - if (!tep) - return; + tep = test_tep; fdata.sched_switch = tep_find_event_by_name(tep, "sched", "sched_switch"); CU_TEST(fdata.sched_switch != NULL); @@ -2529,9 +2520,7 @@ static int test_suite_destroy(void) static int test_suite_init(void) { - const char *systems[] = {"ftrace", NULL}; - - test_tep = tracefs_local_events_system(NULL, systems); + test_tep = tracefs_local_events(NULL); if (test_tep == NULL) return 1; test_instance = tracefs_instance_create(TEST_INSTANCE_NAME); From patchwork Thu Dec 28 20:35:25 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13506189 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 59D4010780 for ; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id D1BECC433C7; Thu, 28 Dec 2023 20:36:25 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.97) (envelope-from ) id 1rIx7z-00000000Dsg-2WyL; Thu, 28 Dec 2023 15:37:15 -0500 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: "Steven Rostedt (Google)" Subject: [PATCH 03/23] libtracefs: Free "missed_followers" of instance Date: Thu, 28 Dec 2023 15:35:25 -0500 Message-ID: <20231228203714.53294-4-rostedt@goodmis.org> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231228203714.53294-1-rostedt@goodmis.org> References: <20231228203714.53294-1-rostedt@goodmis.org> Precedence: bulk X-Mailing-List: linux-trace-devel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: "Steven Rostedt (Google)" The missed_followers array was allocated but not freed in the clean up of the instance. Fixes: 44501430 ("libtracefs: Add tracefs_follow_missed_events() API") Signed-off-by: Steven Rostedt (Google) --- src/tracefs-instance.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tracefs-instance.c b/src/tracefs-instance.c index be1478ee7a92..c385a078ecec 100644 --- a/src/tracefs-instance.c +++ b/src/tracefs-instance.c @@ -124,6 +124,7 @@ __hidden void trace_put_instance(struct tracefs_instance *instance) free(instance->trace_dir); free(instance->followers); + free(instance->missed_followers); free(instance->name); pthread_mutex_destroy(&instance->lock); free(instance); From patchwork Thu Dec 28 20:35:26 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13506192 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 943C71079C for ; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id D91F5C433CC; Thu, 28 Dec 2023 20:36:25 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.97) (envelope-from ) id 1rIx7z-00000000Dsj-2d8f; Thu, 28 Dec 2023 15:37:15 -0500 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: "Steven Rostedt (Google)" Subject: [PATCH 04/23] libtracefs: Free buf in clear_func_filter() Date: Thu, 28 Dec 2023 15:35:26 -0500 Message-ID: <20231228203714.53294-5-rostedt@goodmis.org> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231228203714.53294-1-rostedt@goodmis.org> References: <20231228203714.53294-1-rostedt@goodmis.org> Precedence: bulk X-Mailing-List: linux-trace-devel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: "Steven Rostedt (Google)" The buf variable was allocated via tracefs_instance_file_read() but must be freed with free(). Fixes: 789e82d7 ("libtracefs: New API to reset ftrace instance") Signed-off-by: Steven Rostedt (Google) --- src/tracefs-instance.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tracefs-instance.c b/src/tracefs-instance.c index c385a078ecec..2efcc75dfd98 100644 --- a/src/tracefs-instance.c +++ b/src/tracefs-instance.c @@ -1371,6 +1371,7 @@ static void clear_func_filter(struct tracefs_instance *instance, const char *fil filter[len+1] = '\0'; tracefs_instance_file_append(instance, file, filter); } + free(buf); } static void clear_func_filters(struct tracefs_instance *instance) From patchwork Thu Dec 28 20:35:27 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13506193 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 942E9101F8 for ; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id DE59BC433CD; Thu, 28 Dec 2023 20:36:25 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.97) (envelope-from ) id 1rIx7z-00000000Dsq-2jdB; Thu, 28 Dec 2023 15:37:15 -0500 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: "Steven Rostedt (Google)" Subject: [PATCH 05/23] libtracefs: Free tracing_dir in case of remount Date: Thu, 28 Dec 2023 15:35:27 -0500 Message-ID: <20231228203714.53294-6-rostedt@goodmis.org> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231228203714.53294-1-rostedt@goodmis.org> References: <20231228203714.53294-1-rostedt@goodmis.org> Precedence: bulk X-Mailing-List: linux-trace-devel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: "Steven Rostedt (Google)" On remount, the tracing directory may move, and the tracing_dir needs to be updated. If tracing_dir is the same it is simply returned, but if it is not, it has to be calculated again. This was fixed but if it was changed, the old tracing_dir was never freed. Always free the tracing_dir before calculating a new one. This also requires changing the type by removing the "const" attribute. Fixes: 92c9292a ("libtracefs: Have tracefs_{tracing,debug}_dir() make sure it's still mounted") Signed-off-by: Steven Rostedt (Google) --- src/tracefs-utils.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tracefs-utils.c b/src/tracefs-utils.c index ef90677cf617..e67b698f5b99 100644 --- a/src/tracefs-utils.c +++ b/src/tracefs-utils.c @@ -248,7 +248,7 @@ static int test_dir(const char *dir, const char *file) */ const char *tracefs_tracing_dir(void) { - static const char *tracing_dir; + static char *tracing_dir; /* Do not check custom_tracing_dir */ if (custom_tracing_dir) @@ -257,6 +257,7 @@ const char *tracefs_tracing_dir(void) if (tracing_dir && test_dir(tracing_dir, "trace")) return tracing_dir; + free(tracing_dir); tracing_dir = find_tracing_dir(false, true); return tracing_dir; } From patchwork Thu Dec 28 20:35:28 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13506195 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9432F10794 for ; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id E37F4C433CA; Thu, 28 Dec 2023 20:36:25 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.97) (envelope-from ) id 1rIx7z-00000000Dsu-2pv1; Thu, 28 Dec 2023 15:37:15 -0500 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: "Steven Rostedt (Google)" Subject: [PATCH 06/23] libtracefs: Free dynamic event list in utest Date: Thu, 28 Dec 2023 15:35:28 -0500 Message-ID: <20231228203714.53294-7-rostedt@goodmis.org> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231228203714.53294-1-rostedt@goodmis.org> References: <20231228203714.53294-1-rostedt@goodmis.org> Precedence: bulk X-Mailing-List: linux-trace-devel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: "Steven Rostedt (Google)" When adding the tests for the tracefs_kprobe_destroy() API, the call to get_dynevents_check() did not save the results and free it. This check can only be ignored if the expected result is zero (passed in as the second parameter), as in that case, the return would be NULL. Fixes: 18ede68f ("libtracefs: Add tracefs_kprobe_destory() API") Signed-off-by: Steven Rostedt (Google) --- utest/tracefs-utest.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/utest/tracefs-utest.c b/utest/tracefs-utest.c index 5ce4bafbfda5..28960c191581 100644 --- a/utest/tracefs-utest.c +++ b/utest/tracefs-utest.c @@ -1364,7 +1364,9 @@ static void test_kprobes_instance(struct tracefs_instance *instance) ret = tracefs_kprobe_destroy(ktests[i].system, ktests[i].event, ktests[i].address, ktests[i].format, true); CU_TEST(ret == 0); - get_dynevents_check(TRACEFS_DYNEVENT_KPROBE, kprobe_count - (i + 1)); + devents = get_dynevents_check(TRACEFS_DYNEVENT_KPROBE, + kprobe_count - (i + 1)); + tracefs_dynevent_list_free(devents); } get_dynevents_check(TRACEFS_DYNEVENT_KPROBE, 0); @@ -1372,7 +1374,9 @@ static void test_kprobes_instance(struct tracefs_instance *instance) ret = tracefs_kprobe_destroy(kretests[i].system, kretests[i].event, kretests[i].address, kretests[i].format, true); CU_TEST(ret == 0); - get_dynevents_check(TRACEFS_DYNEVENT_KRETPROBE, kretprobe_count - (i + 1)); + devents = get_dynevents_check(TRACEFS_DYNEVENT_KRETPROBE, + kretprobe_count - (i + 1)); + tracefs_dynevent_list_free(devents); } get_dynevents_check(TRACEFS_DYNEVENT_KRETPROBE, 0); From patchwork Thu Dec 28 20:35:29 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13506196 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9436710795 for ; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 026CFC43391; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.97) (envelope-from ) id 1rIx7z-00000000Dsy-2we4; Thu, 28 Dec 2023 15:37:15 -0500 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: "Steven Rostedt (Google)" Subject: [PATCH 07/23] libtracefs: Reset tracing before and after unit tests Date: Thu, 28 Dec 2023 15:35:29 -0500 Message-ID: <20231228203714.53294-8-rostedt@goodmis.org> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231228203714.53294-1-rostedt@goodmis.org> References: <20231228203714.53294-1-rostedt@goodmis.org> Precedence: bulk X-Mailing-List: linux-trace-devel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: "Steven Rostedt (Google)" After having tests constantly fail because of some left over tracing that was still enabled before the test started, call tracefs_instance_reset() before and after the unit tests to make sure it starts with a clean slate and does not fail due to residual tracing that was from an external event. Signed-off-by: Steven Rostedt (Google) --- utest/tracefs-utest.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/utest/tracefs-utest.c b/utest/tracefs-utest.c index 28960c191581..183f37cd133a 100644 --- a/utest/tracefs-utest.c +++ b/utest/tracefs-utest.c @@ -2516,6 +2516,7 @@ static void test_custom_trace_dir(void) static int test_suite_destroy(void) { + tracefs_instance_reset(NULL); tracefs_instance_destroy(test_instance); tracefs_instance_free(test_instance); tep_free(test_tep); @@ -2531,6 +2532,9 @@ static int test_suite_init(void) if (!test_instance) return 1; + /* Start with a new slate */ + tracefs_instance_reset(NULL); + return 0; } From patchwork Thu Dec 28 20:35:30 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13506204 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A5C23107BF for ; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 05A42C43395; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.97) (envelope-from ) id 1rIx7z-00000000Dt2-33Fj; Thu, 28 Dec 2023 15:37:15 -0500 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: "Steven Rostedt (Google)" Subject: [PATCH 08/23] libtracefs: Add API to remove followers from an instance or toplevel Date: Thu, 28 Dec 2023 15:35:30 -0500 Message-ID: <20231228203714.53294-9-rostedt@goodmis.org> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231228203714.53294-1-rostedt@goodmis.org> References: <20231228203714.53294-1-rostedt@goodmis.org> Precedence: bulk X-Mailing-List: linux-trace-devel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: "Steven Rostedt (Google)" Add the APIs: tracefs_follow_event_clear() tracefs_follow_missed_events_clear() When adding some more tracefs unit tests, the follower callbacks were being called unexpectedly and causing failures and crashes, as the data used by the callbacks was already freed by the time it hit the other test. Add a way to remove the followers and do so in the unit tests. Signed-off-by: Steven Rostedt (Google) --- Documentation/libtracefs-iterator.txt | 30 +++- include/tracefs.h | 4 + src/tracefs-events.c | 103 ++++++++++++ utest/tracefs-utest.c | 230 ++++++++++++++++++++++++++ 4 files changed, 366 insertions(+), 1 deletion(-) diff --git a/Documentation/libtracefs-iterator.txt b/Documentation/libtracefs-iterator.txt index b971bd0e10c5..c2b2be3f4c5c 100644 --- a/Documentation/libtracefs-iterator.txt +++ b/Documentation/libtracefs-iterator.txt @@ -3,7 +3,8 @@ libtracefs(3) NAME ---- -tracefs_iterate_raw_events, tracefs_iterate_stop, tracefs_follow_event, tracefs_follow_missed_events - Iterate over events in the ring buffer +tracefs_iterate_raw_events, tracefs_iterate_stop, tracefs_follow_event, tracefs_follow_missed_events, +tracefs_follow_event_clear, tracefs_follow_missed_events_clear - Iterate over events in the ring buffer SYNOPSIS -------- @@ -28,6 +29,10 @@ int *tracefs_follow_missed_events*(struct tracefs_instance pass:[*]_instance_, struct tep_record pass:[*], int, void pass:[*]), void pass:[*]_callback_data_); + +int *tracefs_follow_event_clear*(struct tracefs_instance pass:[*]_instance_, + const char pass:[*]_system_, const char pass:[*]_event_name_); +int *tracefs_follow_missed_events_clear*(struct tracefs_instance pass:[*]_instance_); -- DESCRIPTION @@ -68,11 +73,24 @@ 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. +The *tracefs_follow_event_clear()* will remove followers from _instance_ that +match _system_ and _event_name_. If _system_ and _event_name_ are both NULL, +then it will remove all event followers associated to _instance_. If just _system_ +is NULL, then it will remove all followers that follow events that match _event_name_. If just _event_name_ +is NULL, then it will remove all followers that are attached to events that are +apart of a system that matches _system_. + +The *tracefs_follow_missed_events_clear()* will remove all followers for missed +events. + RETURN VALUE ------------ The *tracefs_iterate_raw_events()* function returns -1 in case of an error or 0 otherwise. +Both *tracefs_follow_event_clear()* and *tracefs_follow_missed_events_clear()* return +0 on success and -1 on error, or if it found no followers that match and should be removed. + EXAMPLE ------- [source,c] @@ -176,7 +194,17 @@ int main (int argc, char **argv, char **env) 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); + + /* Note, the clear here is to show how to clear all followers + * in case tracefs_iterate_raw_events() is called again, but + * does not want to include the followers. It's not needed + * here because tracefs_instance_free() will clean them up. + */ + tracefs_follow_event_clear(instance, NULL, NULL); + tracefs_follow_missed_events_clear(instance); + tracefs_instance_destroy(instance); + tracefs_instance_free(instance); if (my_data.stopped) { if (counter > MAX_COUNT) diff --git a/include/tracefs.h b/include/tracefs.h index 90df00f531ea..25429a30c028 100644 --- a/include/tracefs.h +++ b/include/tracefs.h @@ -138,6 +138,10 @@ int tracefs_follow_missed_events(struct tracefs_instance *instance, struct tep_record *, int, void *), void *callback_data); +int tracefs_follow_event_clear(struct tracefs_instance *instance, + const char *system, const char *event_name); +int tracefs_follow_missed_events_clear(struct tracefs_instance *instance); + 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 c6b1d7637e72..2e87f9aa3af7 100644 --- a/src/tracefs-events.c +++ b/src/tracefs-events.c @@ -392,6 +392,109 @@ int tracefs_follow_event(struct tep_handle *tep, struct tracefs_instance *instan return 0; } +/** + * tracefs_follow_event_clear - Remove callbacks for specific events for iterators + * @instance: The instance to follow + * @system: The system of the event to remove (NULL for all) + * @event_name: The name of the event to remove (NULL for all) + * + * This removes all callbacks from an instance that matches a specific + * event. If @event_name is NULL, then it removes all followers that match + * @system. If @system is NULL, then it removes all followers that match + * @event_name. If both @system and @event_name are NULL then it removes all + * followers for all events. + * + * Returns 0 on success and -1 on error (which includes no followers found) + */ +int tracefs_follow_event_clear(struct tracefs_instance *instance, + const char *system, const char *event_name) +{ + struct follow_event **followers; + struct follow_event *follower; + int *nr_followers; + int nr; + int i, n; + + if (instance) { + followers = &instance->followers; + nr_followers = &instance->nr_followers; + } else { + followers = &root_followers; + nr_followers = &nr_root_followers; + } + + if (!*nr_followers) + return -1; + + /* If both system and event_name are NULL just remove all */ + if (!system && !event_name) { + free(*followers); + *followers = NULL; + *nr_followers = 0; + return 0; + } + + nr = *nr_followers; + follower = *followers; + + for (i = 0, n = 0; i < nr; i++) { + if (event_name && strcmp(event_name, follower[n].event->name) != 0) { + n++; + continue; + } + if (system && strcmp(system, follower[n].event->system) != 0) { + n++; + continue; + } + /* If there are no more after this, continue to increment i */ + if (i == nr - 1) + continue; + /* Remove this follower */ + memmove(&follower[n], &follower[n + 1], + sizeof(*follower) * (nr - (n + 1))); + } + + /* Did we find anything? */ + if (n == i) + return -1; + + /* NULL out the rest */ + memset(&follower[n], 0, (sizeof(*follower)) * (nr - n)); + *nr_followers = n; + + return 0; +} + +/** + * tracefs_follow_missed_events_clear - Remove callbacks for missed events + * @instance: The instance to remove missed callback followers + * + * This removes all callbacks from an instance that are for missed events. + * + * Returns 0 on success and -1 on error (which includes no followers found) + */ +int tracefs_follow_missed_events_clear(struct tracefs_instance *instance) +{ + struct follow_event **followers; + int *nr_followers; + + if (instance) { + followers = &instance->missed_followers; + nr_followers = &instance->nr_missed_followers; + } else { + followers = &root_missed_followers; + nr_followers = &nr_root_missed_followers; + } + + if (!*nr_followers) + return -1; + + free(*followers); + *followers = NULL; + *nr_followers = 0; + return 0; +} + static bool top_iterate_keep_going; /* diff --git a/utest/tracefs-utest.c b/utest/tracefs-utest.c index 183f37cd133a..492e5c05551f 100644 --- a/utest/tracefs-utest.c +++ b/utest/tracefs-utest.c @@ -63,6 +63,16 @@ static struct test_sample test_array[TEST_ARRAY_SIZE]; static int test_found; static unsigned long long last_ts; +static void msleep(int ms) +{ + struct timespec tspec; + + /* Sleep for 1ms */ + tspec.tv_sec = 0; + tspec.tv_nsec = 1000000 * ms; + nanosleep(&tspec, NULL); +} + static int test_callback(struct tep_event *event, struct tep_record *record, int cpu, void *context) { @@ -608,10 +618,23 @@ static void test_trace_cpu_read(void) struct follow_data { struct tep_event *sched_switch; struct tep_event *sched_waking; + struct tep_event *getppid; struct tep_event *function; int missed; + int switch_hit; + int waking_hit; + int getppid_hit; + int missed_hit; }; +static void clear_hits(struct follow_data *fdata) +{ + fdata->switch_hit = 0; + fdata->waking_hit = 0; + fdata->getppid_hit = 0; + fdata->missed_hit = 0; +} + static int switch_callback(struct tep_event *event, struct tep_record *record, int cpu, void *data) { @@ -619,6 +642,7 @@ static int switch_callback(struct tep_event *event, struct tep_record *record, CU_TEST(cpu == record->cpu); CU_TEST(event->id == fdata->sched_switch->id); + fdata->switch_hit++; return 0; } @@ -629,6 +653,18 @@ static int waking_callback(struct tep_event *event, struct tep_record *record, CU_TEST(cpu == record->cpu); CU_TEST(event->id == fdata->sched_waking->id); + fdata->waking_hit++; + return 0; +} + +static int getppid_callback(struct tep_event *event, struct tep_record *record, + int cpu, void *data) +{ + struct follow_data *fdata = data; + + CU_TEST(cpu == record->cpu); + CU_TEST(event->id == fdata->getppid->id); + fdata->getppid_hit++; return 0; } @@ -648,6 +684,7 @@ static int missed_callback(struct tep_event *event, struct tep_record *record, struct follow_data *fdata = data; fdata->missed = record->missed_events; + fdata->missed_hit++; return 0; } @@ -725,6 +762,11 @@ static void test_instance_follow_events(struct tracefs_instance *instance) ret = tracefs_iterate_raw_events(tep, instance, NULL, 0, all_callback, &fdata); CU_TEST(ret == 0); + ret = tracefs_follow_event_clear(instance, NULL, NULL); + CU_TEST(ret == 0); + ret = tracefs_follow_missed_events_clear(instance); + CU_TEST(ret == 0); + pthread_join(thread, NULL); tracefs_tracer_clear(instance); @@ -737,6 +779,193 @@ static void test_follow_events(void) test_instance_follow_events(test_instance); } +static void test_instance_follow_events_clear(struct tracefs_instance *instance) +{ + struct follow_data fdata; + struct tep_handle *tep; + char **list; + int ret; + + memset(&fdata, 0, sizeof(fdata)); + + tep = test_tep; + + fdata.sched_switch = tep_find_event_by_name(tep, "sched", "sched_switch"); + CU_TEST(fdata.sched_switch != NULL); + if (!fdata.sched_switch) + return; + + fdata.sched_waking = tep_find_event_by_name(tep, "sched", "sched_waking"); + CU_TEST(fdata.sched_waking != NULL); + if (!fdata.sched_waking) + return; + + fdata.getppid = tep_find_event_by_name(tep, EVENT_SYSTEM, EVENT_NAME); + CU_TEST(fdata.getppid != NULL); + if (!fdata.getppid) + return; + + ret = tracefs_follow_event(tep, instance, "sched", "sched_switch", + switch_callback, &fdata); + CU_TEST(ret == 0); + + ret = tracefs_follow_event(tep, instance, "sched", "sched_waking", + waking_callback, &fdata); + CU_TEST(ret == 0); + + ret = tracefs_follow_event(tep, instance, EVENT_SYSTEM, EVENT_NAME, + getppid_callback, &fdata); + CU_TEST(ret == 0); + + ret = tracefs_follow_missed_events(instance, missed_callback, &fdata); + CU_TEST(ret == 0); + + ret = tracefs_event_enable(instance, "sched", "sched_switch"); + CU_TEST(ret == 0); + + ret = tracefs_event_enable(instance, "sched", "sched_waking"); + CU_TEST(ret == 0); + + ret = tracefs_event_enable(instance, EVENT_SYSTEM, EVENT_NAME); + CU_TEST(ret == 0); + + tracefs_trace_on(instance); + call_getppid(100); + msleep(100); + tracefs_trace_off(instance); + + ret = tracefs_iterate_raw_events(tep, instance, NULL, 0, NULL, &fdata); + CU_TEST(ret == 0); + + /* Make sure all are hit */ + CU_TEST(fdata.switch_hit > 0); + CU_TEST(fdata.waking_hit > 0); + CU_TEST(fdata.getppid_hit == 100); + /* No missed events */ + CU_TEST(fdata.missed_hit == 0); + clear_hits(&fdata); + + + + /* Disable getppid and do the same thing */ + ret = tracefs_follow_event_clear(instance, EVENT_SYSTEM, EVENT_NAME); + CU_TEST(ret == 0); + + tracefs_trace_on(instance); + call_getppid(100); + msleep(100); + tracefs_trace_off(instance); + + ret = tracefs_iterate_raw_events(tep, instance, NULL, 0, NULL, &fdata); + CU_TEST(ret == 0); + + /* All but getppid should be hit */ + CU_TEST(fdata.switch_hit > 0); + CU_TEST(fdata.waking_hit > 0); + CU_TEST(fdata.getppid_hit == 0); + /* No missed events */ + CU_TEST(fdata.missed_hit == 0); + clear_hits(&fdata); + + + + /* Add function and remove sched */ + ret = tracefs_follow_event(tep, instance, "ftrace", "function", + function_callback, &fdata); + CU_TEST(ret == 0); + ret = tracefs_follow_event_clear(instance, "sched", NULL); + CU_TEST(ret == 0); + + tracefs_trace_on(instance); + call_getppid(100); + msleep(100); + tracefs_trace_off(instance); + + ret = tracefs_iterate_raw_events(tep, instance, NULL, 0, NULL, &fdata); + CU_TEST(ret == 0); + + /* Nothing should have been hit */ + CU_TEST(fdata.switch_hit == 0); + CU_TEST(fdata.waking_hit == 0); + CU_TEST(fdata.getppid_hit == 0); + /* No missed events */ + CU_TEST(fdata.missed_hit == 0); + clear_hits(&fdata); + + + /* Enable function tracing and see if we missed hits */ + ret = tracefs_tracer_set(instance, TRACEFS_TRACER_FUNCTION); + CU_TEST(ret == 0); + + fdata.function = tep_find_event_by_name(tep, "ftrace", "function"); + CU_TEST(fdata.function != NULL); + if (!fdata.function) + return; + + tracefs_trace_on(instance); + call_getppid(100); + /* Stir the kernel a bit */ + list = tracefs_event_systems(NULL); + tracefs_list_free(list); + sleep(1); + tracefs_trace_off(instance); + + ret = tracefs_iterate_raw_events(tep, instance, NULL, 0, NULL, &fdata); + CU_TEST(ret == 0); + + /* Nothing should have been hit */ + CU_TEST(fdata.switch_hit == 0); + CU_TEST(fdata.waking_hit == 0); + CU_TEST(fdata.getppid_hit == 0); + /* We should have missed events! */ + CU_TEST(fdata.missed_hit > 0); + clear_hits(&fdata); + + + /* Now remove missed events follower */ + ret = tracefs_follow_missed_events_clear(instance); + CU_TEST(ret == 0); + + tracefs_trace_on(instance); + call_getppid(100); + sleep(1); + tracefs_trace_off(instance); + + ret = tracefs_iterate_raw_events(tep, instance, NULL, 0, NULL, &fdata); + CU_TEST(ret == 0); + + /* Nothing should have been hit */ + CU_TEST(fdata.switch_hit == 0); + CU_TEST(fdata.waking_hit == 0); + CU_TEST(fdata.getppid_hit == 0); + /* No missed events either */ + CU_TEST(fdata.missed_hit == 0); + clear_hits(&fdata); + + /* Turn everything off */ + tracefs_tracer_clear(instance); + tracefs_event_disable(instance, NULL, NULL); + + tracefs_trace_on(instance); + + /* Clear the function follower */ + ret = tracefs_follow_event_clear(instance, NULL, "function"); + + /* Should not have any more followers */ + ret = tracefs_follow_event_clear(instance, NULL, NULL); + CU_TEST(ret != 0); + + /* Nor missed event followers */ + ret = tracefs_follow_missed_events_clear(instance); + CU_TEST(ret != 0); +} + +static void test_follow_events_clear(void) +{ + test_instance_follow_events_clear(NULL); + test_instance_follow_events_clear(test_instance); +} + extern char *find_tracing_dir(bool debugfs, bool mount); static void test_mounting(void) { @@ -2570,6 +2799,7 @@ void test_tracefs_lib(void) /* Follow events test must be after the iterate raw events above */ CU_add_test(suite, "Follow events", test_follow_events); + CU_add_test(suite, "Follow events clear", test_follow_events_clear); CU_add_test(suite, "tracefs_tracers API", test_tracers); From patchwork Thu Dec 28 20:35:31 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13506194 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9439C10799 for ; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 10DF4C43397; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.97) (envelope-from ) id 1rIx7z-00000000Dt6-39xz; Thu, 28 Dec 2023 15:37:15 -0500 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: "Steven Rostedt (Google)" Subject: [PATCH 09/23] libtracefs: Increase splice to use pipe max size Date: Thu, 28 Dec 2023 15:35:31 -0500 Message-ID: <20231228203714.53294-10-rostedt@goodmis.org> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231228203714.53294-1-rostedt@goodmis.org> References: <20231228203714.53294-1-rostedt@goodmis.org> Precedence: bulk X-Mailing-List: linux-trace-devel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: "Steven Rostedt (Google)" When a pipe is created for splicing to read the tracing ring buffer, instead of using the default size, which is usually around 65K, read /proc/sys/fs/pipe_max_size and try to set the pipe to use that size instead. Signed-off-by: Steven Rostedt (Google) --- src/tracefs-record.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/tracefs-record.c b/src/tracefs-record.c index b078c8615194..8750fe7e0e29 100644 --- a/src/tracefs-record.c +++ b/src/tracefs-record.c @@ -319,6 +319,7 @@ int tracefs_cpu_read(struct tracefs_cpu *tcpu, void *buffer, bool nonblock) static int init_splice(struct tracefs_cpu *tcpu) { + char *buf; int ret; if (tcpu->splice_pipe[0] >= 0) @@ -328,6 +329,12 @@ static int init_splice(struct tracefs_cpu *tcpu) if (ret < 0) return ret; + if (str_read_file("/proc/sys/fs/pipe-max-size", &buf, false)) { + int size = atoi(buf); + fcntl(tcpu->splice_pipe[0], F_SETPIPE_SZ, &size); + free(buf); + } + ret = fcntl(tcpu->splice_pipe[0], F_GETPIPE_SZ, &tcpu->pipe_size); /* * F_GETPIPE_SZ was introduced in 2.6.35, ftrace was introduced From patchwork Thu Dec 28 20:35:32 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13506199 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A4E9C107BB for ; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 0E7E4C433D9; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.97) (envelope-from ) id 1rIx7z-00000000DtA-3H5G; Thu, 28 Dec 2023 15:37:15 -0500 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: "Steven Rostedt (Google)" Subject: [PATCH 10/23] libtracefs: Add tracefs_instance_file_write_number() Date: Thu, 28 Dec 2023 15:35:32 -0500 Message-ID: <20231228203714.53294-11-rostedt@goodmis.org> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231228203714.53294-1-rostedt@goodmis.org> References: <20231228203714.53294-1-rostedt@goodmis.org> Precedence: bulk X-Mailing-List: linux-trace-devel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: "Steven Rostedt (Google)" Add the helper function to write a number into a tracefs file. As tracefs files require writing strings and not binary numbers, this is a helper function that does the conversion to string followed by a tracefs_instance_file_write() on that string. Signed-off-by: Steven Rostedt (Google) --- .../libtracefs-instances-file-manip.txt | 9 +++++- Documentation/libtracefs.txt | 1 + include/tracefs.h | 2 ++ src/tracefs-instance.c | 21 +++++++++++++ utest/tracefs-utest.c | 30 ++++++++++++++++++- 5 files changed, 61 insertions(+), 2 deletions(-) diff --git a/Documentation/libtracefs-instances-file-manip.txt b/Documentation/libtracefs-instances-file-manip.txt index 8c04240523d3..bb1b36e36c3e 100644 --- a/Documentation/libtracefs-instances-file-manip.txt +++ b/Documentation/libtracefs-instances-file-manip.txt @@ -5,7 +5,7 @@ NAME ---- tracefs_instance_file_open, -tracefs_instance_file_write, tracefs_instance_file_append, tracefs_instance_file_clear, +tracefs_instance_file_write, tracefs_instance_file_write_number, tracefs_instance_file_append, tracefs_instance_file_clear, tracefs_instance_file_read, tracefs_instance_file_read_number - Work with files in tracing instances. SYNOPSIS @@ -16,6 +16,7 @@ SYNOPSIS int *tracefs_instance_file_open*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_file_, int _mode_); int *tracefs_instance_file_write*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_file_, const char pass:[*]_str_); +int *tracefs_instance_file_write_number*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_file_, size_t _val_); int *tracefs_instance_file_append*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_file_, const char pass:[*]_str_); int *tracefs_instance_file_clear*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_file_); char pass:[*]*tracefs_instance_file_read*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_file_, int pass:[*]_psize_); @@ -38,6 +39,10 @@ The *tracefs_instance_file_write()* function writes a string _str_ in a _file_ f the given _instance_, without the terminating NULL character. When opening the file, this function tries to truncates the size of the file to zero, which clears all previously existing settings. +The *tracefs_instance_file_write_number()* function converts _val_ into a string +and then writes it to the given file. This is a helper function that does the number +conversion to string and then calls *tracefs_instance_file_write()*. + The *tracefs_instance_file_append()* function writes a string _str_ in a _file_ from the given _instance_, without the terminating NULL character. This function is similar to *tracefs_instance_file_write()*, but the existing content of the is not cleared. Thus the @@ -61,6 +66,8 @@ closed with *close*(3). In case of an error, -1 is returned. The *tracefs_instance_file_write()* function returns the number of written bytes, or -1 in case of an error. +The *tracefs_instance_file_write_number()* function returns 0 on success and -1 on error. + The *tracefs_instance_file_append()* function returns the number of written bytes, or -1 in case of an error. diff --git a/Documentation/libtracefs.txt b/Documentation/libtracefs.txt index 850118302ab2..11ff576e826c 100644 --- a/Documentation/libtracefs.txt +++ b/Documentation/libtracefs.txt @@ -33,6 +33,7 @@ Trace instances: char pass:[*]*tracefs_instance_get_dir*(struct tracefs_instance pass:[*]_instance_); int *tracefs_instance_file_open*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_file_, int _mode_); int *tracefs_instance_file_write*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_file_, const char pass:[*]_str_); + int *tracefs_instance_file_write_number*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_file_, size_t _val_); int *tracefs_instance_file_append*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_file_, const char pass:[*]_str_); int *tracefs_instance_file_clear*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_file_); char pass:[*]*tracefs_instance_file_read*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_file_, int pass:[*]_psize_); diff --git a/include/tracefs.h b/include/tracefs.h index 25429a30c028..0971d54dd7c7 100644 --- a/include/tracefs.h +++ b/include/tracefs.h @@ -36,6 +36,8 @@ tracefs_instance_get_file(struct tracefs_instance *instance, const char *file); char *tracefs_instance_get_dir(struct tracefs_instance *instance); int tracefs_instance_file_write(struct tracefs_instance *instance, const char *file, const char *str); +int tracefs_instance_file_write_number(struct tracefs_instance *instance, + const char *file, size_t val); int tracefs_instance_file_append(struct tracefs_instance *instance, const char *file, const char *str); int tracefs_instance_file_clear(struct tracefs_instance *instance, diff --git a/src/tracefs-instance.c b/src/tracefs-instance.c index 2efcc75dfd98..b019836333a3 100644 --- a/src/tracefs-instance.c +++ b/src/tracefs-instance.c @@ -493,6 +493,27 @@ int tracefs_instance_file_write(struct tracefs_instance *instance, return instance_file_write(instance, file, str, O_WRONLY | O_TRUNC); } +/** + * tracefs_instance_file_write_number - Write integer from a trace file. + * @instance: ftrace instance, can be NULL for the top instance + * @file: name of the file + * @res: The integer to write to @file + * + * Returns 0 if the write succeeds, -1 on error. + */ +int tracefs_instance_file_write_number(struct tracefs_instance *instance, + const char *file, size_t val) +{ + char buf[64]; + int ret; + + snprintf(buf, 64, "%zd\n", val); + + ret = tracefs_instance_file_write(instance, file, buf); + + return ret > 1 ? 0 : -1; +} + /** * tracefs_instance_file_append - Append to a trace file of specific instance. * @instance: ftrace instance, can be NULL for the top instance. diff --git a/utest/tracefs-utest.c b/utest/tracefs-utest.c index 492e5c05551f..98cfd322b171 100644 --- a/utest/tracefs-utest.c +++ b/utest/tracefs-utest.c @@ -488,7 +488,7 @@ static int setup_trace_cpu(struct tracefs_instance *instance, struct test_cpu_da if (!data->buf) goto fail; - data->kbuf = kbuffer_alloc(sizeof(long) == 8, !tep_is_bigendian()); + data->kbuf = tep_kbuffer(data->tep); CU_TEST(data->kbuf != NULL); if (!data->kbuf) goto fail; @@ -2127,6 +2127,7 @@ static void test_instance_file_fd(struct tracefs_instance *instance) const char *name = get_rand_str(); const char *tdir = tracefs_instance_get_trace_dir(instance); long long res = -1; + long long res2; char rd[2]; int fd; @@ -2146,7 +2147,34 @@ static void test_instance_file_fd(struct tracefs_instance *instance) CU_TEST(read(fd, &rd, 1) == 1); rd[1] = 0; CU_TEST(res == atoi(rd)); + close(fd); + + /* Inverse tracing_on and test changing it with write_number */ + res ^= 1; + CU_TEST(tracefs_instance_file_write_number(instance, TRACE_ON, (size_t)res) == 0); + + CU_TEST(tracefs_instance_file_read_number(instance, TRACE_ON, &res2) == 0); + CU_TEST(res2 == res); + fd = tracefs_instance_file_open(instance, TRACE_ON, O_RDONLY); + CU_TEST(fd >= 0); + CU_TEST(read(fd, &rd, 1) == 1); + rd[1] = 0; + CU_TEST(res2 == atoi(rd)); + close(fd); + + /* Put back the result of tracing_on */ + res ^= 1; + + CU_TEST(tracefs_instance_file_write_number(instance, TRACE_ON, (size_t)res) == 0); + + CU_TEST(tracefs_instance_file_read_number(instance, TRACE_ON, &res2) == 0); + CU_TEST(res2 == res); + fd = tracefs_instance_file_open(instance, TRACE_ON, O_RDONLY); + CU_TEST(fd >= 0); + CU_TEST(read(fd, &rd, 1) == 1); + rd[1] = 0; + CU_TEST(res2 == atoi(rd)); close(fd); } From patchwork Thu Dec 28 20:35:33 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13506198 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A5BCB107BD for ; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 1862EC4339A; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.97) (envelope-from ) id 1rIx7z-00000000DtE-3P7R; Thu, 28 Dec 2023 15:37:15 -0500 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: "Steven Rostedt (Google)" Subject: [PATCH 11/23] libtracefs: Add API to read tracefs_cpu and return a kbuffer Date: Thu, 28 Dec 2023 15:35:33 -0500 Message-ID: <20231228203714.53294-12-rostedt@goodmis.org> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231228203714.53294-1-rostedt@goodmis.org> References: <20231228203714.53294-1-rostedt@goodmis.org> Precedence: bulk X-Mailing-List: linux-trace-devel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: "Steven Rostedt (Google)" Instead of having the caller have to allocate a buffer and a kbuffer to read the content of a tracefs_cpu, add an interface to allow the tracefs_cpu to handle this for the application. The following three functions are added to the API: tracefs_cpu_read_buf() tracefs_cpu_buffered_read_buf() tracefs_cpu_flush_buf() That act the same as the corresponding functions with the same name minus the ending "_buf", but instead of requiring to send a buffer in and then add it to a kbuffer element to read, just return a kbuffer structure that can be used to read. Signed-off-by: Steven Rostedt (Google) --- Documentation/libtracefs-cpu-buf.txt | 171 +++++++++++++++++++++++++++ Documentation/libtracefs-cpu.txt | 3 + Documentation/libtracefs.txt | 3 + include/tracefs.h | 4 + samples/Makefile | 1 + src/tracefs-record.c | 116 ++++++++++++++++++ 6 files changed, 298 insertions(+) create mode 100644 Documentation/libtracefs-cpu-buf.txt diff --git a/Documentation/libtracefs-cpu-buf.txt b/Documentation/libtracefs-cpu-buf.txt new file mode 100644 index 000000000000..943cb1f2f437 --- /dev/null +++ b/Documentation/libtracefs-cpu-buf.txt @@ -0,0 +1,171 @@ +libtracefs(3) +============= + +NAME +---- +tracefs_cpu_read_buf, tracefs_cpu_buffered_read_buf, tracefs_cpu_flush_buf +- Reading trace_pipe_raw data returning a kbuffer + +SYNOPSIS +-------- +[verse] +-- +*#include * + +struct kbuffer pass:[*]*tracefs_cpu_read_buf*(struct tracefs_cpu pass:[*]_tcpu_, bool _nonblock_); +struct kbuffer pass:[*]*tracefs_cpu_buffered_read_buf*(struct tracefs_cpu pass:[*]_tcpu_, bool _nonblock_); +struct kbuffer pass:[*]*tracefs_cpu_flush_buf*(struct tracefs_cpu pass:[*]_tcpu_); +-- + +DESCRIPTION +----------- +This set of APIs can be used to read the raw data from the trace_pipe_raw +files in the tracefs file system and return a kbuffer structure to read it with. + +The *tracefs_cpu_read_buf()* reads the trace_pipe_raw files associated to _tcpu_ +and returns a kbuffer structure that can be used to iterate the events. +If _nonblock_ is set, and there's no data available, it will return immediately. +Otherwise depending on how _tcpu_ was opened, it will block. If _tcpu_ was +opened with nonblock set, then this _nonblock_ will make no difference. + +The *tracefs_cpu_buffered_read_buf()* is basically the same as *tracefs_cpu_read_buf()* +except that it uses a pipe through splice to buffer reads. This will batch +reads keeping the reading from the ring buffer less intrusive to the system, +as just reading all the time can cause quite a disturbance. Note, one +difference between this and *tracefs_cpu_read()* is that it will read only in +sub buffer pages. If the ring buffer has not filled a page, then it will not +return anything, even with _nonblock_ set. Calls to *tracefs_cpu_flush_buf()* +or *tracefs_cpu_flush()* should be done to read the rest of the file at the +end of the trace. + +The *tracefs_cpu_flush_buf()* reads the trace_pipe_raw file associated by the +_tcpu_ and puts it into _buffer_, which must be the size of the sub buffer +which is retrieved. This should be called at the end of tracing +to get the rest of the data. This call will convert the file descriptor of +trace_pipe_raw into non-blocking mode. + +RETURN VALUE +------------ +The functions *tracefs_cpu_read_buf()*, tracefs_cpu_buffered_read_buf()* and +*tracefs_cpu_flush()* returns a kbuffer descriptor that can be iterated +over to find the events. Note, this descriptor is part of the tracefs_cpu structure +and should not be freed. It will be freed. It returns NULL on error or if nonblock +is set and there are no events available. In the case of no events, errno will be +set with EAGAIN. + +EXAMPLE +------- +[source,c] +-- +#include +#include +#include + +static void read_page(struct tep_handle *tep, struct kbuffer *kbuf) +{ + static struct trace_seq seq; + struct tep_record record; + + if (seq.buffer) + trace_seq_reset(&seq); + else + trace_seq_init(&seq); + + while ((record.data = kbuffer_read_event(kbuf, &record.ts))) { + record.size = kbuffer_event_size(kbuf); + kbuffer_next_event(kbuf, NULL); + tep_print_event(tep, &seq, &record, + "%s-%d %9d\t%s: %s\n", + TEP_PRINT_COMM, + TEP_PRINT_PID, + TEP_PRINT_TIME, + TEP_PRINT_NAME, + TEP_PRINT_INFO); + trace_seq_do_printf(&seq); + trace_seq_reset(&seq); + } +} + +int main (int argc, char **argv) +{ + struct tracefs_cpu *tcpu; + struct tep_handle *tep; + struct kbuffer *kbuf; + int cpu; + + if (argc < 2 || !isdigit(argv[1][0])) { + printf("usage: %s cpu\n\n", argv[0]); + exit(-1); + } + + cpu = atoi(argv[1]); + + tep = tracefs_local_events(NULL); + if (!tep) { + perror("Reading trace event formats"); + exit(-1); + } + + tcpu = tracefs_cpu_open(NULL, cpu, 0); + if (!tcpu) { + perror("Open CPU 0 file"); + exit(-1); + } + + while ((kbuf = tracefs_cpu_buffered_read_buf(tcpu, true))) { + read_page(tep, kbuf); + } + + kbuf = tracefs_cpu_flush_buf(tcpu); + if (kbuf) + read_page(tep, kbuf); + + tracefs_cpu_close(tcpu); + tep_free(tep); + + return 0; +} +-- +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 +-------- +*tracefs_cpu_open*(3) +*tracefs_cpu_close*(3) +*tracefs_cpu_read*(3) +*tracefs_cpu_buffered_read*(3) +*tracefs_cpu_flush*(3) +*libtracefs*(3), +*libtraceevent*(3), +*trace-cmd*(1) + +AUTHOR +------ +[verse] +-- +*Steven Rostedt* +-- +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) 2022 Google, Inc. Free use of this software is granted under +the terms of the GNU Public License (GPL). diff --git a/Documentation/libtracefs-cpu.txt b/Documentation/libtracefs-cpu.txt index d6215d9912c0..6fb65248089b 100644 --- a/Documentation/libtracefs-cpu.txt +++ b/Documentation/libtracefs-cpu.txt @@ -212,6 +212,9 @@ SEE ALSO -------- *tracefs_cpu_open*(3) *tracefs_cpu_close*(3) +*tracefs_cpu_read_buf*(3) +*tracefs_cpu_buffered_read_buf*(3) +*tracefs_cpu_flush_buf*(3) *libtracefs*(3), *libtraceevent*(3), *trace-cmd*(1) diff --git a/Documentation/libtracefs.txt b/Documentation/libtracefs.txt index 11ff576e826c..8ca19a04fe77 100644 --- a/Documentation/libtracefs.txt +++ b/Documentation/libtracefs.txt @@ -304,6 +304,9 @@ Recording of trace_pipe_raw files: int *tracefs_cpu_flush*(struct tracefs_cpu pass:[*]_tcpu_, void pass:[*]_buffer_); int *tracefs_cpu_flush_write*(struct tracefs_cpu pass:[*]_tcpu_, int _wfd_); int *tracefs_cpu_pipe*(struct tracefs_cpu pass:[*]_tcpu_, int _wfd_, bool _nonblock_); + struct kbuffer pass:[*]*tracefs_cpu_read_buf*(struct tracefs_cpu pass:[*]_tcpu_, bool _nonblock_); + struct kbuffer pass:[*]*tracefs_cpu_buffered_read_buf*(struct tracefs_cpu pass:[*]_tcpu_, bool _nonblock_); + struct kbuffer pass:[*]*tracefs_cpu_flush_buf*(struct tracefs_cpu pass:[*]_tcpu_); Helper functions for guest tracing: char pass:[*]*tracefs_find_cid_pid*(int _cid_); diff --git a/include/tracefs.h b/include/tracefs.h index 0971d54dd7c7..1722cbd60598 100644 --- a/include/tracefs.h +++ b/include/tracefs.h @@ -9,6 +9,7 @@ #include #include #include +#include char *tracefs_get_tracing_file(const char *name); void tracefs_put_tracing_file(char *name); @@ -641,10 +642,13 @@ void tracefs_cpu_close(struct tracefs_cpu *tcpu); void tracefs_cpu_free_fd(struct tracefs_cpu *tcpu); int tracefs_cpu_read_size(struct tracefs_cpu *tcpu); int tracefs_cpu_read(struct tracefs_cpu *tcpu, void *buffer, bool nonblock); +struct kbuffer *tracefs_cpu_read_buf(struct tracefs_cpu *tcpu, bool nonblock); int tracefs_cpu_buffered_read(struct tracefs_cpu *tcpu, void *buffer, bool nonblock); +struct kbuffer *tracefs_cpu_buffered_read_buf(struct tracefs_cpu *tcpu, bool nonblock); int tracefs_cpu_write(struct tracefs_cpu *tcpu, int wfd, bool nonblock); int tracefs_cpu_stop(struct tracefs_cpu *tcpu); int tracefs_cpu_flush(struct tracefs_cpu *tcpu, void *buffer); +struct kbuffer *tracefs_cpu_flush_buf(struct tracefs_cpu *tcpu); int tracefs_cpu_flush_write(struct tracefs_cpu *tcpu, int wfd); int tracefs_cpu_pipe(struct tracefs_cpu *tcpu, int wfd, bool nonblock); diff --git a/samples/Makefile b/samples/Makefile index d782e8f79424..13ad99579ea3 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -23,6 +23,7 @@ EXAMPLES += stream EXAMPLES += instances-affinity EXAMPLES += cpu EXAMPLES += guest +EXAMPLES += cpu-buf TARGETS := TARGETS += sqlhist diff --git a/src/tracefs-record.c b/src/tracefs-record.c index 8750fe7e0e29..bfeae18fd77f 100644 --- a/src/tracefs-record.c +++ b/src/tracefs-record.c @@ -34,6 +34,8 @@ struct tracefs_cpu { int subbuf_size; int buffered; int splice_read_flags; + struct kbuffer *kbuf; + void *buffer; }; /** @@ -106,6 +108,7 @@ tracefs_cpu_open(struct tracefs_instance *instance, int cpu, bool nonblock) { struct tracefs_cpu *tcpu; struct tep_handle *tep; + struct kbuffer *kbuf; char path[128]; char *buf; int mode = O_RDONLY; @@ -138,6 +141,11 @@ tracefs_cpu_open(struct tracefs_instance *instance, int cpu, bool nonblock) goto fail; subbuf_size = tep_get_sub_buffer_size(tep); + + kbuf = tep_kbuffer(tep); + if (!kbuf) + goto fail; + tep_free(tep); tep = NULL; @@ -145,6 +153,8 @@ tracefs_cpu_open(struct tracefs_instance *instance, int cpu, bool nonblock) if (!tcpu) goto fail; + tcpu->kbuf = kbuf; + return tcpu; fail: tep_free(tep); @@ -173,6 +183,7 @@ void tracefs_cpu_free_fd(struct tracefs_cpu *tcpu) close_fd(tcpu->splice_pipe[0]); close_fd(tcpu->splice_pipe[1]); + kbuffer_free(tcpu->kbuf); free(tcpu); } @@ -317,6 +328,49 @@ int tracefs_cpu_read(struct tracefs_cpu *tcpu, void *buffer, bool nonblock) return ret; } +static bool get_buffer(struct tracefs_cpu *tcpu) +{ + if (!tcpu->buffer) { + tcpu->buffer = malloc(tcpu->subbuf_size); + if (!tcpu->buffer) + return false; + } + return true; +} + +/** + * tracefs_cpu_read_buf - read from the raw trace file and return kbuffer + * @tcpu: The descriptor representing the raw trace file + * @nonblock: Hint to not block on the read if there's no data. + * + * Reads the trace_pipe_raw files associated to @tcpu and returns a kbuffer + * associated with the read that can be used to parse events. + * + * If @nonblock is set, and there's no data available, it will return + * immediately. Otherwise depending on how @tcpu was opened, it will + * block. If @tcpu was opened with nonblock set, then this @nonblock + * will make no difference. + * + * Returns a kbuffer associated to the next sub-buffer or NULL on error + * or no data to read with nonblock set (EAGAIN will be set). + * + * The kbuffer returned should not be freed! + */ +struct kbuffer *tracefs_cpu_read_buf(struct tracefs_cpu *tcpu, bool nonblock) +{ + int ret; + + if (!get_buffer(tcpu)) + return NULL; + + ret = tracefs_cpu_read(tcpu, tcpu->buffer, nonblock); + if (ret <= 0) + return NULL; + + kbuffer_load_subbuffer(tcpu->kbuf, tcpu->buffer); + return tcpu->kbuf; +} + static int init_splice(struct tracefs_cpu *tcpu) { char *buf; @@ -409,6 +463,42 @@ int tracefs_cpu_buffered_read(struct tracefs_cpu *tcpu, void *buffer, bool nonbl return ret; } +/** + * tracefs_cpu_buffered_read_buf - Read the raw trace data buffering through a pipe + * @tcpu: The descriptor representing the raw trace file + * @nonblock: Hint to not block on the read if there's no data. + * + * This is basically the same as tracefs_cpu_read() except that it uses + * a pipe through splice to buffer reads. This will batch reads keeping + * the reading from the ring buffer less intrusive to the system, as + * just reading all the time can cause quite a disturbance. + * + * Note, one difference between this and tracefs_cpu_read() is that it + * will read only in sub buffer pages. If the ring buffer has not filled + * a page, then it will not return anything, even with @nonblock set. + * Calls to tracefs_cpu_flush() should be done to read the rest of + * the file at the end of the trace. + * + * Returns a kbuffer associated to the next sub-buffer or NULL on error + * or no data to read with nonblock set (EAGAIN will be set). + * + * The kbuffer returned should not be freed! + */ +struct kbuffer *tracefs_cpu_buffered_read_buf(struct tracefs_cpu *tcpu, bool nonblock) +{ + int ret; + + if (!get_buffer(tcpu)) + return NULL; + + ret = tracefs_cpu_buffered_read(tcpu, tcpu->buffer, nonblock); + if (ret <= 0) + return NULL; + + kbuffer_load_subbuffer(tcpu->kbuf, tcpu->buffer); + return tcpu->kbuf; +} + /** * tracefs_cpu_stop - Stop a blocked read of the raw tracing file * @tcpu: The descriptor representing the raw trace file @@ -492,6 +582,32 @@ int tracefs_cpu_flush(struct tracefs_cpu *tcpu, void *buffer) return ret; } +/** + * tracefs_cpu_flush_buf - Finish out and read the rest of the raw tracing file + * @tcpu: The descriptor representing the raw trace file + * + * Reads the trace_pipe_raw file associated by the @tcpu and puts it + * into @buffer, which must be the size of the sub buffer which is retrieved. + * by tracefs_cpu_read_size(). This should be called at the end of tracing + * to get the rest of the data. + * + * This will set the file descriptor for reading to non-blocking mode. + */ +struct kbuffer *tracefs_cpu_flush_buf(struct tracefs_cpu *tcpu) +{ + int ret; + + if (!get_buffer(tcpu)) + return NULL; + + ret = tracefs_cpu_flush(tcpu, tcpu->buffer); + if (ret <= 0) + return NULL; + + kbuffer_load_subbuffer(tcpu->kbuf, tcpu->buffer); + return tcpu->kbuf; +} + /** * tracefs_cpu_flush_write - Finish out and read the rest of the raw tracing file * @tcpu: The descriptor representing the raw trace file From patchwork Thu Dec 28 20:35:34 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13506191 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8275D101FE for ; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 19847C433A9; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.97) (envelope-from ) id 1rIx7z-00000000DtI-3WDr; Thu, 28 Dec 2023 15:37:15 -0500 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: "Steven Rostedt (Google)" Subject: [PATCH 12/23] libtracefs: Add tracefs_instance_get/set_buffer_percent() Date: Thu, 28 Dec 2023 15:35:34 -0500 Message-ID: <20231228203714.53294-13-rostedt@goodmis.org> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231228203714.53294-1-rostedt@goodmis.org> References: <20231228203714.53294-1-rostedt@goodmis.org> Precedence: bulk X-Mailing-List: linux-trace-devel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: "Steven Rostedt (Google)" Add an API that allows modifying the buffer_percent of the tracing ring buffer. That is the amount that is written into the ring buffer before the trace is woken up. Signed-off-by: Steven Rostedt (Google) --- Documentation/libtracefs-instances-utils.txt | 28 +++++++++++++++++++- Documentation/libtracefs.txt | 2 ++ include/tracefs.h | 3 +++ src/tracefs-utils.c | 27 +++++++++++++++++++ 4 files changed, 59 insertions(+), 1 deletion(-) diff --git a/Documentation/libtracefs-instances-utils.txt b/Documentation/libtracefs-instances-utils.txt index bc8c9a7bf8e9..d2c4f163902d 100644 --- a/Documentation/libtracefs-instances-utils.txt +++ b/Documentation/libtracefs-instances-utils.txt @@ -4,7 +4,8 @@ libtracefs(3) NAME ---- tracefs_instance_get_name, tracefs_instance_get_trace_dir, tracefs_instances_walk, tracefs_instance_exists, -tracefs_instance_get_buffer_size, tracefs_instance_set_buffer_size - Helper functions for working with tracing instances. +tracefs_instance_get_buffer_size, tracefs_instance_set_buffer_size, tracefs_instance_get_buffer_percent, +tracefs_instance_set_buffer_percent - Helper functions for working with tracing instances. SYNOPSIS -------- @@ -18,6 +19,8 @@ int *tracefs_instances_walk*(int (pass:[*]_callback_)(const char pass:[*], void bool *tracefs_instance_exists*(const char pass:[*]_name_); size_t *tracefs_instance_get_buffer_size*(struct tracefs_instance pass:[*]_instance_, int _cpu_); int *tracefs_instance_set_buffer_size*(struct tracefs_instance pass:[*]_instance_, size_t _size_, int _cpu_); +int *tracefs_instance_get_buffer_percent*(struct tracefs_instance pass:[*]_instance_); +int *tracefs_instance_set_buffer_percent*(struct tracefs_instance pass:[*]_instance_, int _val_); -- DESCRIPTION @@ -48,6 +51,29 @@ If _cpu_ is negative, then it sets all the per CPU ring buffers to _size_ (note the total size is the number of CPUs * _size_). If _cpu_ is specified, then it only sets the size of the per CPU ring buffer. +The *tracefs_instance_set_buffer_percent()* sets the buffer percent value of +the tracing ring buffer for _instance_ or the top level buffer if _instance_ is +NULL. The buffer percent decides when readers on *tracefs_cpu_read*(3), +*tracefs_cpu_buffered_read*(3), *tracefs_cpu_write*(3) and *tracefs_cpu_pipe*(3) +will block when O_NONBLOCK is not set. The value of _val_ must be between 0 and +100, where: + +[verse] +-- + 0 - block until there's any data in the ring buffer + 1 - block until 1% of the ring buffer sub-buffers are filled + 50 - block until 50% of the ring buffer sub-buffers are filled + 100 - block until the entire ring buffer is filled +-- + +Note, any number from 0 to 100 can be used where it is the percentage of the +ring buffer that must be filled before a blocked reader will be notified that +there's data to be retrieved. + +The *tracefs_instance_get_buffer_percent()* retrieves the current buffer percent +setting of the tracing ring buffer for _instance_ or the top level buffer +if _instance_ is NULL. + RETURN VALUE ------------ The *tracefs_instance_get_name()* returns a string or NULL in case of the top diff --git a/Documentation/libtracefs.txt b/Documentation/libtracefs.txt index 8ca19a04fe77..529922580f8d 100644 --- a/Documentation/libtracefs.txt +++ b/Documentation/libtracefs.txt @@ -50,6 +50,8 @@ Trace instances: char pass:[*]*tracefs_instance_get_affinity_raw*(struct tracefs_instance pass:[*]_instance_); size_t *tracefs_instance_get_buffer_size*(struct tracefs_instance pass:[*]_instance_, int _cpu_); int *tracefs_instance_set_buffer_size*(struct tracefs_instance pass:[*]_instance_, size_t _size_, int _cpu_); + int *tracefs_instance_get_buffer_percent*(struct tracefs_instance pass:[*]_instance_); + int *tracefs_instance_set_buffer_percent*(struct tracefs_instance pass:[*]_instance_, int _val_); Trace events: char pass:[*]pass:[*]*tracefs_event_systems*(const char pass:[*]_tracing_dir_); diff --git a/include/tracefs.h b/include/tracefs.h index 1722cbd60598..4be66b488536 100644 --- a/include/tracefs.h +++ b/include/tracefs.h @@ -64,6 +64,9 @@ ssize_t tracefs_instance_get_buffer_size(struct tracefs_instance *instance, int int tracefs_instance_set_buffer_size(struct tracefs_instance *instance, size_t size, int cpu); char **tracefs_instances(const char *regex); +int tracefs_instance_get_buffer_percent(struct tracefs_instance *instance); +int tracefs_instance_set_buffer_percent(struct tracefs_instance *instance, int val); + bool tracefs_instance_exists(const char *name); bool tracefs_file_exists(struct tracefs_instance *instance, const char *name); bool tracefs_dir_exists(struct tracefs_instance *instance, const char *name); diff --git a/src/tracefs-utils.c b/src/tracefs-utils.c index e67b698f5b99..50a7c7474145 100644 --- a/src/tracefs-utils.c +++ b/src/tracefs-utils.c @@ -643,3 +643,30 @@ bool tracefs_tracer_available(const char *tracing_dir, const char *tracer) tracefs_list_free(tracers); return ret; } + +/** + * tracefs_instance_get_buffer_percent - get the instance buffer percent + * @instance: The instance to get from (NULL for toplevel) + * + * Returns the buffer percent setting of the given instance. + * (-1 if not found). + */ +int tracefs_instance_get_buffer_percent(struct tracefs_instance *instance) +{ + long long val; + int ret; + + ret = tracefs_instance_file_read_number(instance, "buffer_percent", &val); + return !ret ? (int)val : ret; +} + +/** + * tracefs_instance_set_buffer_percent - set the instance buffer percent + * @instance: The instance to set (NULL for toplevel) + * + * Returns zero on success or -1 on error + */ +int tracefs_instance_set_buffer_percent(struct tracefs_instance *instance, int val) +{ + return tracefs_instance_file_write_number(instance, "buffer_percent", val); +} From patchwork Thu Dec 28 20:35:35 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13506200 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 949F1107A7 for ; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 1DC07C433AD; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.97) (envelope-from ) id 1rIx7z-00000000DtM-3cLG; Thu, 28 Dec 2023 15:37:15 -0500 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: "Steven Rostedt (Google)" Subject: [PATCH 13/23] libtracefs: Add tracefs_instance_clear() API Date: Thu, 28 Dec 2023 15:35:35 -0500 Message-ID: <20231228203714.53294-14-rostedt@goodmis.org> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231228203714.53294-1-rostedt@goodmis.org> References: <20231228203714.53294-1-rostedt@goodmis.org> Precedence: bulk X-Mailing-List: linux-trace-devel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: "Steven Rostedt (Google)" Add tracefs_instance_clear() that will clear the ring buffer of a given instance or the top level ring buffer if NULL is passed to it. Signed-off-by: Steven Rostedt (Google) --- Documentation/libtracefs-instances-manage.txt | 9 ++++++++- Documentation/libtracefs.txt | 1 + include/tracefs.h | 1 + src/tracefs-instance.c | 11 +++++++++++ 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/Documentation/libtracefs-instances-manage.txt b/Documentation/libtracefs-instances-manage.txt index 1e0735ee153c..4e5c64588772 100644 --- a/Documentation/libtracefs-instances-manage.txt +++ b/Documentation/libtracefs-instances-manage.txt @@ -4,7 +4,7 @@ libtracefs(3) NAME ---- tracefs_instance_create, tracefs_instance_destroy, tracefs_instance_alloc, tracefs_instance_free, -tracefs_instance_is_new, tracefs_instances, tracefs_instance_reset - Manage trace instances. +tracefs_instance_is_new, tracefs_instances, tracefs_instance_clear, tracefs_instance_reset - Manage trace instances. SYNOPSIS -------- @@ -18,6 +18,7 @@ struct tracefs_instance pass:[*]*tracefs_instance_alloc*(const char pass:[*]_tra void *tracefs_instance_free*(struct tracefs_instance pass:[*]_instance_); bool *tracefs_instance_is_new*(struct tracefs_instance pass:[*]_instance_); char pass:[**]*tracefs_instances*(const char pass:[*]_regex_); +void *tracefs_instance_clear*(struct tracefs_instance pass:[*]_instance_); void *tracefs_instance_reset*(struct tracefs_instance pass:[*]_instance_); -- @@ -61,6 +62,9 @@ it will match all instances that exist. The returned list must be freed with *tracefs_list_free*(3). Note, if no instances are found an empty list is returned and that too needs to be free with *tracefs_list_free*(3). +The *tracefs_instance_clear()* function clears the ring buffer of the given _instance_ +or the top level ring buffer if _instance_ is NULL. + The *tracefs_instance_reset*() function resets the given _instance_ to its default state. RETURN VALUE @@ -83,6 +87,9 @@ The list must be freed with *tracefs_list_free*(3). An empty list is returned if no instance exists that matches _regex_, and this needs to be freed with *tracefs_list_free*(3) as well. NULL is returned on error. +The *tracefs_instance_clear()* returns 0 if it successfully cleared the ring buffer, +or -1 on error. + EXAMPLE ------- [source,c] diff --git a/Documentation/libtracefs.txt b/Documentation/libtracefs.txt index 529922580f8d..273423cecf4a 100644 --- a/Documentation/libtracefs.txt +++ b/Documentation/libtracefs.txt @@ -25,6 +25,7 @@ Trace instances: struct tracefs_instance pass:[*]*tracefs_instance_alloc*(const char pass:[*]_tracing_dir_, const char pass:[*]_name_); void *tracefs_instance_free*(struct tracefs_instance pass:[*]_instance_); char pass:[**]*tracefs_instances*(const char pass:[*]_regex_); + void *tracefs_instance_clear*(struct tracefs_instance pass:[*]_instance_); void *tracefs_instance_reset*(struct tracefs_instance pass:[*]_instance_); bool *tracefs_instance_is_new*(struct tracefs_instance pass:[*]_instance_); bool *tracefs_file_exists*(struct tracefs_instance pass:[*]_instance_, char pass:[*]_name_); diff --git a/include/tracefs.h b/include/tracefs.h index 4be66b488536..31aba92d9a16 100644 --- a/include/tracefs.h +++ b/include/tracefs.h @@ -25,6 +25,7 @@ struct tracefs_instance; void tracefs_instance_free(struct tracefs_instance *instance); void tracefs_instance_reset(struct tracefs_instance *instance); +int tracefs_instance_clear(struct tracefs_instance *instance); struct tracefs_instance *tracefs_instance_create(const char *name); struct tracefs_instance *tracefs_instance_alloc(const char *tracing_dir, const char *name); diff --git a/src/tracefs-instance.c b/src/tracefs-instance.c index b019836333a3..4e7434157109 100644 --- a/src/tracefs-instance.c +++ b/src/tracefs-instance.c @@ -1409,6 +1409,17 @@ static void clear_func_filters(struct tracefs_instance *instance) clear_func_filter(instance, files[i]); } +/** + * tracefs_instance_clear - clear the trace buffer + * @instance: The instance to clear the trace for. + * + * Returns 0 on succes, -1 on error + */ +int tracefs_instance_clear(struct tracefs_instance *instance) +{ + return tracefs_instance_file_clear(instance, "trace"); +} + /** * tracefs_instance_reset - Reset a ftrace instance to its default state * @instance - a ftrace instance to be reseted From patchwork Thu Dec 28 20:35:36 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13506197 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 949CD107A3 for ; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 23F8AC433AB; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.97) (envelope-from ) id 1rIx7z-00000000DtQ-3ilH; Thu, 28 Dec 2023 15:37:15 -0500 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: "Steven Rostedt (Google)" Subject: [PATCH 14/23] libtracefs utest: Add test to test tracefs_instance_set/get_buffer_percent() Date: Thu, 28 Dec 2023 15:35:36 -0500 Message-ID: <20231228203714.53294-15-rostedt@goodmis.org> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231228203714.53294-1-rostedt@goodmis.org> References: <20231228203714.53294-1-rostedt@goodmis.org> Precedence: bulk X-Mailing-List: linux-trace-devel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: "Steven Rostedt (Google)" Add a utest that tests different "buffer_percent" values using the tracefs_instance_set_buffer_percent() API. It also will test the tracefs_instance_get_buffer_percent(). Signed-off-by: Steven Rostedt (Google) --- utest/tracefs-utest.c | 163 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 154 insertions(+), 9 deletions(-) diff --git a/utest/tracefs-utest.c b/utest/tracefs-utest.c index 98cfd322b171..6a817125419f 100644 --- a/utest/tracefs-utest.c +++ b/utest/tracefs-utest.c @@ -53,6 +53,8 @@ #define TRACEFS_DEFAULT_PATH "/sys/kernel/tracing" #define TRACEFS_DEFAULT2_PATH "/sys/kernel/debug/tracing" +static pthread_barrier_t trace_barrier; + static struct tracefs_instance *test_instance; static struct tep_handle *test_tep; struct test_sample { @@ -435,6 +437,7 @@ struct test_cpu_data { void *buf; int events_per_buf; int bufsize; + int nr_subbufs; int data_size; int this_pid; int fd; @@ -452,11 +455,21 @@ static void cleanup_trace_cpu(struct test_cpu_data *data) #define EVENT_SYSTEM "syscalls" #define EVENT_NAME "sys_enter_getppid" -static int setup_trace_cpu(struct tracefs_instance *instance, struct test_cpu_data *data) +static int make_trace_temp_file(void) +{ + char tmpfile[] = "/tmp/utest-libtracefsXXXXXX"; + int fd; + + fd = mkstemp(tmpfile); + unlink(tmpfile); + return fd; +} + +static int setup_trace_cpu(struct tracefs_instance *instance, struct test_cpu_data *data, bool nonblock) { struct tep_format_field **fields; struct tep_event *event; - char tmpfile[] = "/tmp/utest-libtracefsXXXXXX"; + ssize_t buffer_size; int max = 0; int ret; int i; @@ -468,20 +481,26 @@ static int setup_trace_cpu(struct tracefs_instance *instance, struct test_cpu_da data->instance = instance; - data->fd = mkstemp(tmpfile); + data->fd = make_trace_temp_file(); CU_TEST(data->fd >= 0); - unlink(tmpfile); if (data->fd < 0) return -1; data->tep = test_tep; - data->tcpu = tracefs_cpu_open(instance, 0, true); + data->tcpu = tracefs_cpu_open(instance, 0, nonblock); CU_TEST(data->tcpu != NULL); if (!data->tcpu) goto fail; data->bufsize = tracefs_cpu_read_size(data->tcpu); + CU_TEST(data->bufsize > 0); + + data->data_size = tep_get_sub_buffer_data_size(data->tep); + CU_TEST(data->data_size > 0); + + buffer_size = tracefs_instance_get_buffer_size(instance, 0) * 1024; + data->nr_subbufs = buffer_size/ data->data_size; data->buf = calloc(1, data->bufsize); CU_TEST(data->buf != NULL); @@ -493,8 +512,6 @@ static int setup_trace_cpu(struct tracefs_instance *instance, struct test_cpu_da if (!data->kbuf) goto fail; - data->data_size = data->bufsize - kbuffer_start_of_data(data->kbuf); - tracefs_instance_file_clear(instance, "trace"); event = tep_find_event_by_name(data->tep, EVENT_SYSTEM, EVENT_NAME); @@ -518,6 +535,12 @@ static int setup_trace_cpu(struct tracefs_instance *instance, struct test_cpu_da if (!max) goto fail; + /* round up to long size alignment */ + max = ((max + sizeof(long) - 1)) & ~(sizeof(long) - 1); + + /* Add meta header */ + max += 4; + data->events_per_buf = data->data_size / max; data->this_pid = getpid(); @@ -549,6 +572,17 @@ static void shutdown_trace_cpu(struct test_cpu_data *data) cleanup_trace_cpu(data); } +static void reset_trace_cpu(struct test_cpu_data *data, bool nonblock) +{ + close(data->fd); + tracefs_cpu_close(data->tcpu); + + data->fd = make_trace_temp_file(); + CU_TEST(data->fd >= 0); + data->tcpu = tracefs_cpu_open(data->instance, 0, nonblock); + CU_TEST(data->tcpu != NULL); +} + static void call_getppid(int cnt) { int i; @@ -597,7 +631,7 @@ static void test_instance_trace_cpu_read(struct tracefs_instance *instance) { struct test_cpu_data data; - if (setup_trace_cpu(instance, &data)) + if (setup_trace_cpu(instance, &data, true)) return; test_cpu_read(&data, 1); @@ -615,6 +649,115 @@ static void test_trace_cpu_read(void) test_instance_trace_cpu_read(test_instance); } +static void *trace_cpu_read_thread(void *arg) +{ + struct test_cpu_data *data = arg; + struct tracefs_cpu *tcpu = data->tcpu; + struct kbuffer *kbuf; + long ret = 0; + + pthread_barrier_wait(&trace_barrier); + + kbuf = tracefs_cpu_read_buf(tcpu, false); + CU_TEST(kbuf != NULL); + data->done = true; + + return (void *)ret; +} + +static void test_cpu_read_buf_percent(struct test_cpu_data *data, int percent) +{ + pthread_t thread; + int save_percent; + ssize_t expect; + int ret; + + tracefs_instance_clear(data->instance); + + save_percent = tracefs_instance_get_buffer_percent(data->instance); + CU_TEST(save_percent >= 0); + + ret = tracefs_instance_set_buffer_percent(data->instance, percent); + CU_TEST(ret == 0); + + data->done = false; + + pthread_barrier_init(&trace_barrier, NULL, 2); + + pthread_create(&thread, NULL, trace_cpu_read_thread, data); + + pthread_barrier_wait(&trace_barrier); + + msleep(100); + + CU_TEST(data->done == false); + + /* For percent == 0, just test for any data */ + if (percent) { + expect = data->nr_subbufs * data->events_per_buf * percent / 100; + + /* Add just under the percent */ + expect -= data->events_per_buf; + CU_TEST(expect > 0); + + call_getppid(expect); + + msleep(100); + + CU_TEST(data->done == false); + + /* Add just over the percent */ + expect = data->events_per_buf * 2; + } else { + expect = data->events_per_buf; + } + + call_getppid(expect); + + msleep(100); + + CU_TEST(data->done == true); + + while (tracefs_cpu_flush_buf(data->tcpu)) + ; + + tracefs_cpu_stop(data->tcpu); + pthread_join(thread, NULL); + + ret = tracefs_instance_set_buffer_percent(data->instance, save_percent); + CU_TEST(ret == 0); +} + +static void test_instance_trace_cpu_read_buf_percent(struct tracefs_instance *instance) +{ + struct test_cpu_data data; + + if (setup_trace_cpu(instance, &data, false)) + return; + + test_cpu_read_buf_percent(&data, 0); + + reset_trace_cpu(&data, false); + + test_cpu_read_buf_percent(&data, 1); + + reset_trace_cpu(&data, false); + + test_cpu_read_buf_percent(&data, 50); + + reset_trace_cpu(&data, false); + + test_cpu_read_buf_percent(&data, 100); + + shutdown_trace_cpu(&data); +} + +static void test_trace_cpu_read_buf_percent(void) +{ + test_instance_trace_cpu_read_buf_percent(NULL); + test_instance_trace_cpu_read_buf_percent(test_instance); +} + struct follow_data { struct tep_event *sched_switch; struct tep_event *sched_waking; @@ -1152,7 +1295,7 @@ static void test_instance_trace_cpu_pipe(struct tracefs_instance *instance) { struct test_cpu_data data; - if (setup_trace_cpu(instance, &data)) + if (setup_trace_cpu(instance, &data, true)) return; test_cpu_pipe(&data, 1); @@ -2808,6 +2951,8 @@ void test_tracefs_lib(void) CU_add_test(suite, "Test tracefs/debugfs mounting", test_mounting); CU_add_test(suite, "trace cpu read", test_trace_cpu_read); + CU_add_test(suite, "trace cpu read_buf_percent", + test_trace_cpu_read_buf_percent); CU_add_test(suite, "trace cpu pipe", test_trace_cpu_pipe); CU_add_test(suite, "trace sql", From patchwork Thu Dec 28 20:35:37 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13506208 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BDAB11095F for ; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 272A4C433B6; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.97) (envelope-from ) id 1rIx7z-00000000DtU-3phk; Thu, 28 Dec 2023 15:37:15 -0500 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: "Steven Rostedt (Google)" Subject: [PATCH 15/23] libtracefs: Add kerneldoc comments to tracefs_instance_set_buffer_size() Date: Thu, 28 Dec 2023 15:35:37 -0500 Message-ID: <20231228203714.53294-16-rostedt@goodmis.org> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231228203714.53294-1-rostedt@goodmis.org> References: <20231228203714.53294-1-rostedt@goodmis.org> Precedence: bulk X-Mailing-List: linux-trace-devel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: "Steven Rostedt (Google)" When the API tracefs_instance_set_buffer_size() was added, the kerneldoc associated to it was not. Signed-off-by: Steven Rostedt (Google) --- src/tracefs-instance.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/tracefs-instance.c b/src/tracefs-instance.c index 4e7434157109..2162424ad3a7 100644 --- a/src/tracefs-instance.c +++ b/src/tracefs-instance.c @@ -402,6 +402,18 @@ ssize_t tracefs_instance_get_buffer_size(struct tracefs_instance *instance, int return size; } +/** + * tracefs_instance_set_buffer_size - modify the ring buffer size + * @instance: The instance to modify (NULL for the top level) + * @size: The size in kilobytes to to set the size to + * @cpu: the CPU to set it to (-1 for all CPUs) + * + * Sets the size of the ring buffer per CPU buffers. If @cpu is negative, + * then it sets the ring buffer size for all the per CPU buffers, otherwise + * it only sets the per CPU buffer specified by @cpu. + * + * Returns 0 on success and -1 on error. + */ int tracefs_instance_set_buffer_size(struct tracefs_instance *instance, size_t size, int cpu) { char *path; From patchwork Thu Dec 28 20:35:38 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13506205 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A5C4D10941 for ; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 33657C433B7; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.97) (envelope-from ) id 1rIx7z-00000000DtY-3wcc; Thu, 28 Dec 2023 15:37:15 -0500 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: "Steven Rostedt (Google)" Subject: [PATCH 16/23] libtracefs: Add tracefs_load_headers() API Date: Thu, 28 Dec 2023 15:35:38 -0500 Message-ID: <20231228203714.53294-17-rostedt@goodmis.org> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231228203714.53294-1-rostedt@goodmis.org> References: <20231228203714.53294-1-rostedt@goodmis.org> Precedence: bulk X-Mailing-List: linux-trace-devel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: "Steven Rostedt (Google)" Sometimes the only thing that is needed from the tracefs directory is how to parse the sub-buffers. The tracefs_fill_local_events() has a lot of overhead as it reads pretty much everything. But if the only thing needed is the header file parsing, add this helper function to do it. Signed-off-by: Steven Rostedt (Google) --- Documentation/libtracefs-events-tep.txt | 7 ++++++- Documentation/libtracefs.txt | 1 + include/tracefs.h | 2 ++ src/tracefs-events.c | 22 ++++++++++++++++++++++ src/tracefs-record.c | 9 +-------- 5 files changed, 32 insertions(+), 9 deletions(-) diff --git a/Documentation/libtracefs-events-tep.txt b/Documentation/libtracefs-events-tep.txt index 22d3dd5fdfd0..ba46532a8db1 100644 --- a/Documentation/libtracefs-events-tep.txt +++ b/Documentation/libtracefs-events-tep.txt @@ -4,7 +4,7 @@ libtracefs(3) NAME ---- tracefs_local_events, tracefs_local_events_system, tracefs_fill_local_events, -tracefs_load_cmdlines - +tracefs_load_cmdlines, tracefs_load_headers - Initialize a tep handler with trace events from the local system. SYNOPSIS @@ -17,6 +17,7 @@ struct tep_handle pass:[*]*tracefs_local_events*(const char pass:[*]_tracing_dir struct tep_handle pass:[*]*tracefs_local_events_system*(const char pass:[*]_tracing_dir_, const char pass:[*] const pass:[*]_sys_names_); int *tracefs_fill_local_events*(const char pass:[*]_tracing_dir_, struct tep_handle pass:[*]_tep_, int pass:[*]_parsing_failures_); int *tracefs_load_cmdlines*(const char pass:[*]_tracing_dir_, struct tep_handle pass:[*]_tep_); +int *tracefs_load_headers*(const char pass:[*]_tracing_dir_, struct tep_handle pass:[*]_tep_); -- DESCRIPTION @@ -55,6 +56,10 @@ The *tracefs_load_cmdlines()* does just that. The _tracing_dir_ is the directory of the mount point to load from, or NULL to use the mount point of the tracefs file system. +The *tracefs_load_headers()* will reade the "header_page" of the events +directory that will update the _tep_ handle with information on how to parse the +tracing ring buffer sub-buffer. + RETURN VALUE ------------ The *tracefs_local_events()* and *tracefs_local_events_system()* functions diff --git a/Documentation/libtracefs.txt b/Documentation/libtracefs.txt index 273423cecf4a..70bd8116b2b1 100644 --- a/Documentation/libtracefs.txt +++ b/Documentation/libtracefs.txt @@ -80,6 +80,7 @@ Trace events: struct tep_handle pass:[*]*tracefs_local_events_system*(const char pass:[*]_tracing_dir_, const char pass:[*] const pass:[*]_sys_names_); int *tracefs_fill_local_events*(const char pass:[*]_tracing_dir_, struct tep_handle pass:[*]_tep_, int pass:[*]_parsing_failures_); int *tracefs_load_cmdlines*(const char pass:[*]_tracing_dir_, struct tep_handle pass:[*]_tep_); + int *tracefs_load_headers*(const char pass:[*]_tracing_dir_, struct tep_handle pass:[*]_tep_); char pass:[*]*tracefs_event_get_file*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_system_, const char pass:[*]_event_, const char pass:[*]_file_); char pass:[*]*tracefs_event_file_read*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_system_, const char pass:[*]_event_, diff --git a/include/tracefs.h b/include/tracefs.h index 31aba92d9a16..95bff1f244f9 100644 --- a/include/tracefs.h +++ b/include/tracefs.h @@ -180,6 +180,8 @@ int tracefs_fill_local_events(const char *tracing_dir, int tracefs_load_cmdlines(const char *tracing_dir, struct tep_handle *tep); +int tracefs_load_headers(const char *tracing_dir, struct tep_handle *tep); + char *tracefs_get_clock(struct tracefs_instance *instance); enum tracefs_option_id { diff --git a/src/tracefs-events.c b/src/tracefs-events.c index 2e87f9aa3af7..413c2df19998 100644 --- a/src/tracefs-events.c +++ b/src/tracefs-events.c @@ -1216,6 +1216,28 @@ int tracefs_load_cmdlines(const char *tracing_dir, struct tep_handle *tep) return load_saved_cmdlines(tracing_dir, tep, true); } +/** + * tracefs_load_headers - load just the headers into a tep handle + * @tracing_dir: The directory to load from (NULL to figure it out) + * @tep: The tep handle to load the headers into. + * + * Updates the @tep handle with the event and sub-buffer header + * information. + * + * Returns 0 on success and -1 on error. + */ +int tracefs_load_headers(const char *tracing_dir, struct tep_handle *tep) +{ + int ret; + + if (!tracing_dir) + tracing_dir = tracefs_tracing_dir(); + + ret = read_header(tep, tracing_dir); + + return ret < 0 ? -1 : 0; +} + static int fill_local_events_system(const char *tracing_dir, struct tep_handle *tep, const char * const *sys_names, diff --git a/src/tracefs-record.c b/src/tracefs-record.c index bfeae18fd77f..1eede996631d 100644 --- a/src/tracefs-record.c +++ b/src/tracefs-record.c @@ -110,10 +110,8 @@ tracefs_cpu_open(struct tracefs_instance *instance, int cpu, bool nonblock) struct tep_handle *tep; struct kbuffer *kbuf; char path[128]; - char *buf; int mode = O_RDONLY; int subbuf_size; - int len; int ret; int fd; @@ -131,12 +129,7 @@ tracefs_cpu_open(struct tracefs_instance *instance, int cpu, bool nonblock) goto fail; /* Get the size of the page */ - buf = tracefs_instance_file_read(NULL, "events/header_page", &len); - if (!buf) - goto fail; - - ret = tep_parse_header_page(tep, buf, len, sizeof(long)); - free(buf); + ret = tracefs_load_headers(NULL, tep); if (ret < 0) goto fail; From patchwork Thu Dec 28 20:35:39 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13506209 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D642E107BF for ; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3625CC433B9; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.97) (envelope-from ) id 1rIx7z-00000000Dtc-43Wc; Thu, 28 Dec 2023 15:37:15 -0500 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: "Steven Rostedt (Google)" Subject: [PATCH 17/23] libtracefs: Add API to extract ring buffer statistics Date: Thu, 28 Dec 2023 15:35:39 -0500 Message-ID: <20231228203714.53294-18-rostedt@goodmis.org> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231228203714.53294-1-rostedt@goodmis.org> References: <20231228203714.53294-1-rostedt@goodmis.org> Precedence: bulk X-Mailing-List: linux-trace-devel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: "Steven Rostedt (Google)" Add the API that reads the tracefs/per_cpu/cpu*/stats tracefs_instance_get_stat() tracefs_instance_put_stat() tracefs_buffer_stat_entries() tracefs_buffer_stat_overrun() tracefs_buffer_stat_commit_overrun() tracefs_buffer_stat_bytes() tracefs_buffer_stat_event_timestamp() tracefs_buffer_stat_timestamp() tracefs_buffer_stat_dropped_events() tracefs_buffer_stat_read_events() Signed-off-by: Steven Rostedt (Google) --- Documentation/libtracefs-instances-stat.txt | 183 ++++++++++++++++++++ Documentation/libtracefs.txt | 12 ++ include/tracefs-local.h | 11 ++ include/tracefs.h | 13 ++ samples/Makefile | 1 + src/Makefile | 1 + src/tracefs-stats.c | 162 +++++++++++++++++ 7 files changed, 383 insertions(+) create mode 100644 Documentation/libtracefs-instances-stat.txt create mode 100644 src/tracefs-stats.c diff --git a/Documentation/libtracefs-instances-stat.txt b/Documentation/libtracefs-instances-stat.txt new file mode 100644 index 000000000000..d3bb3c93d9d1 --- /dev/null +++ b/Documentation/libtracefs-instances-stat.txt @@ -0,0 +1,183 @@ +libtracefs(3) +============= + +NAME +---- +tracefs_instance_get_stat, tracefs_instance_put_stat, tracefs_buffer_stat_entries, tracefs_buffer_stat_overrun, +tracefs_buffer_stat_commit_overrun, tracefs_buffer_stat_bytes, tracefs_buffer_stat_event_timestamp, +tracefs_buffer_stat_timestamp, tracefs_buffer_stat_dropped_events, tracefs_buffer_stat_read_events +- Handling tracing buffer stats + +SYNOPSIS +-------- +[verse] +-- +*#include * + +struct tracefs_buffer_stat pass:[*]*tracefs_instance_get_stat*(struct tracefs_instance pass:[*]_instance_, int _cpu_); +void *tracefs_instance_put_stat*(struct tracefs_buffer_stat pass:[*]_tstat_); +ssize_t *tracefs_buffer_stat_entries*(struct tracefs_buffer_stat pass:[*]_tstat_); +ssize_t *tracefs_buffer_stat_overrun*(struct tracefs_buffer_stat pass:[*]_tstat_); +ssize_t *tracefs_buffer_stat_commit_overrun*(struct tracefs_buffer_stat pass:[*]_tstat_); +ssize_t *tracefs_buffer_stat_bytes*(struct tracefs_buffer_stat pass:[*]_tstat_); +long long *tracefs_buffer_stat_event_timestamp*(struct tracefs_buffer_stat pass:[*]_tstat_); +long long *tracefs_buffer_stat_timestamp*(struct tracefs_buffer_stat pass:[*]_tstat_); +ssize_t *tracefs_buffer_stat_dropped_events*(struct tracefs_buffer_stat pass:[*]_tstat_); +ssize_t *tracefs_buffer_stat_read_events*(struct tracefs_buffer_stat pass:[*]_tstat_); +-- + +DESCRIPTION +----------- +This set of functions read and parse the tracefs/per_cpu/cpuX/stats file. +These files hold the statistics of the per CPU ring buffer, such as how +many events are in the ring buffer, how many have been read and so on. + +The *tracefs_instance_get_stat()* function will read and parse a given statistics +file for a given _instance_ and _cpu_. As the ring buffer is split into per_cpu buffers, +the information is only associated to the given _cpu_. The returned tracefs_buffer_stat +pointer can be used with the other *tracefs_buffer_stat* functions and must be freed with +*tracefs_instance_put_stat()*. + +The *tracefs_instance_put_stat()* will free the resources allocated for the given _stat_ +that was created by *tracefs_instance_get_stat()*. + +The *tracefs_buffer_stat_entries()* returns the number of events that are currently +in the ring buffer associated with _tstat_. + +The *tracefs_buffer_stat_overrun()* returns the number of events that were lost by +the ring buffer writer overrunning the reader. + +The *tracefs_buffer_stat_commit_overrun()* returns the number of events that were +lost because the ring buffer was too small and an interrupt interrupted a lower +context event being recorded and it added more events than the ring buffer could +hold. Note this is not a common occurrence and when it happens it means that +something was not set up properly. + +The *tracefs_buffer_stat_bytes()* returns the number of bytes that the current events +take up. Note, it includes the meta data for the events, but does not include the +meta data for the sub-buffers. + +The *tracefs_buffer_stat_event_timestamp()* returns the timestamp of the last event in the +ring buffer. + +The *tracefs_buffer_stat_timestamp()* returns the current timestamp of the ring buffer. +Note, it is only read when *tracefs_instance_get_stat()* is called. It will have the +timestamp of the ring buffer when that function was called. + +The *tracefs_buffer_stat_dropped_events()* returns the number of events that were +dropped if overwrite mode is disabled. It will show the events that were lost because +the writer caught up to the reader and could not write any more events. + +The *tracefs_buffer_stat_read_events()* returns the number of events that were consumed +by a reader. + + +RETURN VALUE +------------ +The *tracefs_instance_get_stat()* returns a tracefs_buffer_stat structure that can +be used to retrieve the statistics via the other functions. It must be freed with +*tracefs_instance_put_stat()*. + +The other functions that return different values from the tracefs_buffer_stat structure +all return the value, or -1 if the value was not found. + + +EXAMPLE +------- +[source,c] +-- +#include +#include +#include + +int main(int argc, char **argv) +{ + char *trace; + char buf[1000]; + int ret; + int i; + + for (i = 0; i < sizeof(buf) - 1; i++) { + buf[i] = '0' + i % 10; + } + buf[i] = '\0'; + + tracefs_instance_clear(NULL); + + for (i = 0; i < 4; i++) { + ret = tracefs_printf(NULL, "%s\n", buf); + if (ret < 0) + perror("write"); + } + + trace = tracefs_instance_file_read(NULL, "trace", NULL); + printf("%s\n", trace); + free(trace); + + for (i = 0; i < sysconf(_SC_NPROCESSORS_CONF); i++) { + struct tracefs_buffer_stat *tstat; + ssize_t entries, eread; + + tstat = tracefs_instance_get_stat(NULL, i); + if (!tstat) + continue; + + entries = tracefs_buffer_stat_entries(tstat); + eread = tracefs_buffer_stat_read_events(tstat); + if (!entries && !eread) { + tracefs_instance_put_stat(tstat); + continue; + } + + printf("CPU: %d\n", i);; + printf("\tentries: %zd\n", entries); + printf("\toverrun: %zd\n", tracefs_buffer_stat_overrun(tstat)); + printf("\tcommit_overrun: %zd\n", tracefs_buffer_stat_commit_overrun(tstat)); + printf("\tbytes: %zd\n", tracefs_buffer_stat_bytes(tstat)); + printf("\tevent_timestamp: %lld\n", tracefs_buffer_stat_event_timestamp(tstat)); + printf("\ttimestamp: %lld\n", tracefs_buffer_stat_timestamp(tstat)); + printf("\tdropped_events: %zd\n", tracefs_buffer_stat_dropped_events(tstat)); + printf("\tread_events: %zd\n", eread); + + tracefs_instance_put_stat(tstat); + } +} +-- +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* +-- +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). diff --git a/Documentation/libtracefs.txt b/Documentation/libtracefs.txt index 70bd8116b2b1..6752ed3c9b98 100644 --- a/Documentation/libtracefs.txt +++ b/Documentation/libtracefs.txt @@ -155,6 +155,18 @@ Writing data in the trace buffer: Control library logs: int *tracefs_set_loglevel*(enum tep_loglevel _level_); +Read the ring buffer statistics: + struct tracefs_buffer_stat pass:[*]*tracefs_instance_get_stat*(struct tracefs_instance pass:[*]_instance_, int _cpu_); + void *tracefs_instance_put_stat*(struct tracefs_buffer_stat pass:[*]_tstat_); + ssize_t *tracefs_buffer_stat_entries*(struct tracefs_buffer_stat pass:[*]_tstat_); + ssize_t *tracefs_buffer_stat_overrun*(struct tracefs_buffer_stat pass:[*]_tstat_); + ssize_t *tracefs_buffer_stat_commit_overrun*(struct tracefs_buffer_stat pass:[*]_tstat_); + ssize_t *tracefs_buffer_stat_bytes*(struct tracefs_buffer_stat pass:[*]_tstat_); + long long *tracefs_buffer_stat_event_timestamp*(struct tracefs_buffer_stat pass:[*]_tstat_); + long long *tracefs_buffer_stat_timestamp*(struct tracefs_buffer_stat pass:[*]_tstat_); + ssize_t *tracefs_buffer_stat_dropped_events*(struct tracefs_buffer_stat pass:[*]_tstat_); + ssize_t *tracefs_buffer_stat_read_events*(struct tracefs_buffer_stat pass:[*]_tstat_); + Dynamic event generic APIs: struct *tracefs_dynevent*; enum *tracefs_dynevent_type*; diff --git a/include/tracefs-local.h b/include/tracefs-local.h index 9e5a568468b4..9cae73c8b806 100644 --- a/include/tracefs-local.h +++ b/include/tracefs-local.h @@ -51,6 +51,17 @@ struct tracefs_instance { bool iterate_keep_going; }; +struct tracefs_buffer_stat { + ssize_t entries; + ssize_t overrun; + ssize_t commit_overrun; + ssize_t bytes; + long long oldest_ts; + long long now_ts; + ssize_t dropped_events; + ssize_t read_events; +}; + extern const struct tep_format_field common_stacktrace; extern pthread_mutex_t toplevel_lock; diff --git a/include/tracefs.h b/include/tracefs.h index 95bff1f244f9..3ae78d4f3af7 100644 --- a/include/tracefs.h +++ b/include/tracefs.h @@ -68,6 +68,19 @@ char **tracefs_instances(const char *regex); int tracefs_instance_get_buffer_percent(struct tracefs_instance *instance); int tracefs_instance_set_buffer_percent(struct tracefs_instance *instance, int val); +struct tracefs_buffer_stat; + +struct tracefs_buffer_stat *tracefs_instance_get_stat(struct tracefs_instance *instance, int cpu); +void tracefs_instance_put_stat(struct tracefs_buffer_stat *tstat); +ssize_t tracefs_buffer_stat_entries(struct tracefs_buffer_stat *tstat); +ssize_t tracefs_buffer_stat_overrun(struct tracefs_buffer_stat *tstat); +ssize_t tracefs_buffer_stat_commit_overrun(struct tracefs_buffer_stat *tstat); +ssize_t tracefs_buffer_stat_bytes(struct tracefs_buffer_stat *tstat); +long long tracefs_buffer_stat_event_timestamp(struct tracefs_buffer_stat *tstat); +long long tracefs_buffer_stat_timestamp(struct tracefs_buffer_stat *tstat); +ssize_t tracefs_buffer_stat_dropped_events(struct tracefs_buffer_stat *tstat); +ssize_t tracefs_buffer_stat_read_events(struct tracefs_buffer_stat *tstat); + bool tracefs_instance_exists(const char *name); bool tracefs_file_exists(struct tracefs_instance *instance, const char *name); bool tracefs_dir_exists(struct tracefs_instance *instance, const char *name); diff --git a/samples/Makefile b/samples/Makefile index 13ad99579ea3..787d28769051 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -24,6 +24,7 @@ EXAMPLES += instances-affinity EXAMPLES += cpu EXAMPLES += guest EXAMPLES += cpu-buf +EXAMPLES += instances-stat TARGETS := TARGETS += sqlhist diff --git a/src/Makefile b/src/Makefile index 90bd88df44c2..faa3b25c4002 100644 --- a/src/Makefile +++ b/src/Makefile @@ -10,6 +10,7 @@ OBJS += tracefs-tools.o OBJS += tracefs-marker.o OBJS += tracefs-kprobes.o OBJS += tracefs-hist.o +OBJS += tracefs-stats.o OBJS += tracefs-filter.o OBJS += tracefs-dynevents.o OBJS += tracefs-eprobes.o diff --git a/src/tracefs-stats.c b/src/tracefs-stats.c new file mode 100644 index 000000000000..d43235bc0b38 --- /dev/null +++ b/src/tracefs-stats.c @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * Copyright (C) 2023 Google LLC, Steven Rostedt + */ +#include +#include +#include "tracefs.h" +#include "tracefs-local.h" + +static long long convert_ts(char *value) +{ + long long ts; + char *saveptr; + char *secs; + char *usecs; + + secs = strtok_r(value, ".", &saveptr); + if (!secs) + return -1LL; + + ts = strtoll(secs, NULL, 0); + + usecs = strtok_r(NULL, ".", &saveptr); + if (!usecs) + return ts; + + /* Could be in nanoseconds */ + if (strlen(usecs) > 6) + ts *= 1000000000LL; + else + ts *= 1000000LL; + + ts += strtoull(usecs, NULL, 0); + + return ts; +} + +struct tracefs_buffer_stat * +tracefs_instance_get_stat(struct tracefs_instance *instance, int cpu) +{ + struct tracefs_buffer_stat *tstat; + char *saveptr; + char *value; + char *field; + char *path; + char *line; + char *next; + char *buf; + int len; + int ret; + + ret = asprintf(&path, "per_cpu/cpu%d/stats", cpu); + if (ret < 0) + return NULL; + + buf = tracefs_instance_file_read(instance, path, &len); + free(path); + + if (!buf) + return NULL; + + tstat = malloc(sizeof(*tstat)); + if (!tstat) { + free(buf); + return NULL; + } + + /* Set everything to -1 */ + memset(tstat, -1, sizeof(*tstat)); + + next = buf; + while ((line = strtok_r(next, "\n", &saveptr))) { + char *save2; + + next = NULL; + + field = strtok_r(line, ":", &save2); + if (!field) + break; + + value = strtok_r(NULL, ":", &save2); + if (!value) + break; + + while (isspace(*value)) + value++; + + if (strcmp(field, "entries") == 0) { + tstat->entries = strtoull(value, NULL, 0); + + } else if (strcmp(field, "overrun") == 0) { + tstat->overrun = strtoull(value, NULL, 0); + + } else if (strcmp(field, "commit overrun") == 0) { + tstat->commit_overrun = strtoull(value, NULL, 0); + + } else if (strcmp(field, "bytes") == 0) { + tstat->bytes = strtoull(value, NULL, 0); + + } else if (strcmp(field, "oldest event ts") == 0) { + tstat->oldest_ts = convert_ts(value); + + } else if (strcmp(field, "now ts") == 0) { + tstat->now_ts = convert_ts(value); + + } else if (strcmp(field, "dropped events") == 0) { + tstat->dropped_events = strtoull(value, NULL, 0); + + } else if (strcmp(field, "read events") == 0) { + tstat->read_events = strtoull(value, NULL, 0); + } + } + free(buf); + + return tstat; +} + +void tracefs_instance_put_stat(struct tracefs_buffer_stat *tstat) +{ + free(tstat); +} + +ssize_t tracefs_buffer_stat_entries(struct tracefs_buffer_stat *tstat) +{ + return tstat->entries; +} + +ssize_t tracefs_buffer_stat_overrun(struct tracefs_buffer_stat *tstat) +{ + return tstat->overrun; +} + +ssize_t tracefs_buffer_stat_commit_overrun(struct tracefs_buffer_stat *tstat) +{ + return tstat->commit_overrun; +} + +ssize_t tracefs_buffer_stat_bytes(struct tracefs_buffer_stat *tstat) +{ + return tstat->bytes; +} + +long long tracefs_buffer_stat_event_timestamp(struct tracefs_buffer_stat *tstat) +{ + return tstat->oldest_ts; +} + +long long tracefs_buffer_stat_timestamp(struct tracefs_buffer_stat *tstat) +{ + return tstat->now_ts; +} + +ssize_t tracefs_buffer_stat_dropped_events(struct tracefs_buffer_stat *tstat) +{ + return tstat->dropped_events; +} + +ssize_t tracefs_buffer_stat_read_events(struct tracefs_buffer_stat *tstat) +{ + return tstat->read_events; +} + From patchwork Thu Dec 28 20:35:40 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13506203 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A814F10945 for ; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3C58DC433B8; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.97) (envelope-from ) id 1rIx7z-00000000Dtg-49nz; Thu, 28 Dec 2023 15:37:15 -0500 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: "Steven Rostedt (Google)" Subject: [PATCH 18/23] libtracefs: Add tracefs_instance_set/get_subbuf_size() Date: Thu, 28 Dec 2023 15:35:40 -0500 Message-ID: <20231228203714.53294-19-rostedt@goodmis.org> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231228203714.53294-1-rostedt@goodmis.org> References: <20231228203714.53294-1-rostedt@goodmis.org> Precedence: bulk X-Mailing-List: linux-trace-devel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: "Steven Rostedt (Google)" Add API to retrieve and modify the current sub-buffer size. tracefs_instance_set_subbuf_size() tracefs_instance_get_subbuf_size() Signed-off-by: Steven Rostedt (Google) --- Documentation/libtracefs-instances-subbuf.txt | 152 ++++++++++++++++++ Documentation/libtracefs.txt | 4 + include/tracefs.h | 2 + samples/Makefile | 1 + src/tracefs-instance.c | 37 +++++ 5 files changed, 196 insertions(+) create mode 100644 Documentation/libtracefs-instances-subbuf.txt diff --git a/Documentation/libtracefs-instances-subbuf.txt b/Documentation/libtracefs-instances-subbuf.txt new file mode 100644 index 000000000000..8d5c3e0ed3bd --- /dev/null +++ b/Documentation/libtracefs-instances-subbuf.txt @@ -0,0 +1,152 @@ +libtracefs(3) +============= + +NAME +---- +tracefs_instance_get_subbuf_size, tracefs_instance_set_subbuf_size - Helper functions for working with ring buffer sub buffers. + +SYNOPSIS +-------- +[verse] +-- +*#include * + +size_t *tracefs_instance_get_subbuf_size*(struct tracefs_instance pass:[*]_instance_); +int *tracefs_instance_set_subbuf_size*(struct tracefs_instance pass:[*]_instance_, size_t _size_); +-- + +DESCRIPTION +----------- +Helper functions for working with the sub-buffers of the tracing ring buffer. +The tracing ring buffer is broken up into *sub-buffers*. An event can not be +bigger than the data section of the sub-buffer (see *tep_get_sub_buffer_data_size*(3)). +By default, the ring buffer uses the architectures *page_size* as the default +size of the sub-buffer, but this can be limiting if there is a need for large +events, for example, the application wants to write large strings into +the trace_marker file. + +The *tracefs_instance_get_subbuf_size()* returns the current size in kilobytes +fo the ring buffer sub-buffers. + +The *tracefs_instance_set_subbuf_size()* will write the size in kilobytes of +what the new sub-buffer size should be. Note, that this is only a hint to what +the minimum sub-buffer size should be. It also does not take into account the +meta-data that is used by the sub-buffer, so the size written should be no less +than 16 bytes more than the maximum event size that will be used. The kernel +will likely make the sub-buffer size larger than specified, as it may need to +align the size for implementation purposes. + +RETURN VALUE +------------ +The *tracefs_instance_get_subbuf_size()* returns the size of the current +sub-buffer for the given _instance_ ring buffer or -1 on error. + +The *tracefs_instance_set_subbuf_size()* will return 0 if it successfully set +the _instance_ ring buffer sub-buffer size in kilobytes, or -1 on error. + +EXAMPLE +------- +[source,c] +-- +#include +#include +#include + +int main(int argc, char **argv) +{ + struct tep_handle *tep; + ssize_t save_subsize; + ssize_t subsize; + char *trace; + char buf[3000]; + int meta_size; + int ret; + int i; + + tep = tep_alloc(); + ret = tracefs_load_headers(NULL, tep); + tep_free(tep); + + if (ret < 0) { + perror("reading headers"); + exit(-1); + } + + meta_size = tep_get_sub_buffer_size(tep) - tep_get_sub_buffer_data_size(tep); + + save_subsize = tracefs_instance_get_subbuf_size(NULL); + if (save_subsize < 0) { + printf("Changing sub-buffer size not available\n"); + exit(-1); + } + + subsize = save_subsize * 1024; + + /* Have at least 4 writes fit on a sub-buffer */ + if (subsize - meta_size < sizeof(buf) *4 ) { + subsize = ((sizeof(buf) * 4 + meta_size) + 1023) / 1024; + tracefs_instance_set_subbuf_size(NULL, subsize); + } + + for (i = 0; i < sizeof(buf) - 1; i++) { + buf[i] = '0' + i % 10; + } + buf[i] = '\0'; + + tracefs_instance_clear(NULL); + + for (i = 0; i < 4; i++) { + ret = tracefs_printf(NULL, "%s\n", buf); + if (ret < 0) + perror("write"); + } + + trace = tracefs_instance_file_read(NULL, "trace", NULL); + printf("%s\n", trace); + free(trace); + + printf("Buffer size was: %zd * 1024\n", + tracefs_instance_get_subbuf_size(NULL)); + + tracefs_instance_set_subbuf_size(NULL, save_subsize); +} +-- +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). diff --git a/Documentation/libtracefs.txt b/Documentation/libtracefs.txt index 6752ed3c9b98..1962b3b34622 100644 --- a/Documentation/libtracefs.txt +++ b/Documentation/libtracefs.txt @@ -324,6 +324,10 @@ Recording of trace_pipe_raw files: struct kbuffer pass:[*]*tracefs_cpu_buffered_read_buf*(struct tracefs_cpu pass:[*]_tcpu_, bool _nonblock_); struct kbuffer pass:[*]*tracefs_cpu_flush_buf*(struct tracefs_cpu pass:[*]_tcpu_); +Helper functions for modifying the ring buffer sub-buffers: + size_t *tracefs_instance_get_subbuf_size*(struct tracefs_instance pass:[*]_instance_); + int *tracefs_instance_set_subbuf_size*(struct tracefs_instance pass:[*]_instance_, size_t _size_); + Helper functions for guest tracing: char pass:[*]*tracefs_find_cid_pid*(int _cid_); char pass:[*]*tracefs_instance_find_cid_pid*(struct tracefs_instance pass:[*]_instance_, int _cid_); diff --git a/include/tracefs.h b/include/tracefs.h index 3ae78d4f3af7..c66bfd2edf0b 100644 --- a/include/tracefs.h +++ b/include/tracefs.h @@ -63,6 +63,8 @@ int tracefs_instance_get_affinity_set(struct tracefs_instance *instance, cpu_set_t *set, size_t set_size); ssize_t tracefs_instance_get_buffer_size(struct tracefs_instance *instance, int cpu); int tracefs_instance_set_buffer_size(struct tracefs_instance *instance, size_t size, int cpu); +ssize_t tracefs_instance_get_subbuf_size(struct tracefs_instance *instance); +int tracefs_instance_set_subbuf_size(struct tracefs_instance *instance, size_t size); char **tracefs_instances(const char *regex); int tracefs_instance_get_buffer_percent(struct tracefs_instance *instance); diff --git a/samples/Makefile b/samples/Makefile index 787d28769051..77739c8b0aa7 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -25,6 +25,7 @@ EXAMPLES += cpu EXAMPLES += guest EXAMPLES += cpu-buf EXAMPLES += instances-stat +EXAMPLES += instances-subbuf TARGETS := TARGETS += sqlhist diff --git a/src/tracefs-instance.c b/src/tracefs-instance.c index 2162424ad3a7..8d9dd0e21498 100644 --- a/src/tracefs-instance.c +++ b/src/tracefs-instance.c @@ -441,6 +441,43 @@ int tracefs_instance_set_buffer_size(struct tracefs_instance *instance, size_t s return ret < 0 ? -1 : 0; } +/** + * tracefs_instance_get_subbuf_size - return the sub-buffer size of the ring buffer + * @instance: The instance to get the buffer size from + * + * Returns the sub-buffer size in kilobytes. + * Returns -1 on error. + */ +ssize_t tracefs_instance_get_subbuf_size(struct tracefs_instance *instance) +{ + long long size; + int ret; + + ret = tracefs_instance_file_read_number(instance, "buffer_subbuf_size_kb", &size); + if (ret < 0) + return ret; + + return size; +} + +/** + * tracefs_instance_set_buffer_size - modify the ring buffer sub-buffer size + * @instance: The instance to modify (NULL for the top level) + * @size: The size in kilobytes to to set the sub-buffer size to + * + * Sets the sub-buffer size in kilobytes for the given ring buffer. + * + * Returns 0 on success and -1 on error. + */ +int tracefs_instance_set_subbuf_size(struct tracefs_instance *instance, size_t size) +{ + int ret; + + ret = tracefs_instance_file_write_number(instance, "buffer_subbuf_size_kb", size); + + return ret < 0 ? -1 : 0; +} + /** * tracefs_instance_get_trace_dir - return the top trace directory, where the instance is confuigred * @instance: ftrace instance From patchwork Thu Dec 28 20:35:41 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13506210 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D6408107BE for ; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 42263C433BA; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.97) (envelope-from ) id 1rIx80-00000000Dtk-04ze; Thu, 28 Dec 2023 15:37:16 -0500 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: "Steven Rostedt (Google)" Subject: [PATCH 19/23] libtracefs: Add ring buffer memory mapping APIs Date: Thu, 28 Dec 2023 15:35:41 -0500 Message-ID: <20231228203714.53294-20-rostedt@goodmis.org> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231228203714.53294-1-rostedt@goodmis.org> References: <20231228203714.53294-1-rostedt@goodmis.org> Precedence: bulk X-Mailing-List: linux-trace-devel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: "Steven Rostedt (Google)" Add the following APIs: tracefs_cpu_open_mapped() tracefs_cpu_is_mapped() tracefs_cpu_map() tracefs_cpu_unmap() This will allow applications to choose to memory map the tracing ring buffer if it is supported. This will improve the performance of tracefs_cpu_read() and tracefs_cpu_read_buf(), but it is not done by default because it will also hurt the performance of tracefs_cpu_buffered_read() and tracefs_cpu_buffered_read_buf() as those use splicing, and with the ring buffer memory mapped, the splice has to do a copy instead of a copyless subbuffer move. Signed-off-by: Steven Rostedt (Google) --- Documentation/libtracefs-cpu-map.txt | 194 +++++++++++++++++++++++++++ Documentation/libtracefs.txt | 7 + include/tracefs-local.h | 6 + include/tracefs.h | 7 + samples/Makefile | 1 + src/Makefile | 1 + src/tracefs-mmap.c | 190 ++++++++++++++++++++++++++ src/tracefs-record.c | 61 +++++++++ 8 files changed, 467 insertions(+) create mode 100644 Documentation/libtracefs-cpu-map.txt create mode 100644 src/tracefs-mmap.c diff --git a/Documentation/libtracefs-cpu-map.txt b/Documentation/libtracefs-cpu-map.txt new file mode 100644 index 000000000000..9bcd29715795 --- /dev/null +++ b/Documentation/libtracefs-cpu-map.txt @@ -0,0 +1,194 @@ +libtracefs(3) +============= + +NAME +---- +tracefs_cpu_open_mapped, tracefs_cpu_is_mapped, tracefs_cpu_map, tracefs_cpu_unmap - Memory mapping of the ring buffer + +SYNOPSIS +-------- +[verse] +-- +*#include * + +bool *tracefs_cpu_is_mapped*(struct tracefs_cpu pass:[*]tcpu); +int *tracefs_cpu_map*(struct tracefs_cpu pass:[*]tcpu); +void *tracefs_cpu_unmap*(struct tracefs_cpu pass:[*]tcpu); +struct tracefs_cpu pass:[*]*tracefs_cpu_open_mapped*(struct tracefs_instance pass:[*]instance, + int cpu, bool nonblock); +-- + +DESCRIPTION +----------- +If the trace_pipe_raw supports memory mapping, this is usually a more efficient +method to stream data from the kernel ring buffer than by reading it, as it does +not require copying the memory that is being read. + +If memory mapping is supported by the kernel and the application asks to use the +memory mapping via either *tracefs_cpu_map()* or by *tracefs_cpu_open_mapped()* +then the functions *tracefs_cpu_read*(3) and *tracefs_cpu_read_buf*(3) will use +the mapping directly instead of calling the read system call. + +Note, mapping can also slow down *tracefs_cpu_buffered_read*(3) and +*tracefs_cpu_buffered_read_buf*(3), as those use splice piping and when the +kernel ring buffer is memory mapped, splice does a copy instead of using the +ring buffer directly. Thus care must be used when determining to map the +ring buffer or not, and why it does not get mapped by default. + +The *tracefs_cpu_is_mapped()* function will return true if _tcpu_ currently has +its ring buffer memory mapped and false otherwise. This does not return whether or +not that the kernel supports memory mapping, but that can usually be determined +by calling *tracefs_cpu_map()*. + +The *tracefs_cpu_map()* function will attempt to map the ring buffer associated +to _tcpu_ if it is not already mapped. + +The *tracefs_cpu_unmap()* function will unmap the ring buffer associated to +_tcpu_ if it is mapped. + +The *tracefs_cpu_open_mapped()* is equivalent to calling *tracefs_cpu_open*(3) followed +by *tracefs_cpu_map()* on the returned _tcpu_ of *tracefs_cpu_open*(3). Note, this +will still succeed if the mapping fails, in which case it acts the same as +*tracefs_cpu_open*(3). If knowing if the mapping succeed or not, *tracefs_cpu_is_mapped()* +should be called on the return _tcpu_. + +RETURN VALUE +------------ +*tracefs_cpu_is_mapped()* returns true if the given _tcpu_ has its ring buffer +memory mapped or false otherwise. + +*tracefs_cpu_map()* returns 0 on success and -1 on error in mapping. If 0 is +returned then *tracefs_cpu_is_mapped()* will return true afterward, or false +if the mapping failed. + +*tracefs_cpu_open_mapped()* returns an allocated tracefs_cpu on success of creation +regardless if it succeed in mapping the ring buffer or not. It returns NULL for +the same reasons *tracefs_cpu_open*(3) returns NULL. If success of mapping is +to be known, then calling *tracefs_cpu_is_mapped()* afterward is required. + +EXAMPLE +------- +[source,c] +-- +#include +#include +#include + +static void read_page(struct tep_handle *tep, struct kbuffer *kbuf) +{ + static struct trace_seq seq; + struct tep_record record; + + if (seq.buffer) + trace_seq_reset(&seq); + else + trace_seq_init(&seq); + + while ((record.data = kbuffer_read_event(kbuf, &record.ts))) { + record.size = kbuffer_event_size(kbuf); + kbuffer_next_event(kbuf, NULL); + tep_print_event(tep, &seq, &record, + "%s-%d %9d\t%s: %s\n", + TEP_PRINT_COMM, + TEP_PRINT_PID, + TEP_PRINT_TIME, + TEP_PRINT_NAME, + TEP_PRINT_INFO); + trace_seq_do_printf(&seq); + trace_seq_reset(&seq); + } +} + +int main (int argc, char **argv) +{ + struct tracefs_cpu *tcpu; + struct tep_handle *tep; + struct kbuffer *kbuf; + bool mapped; + int cpu; + + if (argc < 2 || !isdigit(argv[1][0])) { + printf("usage: %s cpu\n\n", argv[0]); + exit(-1); + } + + cpu = atoi(argv[1]); + + tep = tracefs_local_events(NULL); + if (!tep) { + perror("Reading trace event formats"); + exit(-1); + } + + tcpu = tracefs_cpu_open_mapped(NULL, cpu, 0); + if (!tcpu) { + perror("Open CPU 0 file"); + exit(-1); + } + + /* + * If this kernel supports mapping, use normal read, + * otherwise use the piped buffer read. + */ + mapped = tracefs_cpu_is_mapped(tcpu); + if (!mapped) + printf("Was not able to map, falling back to buffered read\n"); + while ((kbuf = mapped ? tracefs_cpu_read_buf(tcpu, true) : + tracefs_cpu_buffered_read_buf(tcpu, true))) { + read_page(tep, kbuf); + } + + kbuf = tracefs_cpu_flush_buf(tcpu); + if (kbuf) + read_page(tep, kbuf); + + tracefs_cpu_close(tcpu); + tep_free(tep); + + return 0; +} +-- + +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 +-------- +*tracefs_cpu_open*(3), +*tracefs_cpu_read*(3), +*tracefs_cpu_read_buf*(3), +*tracefs_cpu_buffered_read*(3), +*tracefs_cpu_buffered_read_buf*(3), +*libtracefs*(3), +*libtraceevent*(3), +*trace-cmd*(1) + +AUTHOR +------ +[verse] +-- +*Steven Rostedt* +-- +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) 2022 Google, Inc. Free use of this software is granted under +the terms of the GNU Public License (GPL). diff --git a/Documentation/libtracefs.txt b/Documentation/libtracefs.txt index 1962b3b34622..2f2b4d488942 100644 --- a/Documentation/libtracefs.txt +++ b/Documentation/libtracefs.txt @@ -124,6 +124,13 @@ Trace stream: ssize_t *tracefs_trace_pipe_print*(struct tracefs_instance pass:[*]_instance_, int _flags_); void *tracefs_trace_pipe_stop*(struct tracefs_instance pass:[*]_instance_); +Memory mapping the ring buffer: + bool *tracefs_cpu_is_mapped*(struct tracefs_cpu pass:[*]tcpu); + int *tracefs_cpu_map*(struct tracefs_cpu pass:[*]tcpu); + void *tracefs_cpu_unmap*(struct tracefs_cpu pass:[*]tcpu); + struct tracefs_cpu pass:[*]*tracefs_cpu_open_mapped*(struct tracefs_instance pass:[*]instance, + int cpu, bool nonblock); + Trace options: const struct tracefs_options_mask pass:[*]*tracefs_options_get_supported*(struct tracefs_instance pass:[*]_instance_); bool *tracefs_option_is_supported*(struct tracefs_instance pass:[*]_instance_, enum tracefs_option_id _id_); diff --git a/include/tracefs-local.h b/include/tracefs-local.h index 9cae73c8b806..ffc9d33b1796 100644 --- a/include/tracefs-local.h +++ b/include/tracefs-local.h @@ -6,6 +6,7 @@ #ifndef _TRACE_FS_LOCAL_H #define _TRACE_FS_LOCAL_H +#include #include #define __hidden __attribute__((visibility ("hidden"))) @@ -116,6 +117,11 @@ int trace_append_filter(char **filter, unsigned int *state, enum tracefs_compare compare, const char *val); +void *trace_mmap(int fd, struct kbuffer *kbuf); +void trace_unmap(void *mapping); +int trace_mmap_load_subbuf(void *mapping, struct kbuffer *kbuf); +int trace_mmap_read(void *mapping, void *buffer); + struct tracefs_synth *synth_init_from(struct tep_handle *tep, const char *start_system, const char *start_event); diff --git a/include/tracefs.h b/include/tracefs.h index c66bfd2edf0b..d20d733b7d92 100644 --- a/include/tracefs.h +++ b/include/tracefs.h @@ -673,6 +673,13 @@ struct kbuffer *tracefs_cpu_flush_buf(struct tracefs_cpu *tcpu); int tracefs_cpu_flush_write(struct tracefs_cpu *tcpu, int wfd); int tracefs_cpu_pipe(struct tracefs_cpu *tcpu, int wfd, bool nonblock); +/* Memory mapping of ring buffer */ +bool tracefs_cpu_is_mapped(struct tracefs_cpu *tcpu); +int tracefs_cpu_map(struct tracefs_cpu *tcpu); +void tracefs_cpu_unmap(struct tracefs_cpu *tcpu); +struct tracefs_cpu *tracefs_cpu_open_mapped(struct tracefs_instance *instance, + int cpu, bool nonblock); + /* Mapping vsocket cids to pids using tracing */ int tracefs_instance_find_cid_pid(struct tracefs_instance *instance, int cid); int tracefs_find_cid_pid(int cid); diff --git a/samples/Makefile b/samples/Makefile index 77739c8b0aa7..81c8006f823e 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -26,6 +26,7 @@ EXAMPLES += guest EXAMPLES += cpu-buf EXAMPLES += instances-stat EXAMPLES += instances-subbuf +EXAMPLES += cpu-map TARGETS := TARGETS += sqlhist diff --git a/src/Makefile b/src/Makefile index faa3b25c4002..be81059ce10a 100644 --- a/src/Makefile +++ b/src/Makefile @@ -16,6 +16,7 @@ OBJS += tracefs-dynevents.o OBJS += tracefs-eprobes.o OBJS += tracefs-uprobes.o OBJS += tracefs-record.o +OBJS += tracefs-mmap.o ifeq ($(VSOCK_DEFINED), 1) OBJS += tracefs-vsock.o endif diff --git a/src/tracefs-mmap.c b/src/tracefs-mmap.c new file mode 100644 index 000000000000..5675028acc51 --- /dev/null +++ b/src/tracefs-mmap.c @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * Copyright (C) 2023 Google Inc, Steven Rostedt + */ +#include +#include +#include +#include +#include +#include "tracefs-local.h" + +struct trace_buffer_meta { + unsigned long entries; + unsigned long overrun; + unsigned long read; + + unsigned long subbufs_touched; + unsigned long subbufs_lost; + unsigned long subbufs_read; + + struct { + unsigned long lost_events; /* Events lost at the time of the reader swap */ + __u32 id; /* Reader subbuf ID from 0 to nr_subbufs - 1 */ + __u32 read; /* Number of bytes read on the reader subbuf */ + } reader; + + __u32 subbuf_size; /* Size of each subbuf including the header */ + __u32 nr_subbufs; /* Number of subbufs in the ring-buffer */ + + __u32 meta_page_size; /* Size of the meta-page */ + __u32 meta_struct_len; /* Len of this struct */ +}; + +#define TRACE_MMAP_IOCTL_GET_READER _IO('T', 0x1) + +struct trace_mmap { + struct trace_buffer_meta *map; + struct kbuffer *kbuf; + void *data; + int *data_pages; + int fd; + int last_idx; + int last_read; + int meta_len; + int data_len; +}; + +/** + * trace_mmap - try to mmap the ring buffer + * @fd: The file descriptor to the trace_pipe_raw file + * + * Will try to mmap the ring buffer if it is supported, and + * if not, will return NULL, otherwise it returns a descriptor + * to handle the mapping. + */ +void *trace_mmap(int fd, struct kbuffer *kbuf) +{ + struct trace_mmap *tmap; + int page_size; + void *meta; + void *data; + + page_size = getpagesize(); + meta = mmap(NULL, page_size, PROT_READ, MAP_SHARED, fd, 0); + if (meta == MAP_FAILED) + return NULL; + + tmap = calloc(1, sizeof(*tmap)); + if (!tmap) { + munmap(meta, page_size); + return NULL; + } + + tmap->kbuf = kbuffer_dup(kbuf); + if (!tmap->kbuf) { + munmap(meta, page_size); + free(tmap); + } + + tmap->fd = fd; + + tmap->map = meta; + tmap->meta_len = tmap->map->meta_page_size; + + if (tmap->meta_len > page_size) { + munmap(meta, page_size); + meta = mmap(NULL, tmap->meta_len, PROT_READ, MAP_SHARED, fd, 0); + if (meta == MAP_FAILED) { + kbuffer_free(tmap->kbuf); + free(tmap); + return NULL; + } + tmap->map = meta; + } + + tmap->data_pages = meta + tmap->meta_len; + + tmap->data_len = tmap->map->subbuf_size * tmap->map->nr_subbufs; + + tmap->data = mmap(NULL, tmap->data_len, PROT_READ, MAP_SHARED, + fd, tmap->meta_len); + if (tmap->data == MAP_FAILED) { + munmap(meta, tmap->meta_len); + kbuffer_free(tmap->kbuf); + free(tmap); + return NULL; + } + + tmap->last_idx = tmap->map->reader.id; + + data = tmap->data + tmap->map->subbuf_size * tmap->last_idx; + kbuffer_load_subbuffer(kbuf, data); + + return tmap; +} + +void trace_unmap(void *mapping) +{ + struct trace_mmap *tmap = mapping; + + munmap(tmap->data, tmap->data_len); + munmap(tmap->map, tmap->meta_len); + kbuffer_free(tmap->kbuf); + free(tmap); +} + +int trace_mmap_load_subbuf(void *mapping, struct kbuffer *kbuf) +{ + struct trace_mmap *tmap = mapping; + void *data; + int id; + + id = tmap->map->reader.id; + data = tmap->data + tmap->map->subbuf_size * id; + + /* + * If kbuf doesn't point to the current sub-buffer + * just load it and return. + */ + if (data != kbuffer_subbuffer(kbuf)) { + kbuffer_load_subbuffer(kbuf, data); + return 1; + } + + /* + * Perhaps the reader page had a write that added + * more data. + */ + kbuffer_refresh(kbuf); + + /* Are there still events to read? */ + if (kbuffer_curr_size(kbuf)) + return 1; + + /* See if a new page is ready? */ + if (ioctl(tmap->fd, TRACE_MMAP_IOCTL_GET_READER) < 0) + return -1; + id = tmap->map->reader.id; + data = tmap->data + tmap->map->subbuf_size * id; + + /* + * If the sub-buffer hasn't changed, then there's no more + * events to read. + */ + if (data == kbuffer_subbuffer(kbuf)) + return 0; + + kbuffer_load_subbuffer(kbuf, data); + return 1; +} + +int trace_mmap_read(void *mapping, void *buffer) +{ + struct trace_mmap *tmap = mapping; + struct kbuffer *kbuf; + int ret; + + if (!tmap) + return -1; + + kbuf = tmap->kbuf; + + ret = trace_mmap_load_subbuf(mapping, kbuf); + /* Return for error or no more events */ + if (ret <= 0) + return ret; + + /* Update the buffer */ + return kbuffer_read_buffer(kbuf, buffer, tmap->map->subbuf_size); +} diff --git a/src/tracefs-record.c b/src/tracefs-record.c index 1eede996631d..fcef89e527aa 100644 --- a/src/tracefs-record.c +++ b/src/tracefs-record.c @@ -36,6 +36,7 @@ struct tracefs_cpu { int splice_read_flags; struct kbuffer *kbuf; void *buffer; + void *mapping; }; /** @@ -155,6 +156,31 @@ tracefs_cpu_open(struct tracefs_instance *instance, int cpu, bool nonblock) return NULL; } +/** + * tracefs_cpu_open_mapped - open an instance raw trace file and map it + * @instance: the instance (NULL for toplevel) of the cpu raw file to open + * @cpu: The CPU that the raw trace file is associated with + * @nonblock: If true, the file will be opened in O_NONBLOCK mode + * + * Return a descriptor that can read the tracefs trace_pipe_raw file + * for a give @cpu in a given @instance. + * + * Returns NULL on error. + */ +struct tracefs_cpu * +tracefs_cpu_open_mapped(struct tracefs_instance *instance, int cpu, bool nonblock) +{ + struct tracefs_cpu *tcpu; + + tcpu = tracefs_cpu_open(instance, cpu, nonblock); + if (!tcpu) + return NULL; + + tracefs_cpu_map(tcpu); + + return tcpu; +} + static void close_fd(int fd) { if (fd < 0) @@ -211,6 +237,28 @@ int tracefs_cpu_read_size(struct tracefs_cpu *tcpu) return tcpu->subbuf_size; } +bool tracefs_cpu_is_mapped(struct tracefs_cpu *tcpu) +{ + return tcpu->mapping != NULL; +} + +int tracefs_cpu_map(struct tracefs_cpu *tcpu) +{ + if (tcpu->mapping) + return 0; + + tcpu->mapping = trace_mmap(tcpu->fd, tcpu->kbuf); + return tcpu->mapping ? 0 : -1; +} + +void tracefs_cpu_unmap(struct tracefs_cpu *tcpu) +{ + if (!tcpu->mapping) + return; + + trace_unmap(tcpu->mapping); +} + static void set_nonblock(struct tracefs_cpu *tcpu) { long flags; @@ -309,6 +357,9 @@ int tracefs_cpu_read(struct tracefs_cpu *tcpu, void *buffer, bool nonblock) if (ret <= 0) return ret; + if (tcpu->mapping) + return trace_mmap_read(tcpu->mapping, buffer); + ret = read(tcpu->fd, buffer, tcpu->subbuf_size); /* It's OK if there's no data to read */ @@ -353,6 +404,16 @@ struct kbuffer *tracefs_cpu_read_buf(struct tracefs_cpu *tcpu, bool nonblock) { int ret; + /* If mapping is enabled, just use it directly */ + if (tcpu->mapping) { + ret = wait_on_input(tcpu, nonblock); + if (ret <= 0) + return NULL; + + ret = trace_mmap_load_subbuf(tcpu->mapping, tcpu->kbuf); + return ret > 0 ? tcpu->kbuf : NULL; + } + if (!get_buffer(tcpu)) return NULL; From patchwork Thu Dec 28 20:35:42 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13506201 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A5BF0107BE for ; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 4A53CC433BC; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.97) (envelope-from ) id 1rIx80-00000000Dto-0Bvr; Thu, 28 Dec 2023 15:37:16 -0500 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: "Steven Rostedt (Google)" Subject: [PATCH 20/23] libtracefs: Add TIMESTAMP_USECS_DELTA to simplify SQL timestamp compares Date: Thu, 28 Dec 2023 15:35:42 -0500 Message-ID: <20231228203714.53294-21-rostedt@goodmis.org> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231228203714.53294-1-rostedt@goodmis.org> References: <20231228203714.53294-1-rostedt@goodmis.org> Precedence: bulk X-Mailing-List: linux-trace-devel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: "Steven Rostedt (Google)" Since it is so common to have in the SQL synthetic event creation logic: (end.TIMESTAMP_USECS - start.TIMESTAMP_USECS) as delta for creating latency synthetic events, add a shortcut that does the above. Now the user only needs to write: TIMESTAMP_USECS_DELTA as delta and it will produce the same compare. Note, TIMESTAMP_DELTA is also equivalent to: (end.TIMESTAMP - start.TIMESTAMP) Signed-off-by: Steven Rostedt (Google) --- Documentation/libtracefs-sql.txt | 16 ++++++++ src/tracefs-sqlhist.c | 64 +++++++++++++++++++++++++++++++- utest/tracefs-utest.c | 10 +++++ 3 files changed, 88 insertions(+), 2 deletions(-) diff --git a/Documentation/libtracefs-sql.txt b/Documentation/libtracefs-sql.txt index 62fd771b92bb..7921509f3825 100644 --- a/Documentation/libtracefs-sql.txt +++ b/Documentation/libtracefs-sql.txt @@ -127,6 +127,22 @@ The *TIMESTAMP_USECS* will truncate the time down to microseconds as the timesta recorded in the tracing buffer has nanosecond resolution. If you do not want that truncation, use *TIMESTAMP* instead of *TIMESTAMP_USECS*. +Because it is so common to have: + +[source,c] +-- + (end.TIMESTAMP_USECS - start.TIMESTAMP_USECS) +-- + +The above can be represented with *TIMESTAMP_USECS_DELTA* or if nanoseconds are OK, you can +use *TIMESTAMP_DELTA*. That is, the previous select can also be represented by: + +[source,c] +-- +select start.pid, TIMESTAMP_USECS_DELTA as lat from sched_waking as start JOIN sched_switch as end ON start.pid = end.next_pid +-- + + Finally, the *WHERE* clause can be added, that will let you add filters on either or both events. [source,c] diff --git a/src/tracefs-sqlhist.c b/src/tracefs-sqlhist.c index 003592291cb8..3592000e038d 100644 --- a/src/tracefs-sqlhist.c +++ b/src/tracefs-sqlhist.c @@ -39,6 +39,13 @@ enum field_type { #define for_each_field(expr, field, table) \ for (expr = (table)->fields; expr; expr = (field)->next) +#define TIMESTAMP_COMPARE "TIMESTAMP_DELTA" +#define TIMESTAMP_USECS_COMPARE "TIMESTAMP_USECS_DELTA" +#define EVENT_START "__START_EVENT__" +#define EVENT_END "__END_EVENT__" +#define TIMESTAMP_NSECS "TIMESTAMP" +#define TIMESTAMP_USECS "TIMESTAMP_USECS" + struct field { struct expr *next; /* private link list */ const char *system; @@ -374,6 +381,44 @@ __hidden void *add_field(struct sqlhist_bison *sb, struct sql_table *table = sb->table; struct expr *expr; struct field *field; + bool nsecs; + + /* Check if this is a TIMESTAMP compare */ + if ((nsecs = (strcmp(field_name, TIMESTAMP_COMPARE) == 0)) || + strcmp(field_name, TIMESTAMP_USECS_COMPARE) == 0) { + const char *field_nameA; + const char *field_nameB; + struct expr *exprA; + struct expr *exprB; + struct field *fieldA; + struct field *fieldB; + + if (nsecs) { + field_nameA = EVENT_END "." TIMESTAMP_NSECS; + field_nameB = EVENT_START "." TIMESTAMP_NSECS; + } else { + field_nameA = EVENT_END "." TIMESTAMP_USECS; + field_nameB = EVENT_START "." TIMESTAMP_USECS; + } + + exprA = find_field(sb, field_nameA, NULL); + if (!exprA) { + create_field(fieldA, &exprA); + fieldA->next = table->fields; + table->fields = exprA; + fieldA->raw = field_nameA; + } + + exprB = find_field(sb, field_nameB, NULL); + if (!exprB) { + create_field(fieldB, &exprB); + fieldB->next = table->fields; + table->fields = exprB; + fieldB->raw = field_nameB; + } + + return add_compare(sb, exprA, exprB, COMPARE_SUB); + } expr = find_field(sb, field_name, label); if (expr) @@ -597,17 +642,25 @@ static int update_vars(struct tep_handle *tep, enum field_type ftype = FIELD_NONE; struct tep_event *event; struct field *field; + const char *extra_label; const char *label; const char *raw = event_field->raw; const char *event_name; const char *system; const char *p; int label_len = 0, event_len, system_len; + int extra_label_len = 0; - if (expr == table->to) + if (expr == table->to) { ftype = FIELD_TO; - else if (expr == table->from) + extra_label = EVENT_END; + } else if (expr == table->from) { ftype = FIELD_FROM; + extra_label = EVENT_START; + } + + if (extra_label) + extra_label_len = strlen(extra_label); p = strchr(raw, '.'); if (p) { @@ -673,6 +726,13 @@ static int update_vars(struct tep_handle *tep, goto found; } + len = extra_label_len; + if (extra_label && !strncmp(raw, extra_label, len) && + raw[len] == '.') { + /* Label matches and takes precedence */ + goto found; + } + if (!strncmp(raw, system, system_len) && raw[system_len] == '.') { raw += system_len + 1; diff --git a/utest/tracefs-utest.c b/utest/tracefs-utest.c index 6a817125419f..a94a1f28258a 100644 --- a/utest/tracefs-utest.c +++ b/utest/tracefs-utest.c @@ -49,6 +49,9 @@ #define SQL_5_SQL "select end.common_pid as pid, (end.common_timestamp.usecs - start.common_timestamp.usecs) as irq_lat from irq_disable as start join irq_enable as end on start.common_pid = end.common_pid, start.parent_offs == end.parent_offs where start.common_pid != 0" #define SQL_5_START "irq_disable" +#define SQL_6_EVENT "wakeup_lat_3" +#define SQL_6_SQL "select start.pid, end.next_prio as prio, (end.TIMESTAMP_USECS - start.TIMESTAMP_USECS) as lat from sched_waking as start join sched_switch as end on start.pid = end.next_pid where (start.prio >= 1 && start.prio < 100) || !(start.pid >= 0 && start.pid <= 1) && end.prev_pid != 0" + #define DEBUGFS_DEFAULT_PATH "/sys/kernel/debug" #define TRACEFS_DEFAULT_PATH "/sys/kernel/tracing" #define TRACEFS_DEFAULT2_PATH "/sys/kernel/debug/tracing" @@ -420,6 +423,13 @@ static void test_instance_trace_sql(struct tracefs_instance *instance) trace_seq_reset(&seq); } + synth = tracefs_sql(tep, SQL_6_EVENT, SQL_6_SQL, NULL); + CU_TEST(synth != NULL); + ret = tracefs_synth_echo_cmd(&seq, synth); + CU_TEST(ret == 0); + tracefs_synth_free(synth); + trace_seq_reset(&seq); + trace_seq_destroy(&seq); } From patchwork Thu Dec 28 20:35:43 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13506202 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A5CBC10944 for ; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 4C0F1C433BD; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.97) (envelope-from ) id 1rIx80-00000000Dts-0IpY; Thu, 28 Dec 2023 15:37:16 -0500 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: "Steven Rostedt (Google)" Subject: [PATCH 21/23] libtracefs: Also clear max_graph_depth on reset Date: Thu, 28 Dec 2023 15:35:43 -0500 Message-ID: <20231228203714.53294-22-rostedt@goodmis.org> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231228203714.53294-1-rostedt@goodmis.org> References: <20231228203714.53294-1-rostedt@goodmis.org> Precedence: bulk X-Mailing-List: linux-trace-devel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: "Steven Rostedt (Google)" Clear the "max_graph_depth" file on tracefs_instance_reset() Signed-off-by: Steven Rostedt (Google) --- src/tracefs-instance.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tracefs-instance.c b/src/tracefs-instance.c index 8d9dd0e21498..b94630a324a1 100644 --- a/src/tracefs-instance.c +++ b/src/tracefs-instance.c @@ -1490,6 +1490,7 @@ void tracefs_instance_reset(struct tracefs_instance *instance) tracefs_tracer_clear(instance); tracefs_instance_file_write(instance, "events/enable", "0"); tracefs_instance_file_write(instance, "set_ftrace_pid", ""); + tracefs_instance_file_write(instance, "max_graph_depth", "0"); tracefs_instance_file_clear(instance, "trace"); systems = tracefs_event_systems(NULL); From patchwork Thu Dec 28 20:35:44 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13506207 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B882110947 for ; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 5EEB1C433BF; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.97) (envelope-from ) id 1rIx80-00000000Dty-0Pk7; Thu, 28 Dec 2023 15:37:16 -0500 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: "Steven Rostedt (Google)" Subject: [PATCH 22/23] libtracefs: Add PID filtering API Date: Thu, 28 Dec 2023 15:35:44 -0500 Message-ID: <20231228203714.53294-23-rostedt@goodmis.org> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231228203714.53294-1-rostedt@goodmis.org> References: <20231228203714.53294-1-rostedt@goodmis.org> Precedence: bulk X-Mailing-List: linux-trace-devel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: "Steven Rostedt (Google)" Add an API that sets and clears PID filtering for functions and events. tracefs_filter_pid_function() tracefs_filter_pid_events() tracefs_filter_pid_function_clear() tracefs_filter_pid_events_clear() Signed-off-by: Steven Rostedt (Google) --- Documentation/libtracefs-filter-pid.txt | 181 +++++++++++++++++ Documentation/libtracefs.txt | 8 + include/tracefs.h | 7 + src/tracefs-filter.c | 132 ++++++++++++ utest/tracefs-utest.c | 257 +++++++++++++++++++++++- 5 files changed, 577 insertions(+), 8 deletions(-) create mode 100644 Documentation/libtracefs-filter-pid.txt diff --git a/Documentation/libtracefs-filter-pid.txt b/Documentation/libtracefs-filter-pid.txt new file mode 100644 index 000000000000..fa56b0222df0 --- /dev/null +++ b/Documentation/libtracefs-filter-pid.txt @@ -0,0 +1,181 @@ +libtracefs(3) +============= + +NAME +---- +tracefs_filter_pid_function, tracefs_filter_pid_events, tracefs_filter_pid_function_clear, tracefs_filter_pid_events_clear - +Add and remove PID filtering for functions and events + +SYNOPSIS +-------- +[verse] +-- +*#include * + +int *tracefs_filter_pid_function*(struct tracefs_instance pass:[*]_instance,_ int _pid_, + bool _reset_, bool _notrace_); +int *tracefs_filter_pid_function_clear*(struct tracefs_instance pass:[*]_instance_, bool _notrace_); +int *tracefs_filter_pid_events*(struct tracefs_instance pass:[*]_instance_, int _pid_, + bool _reset_, bool _notrace_); +int *tracefs_filter_pid_events_clear*(struct tracefs_instance pass:[*]_instance_, bool _notrace_); +-- + +DESCRIPTION +----------- +Both events and functions can be filtered by PID, but they are done separately. +PID filtering for functions affect the function and function_graph tracer, where +as PID filtering for events affect all events such as _sched_switch_ and _sched_waking_. +If the *TRACEFS_OPTION_FUNCTION_FORK* is enabled (see *tracefs_option_enable*(3)), +any PID that is set as part of the function PID filtering will automatically +have its children added when they are spawned, as well as the PID removed when +they exit. If the *TRACEFS_OPTION_EVENT_FORK* is set, the same is true for +event PID filtering. This also includes the _notrace_ option where the child +threads and processes of PIDs that are labled as notrace will also not be +traced. + +The *tracefs_filter_pid_function()* affects function PID filtering and *tracefs_filter_pid_events()* +affects the PID event filtering. For both functions, they add a _pid_ to be filtered in the given _instance_. +If _reset_ is true, then any PIDs already being filtered will be removed, otherwise +the _pid_ is simply added to the filtering. If _notrace_ is true, then the PID +is added to the list of PIDs that are not to be traced. Note, that _reset_ only affects +the list associated with _notrace_. That is, if both _reset_ and _notrace_ are true, +then it will not affect PIDs that are to be traced. Same is if _reset_ is true and _notrace_ +is false, it will not affect PIDs that are not to be traced. + +The *tracefs_filter_pid_function_clear()* affects function PID filtering and +*tracefs_filter_pid_events_clear()* affects the PID event filtering. For both +functions it will clear all the PIDs that are being filtered for the given +filter. If _notrace_ is true it clears all the PIDs that are not to be traced +otherwise if it is false, it clears all the PIDs that are to be traced. + +RETURN VALUE +------------ +All the functions return 0 on success and -1 on error. + +EXAMPLE +------- +[source,c] +-- +#include +#include +#include +#include + +static void usage(char **argv) +{ + fprintf(stderr, "usage: %s [-e|-f][-c|-n] pid [pid ...]\n", argv[0]); + fprintf(stderr, " -e enable event filter\n"); + fprintf(stderr, " -f enable function filter\n"); + fprintf(stderr, " (default is both, function and event)\n"); + fprintf(stderr, " -c clear the filter\n"); + fprintf(stderr, " -n notrace filter\n"); + exit(-1); +} + +int main (int argc, char **argv) +{ + bool events = false; + bool funcs = false; + bool neg = false; + bool clear = false; + bool reset = true; + int i; + + for (i = 1; i < argc && argv[i][0] == '-'; i++) { + char *arg = argv[i]; + int c; + for (c = 1; arg[c]; c++) { + switch (arg[c]) { + case 'e': events = true; break; + case 'f': funcs = true; break; + case 'n': neg = true; break; + case 'c': clear = true; break; + default: + usage(argv); + } + } + if (c == 1) + usage(argv); + } + + if (i == argc && !clear) + usage(argv); + + if (!events && !funcs) { + events = true; + funcs = true; + } + + if (clear) { + if (events) + tracefs_filter_pid_events_clear(NULL, neg); + if (funcs) + tracefs_filter_pid_function_clear(NULL, neg); + exit(0); + } + + for (; i < argc; i++) { + int pid = atoi(argv[i]); + + if (events) + tracefs_filter_pid_events(NULL, pid, reset, neg); + if (funcs) + tracefs_filter_pid_function(NULL, pid, reset, neg); + + reset = false; + } + + exit(0); +} + +-- + +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), +*tracefs_hist_alloc*(3), +*tracefs_hist_alloc_2d*(3), +*tracefs_hist_alloc_nd*(3), +*tracefs_hist_free*(3), +*tracefs_hist_add_key*(3), +*tracefs_hist_add_value*(3), +*tracefs_hist_add_name*(3), +*tracefs_hist_start*(3), +*tracefs_hist_destory*(3), +*tracefs_hist_add_sort_key*(3), +*tracefs_hist_sort_key_direction*(3) + +AUTHOR +------ +[verse] +-- +*Steven Rostedt* +-- +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) 2023 Google, LLC. Free use of this software is granted under +the terms of the GNU Public License (GPL). diff --git a/Documentation/libtracefs.txt b/Documentation/libtracefs.txt index 2f2b4d488942..6f26d7f1bdbb 100644 --- a/Documentation/libtracefs.txt +++ b/Documentation/libtracefs.txt @@ -107,6 +107,14 @@ Function filters: int *tracefs_function_notrace*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_filter_, const char pass:[*]_module_, int _flags_); int *tracefs_filter_functions*(const char pass:[*]_filter_, const char pass:[*]_module_, char pass:[*]pass:[*]pass:[*]_list_); +PID filters: + int *tracefs_filter_pid_function*(struct tracefs_instance pass:[*]_instance,_ int _pid_, + bool _reset_, bool _notrace_); + int *tracefs_filter_pid_function_clear*(struct tracefs_instance pass:[*]_instance_, bool _notrace_); + int *tracefs_filter_pid_events*(struct tracefs_instance pass:[*]_instance_, int _pid_, + bool _reset_, bool _notrace_); + int *tracefs_filter_pid_events_clear*(struct tracefs_instance pass:[*]_instance_, bool _notrace_); + Trace helper functions: void *tracefs_list_free*(char pass:[*]pass:[*]_list_); char pass:[**]*tracefs_list_add*(char **_list_, const char *_string_); diff --git a/include/tracefs.h b/include/tracefs.h index d20d733b7d92..da9d3f70d1a1 100644 --- a/include/tracefs.h +++ b/include/tracefs.h @@ -269,6 +269,13 @@ enum { TRACEFS_FL_FUTURE = (1 << 2), }; +int tracefs_filter_pid_function(struct tracefs_instance *instance, int pid, + bool reset, bool notrace); +int tracefs_filter_pid_function_clear(struct tracefs_instance *instance, bool notrace); +int tracefs_filter_pid_events(struct tracefs_instance *instance, int pid, + bool reset, bool notrace); +int tracefs_filter_pid_events_clear(struct tracefs_instance *instance, bool notrace); + int tracefs_function_filter(struct tracefs_instance *instance, const char *filter, const char *module, unsigned int flags); int tracefs_function_notrace(struct tracefs_instance *instance, const char *filter, diff --git a/src/tracefs-filter.c b/src/tracefs-filter.c index 3628eaed202f..afe3338c0bd8 100644 --- a/src/tracefs-filter.c +++ b/src/tracefs-filter.c @@ -801,6 +801,138 @@ int tracefs_event_filter_clear(struct tracefs_instance *instance, "filter", "0"); } +static int write_pid_file(struct tracefs_instance *instance, const char *file, + int pid, bool reset) +{ + char buf[64]; + int ret; + + sprintf(buf, "%d", pid); + + if (reset) + ret = tracefs_instance_file_write(instance, file, buf); + else + ret = tracefs_instance_file_append(instance, file, buf); + + return ret < 0 ? -1 : 0; +} + +/** + * tracefs_filter_pid_function - set function tracing to filter the pid + * @instance: The instance to set the filter to + * @pid: The pid to filter on + * @reset: If set, it will clear out all other pids being filtered + * @notrace: If set, it will filter all but this pid + * + * Set the function tracing to trace or avoid tracing a given @pid. + * If @notrace is set, then it will avoid tracing the @pid. + * If @reset is set, it will clear the filter as well. + * + * Note, @reset only resets what pids will be traced, or what pids will + * not be traced. That is, if both @reset and @notrace is set, then + * it will not affect pids that are being traced. It will only clear + * the pids that are not being traced. To do both, The + * tracefs_filter_pid_function_clear() needs to be called with the + * inverse of @notrace. + * + * Returns -1 on error, 0 on success. + */ +int tracefs_filter_pid_function(struct tracefs_instance *instance, int pid, + bool reset, bool notrace) +{ + const char *file; + + if (notrace) + file = "set_ftrace_notrace_pid"; + else + file = "set_ftrace_pid"; + + return write_pid_file(instance, file, pid, reset); +} + +/** + * tracefs_filter_pid_function_clear - reset pid function filtering + * @instance: The instance to reset function filtering + * @notrace: If set, it will filter reset the pids that are not to be traced + * + * This will clear the function filtering on pids. If @notrace is set, + * it will clear the filtering on what pids should not be traced. + * + * Returns -1 on error, 0 on success. + */ +int tracefs_filter_pid_function_clear(struct tracefs_instance *instance, bool notrace) +{ + const char *file; + int ret; + + if (notrace) + file = "set_ftrace_notrace_pid"; + else + file = "set_ftrace_pid"; + + ret = tracefs_instance_file_write(instance, file, ""); + + return ret < 0 ? -1 : 0; +} + +/** + * tracefs_filter_pid_events - set event filtering to a specific pid + * @instance: The instance to set the filter to + * @pid: The pid to filter on + * @reset: If set, it will clear out all other pids being filtered + * @notrace: If set, it will filter all but this pid + * + * Set the event filtering to trace or avoid tracing a given @pid. + * If @notrace is set, then it will avoid tracing the @pid. + * If @reset is set, it will clear the filter as well. + * + * Note, @reset only resets what pids will be traced, or what pids will + * not be traced. That is, if both @reset and @notrace is set, then + * it will not affect pids that are being traced. It will only clear + * the pids that are not being traced. To do both, The + * tracefs_filter_pid_events_clear() needs to be called with the + * inverse of @notrace. + * + * Returns -1 on error, 0 on success. + */ +int tracefs_filter_pid_events(struct tracefs_instance *instance, int pid, + bool reset, bool notrace) +{ + const char *file; + + if (notrace) + file = "set_event_notrace_pid"; + else + file = "set_event_pid"; + + return write_pid_file(instance, file, pid, reset); +} + +/** + * tracefs_filter_pid_events_clear - reset pid events filtering + * @instance: The instance to reset function filtering + * @notrace: If set, it will filter reset the pids that are not to be traced + * + * This will clear the function filtering on pids. If @notrace is set, + * it will clear the filtering on what pids should not be traced. + * + * Returns -1 on error, 0 on success. + */ +int tracefs_filter_pid_events_clear(struct tracefs_instance *instance, bool notrace) +{ + const char *file; + int ret; + + if (notrace) + file = "set_event_notrace_pid"; + else + file = "set_event_pid"; + + ret = tracefs_instance_file_write(instance, file, ""); + + return ret < 0 ? -1 : 0; +} + /** Deprecated **/ int tracefs_event_append_filter(struct tep_event *event, char **filter, enum tracefs_filter type, diff --git a/utest/tracefs-utest.c b/utest/tracefs-utest.c index a94a1f28258a..658e8c149a0f 100644 --- a/utest/tracefs-utest.c +++ b/utest/tracefs-utest.c @@ -16,12 +16,15 @@ #include #include +#include #include #include #include "tracefs.h" +#define gettid() syscall(__NR_gettid) + #define TRACEFS_SUITE "tracefs library" #define TEST_INSTANCE_NAME "cunit_test_iter" #define TEST_TRACE_DIR "/tmp/trace_utest.XXXXXX" @@ -438,6 +441,248 @@ static void test_trace_sql(void) test_instance_trace_sql(test_instance); } +static void call_getppid(int cnt) +{ + int i; + + for (i = 0; i < cnt; i++) + getppid(); +} + +struct check_data { + int this_pid; + int other_pid; + bool trace_this; + bool trace_other; + bool trace_all; + bool hit; + int (*filter_clear)(struct tracefs_instance *instance, bool notrace); +}; + +static int check_callback(struct tep_event *event, struct tep_record *record, + int cpu, void *data) +{ + struct check_data *cdata = data; + int pid; + + cdata->hit = true; + + pid = tep_data_pid(event->tep, record); + + if (pid == cdata->this_pid) { + CU_TEST(cdata->trace_this); + return cdata->trace_this ? 0 : -1; + } + + if (pid == cdata->other_pid) { + CU_TEST(cdata->trace_other); + return cdata->trace_other ? 0 : -1; + } + + CU_TEST(cdata->trace_all); + if (!cdata->trace_all) { + printf(" (Traced %d but should not have", pid); + if (cdata->trace_this) + printf(", this_pid:%d", cdata->this_pid); + if (cdata->trace_other) + printf(", other_pid:%d", cdata->other_pid); + printf(") "); + } + + return cdata->trace_all ? 0 : -1; +} + +static int check_filtered_pid(struct tep_handle *tep, struct tracefs_instance *instance, + struct check_data *cdata) +{ + int ret; + + cdata->hit = false; + ret = tracefs_iterate_raw_events(tep, instance, NULL, 0, check_callback, cdata); + + tracefs_instance_clear(instance); + + cdata->filter_clear(instance, false); + cdata->filter_clear(instance, true); + + return ret; +} + +struct spin_data { + bool stop; + bool done; + int tid; +}; + +static void *trace_spin_thread(void *arg) +{ + struct spin_data *data = arg; + + data->tid = gettid(); + pthread_barrier_wait(&trace_barrier); + + while (!data->done) { + pthread_barrier_wait(&trace_barrier); + while (!data->stop && !data->done) + getppid(); + pthread_barrier_wait(&trace_barrier); + } + + return NULL; +} + +static void run_test(struct tracefs_instance *instance, struct tep_handle *tep, + struct spin_data *data, struct check_data *cdata) +{ + tracefs_trace_on(instance); + + /* Run a little */ + call_getppid(1000); + + /* Start the spinner */ + data->stop = false; + pthread_barrier_wait(&trace_barrier); + + /* Allow the other threads run */ + msleep(100); + + /* Stop the spinners */ + data->stop = true; + pthread_barrier_wait(&trace_barrier); + /* Run a little more */ + call_getppid(10); + tracefs_trace_off(instance); + + check_filtered_pid(tep, instance, cdata); +} + + +static void test_instance_pid_filter(struct tracefs_instance *instance, + int (*filter_pid)(struct tracefs_instance *instance, + int pid, bool reset, bool notrace), + int (*filter_clear)(struct tracefs_instance *instance, + bool notrace)) +{ + struct tep_handle *tep = test_tep; + struct check_data cdata; + struct spin_data data = { }; + pthread_t thread1; + pthread_t thread2; + int this_pid = getpid(); + + pthread_barrier_init(&trace_barrier, NULL, 3); + + /* create two spinners, one will be used for tracing */ + pthread_create(&thread1, NULL, trace_spin_thread, &data); + pthread_create(&thread2, NULL, trace_spin_thread, &data); + + pthread_barrier_wait(&trace_barrier); + + cdata.this_pid = this_pid; + cdata.other_pid = data.tid; + cdata.filter_clear = filter_clear; + + /* Test 1 */ + cdata.trace_this = true; + cdata.trace_other = false; + cdata.trace_all = false; + + /* Add the thread, but then reset it out */ + filter_pid(instance, data.tid, true, false); + filter_pid(instance, this_pid, true, false); + + /* Only this thread should be traced */ + run_test(instance, tep, &data, &cdata); + CU_TEST(cdata.hit); + + + /* Test 2 */ + cdata.trace_this = true; + cdata.trace_other = true; + cdata.trace_all = false; + + /* Add the thread, but then reset it out */ + filter_pid(instance, data.tid, true, false); + filter_pid(instance, this_pid, false, false); + + /* Only this thread should be traced */ + run_test(instance, tep, &data, &cdata); + CU_TEST(cdata.hit); + + + /* Test 3 */ + cdata.trace_this = false; + cdata.trace_other = true; + cdata.trace_all = true; + + /* Add the thread, but then reset it out */ + filter_pid(instance, data.tid, true, true); + filter_pid(instance, this_pid, true, true); + + /* Only this thread should be traced */ + run_test(instance, tep, &data, &cdata); + CU_TEST(cdata.hit); + + + /* Test 4 */ + cdata.trace_this = false; + cdata.trace_other = false; + cdata.trace_all = true; + + /* Add the thread, but then reset it out */ + filter_pid(instance, data.tid, true, true); + filter_pid(instance, this_pid, false, true); + + /* Only this thread should be traced */ + run_test(instance, tep, &data, &cdata); + CU_TEST(cdata.hit); + + /* exit out */ + data.done = true; + pthread_barrier_wait(&trace_barrier); + pthread_barrier_wait(&trace_barrier); + + pthread_join(thread1, NULL); + pthread_join(thread2, NULL); +} + +static void test_function_pid_filter(struct tracefs_instance *instance) +{ + tracefs_trace_off(instance); + tracefs_instance_clear(instance); + tracefs_tracer_set(instance, TRACEFS_TRACER_FUNCTION); + test_instance_pid_filter(instance, + tracefs_filter_pid_function, + tracefs_filter_pid_function_clear); + tracefs_tracer_clear(instance); + tracefs_trace_on(instance); +} + +static void test_trace_function_pid_filter(void) +{ + test_function_pid_filter(NULL); + test_function_pid_filter(test_instance); +} + +static void test_events_pid_filter(struct tracefs_instance *instance) +{ + tracefs_trace_off(instance); + tracefs_instance_clear(instance); + tracefs_event_enable(instance, "syscalls", NULL); + tracefs_event_enable(instance, "raw_syscalls", NULL); + test_instance_pid_filter(instance, + tracefs_filter_pid_events, + tracefs_filter_pid_events_clear); + tracefs_event_disable(instance, NULL, NULL); + tracefs_trace_on(instance); +} + +static void test_trace_events_pid_filter(void) +{ + test_events_pid_filter(NULL); + test_events_pid_filter(test_instance); +} + struct test_cpu_data { struct tracefs_instance *instance; struct tracefs_cpu *tcpu; @@ -593,14 +838,6 @@ static void reset_trace_cpu(struct test_cpu_data *data, bool nonblock) CU_TEST(data->tcpu != NULL); } -static void call_getppid(int cnt) -{ - int i; - - for (i = 0; i < cnt; i++) - getppid(); -} - static void test_cpu_read(struct test_cpu_data *data, int expect) { struct tracefs_cpu *tcpu = data->tcpu; @@ -2965,6 +3202,10 @@ void test_tracefs_lib(void) test_trace_cpu_read_buf_percent); CU_add_test(suite, "trace cpu pipe", test_trace_cpu_pipe); + CU_add_test(suite, "trace pid events filter", + test_trace_events_pid_filter); + CU_add_test(suite, "trace pid function filter", + test_trace_function_pid_filter); CU_add_test(suite, "trace sql", test_trace_sql); CU_add_test(suite, "tracing file / directory APIs", From patchwork Thu Dec 28 20:35:45 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13506206 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B885110948 for ; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 5046FC433BB; Thu, 28 Dec 2023 20:36:26 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.97) (envelope-from ) id 1rIx80-00000000Du2-0X9t; Thu, 28 Dec 2023 15:37:16 -0500 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: "Steven Rostedt (Google)" Subject: [PATCH 23/23] libtracefs: Add updating and reading snapshot buffers Date: Thu, 28 Dec 2023 15:35:45 -0500 Message-ID: <20231228203714.53294-24-rostedt@goodmis.org> X-Mailer: git-send-email 2.42.0 In-Reply-To: <20231228203714.53294-1-rostedt@goodmis.org> References: <20231228203714.53294-1-rostedt@goodmis.org> Precedence: bulk X-Mailing-List: linux-trace-devel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 From: "Steven Rostedt (Google)" Add the API: tracefs_cpu_snapshot_open() That will read the snapshot_raw file just like tracefs_cpu_open() does to the trace_pipe_raw file, except the blocking will block only if empty and until another snapshot occurs. Add blocking and see if a snapshot will unblock it! Signed-off-by: Steven Rostedt (Google) --- Documentation/libtracefs-cpu-open.txt | 16 +++- Documentation/libtracefs-iterator.txt | 10 ++- Documentation/libtracefs.txt | 6 ++ include/tracefs.h | 13 ++++ src/tracefs-events.c | 97 +++++++++++++++++------- src/tracefs-record.c | 102 ++++++++++++++++++++++---- utest/tracefs-utest.c | 42 +++++++++-- 7 files changed, 237 insertions(+), 49 deletions(-) diff --git a/Documentation/libtracefs-cpu-open.txt b/Documentation/libtracefs-cpu-open.txt index c5a900a06e8e..46667e83f7ff 100644 --- a/Documentation/libtracefs-cpu-open.txt +++ b/Documentation/libtracefs-cpu-open.txt @@ -3,7 +3,7 @@ libtracefs(3) NAME ---- -tracefs_cpu_open, tracefs_cpu_close, tracefs_cpu_alloc_fd, tracefs_cpu_free_fd - Opening trace_pipe_raw data for reading +tracefs_cpu_open, tracefs_cpu_close, tracefs_cpu_alloc_fd, tracefs_cpu_free_fd, tracefs_cpu_snapshot_open - Opening trace_pipe_raw data for reading SYNOPSIS -------- @@ -17,6 +17,9 @@ void *tracefs_cpu_close*(struct tracefs_cpu pass:[*]_tcpu_); struct tracefs_cpu pass:[*]*tracefs_cpu_alloc_fd*(int _fd_, int _subbuf_size_, bool _nonblock_); void *tracefs_cpu_free_fd*(struct tracefs_cpu pass:[*]_tcpu_); + +struct tracefs_cpu pass:[*]*tracefs_cpu_snapshot-open*(struct tracefs_instance pass:[*]_instance_, + int _cpu_, bool _nonblock_); -- DESCRIPTION @@ -47,10 +50,17 @@ the file descriptor passed in. Note that *tracefs_cpu_free_fd()* should not be u on the descriptor returned by *tracefs_cpu_open()* as it will not close the file descriptor created by it. +The *tracefs_cpu_snapshot_open()* is similar to *tracefs_cpu_open()* except that it +opens the snapshot buffer (see *tracefs_snapshot_snap*(3)). The snapshot buffer +does not have a writer to it, it is only created by a snapshot action that swaps +the current ring buffer with the snapshot buffer. The _nonblock_, when false, acts a little +differently here too. Reads are not affected by the "buffer_percent" file. If the +snapshot buffer is empty, it will block until a new snapshot happens. + RETURN VALUE ------------ -The *tracefs_cpu_open()* returns a struct tracefs_cpu descriptor that can be -used by the other functions or NULL on error. +The *tracefs_cpu_open()* and *tracefs_cpu_snapshot_open() both return a struct +tracefs_cpu descriptor that can be used by the other functions or NULL on error. The *tracefs_cpu_alloc_fd()* returns a struct tracefs_cpu descriptor that can be used by the *tracefs_cpu_read*(3) related functions, where the descriptor diff --git a/Documentation/libtracefs-iterator.txt b/Documentation/libtracefs-iterator.txt index c2b2be3f4c5c..b62f66aa2419 100644 --- a/Documentation/libtracefs-iterator.txt +++ b/Documentation/libtracefs-iterator.txt @@ -4,7 +4,7 @@ libtracefs(3) NAME ---- tracefs_iterate_raw_events, tracefs_iterate_stop, tracefs_follow_event, tracefs_follow_missed_events, -tracefs_follow_event_clear, tracefs_follow_missed_events_clear - Iterate over events in the ring buffer +tracefs_follow_event_clear, tracefs_follow_missed_events_clear, tracefs_iterate_snapshot_events - Iterate over events in the ring buffer SYNOPSIS -------- @@ -33,6 +33,11 @@ int *tracefs_follow_missed_events*(struct tracefs_instance pass:[*]_instance_, int *tracefs_follow_event_clear*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_system_, const char pass:[*]_event_name_); int *tracefs_follow_missed_events_clear*(struct tracefs_instance pass:[*]_instance_); + +int *tracefs_iterate_snapshot_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_); -- DESCRIPTION @@ -54,6 +59,9 @@ 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. +The *tracefs_iterate_snapshot_events()* works the same as *tracefs_iterate_raw_events()* +except that it works on the snapshot buffer. + 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 diff --git a/Documentation/libtracefs.txt b/Documentation/libtracefs.txt index 6f26d7f1bdbb..8dc3ba7386e3 100644 --- a/Documentation/libtracefs.txt +++ b/Documentation/libtracefs.txt @@ -94,6 +94,12 @@ Trace events: bool *tracefs_event_file_exists*(struct tracefs_instance pass:[*]_instance_, const char pass:[*]_system_, const char pass:[*]_event_, const char pass:[*]_file_); +Snapshot buffer: + int *tracefs_iterate_snapshot_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_); + Event filters: int *tracefs_filter_string_append*(struct tep_event pass:[*]_event_, char pass:[**]_filter_, struct tracefs_filter _type_, const char pass:[*]_field_, diff --git a/include/tracefs.h b/include/tracefs.h index da9d3f70d1a1..8569171247b7 100644 --- a/include/tracefs.h +++ b/include/tracefs.h @@ -680,6 +680,19 @@ struct kbuffer *tracefs_cpu_flush_buf(struct tracefs_cpu *tcpu); int tracefs_cpu_flush_write(struct tracefs_cpu *tcpu, int wfd); int tracefs_cpu_pipe(struct tracefs_cpu *tcpu, int wfd, bool nonblock); +struct tracefs_cpu * +tracefs_cpu_snapshot_open(struct tracefs_instance *instance, int cpu, bool nonblock); +int tracefs_iterate_snapshot_events(struct tep_handle *tep, + struct tracefs_instance *instance, + cpu_set_t *cpus, int cpu_size, + int (*callback)(struct tep_event *, + struct tep_record *, + int, void *), + void *callback_context); +int tracefs_snapshot_snap(struct tracefs_instance *instance); +int tracefs_snapshot_clear(struct tracefs_instance *instance); +int tracefs_snapshot_free(struct tracefs_instance *instance); + /* Memory mapping of ring buffer */ bool tracefs_cpu_is_mapped(struct tracefs_cpu *tcpu); int tracefs_cpu_map(struct tracefs_cpu *tcpu); diff --git a/src/tracefs-events.c b/src/tracefs-events.c index 413c2df19998..3c844b0ab408 100644 --- a/src/tracefs-events.c +++ b/src/tracefs-events.c @@ -280,7 +280,8 @@ static int read_cpu_pages(struct tep_handle *tep, struct tracefs_instance *insta } static int open_cpu_files(struct tracefs_instance *instance, cpu_set_t *cpus, - int cpu_size, struct cpu_iterate **all_cpus, int *count) + int cpu_size, struct cpu_iterate **all_cpus, int *count, + bool snapshot) { struct tracefs_cpu *tcpu; struct cpu_iterate *tmp; @@ -294,7 +295,10 @@ static int open_cpu_files(struct tracefs_instance *instance, cpu_set_t *cpus, for (cpu = 0; cpu < nr_cpus; cpu++) { if (cpus && !CPU_ISSET_S(cpu, cpu_size, cpus)) continue; - tcpu = tracefs_cpu_open(instance, cpu, true); + if (snapshot) + tcpu = tracefs_cpu_snapshot_open(instance, cpu, true); + else + tcpu = tracefs_cpu_open(instance, cpu, true); tmp = realloc(*all_cpus, (i + 1) * sizeof(*tmp)); if (!tmp) { i--; @@ -497,30 +501,13 @@ int tracefs_follow_missed_events_clear(struct tracefs_instance *instance) static bool top_iterate_keep_going; -/* - * tracefs_iterate_raw_events - Iterate through events in trace_pipe_raw, - * per CPU trace buffers - * @tep: a handle to the trace event parser context - * @instance: ftrace instance, can be NULL for the top instance - * @cpus: Iterate only through the buffers of CPUs, set in the mask. - * If NULL, iterate through all CPUs. - * @cpu_size: size of @cpus set - * @callback: A user function, called for each record from the file - * @callback_context: A custom context, passed to the user callback function - * - * If the @callback returns non-zero, the iteration stops - in that case all - * records from the current page will be lost from future reads - * The events are iterated in sorted order, oldest first. - * - * Returns -1 in case of an error, or 0 otherwise - */ -int tracefs_iterate_raw_events(struct tep_handle *tep, - struct tracefs_instance *instance, - cpu_set_t *cpus, int cpu_size, - int (*callback)(struct tep_event *, - struct tep_record *, +static int iterate_events(struct tep_handle *tep, + struct tracefs_instance *instance, + cpu_set_t *cpus, int cpu_size, + int (*callback)(struct tep_event *, + struct tep_record *, int, void *), - void *callback_context) + void *callback_context, bool snapshot) { bool *keep_going = instance ? &instance->iterate_keep_going : &top_iterate_keep_going; @@ -542,7 +529,7 @@ int tracefs_iterate_raw_events(struct tep_handle *tep, if (!callback && !followers) return -1; - ret = open_cpu_files(instance, cpus, cpu_size, &all_cpus, &count); + ret = open_cpu_files(instance, cpus, cpu_size, &all_cpus, &count, snapshot); if (ret < 0) goto out; ret = read_cpu_pages(tep, instance, all_cpus, count, @@ -562,6 +549,64 @@ out: return ret; } +/* + * tracefs_iterate_raw_events - Iterate through events in trace_pipe_raw, + * per CPU trace buffers + * @tep: a handle to the trace event parser context + * @instance: ftrace instance, can be NULL for the top instance + * @cpus: Iterate only through the buffers of CPUs, set in the mask. + * If NULL, iterate through all CPUs. + * @cpu_size: size of @cpus set + * @callback: A user function, called for each record from the file + * @callback_context: A custom context, passed to the user callback function + * + * If the @callback returns non-zero, the iteration stops - in that case all + * records from the current page will be lost from future reads + * The events are iterated in sorted order, oldest first. + * + * Returns -1 in case of an error, or 0 otherwise + */ +int tracefs_iterate_raw_events(struct tep_handle *tep, + struct tracefs_instance *instance, + cpu_set_t *cpus, int cpu_size, + int (*callback)(struct tep_event *, + struct tep_record *, + int, void *), + void *callback_context) +{ + return iterate_events(tep, instance, cpus, cpu_size, callback, + callback_context, false); +} + +/* + * tracefs_iterate_snapshot_events - Iterate through events in snapshot_raw, + * per CPU trace buffers + * @tep: a handle to the trace event parser context + * @instance: ftrace instance, can be NULL for the top instance + * @cpus: Iterate only through the buffers of CPUs, set in the mask. + * If NULL, iterate through all CPUs. + * @cpu_size: size of @cpus set + * @callback: A user function, called for each record from the file + * @callback_context: A custom context, passed to the user callback function + * + * If the @callback returns non-zero, the iteration stops - in that case all + * records from the current page will be lost from future reads + * The events are iterated in sorted order, oldest first. + * + * Returns -1 in case of an error, or 0 otherwise + */ +int tracefs_iterate_snapshot_events(struct tep_handle *tep, + struct tracefs_instance *instance, + cpu_set_t *cpus, int cpu_size, + int (*callback)(struct tep_event *, + struct tep_record *, + int, void *), + void *callback_context) +{ + return iterate_events(tep, instance, cpus, cpu_size, callback, + callback_context, true); +} + /** * tracefs_iterate_stop - stop the iteration over the raw events. * @instance: ftrace instance, can be NULL for top tracing instance. diff --git a/src/tracefs-record.c b/src/tracefs-record.c index fcef89e527aa..f51e18420bc7 100644 --- a/src/tracefs-record.c +++ b/src/tracefs-record.c @@ -93,19 +93,8 @@ tracefs_cpu_alloc_fd(int fd, int subbuf_size, bool nonblock) return NULL; } -/** - * tracefs_cpu_open - open an instance raw trace file - * @instance: the instance (NULL for toplevel) of the cpu raw file to open - * @cpu: The CPU that the raw trace file is associated with - * @nonblock: If true, the file will be opened in O_NONBLOCK mode - * - * Return a descriptor that can read the tracefs trace_pipe_raw file - * for a give @cpu in a given @instance. - * - * Returns NULL on error. - */ -struct tracefs_cpu * -tracefs_cpu_open(struct tracefs_instance *instance, int cpu, bool nonblock) +static struct tracefs_cpu *cpu_open(struct tracefs_instance *instance, + const char *path_fmt, int cpu, bool nonblock) { struct tracefs_cpu *tcpu; struct tep_handle *tep; @@ -119,7 +108,7 @@ tracefs_cpu_open(struct tracefs_instance *instance, int cpu, bool nonblock) if (nonblock) mode |= O_NONBLOCK; - sprintf(path, "per_cpu/cpu%d/trace_pipe_raw", cpu); + sprintf(path, path_fmt, cpu); fd = tracefs_instance_file_open(instance, path, mode); if (fd < 0) @@ -156,6 +145,91 @@ tracefs_cpu_open(struct tracefs_instance *instance, int cpu, bool nonblock) return NULL; } +/** + * tracefs_cpu_open - open an instance raw trace file + * @instance: the instance (NULL for toplevel) of the cpu raw file to open + * @cpu: The CPU that the raw trace file is associated with + * @nonblock: If true, the file will be opened in O_NONBLOCK mode + * + * Return a descriptor that can read the tracefs trace_pipe_raw file + * for a give @cpu in a given @instance. + * + * Returns NULL on error. + */ +struct tracefs_cpu * +tracefs_cpu_open(struct tracefs_instance *instance, int cpu, bool nonblock) +{ + return cpu_open(instance, "per_cpu/cpu%d/trace_pipe_raw", cpu, nonblock); +} + +/** + * tracefs_cpu_snapshot_open - open an instance snapshot raw trace file + * @instance: the instance (NULL for toplevel) of the cpu raw file to open + * @cpu: The CPU that the raw trace file is associated with + * @nonblock: If true, the file will be opened in O_NONBLOCK mode + * + * Return a descriptor that can read the tracefs snapshot_raw file + * for a give @cpu in a given @instance. + * + * In nonblock mode, it will block if the snapshot is empty and wake up + * when there's a new snapshot. + * + * Returns NULL on error. + */ +struct tracefs_cpu * +tracefs_cpu_snapshot_open(struct tracefs_instance *instance, int cpu, bool nonblock) +{ + return cpu_open(instance, "per_cpu/cpu%d/snapshot_raw", cpu, nonblock); +} + +/** + * tracefs_snapshot_snap - takes a snapshot (allocates if necessary) + * @instance: The instance to take a snapshot on + * + * Takes a snapshot of the current ring buffer. + * + * Returns 0 on success, -1 on error. + */ +int tracefs_snapshot_snap(struct tracefs_instance *instance) +{ + int ret; + + ret = tracefs_instance_file_write(instance, "snapshot", "1"); + return ret < 0 ? -1 : 0; +} + +/** + * tracefs_snapshot_clear - clears the snapshot + * @instance: The instance to clear the snapshot + * + * Clears the snapshot buffer for the @instance. + * + * Returns 0 on success, -1 on error. + */ +int tracefs_snapshot_clear(struct tracefs_instance *instance) +{ + int ret; + + ret = tracefs_instance_file_write(instance, "snapshot", "2"); + return ret < 0 ? -1 : 0; +} + +/** + * tracefs_snapshot_free - frees the snapshot + * @instance: The instance to free the snapshot + * + * Frees the snapshot for the given @instance. + * + * Returns 0 on success, -1 on error. + */ +int tracefs_snapshot_free(struct tracefs_instance *instance) +{ + int ret; + + ret = tracefs_instance_file_write(instance, "snapshot", "0"); + return ret < 0 ? -1 : 0; +} + /** * tracefs_cpu_open_mapped - open an instance raw trace file and map it * @instance: the instance (NULL for toplevel) of the cpu raw file to open diff --git a/utest/tracefs-utest.c b/utest/tracefs-utest.c index 658e8c149a0f..f338a9153c0a 100644 --- a/utest/tracefs-utest.c +++ b/utest/tracefs-utest.c @@ -181,7 +181,7 @@ static void test_iter_write(struct tracefs_instance *instance) } -static void iter_raw_events_on_cpu(struct tracefs_instance *instance, int cpu) +static void iter_raw_events_on_cpu(struct tracefs_instance *instance, int cpu, bool snapshot) { int cpus = sysconf(_SC_NPROCESSORS_CONF); cpu_set_t *cpuset = NULL; @@ -190,6 +190,9 @@ static void iter_raw_events_on_cpu(struct tracefs_instance *instance, int cpu) int ret; int i; + if (snapshot) + tracefs_instance_clear(instance); + if (cpu >= 0) { cpuset = CPU_ALLOC(cpus); cpu_size = CPU_ALLOC_SIZE(cpus); @@ -199,8 +202,15 @@ static void iter_raw_events_on_cpu(struct tracefs_instance *instance, int cpu) test_found = 0; last_ts = 0; test_iter_write(instance); - ret = tracefs_iterate_raw_events(test_tep, instance, cpuset, cpu_size, - test_callback, &cpu); + + if (snapshot) { + tracefs_snapshot_snap(instance); + ret = tracefs_iterate_snapshot_events(test_tep, instance, cpuset, cpu_size, + test_callback, &cpu); + } else { + ret = tracefs_iterate_raw_events(test_tep, instance, cpuset, cpu_size, + test_callback, &cpu); + } CU_TEST(ret == 0); if (cpu < 0) { CU_TEST(test_found == TEST_ARRAY_SIZE); @@ -234,16 +244,35 @@ static void test_instance_iter_raw_events(struct tracefs_instance *instance) ret = tracefs_iterate_raw_events(test_tep, instance, NULL, 0, NULL, NULL); CU_TEST(ret < 0); - iter_raw_events_on_cpu(instance, -1); + iter_raw_events_on_cpu(instance, -1, false); for (i = 0; i < cpus; i++) - iter_raw_events_on_cpu(instance, i); + iter_raw_events_on_cpu(instance, i, false); } static void test_iter_raw_events(void) { + test_instance_iter_raw_events(NULL); test_instance_iter_raw_events(test_instance); } +static void test_instance_iter_snapshot_events(struct tracefs_instance *instance) +{ + int cpus = sysconf(_SC_NPROCESSORS_CONF); + int i; + + iter_raw_events_on_cpu(instance, -1, true); + for (i = 0; i < cpus; i++) + iter_raw_events_on_cpu(instance, i, true); + tracefs_snapshot_free(instance); +} + +static void test_iter_snapshot_events(void) +{ + test_instance_iter_snapshot_events(NULL); + test_instance_iter_snapshot_events(test_instance); +} + + #define RAND_STR_SIZE 20 #define RAND_ASCII "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" static const char *get_rand_str(void) @@ -3218,6 +3247,9 @@ void test_tracefs_lib(void) test_instance_reset); CU_add_test(suite, "systems and events APIs", test_system_event); + CU_add_test(suite, "tracefs_iterate_snapshot_events API", + test_iter_snapshot_events); + CU_add_test(suite, "tracefs_iterate_raw_events API", test_iter_raw_events);