From patchwork Thu Aug 17 01:32:53 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13355864 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E582CC2FC22 for ; Thu, 17 Aug 2023 01:33:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1347544AbjHQBdO (ORCPT ); Wed, 16 Aug 2023 21:33:14 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33648 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1347548AbjHQBdK (ORCPT ); Wed, 16 Aug 2023 21:33:10 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [IPv6:2604:1380:4641:c500::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 17AF8271E for ; Wed, 16 Aug 2023 18:33:08 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id A00E463C7B for ; Thu, 17 Aug 2023 01:33:07 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 0AAC6C433CD; Thu, 17 Aug 2023 01:33:07 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.96) (envelope-from ) id 1qWRst-000N3e-1P; Wed, 16 Aug 2023 21:33:11 -0400 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: Ross Zwisler , Stevie Alvarez , "Steven Rostedt (Google)" Subject: [PATCH v3 01/18] libtraceeval histograms: Fix traceeval_results_release() error message Date: Wed, 16 Aug 2023 21:32:53 -0400 Message-Id: <20230817013310.88582-2-rostedt@goodmis.org> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230817013310.88582-1-rostedt@goodmis.org> References: <20230817013310.88582-1-rostedt@goodmis.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org From: "Steven Rostedt (Google)" The function traceeval_results_release() must have a teval if passed in to free the accompanying results, but currently the error message is only displayed if the results is NULL and teval exists. It should be the other way around. Signed-off-by: Steven Rostedt (Google) --- src/histograms.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/histograms.c b/src/histograms.c index a159892e509b..d22d15238616 100644 --- a/src/histograms.c +++ b/src/histograms.c @@ -681,7 +681,7 @@ void traceeval_results_release(struct traceeval *teval, union traceeval_data *results) { if (!teval || !results) { - if (!results) + if (!teval) print_err("Results to be freed without accompanied traceeval instance!"); return; } From patchwork Thu Aug 17 01:32:54 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13355875 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7E209C2FC18 for ; Thu, 17 Aug 2023 01:34:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1347549AbjHQBdq (ORCPT ); Wed, 16 Aug 2023 21:33:46 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58090 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1347546AbjHQBdM (ORCPT ); Wed, 16 Aug 2023 21:33:12 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [IPv6:2604:1380:4641:c500::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 895A426AA for ; Wed, 16 Aug 2023 18:33:09 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 30DF964A52 for ; Thu, 17 Aug 2023 01:33:08 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id EBBFDC433C8; Thu, 17 Aug 2023 01:33:06 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.96) (envelope-from ) id 1qWRst-000N3h-1V; Wed, 16 Aug 2023 21:33:11 -0400 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: Ross Zwisler , Stevie Alvarez , "Steven Rostedt (Google)" Subject: [PATCH v3 02/18] libtraceeval: Add sample task-eval program Date: Wed, 16 Aug 2023 21:32:54 -0400 Message-Id: <20230817013310.88582-3-rostedt@goodmis.org> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230817013310.88582-1-rostedt@goodmis.org> References: <20230817013310.88582-1-rostedt@goodmis.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org From: "Steven Rostedt (Google)" Add a sample task eval program to test the traceeval logic. This relies on having libtracecmd installed. This version uses the old API, and will be used to compare when converted over to the new API. Signed-off-by: Steven Rostedt (Google) --- Makefile | 3 + samples/Makefile | 29 ++ samples/task-eval.c | 859 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 891 insertions(+) create mode 100644 samples/Makefile create mode 100644 samples/task-eval.c diff --git a/Makefile b/Makefile index 3ea051cbebf5..62ca9285d7a0 100644 --- a/Makefile +++ b/Makefile @@ -348,6 +348,9 @@ $(LIBRARY_STATIC): force $(LIBRARY_SHARED): force $(Q)$(call descend,$(src)/src,$(LIBRARY_SO)) +samples: $(LIBRARY_STATIC) force + $(Q)$(call descend,$(src)/samples,all) + # $(Q)$(call descend_clean,utest) clean: $(Q)$(call descend_clean,src) diff --git a/samples/Makefile b/samples/Makefile new file mode 100644 index 000000000000..012301ec5542 --- /dev/null +++ b/samples/Makefile @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: LGPL-2.1 + +include $(src)/scripts/utils.mk + +TARGETS := +TARGETS += task-eval + +sdir := $(obj)/bin + +CFLAGS += `pkg-config --cflags libtracecmd` +LIBRARY_LIBS += `pkg-config --libs libtracecmd` + +TARGETS := $(patsubst %,$(sdir)/%,$(TARGETS)) + +all: $(TARGETS) + +$(sdir): + @mkdir -p $(sdir) + +$(TARGETS): $(sdir) $(LIBTRACEEVAL_STATIC) + +$(sdir)/%: $(bdir)/%.o + $(call do_sample_build,$@,$<) + +$(bdir)/%.o: $(bdir)/%.c + $(Q)$(CC) -o $@ -c $< $(CFLAGS) $(INCLUDES) + +clean: + $(Q)$(call do_clean,$(sdir)/*) diff --git a/samples/task-eval.c b/samples/task-eval.c new file mode 100644 index 000000000000..9214777ee2e1 --- /dev/null +++ b/samples/task-eval.c @@ -0,0 +1,859 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +static char *argv0; + +static char *get_this_name(void) +{ + static char *this_name; + char *arg; + char *p; + + if (this_name) + return this_name; + + arg = argv0; + p = arg+strlen(arg); + + while (p >= arg && *p != '/') + p--; + p++; + + this_name = p; + return p; +} + +static void usage(void) +{ + char *p = get_this_name(); + + printf("usage: %s [-c comm] trace.dat\n" + "\n" + " Run this after running: trace-cmd record -e sched\n" + "\n" + " Do some work and then hit Ctrl^C to stop the recording.\n" + " Run this on the resulting trace.dat file\n" + "\n" + "-c comm - to look at only a specific process called 'comm'\n" + "\n",p); + exit(-1); +} + +static void __vdie(const char *fmt, va_list ap, int err) +{ + int ret = errno; + char *p = get_this_name(); + + if (err && errno) + perror(p); + else + ret = -1; + + fprintf(stderr, " "); + vfprintf(stderr, fmt, ap); + + fprintf(stderr, "\n"); + exit(ret); +} + +void die(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + __vdie(fmt, ap, 0); + va_end(ap); +} + +void pdie(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + __vdie(fmt, ap, 1); + va_end(ap); +} + +enum sched_state { + RUNNING, + BLOCKED, + PREEMPT, + SLEEP, + IDLE, + OTHER +}; + +struct process_data { + struct traceeval *teval_cpus; + struct traceeval *teval_threads; + char *comm; + int state; +}; + +struct task_data { + struct traceeval *teval_cpus; + struct traceeval *teval_processes; + char *comm; +}; + +enum command { + START, + STOP +}; + +static void update_process(struct task_data *tdata, const char *comm, + enum sched_state state, enum command cmd, + unsigned long long ts) +{ + struct traceeval_key keys[] = { + { + .type = TRACEEVAL_TYPE_STRING, + .string = comm, + }, + { + .type = TRACEEVAL_TYPE_NUMBER, + .number = state, + } + }; + int ret; + + switch (cmd) { + case START: + ret = traceeval_n_start(tdata->teval_processes, keys, ts); + if (ret < 0) + pdie("Could not start process"); + return; + case STOP: + ret = traceeval_n_stop(tdata->teval_processes, keys, ts); + if (ret < 0) + pdie("Could not stop process"); + return; + } +} + +static void start_process(struct task_data *tdata, const char *comm, + enum sched_state state, unsigned long long ts) +{ + update_process(tdata, comm, state, START, ts); +} + +static void stop_process(struct task_data *tdata, const char *comm, + enum sched_state state, unsigned long long ts) +{ + update_process(tdata, comm, state, STOP, ts); +} + +static struct process_data * +get_process_data(struct task_data *tdata, const char *comm) +{ + struct traceeval_key keys[] = { + { + .type = TRACEEVAL_TYPE_STRING, + .string = comm, + }, + { + .type = TRACEEVAL_TYPE_NUMBER, + .number = RUNNING, + } + }; + + return traceeval_n_get_private(tdata->teval_processes, keys); +} + +void set_process_data(struct task_data *tdata, const char *comm, void *data) +{ + struct traceeval_key keys[] = { + { + .type = TRACEEVAL_TYPE_STRING, + .string = comm, + }, + { + .type = TRACEEVAL_TYPE_NUMBER, + .number = RUNNING, + } + }; + int ret; + + ret = traceeval_n_set_private(tdata->teval_processes, keys, data); + if (ret < 0) + pdie("Failed to set process data"); +} + +static void update_cpu(struct traceeval *teval, int cpu, + enum sched_state state, enum command cmd, + unsigned long long ts) +{ + struct traceeval_key keys[] = { + { + .type = TRACEEVAL_TYPE_NUMBER, + .number = cpu, + }, + { + .type = TRACEEVAL_TYPE_NUMBER, + .number = state, + } + }; + int ret; + + switch (cmd) { + case START: + ret = traceeval_n_continue(teval, keys, ts); + if (ret < 0) + pdie("Could not start CPU"); + return; + case STOP: + ret = traceeval_n_stop(teval, keys, ts); + if (ret < 0) + pdie("Could not stop CPU"); + return; + } +} + +static void start_cpu(struct traceeval *teval, int cpu, + enum sched_state state, unsigned long long ts) +{ + update_cpu(teval, cpu, state, START, ts); +} + +static void stop_cpu(struct traceeval *teval, int cpu, + enum sched_state state, unsigned long long ts) +{ + update_cpu(teval, cpu, state, STOP, ts); +} + +static void update_thread(struct process_data *pdata, int tid, + enum sched_state state, enum command cmd, + unsigned long long ts) +{ + struct traceeval_key keys[] = { + { + .type = TRACEEVAL_TYPE_NUMBER, + .number = tid, + }, + { + .type = TRACEEVAL_TYPE_NUMBER, + .number = state, + } + }; + int ret; + + switch (cmd) { + case START: + ret = traceeval_n_start(pdata->teval_threads, keys, ts); + if (ret < 0) + pdie("Could not start thread"); + return; + case STOP: + ret = traceeval_n_stop(pdata->teval_threads, keys, ts); + if (ret < 0) + pdie("Could not stop thread"); + return; + } +} + +static void start_thread(struct process_data *pdata, int tid, + enum sched_state state, unsigned long long ts) +{ + update_thread(pdata, tid, state, START, ts); +} + +static void stop_thread(struct process_data *pdata, int tid, + enum sched_state state, unsigned long long ts) +{ + update_thread(pdata, tid, state, STOP, ts); +} + +static struct tep_format_field *get_field(struct tep_event *event, const char *name) +{ + static struct tep_format_field *field; + + field = tep_find_field(event, name); + if (!field) + die("Could not find field %s for %s", + name, event->name); + + return field; +} + +static void init_process_data(struct process_data *pdata) +{ + struct traceeval_key_info cpu_info[] = { + { + .type = TRACEEVAL_TYPE_NUMBER, + .name = "CPU", + }, + { + .type = TRACEEVAL_TYPE_NUMBER, + .name = "Schedule state", + } + }; + struct traceeval_key_info thread_info[] = { + { + .type = TRACEEVAL_TYPE_NUMBER, + .name = "TID", + }, + { + .type = TRACEEVAL_TYPE_NUMBER, + .name = "Schedule state", + } + }; + + pdata->teval_cpus = traceeval_2_alloc("CPUs", cpu_info); + if (!pdata->teval_cpus) + pdie("Creating trace eval"); + + pdata->teval_threads = traceeval_2_alloc("Threads", thread_info); + if (!pdata->teval_threads) + pdie("Creating trace eval"); +} + +static struct process_data *alloc_pdata(struct task_data *tdata, const char *comm) +{ + struct process_data *pdata; + + pdata = calloc(1, sizeof(*pdata)); + if (!pdata) + pdie("Allocating process data"); + init_process_data(pdata); + set_process_data(tdata, comm, pdata); + + return pdata; +} + +static void sched_out(struct task_data *tdata, const char *comm, + struct tep_event *event, + struct tep_record *record, struct tep_format_field *prev_pid, + struct tep_format_field *prev_state) +{ + struct process_data *pdata; + unsigned long long val; + int pid; + int ret; + + ret = tep_read_number_field(prev_pid, record->data, &val); + if (ret < 0) + die("Could not read sched_switch next_pid for record"); + + /* Ignore the idle task */ + pid = val; + if (!pid) { + /* Record the runtime for the process CPUs */ + stop_cpu(tdata->teval_cpus, record->cpu, IDLE, record->ts); + return; + } + + /* The process is scheduling out. Stop the run time. */ + update_process(tdata, comm, RUNNING, STOP, record->ts); + + /* Get the process data from the process running state */ + pdata = get_process_data(tdata, comm); + if (!pdata) + pdata = alloc_pdata(tdata, comm); + + ret = tep_read_number_field(prev_state, record->data, &val); + if (ret < 0) + die("Could not read sched_switch next_pid for record"); + val &= 3; + /* + * Save the state the process is exiting with. Will need this + * when scheduled back in. + */ + if (!val) + pdata->state = PREEMPT; + else if (val & 1) + pdata->state = SLEEP; + else if (val & 2) + pdata->state = BLOCKED; + + /* Record the state timings for the process */ + start_process(tdata, comm, pdata->state, record->ts); + + /* Record the state timings for the individual thread */ + stop_thread(pdata, pid, RUNNING, record->ts); + + /* Record the state timings for the individual thread */ + start_thread(pdata, pid, pdata->state, record->ts); + + /* Record the runtime for the process CPUs */ + stop_cpu(pdata->teval_cpus, record->cpu, RUNNING, record->ts); + + /* Record the runtime for the all CPUs */ + stop_cpu(tdata->teval_cpus, record->cpu, RUNNING, record->ts); +} + +static void sched_in(struct task_data *tdata, const char *comm, + struct tep_event *event, + struct tep_record *record, struct tep_format_field *next_pid) +{ + struct process_data *pdata; + unsigned long long val; + bool is_new = false; + int ret; + int pid; + + ret = tep_read_number_field(next_pid, record->data, &val); + if (ret < 0) + die("Could not read sched_switch next_pid for record"); + pid = val; + + /* Ignore the idle task */ + if (!pid) { + /* Record the runtime for the process CPUs */ + start_cpu(tdata->teval_cpus, record->cpu, IDLE, record->ts); + return; + } + + /* Start recording the running time of this process */ + start_process(tdata, comm, RUNNING, record->ts); + + pdata = get_process_data(tdata, comm); + + /* Start recording the running time of process CPUs */ + start_cpu(tdata->teval_cpus, record->cpu, RUNNING, record->ts); + + /* If there was no pdata, then this process did not go through sched out */ + if (!pdata) { + pdata = alloc_pdata(tdata, comm); + is_new = true; + } + + /* Record the state timings for the individual thread */ + start_thread(pdata, pid, RUNNING, record->ts); + + /* Start recording the running time of process CPUs */ + start_cpu(pdata->teval_cpus, record->cpu, RUNNING, record->ts); + + /* If it was just created, there's nothing to stop */ + if (is_new) + return; + + /* Stop recording the thread time for its scheduled out state */ + stop_thread(pdata, val, pdata->state, record->ts); + + /* Stop recording the process time for its scheduled out state */ + stop_process(tdata, comm, pdata->state, record->ts); +} + +static int switch_func(struct tracecmd_input *handle, struct tep_event *event, + struct tep_record *record, int cpu, void *data) +{ + static struct tep_format_field *prev_comm; + static struct tep_format_field *prev_pid; + static struct tep_format_field *prev_state; + static struct tep_format_field *next_comm; + static struct tep_format_field *next_pid; + struct task_data *tdata = data; + const char *comm; + + if (!next_comm) { + prev_comm = get_field(event, "prev_comm"); + prev_pid = get_field(event, "prev_pid"); + prev_state = get_field(event, "prev_state"); + + next_comm = get_field(event, "next_comm"); + next_pid = get_field(event, "next_pid"); + } + + comm = record->data + prev_comm->offset; + if (!tdata->comm || strcmp(comm, tdata->comm) == 0) + sched_out(tdata, comm, event, record, prev_pid, prev_state); + + comm = record->data + next_comm->offset; + if (!tdata->comm || strcmp(comm, tdata->comm) == 0) + sched_in(tdata, comm, event, record, next_pid); + + return 0; +} + +static void print_microseconds(int idx, unsigned long long nsecs) +{ + unsigned long long usecs; + + usecs = nsecs / 1000; + if (!nsecs || usecs) + printf("%*lld\n", idx, usecs); + else + printf("%*d.%03lld\n", idx, 0, nsecs); +} + +static void display_cpus(struct traceeval *teval) +{ + struct traceeval_key_array *karray; + const struct traceeval_key *ckey; + const struct traceeval_key *skey; + int last_cpu = -1; + int i, nr; + + printf("\n"); + + nr = traceeval_result_nr(teval); + if (!nr) + die("No result for CPUs\n"); + + for (i = 0; i < nr; i++) { + karray = traceeval_result_indx_key_array(teval, i); + if (!karray) + die("No cpu key for result %d\n", i); + ckey = traceeval_key_array_indx(karray, 0); + skey = traceeval_key_array_indx(karray, 1); + + + if (last_cpu != ckey->number) + printf(" CPU [%d]:\n", (int)ckey->number); + + switch (skey->number) { + case RUNNING: + printf(" Running: "); + break; + case IDLE: + printf(" Idle: "); + break; + case BLOCKED: + case PREEMPT: + case SLEEP: + case OTHER: + printf(" \?\?(%ld): ", skey->number); + break; + } + printf(" time (us):"); + print_microseconds(12, traceeval_result_indx_total(teval, i)); + + last_cpu = ckey->number; + } +} + +static void display_thread(struct traceeval *teval, int tid) +{ + struct traceeval_key keys[2] = + { + { + .type = TRACEEVAL_TYPE_NUMBER, + .number = tid, + }, + { + .type = TRACEEVAL_TYPE_NUMBER, + .number = RUNNING, + } + }; + ssize_t ret; + + printf("\n thread id: %d\n", tid); + + printf(" Total run time (us):"); + print_microseconds(14, (ret = traceeval_result_keys_total(teval, keys)) < 0 ? 0 : ret); + + keys[1].number = PREEMPT; + printf(" Total preempt time (us):"); + print_microseconds(10, (ret = traceeval_result_keys_total(teval, keys)) < 0 ? 0 : ret); + + keys[1].number = BLOCKED; + printf(" Total blocked time (us):"); + print_microseconds(10, (ret = traceeval_result_keys_total(teval, keys)) < 0 ? 0 : ret); + + keys[1].number = SLEEP; + printf(" Total sleep time (us):"); + print_microseconds(12, (ret = traceeval_result_keys_total(teval, keys)) < 0 ? 0 : ret); +}; + +static void display_threads(struct traceeval *teval) +{ + struct traceeval_key_array *karray; + const struct traceeval_key *tkey; + struct traceeval_key keys[2]; + int last_tid = -1; + int i, nr; + + nr = traceeval_result_nr(teval); + if (!nr) + die("No result for threads\n"); + + memset(keys, 0, sizeof(keys)); + keys[1].type = TRACEEVAL_TYPE_NUMBER; + + for (i = 0; i < nr; i++) { + karray = traceeval_result_indx_key_array(teval, i); + if (!karray) + die("No thread key for result %d\n", i); + tkey = traceeval_key_array_indx(karray, 0); + if (!tkey) + die("No thread keys for result?"); + + /* + * All the TIDS should be together in the results, + * as the results are sorted by the first key, which + * is the comm. + */ + if (last_tid == tkey->number) + continue; + + last_tid = tkey->number; + + display_thread(teval, tkey->number); + } +} + +static void display_process(struct traceeval *teval, struct process_data *pdata, + const char *comm) +{ + struct traceeval_key keys[2] = + { + { + .type = TRACEEVAL_TYPE_STRING, + .string = comm, + }, + { + .type = TRACEEVAL_TYPE_NUMBER, + .number = RUNNING, + } + }; + ssize_t ret; + + printf("Task: %s\n", comm); + + printf(" Total run time (us):"); + print_microseconds(18, (ret = traceeval_result_keys_total(teval, keys)) < 0 ? 0 : ret); + + keys[1].number = PREEMPT; + printf(" Total preempt time (us):"); + print_microseconds(14, (ret = traceeval_result_keys_total(teval, keys)) < 0 ? 0 : ret); + + keys[1].number = BLOCKED; + printf(" Total blocked time (us):"); + print_microseconds(14, (ret = traceeval_result_keys_total(teval, keys)) < 0 ? 0 : ret); + + keys[1].number = SLEEP; + printf(" Total sleep time (us):"); + print_microseconds(16, (ret = traceeval_result_keys_total(teval, keys)) < 0 ? 0 : ret); + + display_threads(pdata->teval_threads); + display_cpus(pdata->teval_cpus); + printf("\n"); +} + +static int compare_pdata(struct traceeval *teval, + const struct traceeval_key_array *A, + const struct traceeval_key_array *B, + void *data) +{ + struct traceeval_key akeys[2]; + struct traceeval_key bkeys[2]; + const struct traceeval_key *akey; + const struct traceeval_key *bkey; + long long aval; + long long bval; + int ret; + + /* Get the RUNNING values for this process */ + + akey = traceeval_key_array_indx(A, 0); + akeys[0] = *akey; + akeys[1].type = TRACEEVAL_TYPE_NUMBER; + akeys[1].number = RUNNING; + + bkey = traceeval_key_array_indx(B, 0); + bkeys[0] = *bkey; + bkeys[1].type = TRACEEVAL_TYPE_NUMBER; + bkeys[1].number = RUNNING; + + aval = traceeval_result_keys_total(teval, akey); + bval = traceeval_result_keys_total(teval, bkeys); + + if (aval < 0) + return -1; + if (bval < 0) + return -1; + + if (bval < aval) + return -1; + if (bval > aval) + return 1; + + ret = strcmp(bkeys[0].string, akeys[0].string); + + /* If two different processes have the same runtime, sort by name */ + if (ret) + return ret; + + /* Same process, sort by state */ + + akey = traceeval_key_array_indx(A, 1); + bkey = traceeval_key_array_indx(B, 1); + + if (bkey->number < akey->number) + return -1; + + return bkey->number > akey->number; +} + +static void display_processes(struct traceeval *teval) +{ + struct traceeval_key_array *karray; + const struct traceeval_key *tkey; + struct traceeval_key keys[2]; + struct process_data *pdata; + const char *last_comm = NULL; + int i, nr; + + nr = traceeval_result_nr(teval); + if (!nr) + die("No result for processes\n"); + + memset(keys, 0, sizeof(keys)); + keys[1].type = TRACEEVAL_TYPE_NUMBER; + + for (i = 0; i < nr; i++) { + karray = traceeval_result_indx_key_array(teval, i); + if (!karray) + die("No process key for result %d\n", i); + tkey = traceeval_key_array_indx(karray, 0); + if (!tkey) + die("No process keys for result?"); + + /* + * All the comms should be together in the results, + * as the results are sorted by the first key, which + * is the comm. + */ + if (last_comm && strcmp(tkey->string, last_comm) == 0) + continue; + + last_comm = tkey->string; + + keys[0] = *tkey; + keys[1].number = RUNNING; + + /* All processes should have a running state */ + pdata = traceeval_n_get_private(teval, keys); + if (pdata) + display_process(teval, pdata, keys[0].string); + } +} + +static void display(struct task_data *tdata) +{ + unsigned long long total_time = 0; + unsigned long long idle_time = 0; + struct traceeval_key_array *karray; + const struct traceeval_key *tkey; + unsigned long long val; + int i, nr; + + if (tdata->comm) + return display_processes(tdata->teval_processes); + + printf("Total:\n"); + + nr = traceeval_result_nr(tdata->teval_cpus); + for (i = 0; i < nr; i++) { + karray = traceeval_result_indx_key_array(tdata->teval_cpus, i); + if (!karray) + die("No CPU keys for result %d\n", i); + tkey = traceeval_key_array_indx(karray, 1); + if (!tkey) + die("No state keys for CPU result %d?", i); + + val = traceeval_result_indx_total(tdata->teval_cpus, i); + switch (tkey->number) { + case RUNNING: + total_time += val; + break; + case IDLE: + idle_time += val; + break; + default: + die("Invalid CPU state: %d\n", tkey->number); + } + } + + printf(" Total run time (us):"); + print_microseconds(16, total_time); + printf(" Total idle time (us):"); + print_microseconds(16, idle_time); + + display_cpus(tdata->teval_cpus); + + traceeval_sort_custom(tdata->teval_processes, compare_pdata, NULL); + + printf("\n"); + display_processes(tdata->teval_processes); +} + +static void free_tdata(struct task_data *tdata) +{ +} + +int main (int argc, char **argv) +{ + struct tracecmd_input *handle; + struct task_data data; + struct traceeval_key_info cpu_tinfo[2] = { + { + .type = TRACEEVAL_TYPE_NUMBER, + .name = "CPU" + }, + { + .type = TRACEEVAL_TYPE_NUMBER, + .name = "Schedule state" + } + }; + struct traceeval_key_info process_tinfo[2] = { + { + .type = TRACEEVAL_TYPE_STRING, + .name = "COMM" + }, + { + .type = TRACEEVAL_TYPE_NUMBER, + .name = "Schedule state" + } + }; + int c; + + memset(&data, 0, sizeof(data)); + + argv0 = argv[0]; + + while ((c = getopt(argc, argv, "c:h")) >= 0) { + switch (c) { + case 'c': + data.comm = optarg; + break; + case 'h': + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + if (argc < 1) + usage(); + + handle = tracecmd_open(argv[0], TRACECMD_FL_LOAD_NO_PLUGINS); + if (!handle) + pdie("Error opening %s", argv[0]); + + data.teval_processes = traceeval_2_alloc("Processes", process_tinfo); + if (!data.teval_processes) + pdie("Creating trace eval"); + + data.teval_cpus = traceeval_2_alloc("CPUs", cpu_tinfo); + if (!data.teval_cpus) + pdie("Creating trace eval"); + + tracecmd_follow_event(handle, "sched", "sched_switch", switch_func, &data); + + tracecmd_iterate_events(handle, NULL, 0, NULL, NULL); + + display(&data); + + free_tdata(&data); + + return 0; +} From patchwork Thu Aug 17 01:32:55 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13355860 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 92C03C2FC20 for ; Thu, 17 Aug 2023 01:33:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1347536AbjHQBdN (ORCPT ); Wed, 16 Aug 2023 21:33:13 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33662 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1347547AbjHQBdK (ORCPT ); Wed, 16 Aug 2023 21:33:10 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [IPv6:2604:1380:4641:c500::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1AE802723 for ; Wed, 16 Aug 2023 18:33:08 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id A478163D8D for ; Thu, 17 Aug 2023 01:33:07 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 0E9DAC433CA; Thu, 17 Aug 2023 01:33:07 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.96) (envelope-from ) id 1qWRst-000N3k-1a; Wed, 16 Aug 2023 21:33:11 -0400 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: Ross Zwisler , Stevie Alvarez , "Steven Rostedt (Google)" Subject: [PATCH v3 03/18] libtraceeval hist: Add pointer and const string types Date: Wed, 16 Aug 2023 21:32:55 -0400 Message-Id: <20230817013310.88582-4-rostedt@goodmis.org> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230817013310.88582-1-rostedt@goodmis.org> References: <20230817013310.88582-1-rostedt@goodmis.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org From: "Steven Rostedt (Google)" Add a pointer type to traceeval_data, as this can be used as a generic pointer type. This may even obsolete the dynamic type. Also, add a const char * "cstring" type. There's times where the key and value data needs to be assigned to a const char *string, and without having an option of that type, the compiler complains about losing the const. Signed-off-by: Steven Rostedt (Google) --- include/traceeval-hist.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/traceeval-hist.h b/include/traceeval-hist.h index 08e0696f2d83..f6c4e8efb2be 100644 --- a/include/traceeval-hist.h +++ b/include/traceeval-hist.h @@ -25,6 +25,7 @@ enum traceeval_data_type { TRACEEVAL_TYPE_NUMBER_32, TRACEEVAL_TYPE_NUMBER_64, TRACEEVAL_TYPE_NUMBER, + TRACEEVAL_TYPE_POINTER, TRACEEVAL_TYPE_STRING, TRACEEVAL_TYPE_DYNAMIC }; @@ -52,6 +53,8 @@ struct traceeval_dynamic { union traceeval_data { struct traceeval_dynamic dyn_data; char *string; + const char *cstring; + void *pointer; unsigned long number; unsigned long long number_64; unsigned int number_32; From patchwork Thu Aug 17 01:32:56 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13355870 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4FEFDC2FC0F for ; Thu, 17 Aug 2023 01:34:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S244294AbjHQBdp (ORCPT ); Wed, 16 Aug 2023 21:33:45 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58056 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1347556AbjHQBdM (ORCPT ); Wed, 16 Aug 2023 21:33:12 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [IPv6:2604:1380:4641:c500::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 08FB81FCE for ; Wed, 16 Aug 2023 18:33:10 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 3334364A61 for ; Thu, 17 Aug 2023 01:33:08 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 09C49C433CC; Thu, 17 Aug 2023 01:33:07 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.96) (envelope-from ) id 1qWRst-000N3n-1f; Wed, 16 Aug 2023 21:33:11 -0400 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: Ross Zwisler , Stevie Alvarez , "Steven Rostedt (Google)" Subject: [PATCH v3 04/18] libtraceeval histogram: Have cmp and release functions be generic Date: Wed, 16 Aug 2023 21:32:56 -0400 Message-Id: <20230817013310.88582-5-rostedt@goodmis.org> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230817013310.88582-1-rostedt@goodmis.org> References: <20230817013310.88582-1-rostedt@goodmis.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org From: "Steven Rostedt (Google)" Having the ability to override the compare function for a given type can be very advantageous. There's also no reason that any type could ask for a release callback to be called when the type is being released. It could be used for information as well as for freeing. Rename traceeval_dyn_cmp_fn to traceeval_data_cmp_fn and traceeval_dyn_release_fn to traceeval_data_release_fn and have them take the union traceeval_type instead of struct traceeval_dynamic. Also this changes the compare to pass a pointer to union traceeval_data instead of passing in the structure of the dyn_data type. In the structure, rename dyn_cmp to just cmp, and dyn_release to just release. Reviewed-by: Ross Zwisler Signed-off-by: Steven Rostedt (Google) --- include/traceeval-hist.h | 50 +++++++++++++++++++++------------------- src/histograms.c | 28 +++++++++++----------- 2 files changed, 40 insertions(+), 38 deletions(-) diff --git a/include/traceeval-hist.h b/include/traceeval-hist.h index f6c4e8efb2be..4c42c82ea045 100644 --- a/include/traceeval-hist.h +++ b/include/traceeval-hist.h @@ -64,14 +64,14 @@ union traceeval_data { struct traceeval_type; -/* struct traceeval_dynamic release function signature */ -typedef void (*traceeval_dyn_release_fn)(struct traceeval_type *, - struct traceeval_dynamic); +/* release function callback on traceeval_data */ +typedef void (*traceeval_data_release_fn)(struct traceeval_type *, + union traceeval_data *); -/* struct traceeval_dynamic compare function signature */ -typedef int (*traceeval_dyn_cmp_fn)(struct traceeval_dynamic, - struct traceeval_dynamic, - struct traceeval_type *); +/* compare function callback to compare traceeval_data */ +typedef int (*traceeval_data_cmp_fn)(const union traceeval_data *, + const union traceeval_data *, + struct traceeval_type *); /* * struct traceeval_type - Describes the type of a traceevent_data instance @@ -79,8 +79,8 @@ typedef int (*traceeval_dyn_cmp_fn)(struct traceeval_dynamic, * @name: The string name of the traceeval_data * @flags: flags to describe the traceeval_data * @id: User specified identifier - * @dyn_release: For dynamic types called on release (ignored for other types) - * @dyn_cmp: A way to compare dynamic types (ignored for other types) + * @release: An optional callback for when the data is being released + * @cmp: An optional callback to specify a way to compare the type * * The traceeval_type structure defines expectations for a corresponding * traceeval_data instance for a traceeval histogram instance. Used to @@ -91,29 +91,31 @@ typedef int (*traceeval_dyn_cmp_fn)(struct traceeval_dynamic, * which each relate to distinct user defined struct traceeval_dynamic * 'sub-types'. * - * For flexibility, @dyn_cmp() and @dyn_release() take a struct - * traceeval_type instance. This allows the user to distinguish between - * different sub-types of struct traceeval_dynamic within a single - * callback function by examining the @id field. This is not a required - * approach, merely one that is accommodated. + * For flexibility, @cmp() and @release() take a struct traceeval_type + * instance. This allows the user to handle dyn_data and pointer types. + * It may also be used for other types if the default cmp() or release() + * need to be overridden. Note for string types, even if the release() + * is called, the string freeing is still taken care of by the traceeval + * infrastructure. * - * @dyn_cmp() is used to compare two struct traceeval_dynamic instances when a - * corresponding struct traceeval_type is reached with its type field set to - * TRACEEVAL_TYPE_DYNAMIC. It should return 0 on equality, 1 if the first - * argument is greater than the second, -1 for the other way around, and -2 on - * error. + * The @id field is a user specified field that may allow the same callback + * to be used by multiple types and not needing to do a strcmp() against the + * name (could be used for switch statements). * - * dyn_release() is used during traceeval_release() to release a union - * traceeval_data's struct traceeval_dynamic field when the corresponding - * traceeval_type type is set to TRACEEVAL_TYPE_DYNAMIC. + * @cmp() is used to override the default compare of a type. This is + * required to compare dyn_data and pointer types. It should return 0 + * on equality, 1 if the first argument is greater than the second, + * -1 for the other way around, and -2 on error. + * + * @release() is called when a data element is being released (or freed). */ struct traceeval_type { char *name; enum traceeval_data_type type; size_t flags; size_t id; - traceeval_dyn_release_fn dyn_release; - traceeval_dyn_cmp_fn dyn_cmp; + traceeval_data_release_fn release; + traceeval_data_cmp_fn cmp; }; /* Statistics about a given entry element */ diff --git a/src/histograms.c b/src/histograms.c index d22d15238616..0c10edbc9acc 100644 --- a/src/histograms.c +++ b/src/histograms.c @@ -90,9 +90,9 @@ static int compare_traceeval_type(struct traceeval_type *orig, return 0; if (orig[i].id != copy[i].id) return 0; - if (orig[i].dyn_release != copy[i].dyn_release) + if (orig[i].release != copy[i].release) return 0; - if (orig[i].dyn_cmp != copy[i].dyn_cmp) + if (orig[i].cmp != copy[i].cmp) return 0; // make sure both names are same type @@ -128,6 +128,9 @@ static int compare_traceeval_data(union traceeval_data *orig, if (!copy) return 1; + if (type->cmp) + return type->cmp(orig, copy, type); + switch (type->type) { case TRACEEVAL_TYPE_STRING: i = strcmp(orig->string, copy->string); @@ -149,8 +152,7 @@ static int compare_traceeval_data(union traceeval_data *orig, compare_numbers_return(orig->number_8, copy->number_8); case TRACEEVAL_TYPE_DYNAMIC: - if (type->dyn_cmp) - return type->dyn_cmp(orig->dyn_data, copy->dyn_data, type); + /* If it didn't specify a cmp function, then punt */ return 0; default: @@ -236,8 +238,8 @@ static int compare_hist(struct traceeval *orig, struct traceeval *copy) * * This compares the values of the key definitions, value definitions, and * inserted data between @orig and @copy in order. It does not compare - * by memory address, except for struct traceeval_type's dyn_release() and - * dyn_cmp() fields. + * by memory address, except for struct traceeval_type's release() and + * cmp() fields. * * Returns 1 if @orig and @copy are the same, 0 if not, and -1 on error. */ @@ -438,14 +440,13 @@ fail: */ static void clean_data(union traceeval_data *data, struct traceeval_type *type) { + if (type->release) + type->release(type, data); + switch (type->type) { case TRACEEVAL_TYPE_STRING: free(data->string); break; - case TRACEEVAL_TYPE_DYNAMIC: - if (type->dyn_release) - type->dyn_release(type, data->dyn_data); - break; default: break; } @@ -465,9 +466,8 @@ static void clean_data_set(union traceeval_data *data, struct traceeval_type *de return; } - for (i = 0; i < size; i++) { + for (i = 0; i < size; i++) clean_data(data + i, defs + i); - } free(data); } @@ -512,7 +512,7 @@ static void hist_table_release(struct traceeval *teval) * it must call traceeval_release(). * * This frees all internally allocated data of @teval and will call the - * corresponding dyn_release() functions registered for keys and values of + * corresponding release() functions registered for keys and values of * type TRACEEVAL_TYPE_DYNAMIC. */ void traceeval_release(struct traceeval *teval) @@ -588,7 +588,7 @@ static int copy_traceeval_data(struct traceeval_type *type, /* * Free @data with respect to @size and @type. * - * Does not call dyn_release on type TRACEEVAL_TYPE_DYNAMIC. + * Does not call the release callback on the data. */ static void data_release(size_t size, union traceeval_data **data, struct traceeval_type *type) From patchwork Thu Aug 17 01:32:57 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13355861 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 59C4DC2FC18 for ; Thu, 17 Aug 2023 01:33:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1347553AbjHQBdO (ORCPT ); Wed, 16 Aug 2023 21:33:14 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33638 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1347544AbjHQBdJ (ORCPT ); Wed, 16 Aug 2023 21:33:09 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [IPv6:2604:1380:4641:c500::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 07F3C26AA for ; Wed, 16 Aug 2023 18:33:08 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 8E2666343C for ; Thu, 17 Aug 2023 01:33:07 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id F0C3FC433C7; Thu, 17 Aug 2023 01:33:06 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.96) (envelope-from ) id 1qWRst-000N3q-1k; Wed, 16 Aug 2023 21:33:11 -0400 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: Ross Zwisler , Stevie Alvarez , "Steven Rostedt (Google)" Subject: [PATCH v3 05/18] libtraceeval histograms: Add traceeval struct to compare function Date: Wed, 16 Aug 2023 21:32:57 -0400 Message-Id: <20230817013310.88582-6-rostedt@goodmis.org> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230817013310.88582-1-rostedt@goodmis.org> References: <20230817013310.88582-1-rostedt@goodmis.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org From: "Steven Rostedt (Google)" When looking at how this code would be implemented, I found that I needed access to the traceeval structure within the compare callbacks. Pass that in too. Also, rearrange the parameters a little. The traceeval and type should go first as they describe the "object", and the data should go last as they are the values of the function. Reviewed-by: Ross Zwisler Signed-off-by: Steven Rostedt (Google) --- include/traceeval-hist.h | 13 +++++++------ src/histograms.c | 31 ++++++++++++++++--------------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/include/traceeval-hist.h b/include/traceeval-hist.h index 4c42c82ea045..d7ffe42f3231 100644 --- a/include/traceeval-hist.h +++ b/include/traceeval-hist.h @@ -63,16 +63,17 @@ union traceeval_data { }; struct traceeval_type; +struct traceeval; /* release function callback on traceeval_data */ -typedef void (*traceeval_data_release_fn)(struct traceeval_type *, - union traceeval_data *); +typedef void (*traceeval_data_release_fn)(const struct traceeval_type *type, + union traceeval_data *data); /* compare function callback to compare traceeval_data */ -typedef int (*traceeval_data_cmp_fn)(const union traceeval_data *, - const union traceeval_data *, - struct traceeval_type *); - +typedef int (*traceeval_data_cmp_fn)(struct traceeval *teval, + const struct traceeval_type *type, + const union traceeval_data *A, + const union traceeval_data *B); /* * struct traceeval_type - Describes the type of a traceevent_data instance * @type: The enum type that describes the traceeval_data diff --git a/src/histograms.c b/src/histograms.c index 0c10edbc9acc..1a7fa03e031c 100644 --- a/src/histograms.c +++ b/src/histograms.c @@ -113,9 +113,10 @@ static int compare_traceeval_type(struct traceeval_type *orig, * Return 0 if @orig and @copy are the same, 1 if @orig is greater than @copy, * -1 for the other way around, and -2 on error. */ -static int compare_traceeval_data(union traceeval_data *orig, - const union traceeval_data *copy, - struct traceeval_type *type) +static int compare_traceeval_data(struct traceeval *teval, + struct traceeval_type *type, + union traceeval_data *orig, + const union traceeval_data *copy) { int i; @@ -129,7 +130,7 @@ static int compare_traceeval_data(union traceeval_data *orig, return 1; if (type->cmp) - return type->cmp(orig, copy, type); + return type->cmp(teval, type, orig, copy); switch (type->type) { case TRACEEVAL_TYPE_STRING: @@ -167,16 +168,17 @@ static int compare_traceeval_data(union traceeval_data *orig, * * Return 1 if @orig and @copy are the same, 0 if not, and -1 on error. */ -static int compare_traceeval_data_set(union traceeval_data *orig, - const union traceeval_data *copy, - struct traceeval_type *defs, size_t size) +static int compare_traceeval_data_set(struct traceeval *teval, + struct traceeval_type *defs, + union traceeval_data *orig, + const union traceeval_data *copy, size_t size) { int check; size_t i; /* compare data arrays */ for (i = 0; i < size; i++) { - if ((check = compare_traceeval_data(orig + i, copy + i, defs + i))) + if ((check = compare_traceeval_data(teval, defs + i, orig + i, copy + i))) return check == -2 ? -1 : 0; } @@ -192,14 +194,14 @@ static int compare_entries(struct entry *orig, struct entry *copy, int check; /* compare keys */ - check = compare_traceeval_data_set(orig->keys, copy->keys, - teval->key_types, teval->nr_key_types); + check = compare_traceeval_data_set(teval, teval->key_types, + orig->keys, copy->keys, teval->nr_key_types); if (check < 1) return check; /* compare values */ - check = compare_traceeval_data_set(orig->vals, copy->vals, - teval->val_types, teval->nr_val_types); + check = compare_traceeval_data_set(teval, teval->val_types, + orig->vals, copy->vals, teval->nr_val_types); return check; } @@ -546,9 +548,8 @@ static int get_entry(struct traceeval *teval, const union traceeval_data *keys, hist = teval->hist; for (i = 0, entry = hist->map; i < hist->nr_entries; entry = &hist->map[++i]) { - check = compare_traceeval_data_set(entry->keys, keys, - teval->key_types, teval->nr_key_types); - + check = compare_traceeval_data_set(teval, teval->key_types, + entry->keys, keys, teval->nr_key_types); if (!check) continue; break; From patchwork Thu Aug 17 01:32:58 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13355867 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B11EEC2FC1F for ; Thu, 17 Aug 2023 01:33:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1347551AbjHQBdP (ORCPT ); Wed, 16 Aug 2023 21:33:15 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33640 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1347545AbjHQBdJ (ORCPT ); Wed, 16 Aug 2023 21:33:09 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [139.178.84.217]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 016971FC3 for ; Wed, 16 Aug 2023 18:33:08 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 94ADF612B2 for ; Thu, 17 Aug 2023 01:33:07 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 02F90C433C9; Thu, 17 Aug 2023 01:33:07 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.96) (envelope-from ) id 1qWRst-000N3t-1o; Wed, 16 Aug 2023 21:33:11 -0400 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: Ross Zwisler , Stevie Alvarez , "Steven Rostedt (Google)" Subject: [PATCH v3 06/18] libtraceeval histogram: Remove comparing of traceeval and types Date: Wed, 16 Aug 2023 21:32:58 -0400 Message-Id: <20230817013310.88582-7-rostedt@goodmis.org> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230817013310.88582-1-rostedt@goodmis.org> References: <20230817013310.88582-1-rostedt@goodmis.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org From: "Steven Rostedt (Google)" The compare functions for traceeval and types were created for testing. But because the tests have not yet been pulled in, remove these functions as they are currently unused and cause an unnecessary burden of making sure they are updated when other code they use is updated. Signed-off-by: Steven Rostedt (Google) --- Makefile | 1 - include/traceeval-test.h | 16 ----- src/histograms.c | 127 --------------------------------------- 3 files changed, 144 deletions(-) delete mode 100644 include/traceeval-test.h diff --git a/Makefile b/Makefile index 62ca9285d7a0..d8c57ef4e356 100644 --- a/Makefile +++ b/Makefile @@ -172,7 +172,6 @@ libs: $(LIBRARY_A) $(LIBRARY_SO) VALGRIND = $(shell which valgrind) UTEST_DIR = utest -UTEST_BINARY = eval-utest test: force $(LIBRARY_STATIC) ifneq ($(CUNIT_INSTALLED),1) diff --git a/include/traceeval-test.h b/include/traceeval-test.h deleted file mode 100644 index bb8092ac2e50..000000000000 --- a/include/traceeval-test.h +++ /dev/null @@ -1,16 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -/* - * libtraceeval interface for unit testing. - * - * Copyright (C) 2023 Google Inc, Steven Rostedt - * Copyright (C) 2023 Google Inc, Stevie Alvarez - */ - -#ifndef __LIBTRACEEVAL_TEST_H__ -#define __LIBTRACEEVAL_TEST_H__ - -#include - -int traceeval_compare(struct traceeval *orig, struct traceeval *copy); - -#endif /* __LIBTRACEEVAL_TEST_H__ */ diff --git a/src/histograms.c b/src/histograms.c index 1a7fa03e031c..ccb52d0f1d42 100644 --- a/src/histograms.c +++ b/src/histograms.c @@ -12,8 +12,6 @@ #include -#include "traceeval-test.h" - /* * Compare two integers of variable length. * @@ -63,50 +61,6 @@ static void print_err(const char *fmt, ...) fprintf(stderr, "\n"); } -/* - * Compare traceeval_type instances for equality. - * - * Return 1 if @orig and @copy are the same, 0 otherwise. - */ -static int compare_traceeval_type(struct traceeval_type *orig, - struct traceeval_type *copy, - size_t orig_size, size_t copy_size) -{ - size_t i; - - /* same memory/NULL */ - if (orig == copy) - return 1; - if (!!orig != !!copy) - return 0; - - if (orig_size != copy_size) - return 0; - - for (i = 0; i < orig_size; i++) { - if (orig[i].type != copy[i].type) - return 0; - if (orig[i].flags != copy[i].flags) - return 0; - if (orig[i].id != copy[i].id) - return 0; - if (orig[i].release != copy[i].release) - return 0; - if (orig[i].cmp != copy[i].cmp) - return 0; - - // make sure both names are same type - if (!!orig[i].name != !!copy[i].name) - return 0; - if (!orig[i].name) - continue; - if (strcmp(orig[i].name, copy[i].name) != 0) - return 0; - } - - return 1; -} - /* * Compare traceeval_data instances. * @@ -185,87 +139,6 @@ static int compare_traceeval_data_set(struct traceeval *teval, return 1; } -/* - * Return 1 if @orig and @copy are the same, 0 if not, and -1 on error. - */ -static int compare_entries(struct entry *orig, struct entry *copy, - struct traceeval *teval) -{ - int check; - - /* compare keys */ - check = compare_traceeval_data_set(teval, teval->key_types, - orig->keys, copy->keys, teval->nr_key_types); - if (check < 1) - return check; - - /* compare values */ - check = compare_traceeval_data_set(teval, teval->val_types, - orig->vals, copy->vals, teval->nr_val_types); - return check; -} - -/* - * Compares the hist fields of @orig and @copy for equality. - * - * Assumes all other aspects of @orig and @copy are the same. - * - * Return 1 if struct hist_table of @orig and @copy are the same, 0 if not, - * and -1 on error. - */ -static int compare_hist(struct traceeval *orig, struct traceeval *copy) -{ - struct hist_table *o_hist; - struct hist_table *c_hist; - int c; - - o_hist = orig->hist; - c_hist = copy->hist; - - if (o_hist->nr_entries != c_hist->nr_entries) - return 0; - - for (size_t i = 0; i < o_hist->nr_entries; i++) { - if ((c = compare_entries(o_hist->map + i, c_hist->map + i, orig)) < 1) - return c; - } - - return 1; -} - -/* - * traceeval_compare - Check equality between two traceeval instances - * @orig: The first traceeval instance - * @copy: The second traceeval instance - * - * This compares the values of the key definitions, value definitions, and - * inserted data between @orig and @copy in order. It does not compare - * by memory address, except for struct traceeval_type's release() and - * cmp() fields. - * - * Returns 1 if @orig and @copy are the same, 0 if not, and -1 on error. - */ - int traceeval_compare(struct traceeval *orig, struct traceeval *copy) -{ - int keys; - int vals; - int hists; - - if (!orig || !copy) - return -1; - - keys = compare_traceeval_type(orig->key_types, copy->key_types, - orig->nr_key_types, copy->nr_key_types); - vals = compare_traceeval_type(orig->val_types, copy->val_types, - orig->nr_val_types, copy->nr_val_types); - hists = compare_hist(orig, copy); - - if (hists == -1) - return -1; - - return keys && vals && hists; -} - /* * type_release - free a struct traceeval_type array * @defs: The array to release From patchwork Thu Aug 17 01:32:59 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13355862 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 73F46C2FC1C for ; Thu, 17 Aug 2023 01:33:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1347550AbjHQBdO (ORCPT ); Wed, 16 Aug 2023 21:33:14 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58044 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1347553AbjHQBdK (ORCPT ); Wed, 16 Aug 2023 21:33:10 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [IPv6:2604:1380:4641:c500::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 79E481FC8 for ; Wed, 16 Aug 2023 18:33:09 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 2EA8C648CA for ; Thu, 17 Aug 2023 01:33:08 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 081CDC433CB; Thu, 17 Aug 2023 01:33:07 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.96) (envelope-from ) id 1qWRst-000N3w-1t; Wed, 16 Aug 2023 21:33:11 -0400 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: Ross Zwisler , Stevie Alvarez , "Steven Rostedt (Google)" Subject: [PATCH v3 07/18] libtraceeval: Convert hist array into a hash table Date: Wed, 16 Aug 2023 21:32:59 -0400 Message-Id: <20230817013310.88582-8-rostedt@goodmis.org> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230817013310.88582-1-rostedt@goodmis.org> References: <20230817013310.88582-1-rostedt@goodmis.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org From: "Steven Rostedt (Google)" The lookups need to be extremely fast. Instead of doing a linear search across all entries (which could be thousands), do a hash lookup instead. Signed-off-by: Steven Rostedt (Google) --- include/traceeval-hist.h | 7 ++ src/histograms.c | 166 +++++++++++++++++++++++++++++++++------ 2 files changed, 147 insertions(+), 26 deletions(-) diff --git a/include/traceeval-hist.h b/include/traceeval-hist.h index d7ffe42f3231..1edda56a712f 100644 --- a/include/traceeval-hist.h +++ b/include/traceeval-hist.h @@ -74,6 +74,12 @@ typedef int (*traceeval_data_cmp_fn)(struct traceeval *teval, const struct traceeval_type *type, const union traceeval_data *A, const union traceeval_data *B); + +/* make a unique value */ +typedef int (*traceeval_data_hash_fn)(struct traceeval *teval, + const struct traceeval_type *type, + const union traceeval_data *data); + /* * struct traceeval_type - Describes the type of a traceevent_data instance * @type: The enum type that describes the traceeval_data @@ -117,6 +123,7 @@ struct traceeval_type { size_t id; traceeval_data_release_fn release; traceeval_data_cmp_fn cmp; + traceeval_data_hash_fn hash; }; /* Statistics about a given entry element */ diff --git a/src/histograms.c b/src/histograms.c index ccb52d0f1d42..08f6ddf55ae1 100644 --- a/src/histograms.c +++ b/src/histograms.c @@ -3,6 +3,7 @@ * libtraceeval histogram interface implementation. * * Copyright (C) 2023 Google Inc, Stevie Alvarez + * Copyright (C) 2023 Google Inc, Steven Rostedt */ #include @@ -12,6 +13,14 @@ #include +#define offset_of(type, field) ((size_t)(&(((type *)(NULL))->field))) +#define container_of(ptr, type, field) \ + (type *)((void *)(ptr) - (void *)offset_of(type, field)) + +#define HASH_BITS 10 /* Start with 1K of buckets */ +#define HASH_SIZE(bits) (1 << (bits)) +#define HASH_MASK(bits) (HASH_SIZE(bits) - 1) + /* * Compare two integers of variable length. * @@ -25,23 +34,31 @@ do { \ return (a) != (b); \ } while (0) \ + +struct hash_item { + struct hash_item *next; + unsigned key; +}; + +/* A hash of key-value entries */ +struct hash_table { + struct hash_item **hash; + unsigned bits; + size_t nr_items; +}; + /* A key-value pair */ struct entry { + struct hash_item hash; union traceeval_data *keys; union traceeval_data *vals; }; -/* A table of key-value entries */ -struct hist_table { - struct entry *map; - size_t nr_entries; -}; - /* Histogram */ struct traceeval { struct traceeval_type *key_types; struct traceeval_type *val_types; - struct hist_table *hist; + struct hash_table *hist; size_t nr_key_types; size_t nr_val_types; }; @@ -299,6 +316,11 @@ struct traceeval *traceeval_init(const struct traceeval_type *keys, err_msg = "Failed to allocate memory for histogram"; goto fail_release; } + teval->hist->bits = HASH_BITS; + teval->hist->hash = calloc(HASH_SIZE(teval->hist->bits), + sizeof(*teval->hist->hash)); + if (!teval->hist->hash) + goto fail_release; return teval; @@ -350,7 +372,7 @@ static void clean_data_set(union traceeval_data *data, struct traceeval_type *de /* * Free all possible data stored within the entry. */ -static void clean_entry(struct entry *entry, struct traceeval *teval) +static void free_entry(struct traceeval *teval, struct entry *entry) { if (!entry) return; @@ -358,6 +380,19 @@ static void clean_entry(struct entry *entry, struct traceeval *teval) /* free dynamic traceeval_data */ clean_data_set(entry->keys, teval->key_types, teval->nr_key_types); clean_data_set(entry->vals, teval->val_types, teval->nr_val_types); + + free(entry); +} + +static void free_entries(struct traceeval *teval, struct hash_item *item) +{ + struct entry *entry; + + while (item) { + entry = container_of(item, struct entry, hash); + item = item->next; + free_entry(teval, entry); + } } /* @@ -365,16 +400,19 @@ static void clean_entry(struct entry *entry, struct traceeval *teval) */ static void hist_table_release(struct traceeval *teval) { - struct hist_table *hist = teval->hist; + struct hash_table *hist = teval->hist; if (!hist) return; - for (size_t i = 0; i < hist->nr_entries; i++) { - clean_entry(hist->map + i, teval); + for (size_t i = 0; i < HASH_SIZE(hist->bits); i++) { + if (!hist->hash[i]) + continue; + + free_entries(teval, hist->hash[i]); } - free(hist->map); + free(hist->hash); free(hist); teval->hist = NULL; } @@ -403,6 +441,58 @@ void traceeval_release(struct traceeval *teval) free(teval); } +static unsigned long long hash_string(const char *str) +{ + unsigned long long key = 0; + int len = strlen(str); + int i; + + for (i = 0; i < len; i++) + key += (unsigned long long)str[i] << ((i & 7) * 8); + + return key; +} + +static unsigned make_hash(struct traceeval *teval, const union traceeval_data *keys, + int bits) +{ + const struct traceeval_type *types = teval->key_types; + unsigned long long val; + unsigned key = 0; + int nr = teval->nr_key_types; + + for (int i = 0; i < nr; i++) { + if (types[i].hash) { + key += types[i].hash(teval, &types[i], &keys[i]); + continue; + } + + switch (types[i].type) { + case TRACEEVAL_TYPE_NUMBER_8: + case TRACEEVAL_TYPE_NUMBER_16: + case TRACEEVAL_TYPE_NUMBER_32: + case TRACEEVAL_TYPE_NUMBER_64: + case TRACEEVAL_TYPE_NUMBER: + val = keys[i].number_64; + break; + case TRACEEVAL_TYPE_STRING: + val = hash_string(keys[i].cstring); + break; + default: + val = 0; + } + /* + * This is a quick hashing function adapted from Donald E. Knuth's 32 + * bit multiplicative hash. See The Art of Computer Programming (TAOCP). + * Multiplication by the Prime number, closest to the golden ratio of + * 2^32. + */ + key += val * 2654435761; + } + + return key & HASH_MASK(bits); +} + /* * Find the entry that @keys corresponds to within @teval. * @@ -411,21 +501,24 @@ void traceeval_release(struct traceeval *teval) static int get_entry(struct traceeval *teval, const union traceeval_data *keys, struct entry **result) { - struct hist_table *hist; + struct hash_table *hist = teval->hist; struct entry *entry; + unsigned key; int check = 0; - int i; if (!teval || !keys) return -1; + key = make_hash(teval, keys, hist->bits); + hist = teval->hist; - for (i = 0, entry = hist->map; i < hist->nr_entries; entry = &hist->map[++i]) { + + for (struct hash_item *item = hist->hash[key]; item; item = item->next) { + entry = container_of(item, struct entry, hash); check = compare_traceeval_data_set(teval, teval->key_types, entry->keys, keys, teval->nr_key_types); - if (!check) - continue; - break; + if (check) + break; } if (check > 0) @@ -536,6 +629,7 @@ int traceeval_query(struct traceeval *teval, const union traceeval_data *keys, /* find key and copy its corresponding value pair */ if ((check = get_entry(teval, keys, &entry)) < 1) return check; + return copy_traceeval_data_set(teval->nr_val_types, teval->val_types, entry->vals, results); } @@ -563,6 +657,28 @@ void traceeval_results_release(struct traceeval *teval, data_release(teval->nr_val_types, &results, teval->val_types); } +static struct entry *create_hist_entry(struct traceeval *teval, + const union traceeval_data *keys) +{ + struct hash_table *hist = teval->hist; + struct hash_item *item; + unsigned key = make_hash(teval, keys, hist->bits); + struct entry *entry; + + entry = calloc(1, sizeof(*entry)); + if (!entry) + return NULL; + + item = &entry->hash; + item->next = hist->hash[key]; + hist->hash[key] = item; + item->key = key; + + hist->nr_items++; + + return entry; +} + /* * Create a new entry in @teval with respect to @keys and @vals. * @@ -574,8 +690,7 @@ static int create_entry(struct traceeval *teval, { union traceeval_data *new_keys; union traceeval_data *new_vals; - struct entry *tmp_map; - struct hist_table *hist = teval->hist; + struct entry *entry; /* copy keys */ if (copy_traceeval_data_set(teval->nr_key_types, teval->key_types, @@ -587,13 +702,12 @@ static int create_entry(struct traceeval *teval, vals, &new_vals) == -1) goto fail_vals; - /* create new entry */ - tmp_map = realloc(hist->map, ++hist->nr_entries * sizeof(*tmp_map)); - if (!tmp_map) + entry = create_hist_entry(teval, keys); + if (!entry) goto fail; - tmp_map->keys = new_keys; - tmp_map->vals = new_vals; - hist->map = tmp_map; + + entry->keys = new_keys; + entry->vals = new_vals; return 0; From patchwork Thu Aug 17 01:33:00 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13355868 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1A418C2FC24 for ; Thu, 17 Aug 2023 01:33:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1347545AbjHQBdP (ORCPT ); Wed, 16 Aug 2023 21:33:15 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58058 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1347554AbjHQBdM (ORCPT ); Wed, 16 Aug 2023 21:33:12 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [IPv6:2604:1380:4641:c500::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 09506271E for ; Wed, 16 Aug 2023 18:33:10 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 434D964ABB for ; Thu, 17 Aug 2023 01:33:08 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 160AEC433D9; Thu, 17 Aug 2023 01:33:07 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.96) (envelope-from ) id 1qWRst-000N3z-1y; Wed, 16 Aug 2023 21:33:11 -0400 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: Ross Zwisler , Stevie Alvarez , "Steven Rostedt (Google)" Subject: [PATCH v3 08/18] libtraceeval histograms: Move hash functions into their own file Date: Wed, 16 Aug 2023 21:33:00 -0400 Message-Id: <20230817013310.88582-9-rostedt@goodmis.org> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230817013310.88582-1-rostedt@goodmis.org> References: <20230817013310.88582-1-rostedt@goodmis.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org From: "Steven Rostedt (Google)" Move the hash functions into their own file so that it does not clutter the histogram.c file. Functionality should be the same, but some code restructuring was done. To keep the hash abstract, helper functions were introduced: hash_iter_start() - to start iterating over all items in the hash hash_iter_next() - to get the next item in the hash hash_iter_bucket() - to start iterating over a single bucket in the hash hash_iter_bucket_next() - to get the next item in the bucket hash_nr_items() - to get the number of items in the hash Also implemented hash_remove() to remove an item from the hash. Also created a eval-local.h header file to store the prototypes of the local functions as well as moved the structures there too. Signed-off-by: Steven Rostedt (Google) --- src/Makefile | 1 + src/eval-local.h | 103 +++++++++++++++++++++++++++++++++++++++ src/hash.c | 123 +++++++++++++++++++++++++++++++++++++++++++++++ src/histograms.c | 121 +++++++--------------------------------------- 4 files changed, 244 insertions(+), 104 deletions(-) create mode 100644 src/eval-local.h create mode 100644 src/hash.c diff --git a/src/Makefile b/src/Makefile index b32a3891333d..4b7b3051a8b2 100644 --- a/src/Makefile +++ b/src/Makefile @@ -5,6 +5,7 @@ include $(src)/scripts/utils.mk OBJS = OBJS += trace-analysis.o OBJS += histograms.o +OBJS += hash.o OBJS := $(OBJS:%.o=$(bdir)/%.o) diff --git a/src/eval-local.h b/src/eval-local.h new file mode 100644 index 000000000000..007fdb146b96 --- /dev/null +++ b/src/eval-local.h @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __EVAL_LOCAL_H +#define __EVAL_LOCAL_H + +#include + +#define __hidden __attribute__((visibility ("hidden"))) + +#define offset_of(type, field) ((size_t)(&(((type *)(NULL))->field))) +#define container_of(ptr, type, field) \ + (type *)((void *)(ptr) - (void *)offset_of(type, field)) + +#define HASH_BITS 10 /* Start with 1K of buckets */ +#define HASH_SIZE(bits) (1 << (bits)) +#define HASH_MASK(bits) (HASH_SIZE(bits) - 1) + +/* + * Compare two integers of variable length. + * + * Return 0 if @a and @b are the same, 1 if @a is greater than @b, and -1 + * if @b is greater than @a. + */ +#define compare_numbers_return(a, b) \ +do { \ + if ((a) < (b)) \ + return -1; \ + return (a) != (b); \ +} while (0) \ + +struct hash_item { + struct hash_item *next; + unsigned key; +}; + +struct hash_iter { + struct hash_item *next_item; + size_t current_bucket; +}; + +/* A table of key-value entries */ +struct hash_table { + struct hash_item **hash; + unsigned bits; + size_t nr_items; + struct hash_iter iter; +}; + +/* A key-value pair */ +struct entry { + struct hash_item hash; + union traceeval_data *keys; + union traceeval_data *vals; +}; + +/* Histogram */ +struct traceeval { + struct traceeval_type *key_types; + struct traceeval_type *val_types; + struct hash_table *hist; + size_t nr_key_types; + size_t nr_val_types; +}; + +extern struct hash_table *hash_alloc(void); +extern void hash_free(struct hash_table *hash); +extern void hash_add(struct hash_table *hash, struct hash_item *item, unsigned key); +extern int hash_remove(struct hash_table *hash, struct hash_item *item); + +extern struct hash_iter *hash_iter_start(struct hash_table *hash); +extern struct hash_item *hash_iter_next(struct hash_iter *iter); + +extern struct hash_iter *hash_iter_bucket(struct hash_table *hash, unsigned key); +extern struct hash_item *hash_iter_bucket_next(struct hash_iter *iter); + +static inline size_t hash_nr_items(struct hash_table *hash) +{ + return hash->nr_items; +} + +static inline unsigned long long hash_string(const char *str) +{ + unsigned long long key = 0; + int len = strlen(str); + int i; + + for (i = 0; i < len; i++) + key += (unsigned long long)str[i] << ((i & 7) * 8); + + return key; +} + + /* + * This is a quick hashing function adapted from Donald E. Knuth's 32 + * bit multiplicative hash. See The Art of Computer Programming (TAOCP). + * Multiplication by the Prime number, closest to the golden ratio of + * 2^32. + */ +static inline unsigned long long hash_number(unsigned long long val) +{ + return val * 2654435761ULL; +} + +#endif diff --git a/src/hash.c b/src/hash.c new file mode 100644 index 000000000000..82962fbba8d8 --- /dev/null +++ b/src/hash.c @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: MIT */ +/* + * libtraceeval hashtable interface implementation. + * + * Copyright (C) 2023 Google Inc, Steven Rostedt + */ + +#include + +#include "eval-local.h" + +__hidden struct hash_table *hash_alloc(void) +{ + struct hash_table *hash; + + hash = calloc(1, sizeof(*hash)); + if (!hash) + return NULL; + + hash->bits = HASH_BITS; + hash->hash = calloc(HASH_SIZE(hash->bits), sizeof(*hash->hash)); + if (!hash->hash) { + free(hash); + hash = NULL; + } + + return hash; +} + +__hidden void hash_free(struct hash_table *hash) +{ + free(hash->hash); + free(hash); +} + +__hidden void hash_add(struct hash_table *hash, struct hash_item *item, unsigned key) +{ + key &= HASH_MASK(hash->bits); + + item->next = hash->hash[key]; + hash->hash[key] = item; + item->key = key; + + hash->nr_items++; +} + +__hidden int hash_remove(struct hash_table *hash, struct hash_item *item) +{ + struct hash_item **parent; + + for (parent = &hash->hash[item->key]; *parent; parent = &(*parent)->next) { + if (*parent == item) { + *parent = item->next; + hash->nr_items--; + return 1; + } + } + return 0; +} + +__hidden struct hash_iter *hash_iter_start(struct hash_table *hash) +{ + struct hash_iter *iter = &hash->iter; + size_t i; + + for (i = 0; i < HASH_SIZE(hash->bits); i++) { + if (!hash->hash[i]) + continue; + iter->next_item = hash->hash[i]; + break; + } + iter->current_bucket = i; + return iter; +} + +__hidden struct hash_item *hash_iter_next(struct hash_iter *iter) +{ + struct hash_table *hash = container_of(iter, struct hash_table, iter); + struct hash_item *item; + + if (iter->current_bucket >= HASH_SIZE(hash->bits)) + return NULL; + + item = iter->next_item; + if (!item) + return NULL; + + iter->next_item = item->next; + if (!iter->next_item) { + size_t i; + + for (i = iter->current_bucket + 1; i < HASH_SIZE(hash->bits); i++) { + if (!hash->hash[i]) + continue; + iter->next_item = hash->hash[i]; + break; + } + iter->current_bucket = i; + } + return item; +} + +__hidden struct hash_iter *hash_iter_bucket(struct hash_table *hash, unsigned key) +{ + struct hash_iter *iter = &hash->iter; + + key &= HASH_MASK(hash->bits); + + iter->current_bucket = key; + iter->next_item = hash->hash[key]; + + return iter; +} + +__hidden struct hash_item *hash_iter_bucket_next(struct hash_iter *iter) +{ + struct hash_item *item = iter->next_item; + + if (item) + iter->next_item = item->next; + + return item; +} diff --git a/src/histograms.c b/src/histograms.c index 08f6ddf55ae1..45330fab5660 100644 --- a/src/histograms.c +++ b/src/histograms.c @@ -12,56 +12,7 @@ #include #include - -#define offset_of(type, field) ((size_t)(&(((type *)(NULL))->field))) -#define container_of(ptr, type, field) \ - (type *)((void *)(ptr) - (void *)offset_of(type, field)) - -#define HASH_BITS 10 /* Start with 1K of buckets */ -#define HASH_SIZE(bits) (1 << (bits)) -#define HASH_MASK(bits) (HASH_SIZE(bits) - 1) - -/* - * Compare two integers of variable length. - * - * Return 0 if @a and @b are the same, 1 if @a is greater than @b, and -1 - * if @b is greater than @a. - */ -#define compare_numbers_return(a, b) \ -do { \ - if ((a) < (b)) \ - return -1; \ - return (a) != (b); \ -} while (0) \ - - -struct hash_item { - struct hash_item *next; - unsigned key; -}; - -/* A hash of key-value entries */ -struct hash_table { - struct hash_item **hash; - unsigned bits; - size_t nr_items; -}; - -/* A key-value pair */ -struct entry { - struct hash_item hash; - union traceeval_data *keys; - union traceeval_data *vals; -}; - -/* Histogram */ -struct traceeval { - struct traceeval_type *key_types; - struct traceeval_type *val_types; - struct hash_table *hist; - size_t nr_key_types; - size_t nr_val_types; -}; +#include "eval-local.h" /* * print_err - print an error message @@ -311,16 +262,11 @@ struct traceeval *traceeval_init(const struct traceeval_type *keys, } /* alloc hist */ - teval->hist = calloc(1, sizeof(*teval->hist)); + teval->hist = hash_alloc(); if (!teval->hist) { err_msg = "Failed to allocate memory for histogram"; goto fail_release; } - teval->hist->bits = HASH_BITS; - teval->hist->hash = calloc(HASH_SIZE(teval->hist->bits), - sizeof(*teval->hist->hash)); - if (!teval->hist->hash) - goto fail_release; return teval; @@ -384,36 +330,26 @@ static void free_entry(struct traceeval *teval, struct entry *entry) free(entry); } -static void free_entries(struct traceeval *teval, struct hash_item *item) -{ - struct entry *entry; - - while (item) { - entry = container_of(item, struct entry, hash); - item = item->next; - free_entry(teval, entry); - } -} - /* * Free the hist_table allocated to a traceeval instance. */ static void hist_table_release(struct traceeval *teval) { struct hash_table *hist = teval->hist; + struct hash_iter *iter; + struct hash_item *item; if (!hist) return; - for (size_t i = 0; i < HASH_SIZE(hist->bits); i++) { - if (!hist->hash[i]) - continue; + for (iter = hash_iter_start(hist); (item = hash_iter_next(iter)); ) { + struct entry *entry = container_of(item, struct entry, hash); - free_entries(teval, hist->hash[i]); + hash_remove(hist, &entry->hash); + free_entry(teval, entry); } - free(hist->hash); - free(hist); + hash_free(hist); teval->hist = NULL; } @@ -441,18 +377,6 @@ void traceeval_release(struct traceeval *teval) free(teval); } -static unsigned long long hash_string(const char *str) -{ - unsigned long long key = 0; - int len = strlen(str); - int i; - - for (i = 0; i < len; i++) - key += (unsigned long long)str[i] << ((i & 7) * 8); - - return key; -} - static unsigned make_hash(struct traceeval *teval, const union traceeval_data *keys, int bits) { @@ -481,16 +405,10 @@ static unsigned make_hash(struct traceeval *teval, const union traceeval_data *k default: val = 0; } - /* - * This is a quick hashing function adapted from Donald E. Knuth's 32 - * bit multiplicative hash. See The Art of Computer Programming (TAOCP). - * Multiplication by the Prime number, closest to the golden ratio of - * 2^32. - */ - key += val * 2654435761; + key += hash_number(val); } - return key & HASH_MASK(bits); + return key; } /* @@ -502,7 +420,9 @@ static int get_entry(struct traceeval *teval, const union traceeval_data *keys, struct entry **result) { struct hash_table *hist = teval->hist; - struct entry *entry; + struct entry *entry = NULL; + struct hash_iter *iter; + struct hash_item *item; unsigned key; int check = 0; @@ -511,10 +431,9 @@ static int get_entry(struct traceeval *teval, const union traceeval_data *keys, key = make_hash(teval, keys, hist->bits); - hist = teval->hist; - - for (struct hash_item *item = hist->hash[key]; item; item = item->next) { + for (iter = hash_iter_bucket(hist, key); (item = hash_iter_bucket_next(iter)); ) { entry = container_of(item, struct entry, hash); + check = compare_traceeval_data_set(teval, teval->key_types, entry->keys, keys, teval->nr_key_types); if (check) @@ -661,7 +580,6 @@ static struct entry *create_hist_entry(struct traceeval *teval, const union traceeval_data *keys) { struct hash_table *hist = teval->hist; - struct hash_item *item; unsigned key = make_hash(teval, keys, hist->bits); struct entry *entry; @@ -669,12 +587,7 @@ static struct entry *create_hist_entry(struct traceeval *teval, if (!entry) return NULL; - item = &entry->hash; - item->next = hist->hash[key]; - hist->hash[key] = item; - item->key = key; - - hist->nr_items++; + hash_add(hist, &entry->hash, key); return entry; } From patchwork Thu Aug 17 01:33:01 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13355859 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 49B86C2FC04 for ; Thu, 17 Aug 2023 01:33:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1347538AbjHQBdM (ORCPT ); Wed, 16 Aug 2023 21:33:12 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58012 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1347549AbjHQBdK (ORCPT ); Wed, 16 Aug 2023 21:33:10 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [139.178.84.217]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 29A9E2727 for ; Wed, 16 Aug 2023 18:33:08 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id A7D0863C2C for ; Thu, 17 Aug 2023 01:33:07 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 11CB1C43391; Thu, 17 Aug 2023 01:33:07 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.96) (envelope-from ) id 1qWRst-000N4A-22; Wed, 16 Aug 2023 21:33:11 -0400 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: Ross Zwisler , Stevie Alvarez , "Steven Rostedt (Google)" Subject: [PATCH v3 09/18] libtraceeval histogram: Label and check keys and values Date: Wed, 16 Aug 2023 21:33:01 -0400 Message-Id: <20230817013310.88582-10-rostedt@goodmis.org> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230817013310.88582-1-rostedt@goodmis.org> References: <20230817013310.88582-1-rostedt@goodmis.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org From: "Steven Rostedt (Google)" When initializing the traceeval descriptor, mark each key and value as their type via the flags (keys get TRACEEVAL_FL_KEY and values get TRACEEVAL_FL_VALUE) as well as adding the index of that key/value type of the key/value data it represents. This will be used by the iterators for sorting. The iterator will point to the key/value type and use that information to know which key/value data to sort with. The keys and values passed in will also be updated to have their flags match the proper key/value type as well as the index. This will be useful for traceeval_stat() that will take the type as a parameter, and use it to figure out fast where the data is that is to be looked up. All pointer and dynamic types in keys must have both a cmp and hash function defined, otherwise they cannot be indexed or hashed. Add a check to make sure all key types of pointer and dynamic have those functions. Signed-off-by: Steven Rostedt (Google) --- include/traceeval-hist.h | 11 +++++--- src/histograms.c | 60 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 64 insertions(+), 7 deletions(-) diff --git a/include/traceeval-hist.h b/include/traceeval-hist.h index 1edda56a712f..f80d039baa61 100644 --- a/include/traceeval-hist.h +++ b/include/traceeval-hist.h @@ -32,8 +32,10 @@ enum traceeval_data_type { /* Statistics specification flags */ enum traceeval_flags { - TRACEEVAL_FL_SIGNED = (1 << 0), - TRACEEVAL_FL_TIMESTAMP = (1 << 1), + TRACEEVAL_FL_KEY = (1 << 0), + TRACEEVAL_FL_VALUE = (1 << 1), + TRACEEVAL_FL_SIGNED = (1 << 2), + TRACEEVAL_FL_TIMESTAMP = (1 << 3), }; /* @@ -120,6 +122,7 @@ struct traceeval_type { char *name; enum traceeval_data_type type; size_t flags; + size_t index; size_t id; traceeval_data_release_fn release; traceeval_data_cmp_fn cmp; @@ -142,8 +145,8 @@ struct traceeval; /* Histogram interfaces */ -struct traceeval *traceeval_init(const struct traceeval_type *keys, - const struct traceeval_type *vals); +struct traceeval *traceeval_init(struct traceeval_type *keys, + struct traceeval_type *vals); void traceeval_release(struct traceeval *teval); diff --git a/src/histograms.c b/src/histograms.c index 45330fab5660..808979915369 100644 --- a/src/histograms.c +++ b/src/histograms.c @@ -202,6 +202,44 @@ fail: return -1; } +static int check_keys(struct traceeval_type *keys) +{ + for (int i = 0; keys[i].type != TRACEEVAL_TYPE_NONE; i++) { + /* Define this as a key */ + keys[i].flags |= TRACEEVAL_FL_KEY; + keys[i].flags &= ~TRACEEVAL_FL_VALUE; + + keys[i].index = i; + + switch (keys[i].type) { + case TRACEEVAL_TYPE_POINTER: + case TRACEEVAL_TYPE_DYNAMIC: + /* + * Key pointers and dynamic types must have a + * cmp and hash function + */ + if (!keys[i].cmp || !keys[i].hash) + return -1; + break; + default: + break; + } + } + return 0; +} + +static int check_vals(struct traceeval_type *vals) +{ + for (int i = 0; vals[i].type != TRACEEVAL_TYPE_NONE; i++) { + /* Define this as a value */ + vals[i].flags |= TRACEEVAL_FL_VALUE; + vals[i].flags &= ~TRACEEVAL_FL_KEY; + + vals[i].index = i; + } + return 0; +} + /* * traceeval_init - create a traceeval descriptor * @keys: Defines the keys to differentiate traceeval entries @@ -212,7 +250,12 @@ fail: * the "histogram". Note, both the @keys and @vals array must end with: * { .type = TRACEEVAL_TYPE_NONE }. * - * The @keys and @vals passed in are copied for internal use. + * The @keys and @vals passed in are copied for internal use, but they are + * still modified to add the flags to denote their type (key or value) as + * well as the index into the keys or vals array respectively. This is + * to help speed up other operations that may need to know the index of + * the given type, and remove the burden from the user to make sure they + * are added. * * For any member of @keys or @vals that isn't of type TRACEEVAL_TYPE_NONE, * the name field must be a null-terminated string. Members of type @@ -226,11 +269,12 @@ fail: * * Returns the descriptor on success, or NULL on error. */ -struct traceeval *traceeval_init(const struct traceeval_type *keys, - const struct traceeval_type *vals) +struct traceeval *traceeval_init(struct traceeval_type *keys, + struct traceeval_type *vals) { struct traceeval *teval; char *err_msg; + int ret; if (!keys) return NULL; @@ -247,6 +291,16 @@ struct traceeval *traceeval_init(const struct traceeval_type *keys, goto fail; } + ret = check_keys(keys); + if (ret < 0) + goto fail_release; + + if (vals) { + ret = check_vals(vals); + if (ret < 0) + goto fail_release; + } + /* alloc key types */ teval->nr_key_types = type_alloc(keys, &teval->key_types); if (teval->nr_key_types <= 0) { From patchwork Thu Aug 17 01:33:02 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13355873 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 93037C2FC15 for ; Thu, 17 Aug 2023 01:34:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1347552AbjHQBds (ORCPT ); Wed, 16 Aug 2023 21:33:48 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58122 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1347537AbjHQBdN (ORCPT ); Wed, 16 Aug 2023 21:33:13 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [IPv6:2604:1380:4641:c500::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0495E1FC3 for ; Wed, 16 Aug 2023 18:33:11 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 6D07564CA6 for ; Thu, 17 Aug 2023 01:33:08 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 2BE52C433AB; Thu, 17 Aug 2023 01:33:07 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.96) (envelope-from ) id 1qWRst-000N4E-28; Wed, 16 Aug 2023 21:33:11 -0400 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: Ross Zwisler , Stevie Alvarez , "Steven Rostedt (Google)" Subject: [PATCH v3 10/18] libtraceeval histogram: Add updating of stats Date: Wed, 16 Aug 2023 21:33:02 -0400 Message-Id: <20230817013310.88582-11-rostedt@goodmis.org> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230817013310.88582-1-rostedt@goodmis.org> References: <20230817013310.88582-1-rostedt@goodmis.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org From: "Steven Rostedt (Google)" Whenever an entry is added that already exists (overwriting the values) keep track of the stats for the number values (max, min, total, count). Also move the stat structure out of the public view. We may want to modify this structure in the future, and so it should not become an API. Add accessor functions to get to the stat values. Add traceeval_stat() to acquire a stat handle from a specific key for a specific value. Signed-off-by: Steven Rostedt (Google) --- include/traceeval-hist.h | 17 ++-- src/eval-local.h | 9 ++ src/histograms.c | 182 +++++++++++++++++++++++++++++++++++---- 3 files changed, 183 insertions(+), 25 deletions(-) diff --git a/include/traceeval-hist.h b/include/traceeval-hist.h index f80d039baa61..164d34c0fa81 100644 --- a/include/traceeval-hist.h +++ b/include/traceeval-hist.h @@ -130,13 +130,7 @@ struct traceeval_type { }; /* Statistics about a given entry element */ -struct traceeval_stat { - unsigned long long max; - unsigned long long min; - unsigned long long total; - unsigned long long avg; - unsigned long long std; -}; +struct traceeval_stat; /* Iterator over aggregated data */ struct traceeval_iterator; @@ -160,4 +154,13 @@ int traceeval_query(struct traceeval *teval, const union traceeval_data *keys, void traceeval_results_release(struct traceeval *teval, union traceeval_data *results); +struct traceeval_stat *traceeval_stat(struct traceeval *teval, + const union traceeval_data *keys, + struct traceeval_type *type); + +unsigned long long traceeval_stat_max(struct traceeval_stat *stat); +unsigned long long traceeval_stat_min(struct traceeval_stat *stat); +unsigned long long traceeval_stat_total(struct traceeval_stat *stat); +unsigned long long traceeval_stat_count(struct traceeval_stat *stat); + #endif /* __LIBTRACEEVAL_HIST_H__ */ diff --git a/src/eval-local.h b/src/eval-local.h index 007fdb146b96..ed2e8abc9b63 100644 --- a/src/eval-local.h +++ b/src/eval-local.h @@ -45,11 +45,20 @@ struct hash_table { struct hash_iter iter; }; +struct traceeval_stat { + unsigned long long max; + unsigned long long min; + unsigned long long total; + unsigned long long std; + size_t count; +}; + /* A key-value pair */ struct entry { struct hash_item hash; union traceeval_data *keys; union traceeval_data *vals; + struct traceeval_stat *val_stats; }; /* Histogram */ diff --git a/src/histograms.c b/src/histograms.c index 808979915369..e0405ec31542 100644 --- a/src/histograms.c +++ b/src/histograms.c @@ -505,21 +505,85 @@ static int get_entry(struct traceeval *teval, const union traceeval_data *keys, * Return 0 on success, -1 on error. */ static int copy_traceeval_data(struct traceeval_type *type, - const union traceeval_data *orig, - union traceeval_data *copy) + struct traceeval_stat *stat, + const union traceeval_data *orig, + union traceeval_data *copy) { + unsigned long long val; + *copy = *orig; - if (type->type == TRACEEVAL_TYPE_STRING) { + switch(type->type) { + case TRACEEVAL_TYPE_NUMBER: + if (type->flags & TRACEEVAL_FL_SIGNED) + val = (long long)copy->number; + else + val = (unsigned long long)copy->number; + break; + + case TRACEEVAL_TYPE_NUMBER_64: + if (type->flags & TRACEEVAL_FL_SIGNED) + val = (long long)copy->number_64; + else + val = (unsigned long long)copy->number_64; + break; + + case TRACEEVAL_TYPE_NUMBER_32: + if (type->flags & TRACEEVAL_FL_SIGNED) + val = (long long)copy->number_32; + else + val = (unsigned long long)copy->number_32; + break; + + case TRACEEVAL_TYPE_NUMBER_16: + if (type->flags & TRACEEVAL_FL_SIGNED) + val = (long long)copy->number_16; + else + val = (unsigned long long)copy->number_16; + break; + + case TRACEEVAL_TYPE_NUMBER_8: + if (type->flags & TRACEEVAL_FL_SIGNED) + val = (long long)copy->number_8; + else + val = (unsigned long long)copy->number_8; + break; + + case TRACEEVAL_TYPE_STRING: copy->string = NULL; if (orig->string) copy->string = strdup(orig->string); - else - return 0; if (!copy->string) return -1; + return 0; + default: + return 0; + } + + if (!stat) + return 0; + + if (!stat->count++) { + stat->max = val; + stat->min = val; + stat->total = val; + return 0; + } + + if (type->flags & TRACEEVAL_FL_SIGNED) { + if ((long long)stat->max < (long long)val) + stat->max = val; + if ((long long)stat->min > (long long)val) + stat->min = val; + stat->total += (long long)val; + } else { + if (stat->max < val) + stat->max = val; + if (stat->min > val) + stat->min = val; + stat->total += val; } return 0; @@ -548,6 +612,7 @@ static void data_release(size_t size, union traceeval_data **data, */ static int copy_traceeval_data_set(size_t size, struct traceeval_type *type, const union traceeval_data *orig, + struct traceeval_stat *stats, union traceeval_data **copy) { size_t i; @@ -561,7 +626,8 @@ static int copy_traceeval_data_set(size_t size, struct traceeval_type *type, return -1; for (i = 0; i < size; i++) { - if (copy_traceeval_data(type + i, orig + i, (*copy) + i)) + if (copy_traceeval_data(type + i, stats ? stats + i : NULL, + orig + i, (*copy) + i)) goto fail; } @@ -604,7 +670,7 @@ int traceeval_query(struct traceeval *teval, const union traceeval_data *keys, return check; return copy_traceeval_data_set(teval->nr_val_types, teval->val_types, - entry->vals, results); + entry->vals, NULL, results); } /* @@ -659,18 +725,22 @@ static int create_entry(struct traceeval *teval, union traceeval_data *new_vals; struct entry *entry; + entry = create_hist_entry(teval, keys); + if (!entry) + return -1; + + entry->val_stats = calloc(teval->nr_key_types, sizeof(*entry->val_stats)); + if (!entry->val_stats) + goto fail_entry; + /* copy keys */ if (copy_traceeval_data_set(teval->nr_key_types, teval->key_types, - keys, &new_keys) == -1) - return -1; + keys, NULL, &new_keys) == -1) + goto fail_stats; /* copy vals */ if (copy_traceeval_data_set(teval->nr_val_types, teval->val_types, - vals, &new_vals) == -1) - goto fail_vals; - - entry = create_hist_entry(teval, keys); - if (!entry) + vals, entry->val_stats, &new_vals) == -1) goto fail; entry->keys = new_keys; @@ -679,10 +749,13 @@ static int create_entry(struct traceeval *teval, return 0; fail: - data_release(teval->nr_val_types, &new_vals, teval->val_types); - -fail_vals: data_release(teval->nr_key_types, &new_keys, teval->key_types); + +fail_stats: + free(entry->val_stats); + +fail_entry: + free(entry); return -1; } @@ -699,7 +772,7 @@ static int update_entry(struct traceeval *teval, struct entry *entry, union traceeval_data *new_vals; if (copy_traceeval_data_set(teval->nr_val_types, teval->val_types, - vals, &new_vals) == -1) + vals, entry->val_stats, &new_vals) == -1) return -1; clean_data_set(entry->vals, teval->val_types, teval->nr_val_types); @@ -707,6 +780,79 @@ static int update_entry(struct traceeval *teval, struct entry *entry, return 0; } +struct traceeval_stat *traceeval_stat(struct traceeval *teval, + const union traceeval_data *keys, + struct traceeval_type *type) +{ + struct entry *entry; + int ret; + + /* Only value numbers have stats */ + if (!(type->flags & TRACEEVAL_FL_VALUE)) + return NULL; + + switch (type->type) { + case TRACEEVAL_TYPE_NUMBER: + case TRACEEVAL_TYPE_NUMBER_64: + case TRACEEVAL_TYPE_NUMBER_32: + case TRACEEVAL_TYPE_NUMBER_16: + case TRACEEVAL_TYPE_NUMBER_8: + break; + default: + return NULL; + } + + ret = get_entry(teval, keys, &entry); + if (ret <= 0) + return NULL; + + return &entry->val_stats[type->index]; +} + +/** + * traceeval_stat_max - return max value of stat + * @stat: The stat structure that holds the stats + * + * Returns the max value within @stat. + */ +unsigned long long traceeval_stat_max(struct traceeval_stat *stat) +{ + return stat->max; +} + +/** + * traceeval_stat_min - return min value of stat + * @stat: The stat structure that holds the stats + * + * Returns the min value within @stat. + */ +unsigned long long traceeval_stat_min(struct traceeval_stat *stat) +{ + return stat->min; +} + +/** + * traceeval_stat_total - return total value of stat + * @stat: The stat structure that holds the stats + * + * Returns the total value within @stat. + */ +unsigned long long traceeval_stat_total(struct traceeval_stat *stat) +{ + return stat->total; +} + +/** + * traceeval_stat_count - return count value of stat + * @stat: The stat structure that holds the stats + * + * Returns the count value within @stat. + */ +unsigned long long traceeval_stat_count(struct traceeval_stat *stat) +{ + return stat->count; +} + /* * traceeval_insert - insert an item into the traceeval descriptor * @teval: The descriptor to insert into From patchwork Thu Aug 17 01:33:03 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13355874 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5F5EFC41513 for ; Thu, 17 Aug 2023 01:34:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1347566AbjHQBdp (ORCPT ); Wed, 16 Aug 2023 21:33:45 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58064 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1345962AbjHQBdM (ORCPT ); Wed, 16 Aug 2023 21:33:12 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [IPv6:2604:1380:4641:c500::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 19794273C for ; Wed, 16 Aug 2023 18:33:10 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 5D77664B65 for ; Thu, 17 Aug 2023 01:33:08 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 2195DC433A9; Thu, 17 Aug 2023 01:33:07 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.96) (envelope-from ) id 1qWRst-000N4I-2D; Wed, 16 Aug 2023 21:33:11 -0400 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: Ross Zwisler , Stevie Alvarez , "Steven Rostedt (Google)" Subject: [PATCH v3 11/18] libtraceeval histogram: Add iterator APIs Date: Wed, 16 Aug 2023 21:33:03 -0400 Message-Id: <20230817013310.88582-12-rostedt@goodmis.org> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230817013310.88582-1-rostedt@goodmis.org> References: <20230817013310.88582-1-rostedt@goodmis.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org From: "Steven Rostedt (Google)" Provide an interface for the application to iterate over all the entries in the traceeval histogram. traceeval_iterator_get() - acquire an iterator for the given traceeval traceveal_iterator_put() - release the iterator traceeval_iterator_sort() - sort the iterator for a given key/value traceeval_iterator_next() - return the keys of the next entry in the traceeval Signed-off-by: Steven Rostedt (Google) --- include/traceeval-hist.h | 7 ++ src/eval-local.h | 11 ++ src/histograms.c | 258 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 275 insertions(+), 1 deletion(-) diff --git a/include/traceeval-hist.h b/include/traceeval-hist.h index 164d34c0fa81..e8a7981ac2c0 100644 --- a/include/traceeval-hist.h +++ b/include/traceeval-hist.h @@ -163,4 +163,11 @@ unsigned long long traceeval_stat_min(struct traceeval_stat *stat); unsigned long long traceeval_stat_total(struct traceeval_stat *stat); unsigned long long traceeval_stat_count(struct traceeval_stat *stat); +struct traceeval_iterator *traceeval_iterator_get(struct traceeval *teval); +void traceeval_iterator_put(struct traceeval_iterator *iter); +int traceeval_iterator_sort(struct traceeval_iterator *iter, const char *sort_field, + int level, bool ascending); +int traceeval_iterator_next(struct traceeval_iterator *iter, + const union traceeval_data **keys); + #endif /* __LIBTRACEEVAL_HIST_H__ */ diff --git a/src/eval-local.h b/src/eval-local.h index ed2e8abc9b63..5c3918f17cc1 100644 --- a/src/eval-local.h +++ b/src/eval-local.h @@ -70,6 +70,17 @@ struct traceeval { size_t nr_val_types; }; +struct traceeval_iterator { + struct traceeval *teval; + struct entry **entries; + struct traceeval_type **sort; + bool *direction; + size_t nr_entries; + size_t nr_sort; + size_t next; + bool needs_sort; +}; + extern struct hash_table *hash_alloc(void); extern void hash_free(struct hash_table *hash); extern void hash_add(struct hash_table *hash, struct hash_item *item, unsigned key); diff --git a/src/histograms.c b/src/histograms.c index e0405ec31542..8cd1b288a7fc 100644 --- a/src/histograms.c +++ b/src/histograms.c @@ -37,7 +37,7 @@ static void print_err(const char *fmt, ...) */ static int compare_traceeval_data(struct traceeval *teval, struct traceeval_type *type, - union traceeval_data *orig, + const union traceeval_data *orig, const union traceeval_data *copy) { int i; @@ -903,3 +903,259 @@ int traceeval_insert(struct traceeval *teval, else return update_entry(teval, entry, vals); } + +/** + * traceeval_iterator_put - release a given iterator + * @iter: The iterartor to release + * + * Frees the resources of an @iter that was created by + * traceeval_iterator_get(). + */ +void traceeval_iterator_put(struct traceeval_iterator *iter) +{ + if (!iter) + return; + + free(iter->direction); + free(iter->entries); + free(iter->sort); + free(iter); +} + +/** + * traceeval_iterator_get - get a handle to iterate over a given traceeval + * @teval: The traceeval handle to iterate over + * + * Returns a handle to iterate over the given @teval. Must be freed with + * traceeval_iterator_put(). It can be used with traceeval_iterator_next() + * to retrieve the keys of the next entry in @teval. + * + * Use traceeval_iterator_sort() to specify the order of the entries + * returned by traceeval_iterator_next(). + * + * Returns an allocated iterator on success, and NULL on failure. + */ +struct traceeval_iterator *traceeval_iterator_get(struct traceeval *teval) +{ + struct traceeval_iterator *iter; + struct hash_table *hist = teval->hist; + struct hash_iter *hiter; + struct hash_item *item; + int i; + + iter = calloc(1, sizeof(*iter)); + if (!iter) + return NULL; + + iter->teval = teval; + iter->nr_entries = hash_nr_items(hist); + iter->entries = calloc(iter->nr_entries, sizeof(*iter->entries)); + if (!iter->entries) { + free(iter); + return NULL; + } + + for (i = 0, hiter = hash_iter_start(hist); (item = hash_iter_next(hiter)); i++) { + struct entry *entry = container_of(item, struct entry, hash); + + iter->entries[i] = entry; + } + + /* Loop must match entries */ + if (i != iter->nr_entries) { + traceeval_iterator_put(iter); + return NULL; + } + + return iter; +} + +static struct traceeval_type *find_sort_type(struct traceeval *teval, + const char *name) +{ + struct traceeval_type *type; + int i; + + /* Check values first, and then keys */ + for (i = 0; i < teval->nr_val_types; i++) { + type = &teval->val_types[i]; + + if (strcmp(type->name, name) == 0) + return type; + } + + for (i = 0; i < teval->nr_key_types; i++) { + type = &teval->key_types[i]; + + if (strcmp(type->name, name) == 0) + return type; + } + + return NULL; +} + +/** + * traceeval_iterator_sort - sort the entries that an iterator will return + * @iter: The iterator to specify the sort order of the entries + * @sort_field: The name of the key or value to sort with. + * @level: The level of sorting (0 for first order, 1 for second, ...) + * @ascending: If the sort should go forward or backward. + * + * The iterator has a list of entries to produce with traceeval_iterator_next(). + * This function specifies what the order of the output of that function will + * be. Note, whenever this function is called, it resets the @iter so that + * the traceveal_iterator_next() will start from the beginning again. + * + * In other words, be very careful to ever call this function in a middle + * of a loop that is using traceeval_iterator_next(), otherwise you may end + * up in an infinite loop! + * + * The @level specifies the level of sorting. That is, for @level = 0, + * it will decide the main sorting of the @iter. For @level = 1, it will + * be the tie breaker for two entries that are equal for the @level = 0 + * sort. @level = 2, will be the tie breaker for @level = 1, and so on. + * + * Note, if traceeval_iterator_next() is called, and there's a missing @level, + * it will fail. That is, if this function is called once with @level = 0 and + * againg with @level = 2, but never with @level = 1, the call to + * traceeval_iterator_next() will fail. + * + * If this function is called multiple times with the same @level, then the + * last call will define the what that @level will do. + * + * The @ascending will determine if "smaller" items go first if true, and + * "larger" items go first if false. + * + * Return 0 on success and -1 on failure. + */ +int traceeval_iterator_sort(struct traceeval_iterator *iter, const char *sort_field, + int level, bool ascending) +{ + bool *direction = iter->direction; + struct traceeval_type **sort = iter->sort; + struct traceeval_type *type; + int num_levels = level + 1; + + type = find_sort_type(iter->teval, sort_field); + if (!type) + return -1; + + /* pointer and dynamic types must have a cmp function */ + switch (type->type) { + case TRACEEVAL_TYPE_POINTER: + case TRACEEVAL_TYPE_DYNAMIC: + if (!type->cmp) + return -1; + break; + default: + break; + } + + if (num_levels > iter->nr_sort) { + sort = realloc(sort, sizeof(*sort) * num_levels); + if (!sort) + return -1; + + iter->sort = sort; + + direction = realloc(direction, sizeof(*direction) * num_levels); + if (!direction) + return -1; + + iter->direction = direction; + + /* Make sure the newly allocated contain NULL */ + for (int i = iter->nr_sort; i < num_levels; i++) + sort[i] = NULL; + + iter->nr_sort = level + 1; + } + + sort[level] = type; + direction[level] = ascending; + iter->needs_sort = true; + return 0; +} + +static int iter_cmp(const void *A, const void *B, void *data) +{ + struct traceeval_iterator *iter = data; + struct traceeval *teval = iter->teval; + const struct entry *a = *((const struct entry **)A); + const struct entry *b = *((const struct entry **)B); + int ret; + + for (int i = 0; i < iter->nr_sort; i++) { + struct traceeval_type *type; + union traceeval_data *dataA; + union traceeval_data *dataB; + + type = iter->sort[i]; + + if (type->flags & TRACEEVAL_FL_KEY) { + dataA = &a->keys[type->index]; + dataB = &b->keys[type->index]; + } else { + dataA = &a->vals[type->index]; + dataB = &b->vals[type->index]; + } + + ret = compare_traceeval_data(teval, type, dataA, dataB); + + if (ret) + return iter->direction[i] ? ret : ret * -1; + } + + return 0; +} + +static int sort_iter(struct traceeval_iterator *iter) +{ + int i; + + /* Make sure all levels are filled */ + for (i = 0; i < iter->nr_sort; i++) { + if (!iter->sort[i]) + return -1; + } + + qsort_r(iter->entries, iter->nr_entries, sizeof(*iter->entries), + iter_cmp, iter); + + iter->needs_sort = false; + iter->next = 0; + + return 0; +} + +/** + * traceeval_iterator_next - retrieve the next entry from an iterator + * @iter: The iterator to retrieve the next entry from + * @keys: The returned keys of the next entry (if exists) + * + * This returns the keys for the next entry in the traceeval being + * iterated over by @iter. If there are no more entries, 0 is returned + * and @keys are untouched. + * + * Returns 1 if another entry is returned, or 0 if not (or negative on error) + */ +int traceeval_iterator_next(struct traceeval_iterator *iter, + const union traceeval_data **keys) +{ + struct entry *entry; + int ret; + + if (iter->needs_sort) { + ret = sort_iter(iter); + if (ret < 0) + return ret; + iter->next = 0; + } + + if (iter->next >= iter->nr_entries) + return 0; + + entry = iter->entries[iter->next++]; + *keys = entry->keys; + return 1; +} From patchwork Thu Aug 17 01:33:04 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13355858 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id CD1D4C05052 for ; Thu, 17 Aug 2023 01:33:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1347558AbjHQBdM (ORCPT ); Wed, 16 Aug 2023 21:33:12 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58016 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1347550AbjHQBdK (ORCPT ); Wed, 16 Aug 2023 21:33:10 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [IPv6:2604:1380:4641:c500::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8A9E7272B for ; Wed, 16 Aug 2023 18:33:09 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 3FFEC64A72 for ; Thu, 17 Aug 2023 01:33:08 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 2096CC4339A; Thu, 17 Aug 2023 01:33:07 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.96) (envelope-from ) id 1qWRst-000N4M-2I; Wed, 16 Aug 2023 21:33:11 -0400 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: Ross Zwisler , Stevie Alvarez , "Steven Rostedt (Google)" Subject: [PATCH v3 12/18] libtraceeval histogram: Add data copy callback Date: Wed, 16 Aug 2023 21:33:04 -0400 Message-Id: <20230817013310.88582-13-rostedt@goodmis.org> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230817013310.88582-1-rostedt@goodmis.org> References: <20230817013310.88582-1-rostedt@goodmis.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org From: "Steven Rostedt (Google)" Add a callback "copy" to the type that gets called when a copy is done. This is can be used to copy dynamic types. Otherwise the old one is just going to be released. Signed-off-by: Steven Rostedt (Google) --- include/traceeval-hist.h | 5 +++++ src/histograms.c | 3 +++ 2 files changed, 8 insertions(+) diff --git a/include/traceeval-hist.h b/include/traceeval-hist.h index e8a7981ac2c0..8992ace81cdf 100644 --- a/include/traceeval-hist.h +++ b/include/traceeval-hist.h @@ -82,6 +82,10 @@ typedef int (*traceeval_data_hash_fn)(struct traceeval *teval, const struct traceeval_type *type, const union traceeval_data *data); +typedef int (*traceeval_data_copy_fn)(const struct traceeval_type *type, + union traceeval_data *copy, + const union traceeval_data *origin); + /* * struct traceeval_type - Describes the type of a traceevent_data instance * @type: The enum type that describes the traceeval_data @@ -126,6 +130,7 @@ struct traceeval_type { size_t id; traceeval_data_release_fn release; traceeval_data_cmp_fn cmp; + traceeval_data_copy_fn copy; traceeval_data_hash_fn hash; }; diff --git a/src/histograms.c b/src/histograms.c index 8cd1b288a7fc..fe13884ccff6 100644 --- a/src/histograms.c +++ b/src/histograms.c @@ -511,6 +511,9 @@ static int copy_traceeval_data(struct traceeval_type *type, { unsigned long long val; + if (type->copy) + return type->copy(type, copy, orig); + *copy = *orig; switch(type->type) { From patchwork Thu Aug 17 01:33:05 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13355865 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0BD32C2FC27 for ; Thu, 17 Aug 2023 01:33:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1347554AbjHQBdQ (ORCPT ); Wed, 16 Aug 2023 21:33:16 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58034 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1347552AbjHQBdK (ORCPT ); Wed, 16 Aug 2023 21:33:10 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [139.178.84.217]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 12E412723 for ; Wed, 16 Aug 2023 18:33:10 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 47C2764B0F for ; Thu, 17 Aug 2023 01:33:08 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 2B090C43397; Thu, 17 Aug 2023 01:33:07 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.96) (envelope-from ) id 1qWRst-000N4Q-2O; Wed, 16 Aug 2023 21:33:11 -0400 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: Ross Zwisler , Stevie Alvarez , "Steven Rostedt (Google)" Subject: [PATCH v3 13/18] libtraceeval histogram: Do the release on updates Date: Wed, 16 Aug 2023 21:33:05 -0400 Message-Id: <20230817013310.88582-14-rostedt@goodmis.org> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230817013310.88582-1-rostedt@goodmis.org> References: <20230817013310.88582-1-rostedt@goodmis.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org From: "Steven Rostedt (Google)" If no copy() function exists for a type but a release() function does, then do the release on the old copy. Signed-off-by: Steven Rostedt (Google) --- src/histograms.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/histograms.c b/src/histograms.c index fe13884ccff6..57b4b2166e3b 100644 --- a/src/histograms.c +++ b/src/histograms.c @@ -595,12 +595,16 @@ static int copy_traceeval_data(struct traceeval_type *type, /* * Free @data with respect to @size and @type. * - * Does not call the release callback on the data. + * Does not call the release() callback if a copy() exists. */ static void data_release(size_t size, union traceeval_data **data, struct traceeval_type *type) { for (size_t i = 0; i < size; i++) { + /* A copy should handle releases */ + if (type[i].release && !type[i].copy) + type[i].release(&type[i], &(*data)[i]); + if (type[i].type == TRACEEVAL_TYPE_STRING) free((*data)[i].string); } From patchwork Thu Aug 17 01:33:06 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13355869 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3FAECC05052 for ; Thu, 17 Aug 2023 01:34:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1345962AbjHQBdq (ORCPT ); Wed, 16 Aug 2023 21:33:46 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58114 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1347549AbjHQBdM (ORCPT ); Wed, 16 Aug 2023 21:33:12 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [IPv6:2604:1380:4641:c500::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6C91B2D44 for ; Wed, 16 Aug 2023 18:33:10 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 6070064BCD for ; Thu, 17 Aug 2023 01:33:08 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 34324C433B7; Thu, 17 Aug 2023 01:33:07 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.96) (envelope-from ) id 1qWRst-000N4U-2S; Wed, 16 Aug 2023 21:33:11 -0400 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: Ross Zwisler , Stevie Alvarez , "Steven Rostedt (Google)" Subject: [PATCH v3 14/18] libtraceeval histogram: Use stack for old copy in update Date: Wed, 16 Aug 2023 21:33:06 -0400 Message-Id: <20230817013310.88582-15-rostedt@goodmis.org> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230817013310.88582-1-rostedt@goodmis.org> References: <20230817013310.88582-1-rostedt@goodmis.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org From: "Steven Rostedt (Google)" In the update, instead of using the heap, use the stack to save the old values. This should save time where it does not need to allocate and then free the value list to do an update. Signed-off-by: Steven Rostedt (Google) --- src/histograms.c | 51 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/src/histograms.c b/src/histograms.c index 57b4b2166e3b..3050bad4190e 100644 --- a/src/histograms.c +++ b/src/histograms.c @@ -597,17 +597,23 @@ static int copy_traceeval_data(struct traceeval_type *type, * * Does not call the release() callback if a copy() exists. */ -static void data_release(size_t size, union traceeval_data **data, - struct traceeval_type *type) +static void data_release(size_t size, union traceeval_data *data, + struct traceeval_type *type) { for (size_t i = 0; i < size; i++) { /* A copy should handle releases */ if (type[i].release && !type[i].copy) - type[i].release(&type[i], &(*data)[i]); + type[i].release(&type[i], &data[i]); if (type[i].type == TRACEEVAL_TYPE_STRING) - free((*data)[i].string); + free(data[i].string); } +} + +static void data_release_and_free(size_t size, union traceeval_data **data, + struct traceeval_type *type) +{ + data_release(size, *data, type); free(*data); *data = NULL; } @@ -641,7 +647,7 @@ static int copy_traceeval_data_set(size_t size, struct traceeval_type *type, return 1; fail: - data_release(i, copy, type); + data_release_and_free(i, copy, type); return -1; } @@ -700,7 +706,7 @@ void traceeval_results_release(struct traceeval *teval, return; } - data_release(teval->nr_val_types, &results, teval->val_types); + data_release_and_free(teval->nr_val_types, &results, teval->val_types); } static struct entry *create_hist_entry(struct traceeval *teval, @@ -756,7 +762,7 @@ static int create_entry(struct traceeval *teval, return 0; fail: - data_release(teval->nr_key_types, &new_keys, teval->key_types); + data_release_and_free(teval->nr_key_types, &new_keys, teval->key_types); fail_stats: free(entry->val_stats); @@ -776,15 +782,34 @@ fail_entry: static int update_entry(struct traceeval *teval, struct entry *entry, const union traceeval_data *vals) { - union traceeval_data *new_vals; + struct traceeval_stat *stats = entry->val_stats; + struct traceeval_type *types = teval->val_types; + union traceeval_data *copy = entry->vals; + union traceeval_data old[teval->nr_val_types]; + size_t size = teval->nr_val_types; + size_t i; - if (copy_traceeval_data_set(teval->nr_val_types, teval->val_types, - vals, entry->val_stats, &new_vals) == -1) - return -1; + if (!size) + return 0; - clean_data_set(entry->vals, teval->val_types, teval->nr_val_types); - entry->vals = new_vals; + for (i = 0; i < size; i++) { + old[i] = copy[i]; + + if (copy_traceeval_data(types + i, stats + i, + vals + i, copy + i)) + goto fail; + } + data_release(size, old, types); return 0; + fail: + /* Free the new values that were added */ + data_release(i, copy, types); + /* Put back the old values */ + for (i--; i >= 0; i--) { + copy_traceeval_data(types + i, NULL, + copy + i, old + i); + } + return -1; } struct traceeval_stat *traceeval_stat(struct traceeval *teval, From patchwork Thu Aug 17 01:33:07 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13355863 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 839C0C2FC1A for ; Thu, 17 Aug 2023 01:33:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1347547AbjHQBdN (ORCPT ); Wed, 16 Aug 2023 21:33:13 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58060 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1347555AbjHQBdM (ORCPT ); Wed, 16 Aug 2023 21:33:12 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [139.178.84.217]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 68CD52D40 for ; Wed, 16 Aug 2023 18:33:10 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 4FB3364AFF for ; Thu, 17 Aug 2023 01:33:08 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 2EE26C433B6; Thu, 17 Aug 2023 01:33:07 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.96) (envelope-from ) id 1qWRst-000N4Y-2X; Wed, 16 Aug 2023 21:33:11 -0400 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: Ross Zwisler , Stevie Alvarez , "Steven Rostedt (Google)" Subject: [PATCH v3 15/18] libtraceeval histogram: Add traceeval_iterator_sort_custom() Date: Wed, 16 Aug 2023 21:33:07 -0400 Message-Id: <20230817013310.88582-16-rostedt@goodmis.org> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230817013310.88582-1-rostedt@goodmis.org> References: <20230817013310.88582-1-rostedt@goodmis.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org From: "Steven Rostedt (Google)" Add an iterator where the application can supply the sort algorithm where it gets the teval descriptor along with the keys and values of both of the entries to compare against. Also, allow it to submit its own data to the compare function: int traceeval_iterator_sort_custom(struct traceeval_iterator *iter, traceeval_cmp_fn sort_fn, void *data); with typedef int (*traceeval_cmp_fn)(struct traceeval *teval, const union traceeval_data *Akeys, const union traceeval_data *Avals, const union traceeval_data *Bkeys, const union traceeval_data *Bvals, void *data); Signed-off-by: Steven Rostedt (Google) --- include/traceeval-hist.h | 9 +++++++++ src/histograms.c | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/include/traceeval-hist.h b/include/traceeval-hist.h index 8992ace81cdf..fc1e288a43c6 100644 --- a/include/traceeval-hist.h +++ b/include/traceeval-hist.h @@ -86,6 +86,13 @@ typedef int (*traceeval_data_copy_fn)(const struct traceeval_type *type, union traceeval_data *copy, const union traceeval_data *origin); +typedef int (*traceeval_cmp_fn)(struct traceeval *teval, + const union traceeval_data *Akeys, + const union traceeval_data *Avals, + const union traceeval_data *Bkeys, + const union traceeval_data *Bvals, + void *data); + /* * struct traceeval_type - Describes the type of a traceevent_data instance * @type: The enum type that describes the traceeval_data @@ -172,6 +179,8 @@ struct traceeval_iterator *traceeval_iterator_get(struct traceeval *teval); void traceeval_iterator_put(struct traceeval_iterator *iter); int traceeval_iterator_sort(struct traceeval_iterator *iter, const char *sort_field, int level, bool ascending); +int traceeval_iterator_sort_custom(struct traceeval_iterator *iter, + traceeval_cmp_fn sort_fn, void *data); int traceeval_iterator_next(struct traceeval_iterator *iter, const union traceeval_data **keys); diff --git a/src/histograms.c b/src/histograms.c index 3050bad4190e..c0f2248cce6f 100644 --- a/src/histograms.c +++ b/src/histograms.c @@ -1160,6 +1160,41 @@ static int sort_iter(struct traceeval_iterator *iter) return 0; } +struct iter_custom_data { + struct traceeval_iterator *iter; + traceeval_cmp_fn sort_fn; + void *data; +}; + +static int iter_custom_cmp(const void *A, const void *B, void *data) +{ + struct iter_custom_data *cust_data = data; + struct traceeval_iterator *iter = cust_data->iter; + struct traceeval *teval = iter->teval; + const struct entry *a = *((const struct entry **)A); + const struct entry *b = *((const struct entry **)B); + + return cust_data->sort_fn(teval, a->keys, a->vals, b->keys, b->vals, + cust_data->data); +} + +int traceeval_iterator_sort_custom(struct traceeval_iterator *iter, + traceeval_cmp_fn sort_fn, void *data) +{ + struct iter_custom_data cust_data = { + .iter = iter, + .sort_fn = sort_fn, + .data = data + }; + + qsort_r(iter->entries, iter->nr_entries, sizeof(*iter->entries), + iter_custom_cmp, &cust_data); + + iter->needs_sort = false; + iter->next = 0; + return 0; +} + /** * traceeval_iterator_next - retrieve the next entry from an iterator * @iter: The iterator to retrieve the next entry from From patchwork Thu Aug 17 01:33:08 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13355872 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id A4F70C2FC1A for ; Thu, 17 Aug 2023 01:34:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1347535AbjHQBdr (ORCPT ); Wed, 16 Aug 2023 21:33:47 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58098 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1347557AbjHQBdM (ORCPT ); Wed, 16 Aug 2023 21:33:12 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [IPv6:2604:1380:4641:c500::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 66B2B2727 for ; Wed, 16 Aug 2023 18:33:10 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 6BB5264C30 for ; Thu, 17 Aug 2023 01:33:08 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3B7BBC433B8; Thu, 17 Aug 2023 01:33:07 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.96) (envelope-from ) id 1qWRst-000N4c-2b; Wed, 16 Aug 2023 21:33:11 -0400 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: Ross Zwisler , Stevie Alvarez , "Steven Rostedt (Google)" Subject: [PATCH v3 16/18] libtraceeval histogram: Have traceeval_query() just give the pointer to results Date: Wed, 16 Aug 2023 21:33:08 -0400 Message-Id: <20230817013310.88582-17-rostedt@goodmis.org> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230817013310.88582-1-rostedt@goodmis.org> References: <20230817013310.88582-1-rostedt@goodmis.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org From: "Steven Rostedt (Google)" Instead of wasting allocation to return the results for queries, especially since the query is likely not to modify the results, just give a direct pointer to the entry->vals. Mark it as const so (hopefully) nobody modifies it. We still require the traceeval_results_release() to be called on it, as we may in the future add a ref count to it and perhaps do a copy-on-write if an update happens while something still has a hold on it. Signed-off-by: Steven Rostedt (Google) --- include/traceeval-hist.h | 4 ++-- src/histograms.c | 10 ++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/include/traceeval-hist.h b/include/traceeval-hist.h index fc1e288a43c6..37147d626fbb 100644 --- a/include/traceeval-hist.h +++ b/include/traceeval-hist.h @@ -161,10 +161,10 @@ int traceeval_insert(struct traceeval *teval, const union traceeval_data *vals); int traceeval_query(struct traceeval *teval, const union traceeval_data *keys, - union traceeval_data **results); + const union traceeval_data **results); void traceeval_results_release(struct traceeval *teval, - union traceeval_data *results); + const union traceeval_data *results); struct traceeval_stat *traceeval_stat(struct traceeval *teval, const union traceeval_data *keys, diff --git a/src/histograms.c b/src/histograms.c index c0f2248cce6f..c16b8866b84a 100644 --- a/src/histograms.c +++ b/src/histograms.c @@ -670,7 +670,7 @@ fail: * Returns 1 if found, 0 if not found, and -1 on error. */ int traceeval_query(struct traceeval *teval, const union traceeval_data *keys, - union traceeval_data **results) + const union traceeval_data **results) { struct entry *entry; int check; @@ -682,8 +682,8 @@ int traceeval_query(struct traceeval *teval, const union traceeval_data *keys, if ((check = get_entry(teval, keys, &entry)) < 1) return check; - return copy_traceeval_data_set(teval->nr_val_types, teval->val_types, - entry->vals, NULL, results); + *results = entry->vals; + return 1; } /* @@ -698,15 +698,13 @@ int traceeval_query(struct traceeval *teval, const union traceeval_data *keys, * allow traceeval to clean up its references. */ void traceeval_results_release(struct traceeval *teval, - union traceeval_data *results) + const union traceeval_data *results) { if (!teval || !results) { if (!teval) print_err("Results to be freed without accompanied traceeval instance!"); return; } - - data_release_and_free(teval->nr_val_types, &results, teval->val_types); } static struct entry *create_hist_entry(struct traceeval *teval, From patchwork Thu Aug 17 01:33:09 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13355876 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B48F8C2FC1F for ; Thu, 17 Aug 2023 01:34:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1347537AbjHQBds (ORCPT ); Wed, 16 Aug 2023 21:33:48 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58132 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1347555AbjHQBdO (ORCPT ); Wed, 16 Aug 2023 21:33:14 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [IPv6:2604:1380:4641:c500::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id AB6182D46 for ; Wed, 16 Aug 2023 18:33:10 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 6BEC864B74 for ; Thu, 17 Aug 2023 01:33:08 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3F55BC433AD; Thu, 17 Aug 2023 01:33:07 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.96) (envelope-from ) id 1qWRst-000N4g-2g; Wed, 16 Aug 2023 21:33:11 -0400 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: Ross Zwisler , Stevie Alvarez , "Steven Rostedt (Google)" Subject: [PATCH v3 17/18] libtraceeval samples: Update task-eval to use the histogram logic Date: Wed, 16 Aug 2023 21:33:09 -0400 Message-Id: <20230817013310.88582-18-rostedt@goodmis.org> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230817013310.88582-1-rostedt@goodmis.org> References: <20230817013310.88582-1-rostedt@goodmis.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org From: "Steven Rostedt (Google)" Convert task-eval over to the new API. Signed-off-by: Steven Rostedt (Google) --- samples/Makefile | 2 +- samples/task-eval.c | 743 +++++++++++++++++++++++++------------------- 2 files changed, 426 insertions(+), 319 deletions(-) diff --git a/samples/Makefile b/samples/Makefile index 012301ec5542..eb14411189f6 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -19,7 +19,7 @@ $(sdir): $(TARGETS): $(sdir) $(LIBTRACEEVAL_STATIC) -$(sdir)/%: $(bdir)/%.o +$(sdir)/%: $(LIBTRACEEVAL_STATIC) $(bdir)/%.o $(call do_sample_build,$@,$<) $(bdir)/%.o: $(bdir)/%.c diff --git a/samples/task-eval.c b/samples/task-eval.c index 9214777ee2e1..66d0c40dc0c8 100644 --- a/samples/task-eval.c +++ b/samples/task-eval.c @@ -5,7 +5,7 @@ #include #include #include -#include +#include static char *argv0; @@ -80,6 +80,79 @@ void pdie(const char *fmt, ...) va_end(ap); } +static struct traceeval_type cpu_keys[] = { + { + .type = TRACEEVAL_TYPE_NUMBER, + .name = "CPU", + }, + { + .type = TRACEEVAL_TYPE_NUMBER, + .name = "Schedule state", + }, + { + .type = TRACEEVAL_TYPE_NONE + } +}; + +static struct traceeval_type process_keys[] = { + { + .type = TRACEEVAL_TYPE_STRING, + .name = "COMM" + }, + { + .type = TRACEEVAL_TYPE_NUMBER, + .name = "Schedule state" + }, + { + .type = TRACEEVAL_TYPE_NONE, + } +}; + +static struct traceeval_type process_data_vals[] = { + { + .type = TRACEEVAL_TYPE_POINTER, + .name = "data", + }, + { + .type = TRACEEVAL_TYPE_NONE + } +}; + +static struct traceeval_type thread_keys[] = { + { + .type = TRACEEVAL_TYPE_NUMBER, + .name = "TID", + }, + { + .type = TRACEEVAL_TYPE_NUMBER, + .name = "Schedule state", + }, + { + .type = TRACEEVAL_TYPE_NONE, + } +}; + +static struct traceeval_type timestamp_vals[] = { + { + .type = TRACEEVAL_TYPE_NUMBER_64, + .name = "Timestamp", + .flags = TRACEEVAL_FL_TIMESTAMP, + }, + { + .type = TRACEEVAL_TYPE_NONE + } +}; + +static struct traceeval_type delta_vals[] = { + { + .type = TRACEEVAL_TYPE_NUMBER_64, + .name = "delta", + }, + { + .type = TRACEEVAL_TYPE_NONE, + }, +}; + enum sched_state { RUNNING, BLOCKED, @@ -89,16 +162,22 @@ enum sched_state { OTHER }; +struct teval_pair { + struct traceeval *start; + struct traceeval *stop; +}; + struct process_data { - struct traceeval *teval_cpus; - struct traceeval *teval_threads; + struct teval_pair teval_cpus; + struct teval_pair teval_threads; char *comm; int state; }; struct task_data { - struct traceeval *teval_cpus; - struct traceeval *teval_processes; + struct teval_pair teval_cpus; + struct teval_pair teval_processes; + struct traceeval *teval_processes_data; char *comm; }; @@ -111,30 +190,52 @@ static void update_process(struct task_data *tdata, const char *comm, enum sched_state state, enum command cmd, unsigned long long ts) { - struct traceeval_key keys[] = { + union traceeval_data keys[] = { { - .type = TRACEEVAL_TYPE_STRING, - .string = comm, + .cstring = comm, }, { - .type = TRACEEVAL_TYPE_NUMBER, - .number = state, - } + .number = state, + }, }; + union traceeval_data vals[] = { + { + .number_64 = ts, + }, + }; + union traceeval_data new_vals[1] = { }; + const union traceeval_data *results; int ret; switch (cmd) { case START: - ret = traceeval_n_start(tdata->teval_processes, keys, ts); + ret = traceeval_insert(tdata->teval_processes.start, keys, vals); if (ret < 0) pdie("Could not start process"); return; case STOP: - ret = traceeval_n_stop(tdata->teval_processes, keys, ts); + ret = traceeval_query(tdata->teval_processes.start, keys, &results); + if (ret < 0) + pdie("Could not query start process"); + if (ret == 0) + return; + if (!results[0].number_64) + break; + + new_vals[0].number_64 = ts - results[0].number_64; + + ret = traceeval_insert(tdata->teval_processes.stop, keys, new_vals); if (ret < 0) pdie("Could not stop process"); - return; + + /* Reset the start */ + new_vals[0].number_64 = 0; + ret = traceeval_insert(tdata->teval_processes.start, keys, new_vals); + if (ret < 0) + pdie("Could not start CPU"); + break; } + traceeval_results_release(tdata->teval_processes.start, results); } static void start_process(struct task_data *tdata, const char *comm, @@ -152,105 +253,172 @@ static void stop_process(struct task_data *tdata, const char *comm, static struct process_data * get_process_data(struct task_data *tdata, const char *comm) { - struct traceeval_key keys[] = { + union traceeval_data keys[] = { { - .type = TRACEEVAL_TYPE_STRING, - .string = comm, + .cstring = comm, }, { - .type = TRACEEVAL_TYPE_NUMBER, .number = RUNNING, } }; + const union traceeval_data *results; + void *data; + int ret; + + ret = traceeval_query(tdata->teval_processes_data, keys, &results); + if (ret < 0) + pdie("Could not query process data"); + if (ret == 0) + return NULL; - return traceeval_n_get_private(tdata->teval_processes, keys); + data = results[0].pointer; + traceeval_results_release(tdata->teval_processes_data, results); + return data; } void set_process_data(struct task_data *tdata, const char *comm, void *data) { - struct traceeval_key keys[] = { + union traceeval_data keys[] = { { - .type = TRACEEVAL_TYPE_STRING, - .string = comm, + .cstring = comm, }, { - .type = TRACEEVAL_TYPE_NUMBER, .number = RUNNING, } }; + union traceeval_data new_vals[1] = { }; + const union traceeval_data *results; int ret; - ret = traceeval_n_set_private(tdata->teval_processes, keys, data); + ret = traceeval_query(tdata->teval_processes_data, keys, &results); + if (ret > 0) + goto out; /* It already exists ? */ + if (ret < 0) + pdie("Could not query process data"); + + new_vals[0].pointer = data; + ret = traceeval_insert(tdata->teval_processes_data, keys, new_vals); if (ret < 0) pdie("Failed to set process data"); + + out: + traceeval_results_release(tdata->teval_processes_data, results); } -static void update_cpu(struct traceeval *teval, int cpu, +static void update_cpu(struct teval_pair *teval_pair, int cpu, enum sched_state state, enum command cmd, unsigned long long ts) { - struct traceeval_key keys[] = { + const union traceeval_data *results; + union traceeval_data keys[] = { { - .type = TRACEEVAL_TYPE_NUMBER, .number = cpu, }, { - .type = TRACEEVAL_TYPE_NUMBER, .number = state, } }; + union traceeval_data vals[] = { + { + .number_64 = ts, + }, + }; + union traceeval_data new_vals[1] = { }; int ret; switch (cmd) { case START: - ret = traceeval_n_continue(teval, keys, ts); + /* Only set if the timestamp is zero (or doesn't exist) */ + ret = traceeval_query(teval_pair->start, keys, &results); + if (ret > 0) { + if (results[0].number_64) + break; + } + if (ret < 0) + pdie("Could not query cpu start data"); + ret = traceeval_insert(teval_pair->start, keys, vals); if (ret < 0) pdie("Could not start CPU"); - return; + break; case STOP: - ret = traceeval_n_stop(teval, keys, ts); + ret = traceeval_query(teval_pair->start, keys, &results); + if (ret < 0) + pdie("Could not query cpu stop data"); + if (ret == 0) + return; + + if (!results[0].number_64) + break; + + new_vals[0].number_64 = ts - results[0].number_64; + + ret = traceeval_insert(teval_pair->stop, keys, new_vals); if (ret < 0) pdie("Could not stop CPU"); - return; + + /* Reset the start */ + new_vals[0].number_64 = 0; + ret = traceeval_insert(teval_pair->start, keys, new_vals); + if (ret < 0) + pdie("Could not start CPU"); + + break; + default: + return; } + traceeval_results_release(teval_pair->start, results); } -static void start_cpu(struct traceeval *teval, int cpu, +static void start_cpu(struct teval_pair *teval_pair, int cpu, enum sched_state state, unsigned long long ts) { - update_cpu(teval, cpu, state, START, ts); + update_cpu(teval_pair, cpu, state, START, ts); } -static void stop_cpu(struct traceeval *teval, int cpu, +static void stop_cpu(struct teval_pair *teval_pair, int cpu, enum sched_state state, unsigned long long ts) { - update_cpu(teval, cpu, state, STOP, ts); + update_cpu(teval_pair, cpu, state, STOP, ts); } static void update_thread(struct process_data *pdata, int tid, enum sched_state state, enum command cmd, unsigned long long ts) { - struct traceeval_key keys[] = { + const union traceeval_data *results; + union traceeval_data keys[] = { { - .type = TRACEEVAL_TYPE_NUMBER, .number = tid, }, { - .type = TRACEEVAL_TYPE_NUMBER, .number = state, } }; + union traceeval_data vals[] = { + { + .number_64 = ts, + }, + }; + union traceeval_data new_vals[1] = { }; int ret; switch (cmd) { case START: - ret = traceeval_n_start(pdata->teval_threads, keys, ts); + ret = traceeval_insert(pdata->teval_threads.start, keys, vals); if (ret < 0) pdie("Could not start thread"); return; case STOP: - ret = traceeval_n_stop(pdata->teval_threads, keys, ts); + ret = traceeval_query(pdata->teval_threads.start, keys, &results); + if (ret < 0) + pdie("Could not query thread start"); + if (ret == 0) + return; + + new_vals[0].number_64 = ts - results[0].number_64; + + ret = traceeval_insert(pdata->teval_threads.stop, keys, new_vals); + traceeval_results_release(pdata->teval_threads.start, results); if (ret < 0) pdie("Could not stop thread"); return; @@ -283,34 +451,21 @@ static struct tep_format_field *get_field(struct tep_event *event, const char *n static void init_process_data(struct process_data *pdata) { - struct traceeval_key_info cpu_info[] = { - { - .type = TRACEEVAL_TYPE_NUMBER, - .name = "CPU", - }, - { - .type = TRACEEVAL_TYPE_NUMBER, - .name = "Schedule state", - } - }; - struct traceeval_key_info thread_info[] = { - { - .type = TRACEEVAL_TYPE_NUMBER, - .name = "TID", - }, - { - .type = TRACEEVAL_TYPE_NUMBER, - .name = "Schedule state", - } - }; - pdata->teval_cpus = traceeval_2_alloc("CPUs", cpu_info); - if (!pdata->teval_cpus) - pdie("Creating trace eval"); + pdata->teval_cpus.start = traceeval_init(cpu_keys, timestamp_vals); + if (!pdata->teval_cpus.start) + pdie("Creating trace eval cpus start"); + pdata->teval_cpus.stop = traceeval_init(cpu_keys, delta_vals); + if (!pdata->teval_cpus.stop) + pdie("Creating trace eval cpus"); - pdata->teval_threads = traceeval_2_alloc("Threads", thread_info); - if (!pdata->teval_threads) - pdie("Creating trace eval"); + pdata->teval_threads.start = traceeval_init(thread_keys, timestamp_vals); + if (!pdata->teval_threads.start) + pdie("Creating trace eval threads start"); + + pdata->teval_threads.stop = traceeval_init(thread_keys, delta_vals); + if (!pdata->teval_threads.stop) + pdie("Creating trace eval threads"); } static struct process_data *alloc_pdata(struct task_data *tdata, const char *comm) @@ -344,7 +499,7 @@ static void sched_out(struct task_data *tdata, const char *comm, pid = val; if (!pid) { /* Record the runtime for the process CPUs */ - stop_cpu(tdata->teval_cpus, record->cpu, IDLE, record->ts); + stop_cpu(&tdata->teval_cpus, record->cpu, IDLE, record->ts); return; } @@ -381,10 +536,10 @@ static void sched_out(struct task_data *tdata, const char *comm, start_thread(pdata, pid, pdata->state, record->ts); /* Record the runtime for the process CPUs */ - stop_cpu(pdata->teval_cpus, record->cpu, RUNNING, record->ts); + stop_cpu(&pdata->teval_cpus, record->cpu, RUNNING, record->ts); /* Record the runtime for the all CPUs */ - stop_cpu(tdata->teval_cpus, record->cpu, RUNNING, record->ts); + stop_cpu(&tdata->teval_cpus, record->cpu, RUNNING, record->ts); } static void sched_in(struct task_data *tdata, const char *comm, @@ -405,7 +560,7 @@ static void sched_in(struct task_data *tdata, const char *comm, /* Ignore the idle task */ if (!pid) { /* Record the runtime for the process CPUs */ - start_cpu(tdata->teval_cpus, record->cpu, IDLE, record->ts); + start_cpu(&tdata->teval_cpus, record->cpu, IDLE, record->ts); return; } @@ -415,7 +570,7 @@ static void sched_in(struct task_data *tdata, const char *comm, pdata = get_process_data(tdata, comm); /* Start recording the running time of process CPUs */ - start_cpu(tdata->teval_cpus, record->cpu, RUNNING, record->ts); + start_cpu(&tdata->teval_cpus, record->cpu, RUNNING, record->ts); /* If there was no pdata, then this process did not go through sched out */ if (!pdata) { @@ -427,7 +582,7 @@ static void sched_in(struct task_data *tdata, const char *comm, start_thread(pdata, pid, RUNNING, record->ts); /* Start recording the running time of process CPUs */ - start_cpu(pdata->teval_cpus, record->cpu, RUNNING, record->ts); + start_cpu(&pdata->teval_cpus, record->cpu, RUNNING, record->ts); /* If it was just created, there's nothing to stop */ if (is_new) @@ -482,32 +637,83 @@ static void print_microseconds(int idx, unsigned long long nsecs) printf("%*d.%03lld\n", idx, 0, nsecs); } +/* + * Sort all the processes by the RUNNING state. + * If A and B have the same COMM, then sort by state. + * else + * Find the RUNNNIG state for A and B + * If the RUNNING state does not exist, it's considered -1 + * If RUNNING is equal, then sort by COMM. + */ +static int compare_pdata(struct traceeval *teval_data, + const union traceeval_data *Akeys, + const union traceeval_data *Avals, + const union traceeval_data *Bkeys, + const union traceeval_data *Bvals, + void *data) +{ + struct traceeval *teval = data; /* The deltas are here */ + union traceeval_data keysA[] = { + { .cstring = Akeys[0].cstring }, { .number = RUNNING } }; + union traceeval_data keysB[] = { + { .cstring = Bkeys[0].cstring }, { .number = RUNNING } }; + struct traceeval_stat *statA; + struct traceeval_stat *statB; + unsigned long long totalA = -1; + unsigned long long totalB = -1; + + /* First check if we are on the same task */ + if (strcmp(Akeys[0].cstring, Bkeys[0].cstring) == 0) { + /* Sort decending */ + if (Bkeys[1].number > Akeys[1].number) + return -1; + return Bkeys[1].number != Akeys[1].number; + } + + /* Get the RUNNING values for both processes */ + statA = traceeval_stat(teval, keysA, &delta_vals[0]); + if (statA) + totalA = traceeval_stat_total(statA); + + statB = traceeval_stat(teval, keysB, &delta_vals[0]); + if (statB) + totalB = traceeval_stat_total(statB); + + if (totalB < totalA) + return -1; + if (totalB > totalA) + return 1; + + return strcmp(Bkeys[0].cstring, Akeys[0].cstring); +} + static void display_cpus(struct traceeval *teval) { - struct traceeval_key_array *karray; - const struct traceeval_key *ckey; - const struct traceeval_key *skey; + struct traceeval_iterator *iter = traceeval_iterator_get(teval); + const union traceeval_data *keys; + struct traceeval_stat *stat; int last_cpu = -1; - int i, nr; + + if (!iter) + pdie("Could not get iterator?"); printf("\n"); - nr = traceeval_result_nr(teval); - if (!nr) - die("No result for CPUs\n"); + traceeval_iterator_sort(iter, cpu_keys[0].name, 0, true); + traceeval_iterator_sort(iter, cpu_keys[1].name, 1, true); - for (i = 0; i < nr; i++) { - karray = traceeval_result_indx_key_array(teval, i); - if (!karray) - die("No cpu key for result %d\n", i); - ckey = traceeval_key_array_indx(karray, 0); - skey = traceeval_key_array_indx(karray, 1); + while (traceeval_iterator_next(iter, &keys) > 0) { + int state = keys[1].number; + int cpu = keys[0].number; + stat = traceeval_stat(teval, keys, &delta_vals[0]); + if (!stat) + continue; // die? - if (last_cpu != ckey->number) - printf(" CPU [%d]:\n", (int)ckey->number); + if (last_cpu != cpu) + printf(" CPU [%d]:\n", cpu); - switch (skey->number) { + switch (state) { case RUNNING: printf(" Running: "); break; @@ -518,256 +724,170 @@ static void display_cpus(struct traceeval *teval) case PREEMPT: case SLEEP: case OTHER: - printf(" \?\?(%ld): ", skey->number); + printf(" \?\?(%d): ", state); break; } printf(" time (us):"); - print_microseconds(12, traceeval_result_indx_total(teval, i)); + print_microseconds(12, traceeval_stat_total(stat)); - last_cpu = ckey->number; + last_cpu = cpu; } + + if (last_cpu < 0) + die("No result for CPUs\n"); + } -static void display_thread(struct traceeval *teval, int tid) -{ - struct traceeval_key keys[2] = - { - { - .type = TRACEEVAL_TYPE_NUMBER, - .number = tid, - }, - { - .type = TRACEEVAL_TYPE_NUMBER, - .number = RUNNING, - } - }; - ssize_t ret; - - printf("\n thread id: %d\n", tid); - - printf(" Total run time (us):"); - print_microseconds(14, (ret = traceeval_result_keys_total(teval, keys)) < 0 ? 0 : ret); - - keys[1].number = PREEMPT; - printf(" Total preempt time (us):"); - print_microseconds(10, (ret = traceeval_result_keys_total(teval, keys)) < 0 ? 0 : ret); - - keys[1].number = BLOCKED; - printf(" Total blocked time (us):"); - print_microseconds(10, (ret = traceeval_result_keys_total(teval, keys)) < 0 ? 0 : ret); - - keys[1].number = SLEEP; - printf(" Total sleep time (us):"); - print_microseconds(12, (ret = traceeval_result_keys_total(teval, keys)) < 0 ? 0 : ret); -}; +static void display_state_times(int state, unsigned long long total) +{ + switch (state) { + case RUNNING: + printf(" Total run time (us):"); + print_microseconds(14, total); + break; + case BLOCKED: + printf(" Total blocked time (us):"); + print_microseconds(10, total); + break; + case PREEMPT: + printf(" Total preempt time (us):"); + print_microseconds(10, total); + break; + case SLEEP: + printf(" Total sleep time (us):"); + print_microseconds(12, total); + } +} static void display_threads(struct traceeval *teval) { - struct traceeval_key_array *karray; - const struct traceeval_key *tkey; - struct traceeval_key keys[2]; + struct traceeval_iterator *iter = traceeval_iterator_get(teval); + const union traceeval_data *keys; + struct traceeval_stat *stat; int last_tid = -1; - int i, nr; - nr = traceeval_result_nr(teval); - if (!nr) - die("No result for threads\n"); + traceeval_iterator_sort(iter, thread_keys[0].name, 0, true); + traceeval_iterator_sort(iter, thread_keys[1].name, 1, true); - memset(keys, 0, sizeof(keys)); - keys[1].type = TRACEEVAL_TYPE_NUMBER; - - for (i = 0; i < nr; i++) { - karray = traceeval_result_indx_key_array(teval, i); - if (!karray) - die("No thread key for result %d\n", i); - tkey = traceeval_key_array_indx(karray, 0); - if (!tkey) - die("No thread keys for result?"); - - /* - * All the TIDS should be together in the results, - * as the results are sorted by the first key, which - * is the comm. - */ - if (last_tid == tkey->number) - continue; + while (traceeval_iterator_next(iter, &keys) > 0) { + int state = keys[1].number; + int tid = keys[0].number; + + stat = traceeval_stat(teval, keys, &delta_vals[0]); + if (!stat) + continue; // die? + + if (last_tid != keys[0].number) + printf("\n thread id: %d\n", tid); - last_tid = tkey->number; + last_tid = tid; - display_thread(teval, tkey->number); + display_state_times(state, traceeval_stat_total(stat)); } + + if (last_tid < 0) + die("No result for threads\n"); + } -static void display_process(struct traceeval *teval, struct process_data *pdata, - const char *comm) +static void display_process(struct process_data *pdata) { - struct traceeval_key keys[2] = - { - { - .type = TRACEEVAL_TYPE_STRING, - .string = comm, - }, - { - .type = TRACEEVAL_TYPE_NUMBER, - .number = RUNNING, - } - }; - ssize_t ret; - - printf("Task: %s\n", comm); - - printf(" Total run time (us):"); - print_microseconds(18, (ret = traceeval_result_keys_total(teval, keys)) < 0 ? 0 : ret); - - keys[1].number = PREEMPT; - printf(" Total preempt time (us):"); - print_microseconds(14, (ret = traceeval_result_keys_total(teval, keys)) < 0 ? 0 : ret); - - keys[1].number = BLOCKED; - printf(" Total blocked time (us):"); - print_microseconds(14, (ret = traceeval_result_keys_total(teval, keys)) < 0 ? 0 : ret); - - keys[1].number = SLEEP; - printf(" Total sleep time (us):"); - print_microseconds(16, (ret = traceeval_result_keys_total(teval, keys)) < 0 ? 0 : ret); - - display_threads(pdata->teval_threads); - display_cpus(pdata->teval_cpus); + display_threads(pdata->teval_threads.stop); + display_cpus(pdata->teval_cpus.stop); printf("\n"); } -static int compare_pdata(struct traceeval *teval, - const struct traceeval_key_array *A, - const struct traceeval_key_array *B, - void *data) +static void display_process_stats(struct traceeval *teval, + struct process_data *pdata, const char *comm) { - struct traceeval_key akeys[2]; - struct traceeval_key bkeys[2]; - const struct traceeval_key *akey; - const struct traceeval_key *bkey; - long long aval; - long long bval; - int ret; - - /* Get the RUNNING values for this process */ - - akey = traceeval_key_array_indx(A, 0); - akeys[0] = *akey; - akeys[1].type = TRACEEVAL_TYPE_NUMBER; - akeys[1].number = RUNNING; - - bkey = traceeval_key_array_indx(B, 0); - bkeys[0] = *bkey; - bkeys[1].type = TRACEEVAL_TYPE_NUMBER; - bkeys[1].number = RUNNING; - - aval = traceeval_result_keys_total(teval, akey); - bval = traceeval_result_keys_total(teval, bkeys); - - if (aval < 0) - return -1; - if (bval < 0) - return -1; - - if (bval < aval) - return -1; - if (bval > aval) - return 1; - - ret = strcmp(bkeys[0].string, akeys[0].string); + struct traceeval_stat *stat; + unsigned long long delta; + union traceeval_data keys[] = { + { + .cstring = comm, + }, + { + .number = RUNNING, + } + }; - /* If two different processes have the same runtime, sort by name */ - if (ret) - return ret; + for (int i = 0; i < OTHER; i++) { + keys[1].number = i; - /* Same process, sort by state */ + delta = 0; + stat = traceeval_stat(teval, keys, &delta_vals[0]); + if (stat) + delta = traceeval_stat_total(stat); + display_state_times(i, delta); + } +} - akey = traceeval_key_array_indx(A, 1); - bkey = traceeval_key_array_indx(B, 1); +static void display_processes(struct traceeval *teval, + struct traceeval *teval_data) +{ + struct traceeval_iterator *iter = traceeval_iterator_get(teval_data); + const union traceeval_data *keys; + int ret; - if (bkey->number < akey->number) - return -1; + traceeval_iterator_sort_custom(iter, compare_pdata, teval); - return bkey->number > akey->number; -} + while (traceeval_iterator_next(iter, &keys) > 0) { + const union traceeval_data *results; + struct process_data *pdata = NULL; + const char *comm = keys[0].cstring; -static void display_processes(struct traceeval *teval) -{ - struct traceeval_key_array *karray; - const struct traceeval_key *tkey; - struct traceeval_key keys[2]; - struct process_data *pdata; - const char *last_comm = NULL; - int i, nr; - - nr = traceeval_result_nr(teval); - if (!nr) - die("No result for processes\n"); - - memset(keys, 0, sizeof(keys)); - keys[1].type = TRACEEVAL_TYPE_NUMBER; - - for (i = 0; i < nr; i++) { - karray = traceeval_result_indx_key_array(teval, i); - if (!karray) - die("No process key for result %d\n", i); - tkey = traceeval_key_array_indx(karray, 0); - if (!tkey) - die("No process keys for result?"); - - /* - * All the comms should be together in the results, - * as the results are sorted by the first key, which - * is the comm. - */ - if (last_comm && strcmp(tkey->string, last_comm) == 0) - continue; + ret = traceeval_query(teval_data, keys, &results); + if (ret < 0) + pdie("Could not query iterator"); + if (ret < 1) + continue; /* ?? */ - last_comm = tkey->string; + pdata = results[0].pointer; + traceeval_results_release(teval_data, results); - keys[0] = *tkey; - keys[1].number = RUNNING; + printf("Task: %s\n", comm); - /* All processes should have a running state */ - pdata = traceeval_n_get_private(teval, keys); + display_process_stats(teval, pdata, comm); if (pdata) - display_process(teval, pdata, keys[0].string); + display_process(pdata); } } static void display(struct task_data *tdata) { + struct traceeval *teval = tdata->teval_cpus.stop; + struct traceeval_iterator *iter = traceeval_iterator_get(teval); + const union traceeval_data *keys; + struct traceeval_stat *stat; unsigned long long total_time = 0; unsigned long long idle_time = 0; - struct traceeval_key_array *karray; - const struct traceeval_key *tkey; - unsigned long long val; - int i, nr; - if (tdata->comm) - return display_processes(tdata->teval_processes); + if (tdata->comm) { + return display_processes(tdata->teval_processes.stop, + tdata->teval_processes_data); + } printf("Total:\n"); - nr = traceeval_result_nr(tdata->teval_cpus); - for (i = 0; i < nr; i++) { - karray = traceeval_result_indx_key_array(tdata->teval_cpus, i); - if (!karray) - die("No CPU keys for result %d\n", i); - tkey = traceeval_key_array_indx(karray, 1); - if (!tkey) - die("No state keys for CPU result %d?", i); - - val = traceeval_result_indx_total(tdata->teval_cpus, i); - switch (tkey->number) { + if (!iter) + pdie("No cpus?"); + + while (traceeval_iterator_next(iter, &keys) > 0) { + int state = keys[1].number; + + stat = traceeval_stat(teval, keys, &delta_vals[0]); + if (!stat) + continue; + + switch (state) { case RUNNING: - total_time += val; + total_time += traceeval_stat_total(stat); break; case IDLE: - idle_time += val; + idle_time += traceeval_stat_total(stat); break; default: - die("Invalid CPU state: %d\n", tkey->number); + die("Invalid CPU state: %d\n", state); } } @@ -776,12 +896,10 @@ static void display(struct task_data *tdata) printf(" Total idle time (us):"); print_microseconds(16, idle_time); - display_cpus(tdata->teval_cpus); - - traceeval_sort_custom(tdata->teval_processes, compare_pdata, NULL); + display_cpus(tdata->teval_cpus.stop); printf("\n"); - display_processes(tdata->teval_processes); + display_processes(tdata->teval_processes.stop, tdata->teval_processes_data); } static void free_tdata(struct task_data *tdata) @@ -792,26 +910,6 @@ int main (int argc, char **argv) { struct tracecmd_input *handle; struct task_data data; - struct traceeval_key_info cpu_tinfo[2] = { - { - .type = TRACEEVAL_TYPE_NUMBER, - .name = "CPU" - }, - { - .type = TRACEEVAL_TYPE_NUMBER, - .name = "Schedule state" - } - }; - struct traceeval_key_info process_tinfo[2] = { - { - .type = TRACEEVAL_TYPE_STRING, - .name = "COMM" - }, - { - .type = TRACEEVAL_TYPE_NUMBER, - .name = "Schedule state" - } - }; int c; memset(&data, 0, sizeof(data)); @@ -839,12 +937,21 @@ int main (int argc, char **argv) if (!handle) pdie("Error opening %s", argv[0]); - data.teval_processes = traceeval_2_alloc("Processes", process_tinfo); - if (!data.teval_processes) + data.teval_processes.start = traceeval_init(process_keys, timestamp_vals); + if (!data.teval_processes.start) + pdie("Creating trace eval start"); + data.teval_processes_data = traceeval_init(process_keys, process_data_vals); + if (!data.teval_processes_data) + pdie("Creating trace eval data"); + data.teval_processes.stop = traceeval_init(process_keys, delta_vals); + if (!data.teval_processes.stop) pdie("Creating trace eval"); - data.teval_cpus = traceeval_2_alloc("CPUs", cpu_tinfo); - if (!data.teval_cpus) + data.teval_cpus.start = traceeval_init(cpu_keys, timestamp_vals); + if (!data.teval_cpus.start) + pdie("Creating trace eval"); + data.teval_cpus.stop = traceeval_init(cpu_keys, delta_vals); + if (!data.teval_cpus.stop) pdie("Creating trace eval"); tracecmd_follow_event(handle, "sched", "sched_switch", switch_func, &data); From patchwork Thu Aug 17 01:33:10 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steven Rostedt X-Patchwork-Id: 13355871 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6E83CC2FC04 for ; Thu, 17 Aug 2023 01:34:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1347546AbjHQBdr (ORCPT ); Wed, 16 Aug 2023 21:33:47 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58074 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1347535AbjHQBdM (ORCPT ); Wed, 16 Aug 2023 21:33:12 -0400 Received: from dfw.source.kernel.org (dfw.source.kernel.org [139.178.84.217]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A8BF4272B for ; Wed, 16 Aug 2023 18:33:10 -0700 (PDT) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 6BBDB64C39 for ; Thu, 17 Aug 2023 01:33:08 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 52FFAC433B9; Thu, 17 Aug 2023 01:33:07 +0000 (UTC) Received: from rostedt by gandalf with local (Exim 4.96) (envelope-from ) id 1qWRst-000N4k-2l; Wed, 16 Aug 2023 21:33:11 -0400 From: Steven Rostedt To: linux-trace-devel@vger.kernel.org Cc: Ross Zwisler , Stevie Alvarez , "Steven Rostedt (Google)" Subject: [PATCH v3 18/18] libtraceeval: Add traceeval_remove() Date: Wed, 16 Aug 2023 21:33:10 -0400 Message-Id: <20230817013310.88582-19-rostedt@goodmis.org> X-Mailer: git-send-email 2.40.1 In-Reply-To: <20230817013310.88582-1-rostedt@goodmis.org> References: <20230817013310.88582-1-rostedt@goodmis.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-trace-devel@vger.kernel.org From: "Steven Rostedt (Google)" Add a way to remove an entry that's in the traceeval. Currently there's only ways to add entries, but there also needs to be a way to remove them too. Signed-off-by: Steven Rostedt (Google) Reviewed-by: Ross Zwisler --- include/traceeval-hist.h | 3 +++ src/histograms.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/include/traceeval-hist.h b/include/traceeval-hist.h index 37147d626fbb..8e5a6451f399 100644 --- a/include/traceeval-hist.h +++ b/include/traceeval-hist.h @@ -160,6 +160,9 @@ int traceeval_insert(struct traceeval *teval, const union traceeval_data *keys, const union traceeval_data *vals); +int traceeval_remove(struct traceeval *teval, + const union traceeval_data *keys); + int traceeval_query(struct traceeval *teval, const union traceeval_data *keys, const union traceeval_data **results); diff --git a/src/histograms.c b/src/histograms.c index c16b8866b84a..f35d1b2e583d 100644 --- a/src/histograms.c +++ b/src/histograms.c @@ -934,6 +934,35 @@ int traceeval_insert(struct traceeval *teval, return update_entry(teval, entry, vals); } +/** + * traceeval_remove - remove an item from the traceeval descriptor + * @teval: The descriptor to insert into + * @keys: The list of keys that defines what is being removed + * + * This is the opposite of traceeval_insert(). Instead of inserting + * an item into the traceeval historgram, it removes it. + * + * Returns 1 if it found and removed an item, + * 0 if it did not find an time matching @keys + * -1 if there was an error. + */ +int traceeval_remove(struct traceeval *teval, + const union traceeval_data *keys) +{ + struct hash_table *hist = teval->hist; + struct entry *entry; + int check; + + entry = NULL; + check = get_entry(teval, keys, &entry); + + if (check < 1) + return check; + + hash_remove(hist, &entry->hash); + return 1; +} + /** * traceeval_iterator_put - release a given iterator * @iter: The iterartor to release