From: Steven Rostedt <rostedt@goodmis.org>
To: Linux Trace Devel <linux-trace-devel@vger.kernel.org>
Cc: Ross Zwisler <zwisler@google.com>
Subject: [PATCH] libtraceeval: Add traceeval_stat_max/min_timestamp() API
Date: Thu, 5 Oct 2023 18:14:48 -0400 [thread overview]
Message-ID: <20231005181448.176e9563@gandalf.local.home> (raw)
From: "Steven Rostedt (Google)" <rostedt@goodmis.org>
If a value field is flagged as a TIMESTAMP, and another field is flagged
as a STAT, have the statistics for the STAT field automatically keep track
of when the max and min happened (what the TIMESTAMP field was at those
instances).
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
---
include/traceeval.h | 4 ++
src/eval-local.h | 3 ++
src/histograms.c | 105 +++++++++++++++++++++++++++++++++++++-------
3 files changed, 97 insertions(+), 15 deletions(-)
diff --git a/include/traceeval.h b/include/traceeval.h
index a70a5b09057b..bbf8f6ac7dd1 100644
--- a/include/traceeval.h
+++ b/include/traceeval.h
@@ -242,6 +242,10 @@ 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);
+unsigned long long traceeval_stat_max_timestamp(struct traceeval_stat *stat,
+ unsigned long long *ts);
+unsigned long long traceeval_stat_min_timestamp(struct traceeval_stat *stat,
+ unsigned long long *ts);
struct traceeval_iterator *traceeval_iterator_get(struct traceeval *teval);
void traceeval_iterator_put(struct traceeval_iterator *iter);
diff --git a/src/eval-local.h b/src/eval-local.h
index 01b1b838e8bf..fabb1a06e7df 100644
--- a/src/eval-local.h
+++ b/src/eval-local.h
@@ -47,7 +47,9 @@ struct hash_table {
struct traceeval_stat {
unsigned long long max;
+ unsigned long long max_ts;
unsigned long long min;
+ unsigned long long min_ts;
unsigned long long total;
unsigned long long std;
size_t count;
@@ -72,6 +74,7 @@ struct traceeval {
size_t nr_elements;
size_t sizeof_type;
size_t sizeof_data;
+ ssize_t timestamp_idx;
};
struct traceeval_iterator {
diff --git a/src/histograms.c b/src/histograms.c
index 2be95e6a8d1f..0cf52225a03a 100644
--- a/src/histograms.c
+++ b/src/histograms.c
@@ -222,15 +222,32 @@ static int check_keys(struct traceeval_type *keys, int cnt)
return 0;
}
-static int check_vals(struct traceeval_type *vals, int cnt)
+static int check_vals(struct traceeval *teval, struct traceeval_type *vals, int cnt)
{
+ bool ts_found = false;
+
for (int i = 0; i < cnt && vals[i].type != TRACEEVAL_TYPE_NONE; i++) {
/* Define this as a value */
vals[i].flags |= TRACEEVAL_FL_VALUE;
vals[i].flags &= ~TRACEEVAL_FL_KEY;
+ if (vals[i].flags & TRACEEVAL_FL_TIMESTAMP) {
+ /* Only one field may be marked as a timestamp */
+ if (ts_found)
+ return -1;
+ /* The type must be numeric */
+ if (vals[i].type > TRACEEVAL_TYPE_NUMBER)
+ return -1;
+ /* TIMESTAMPS can not be STATs themselves */
+ if (vals[i].flags & TRACEEVAL_FL_STAT)
+ return -1;
+ ts_found = true;
+ teval->timestamp_idx = i;
+ }
vals[i].index = i;
}
+ if (!ts_found)
+ teval->timestamp_idx = -1;
return 0;
}
@@ -298,7 +315,7 @@ struct traceeval *traceeval_init_data_size(struct traceeval_type *keys,
goto fail_release;
if (vals) {
- ret = check_vals(vals, nr_vals);
+ ret = check_vals(teval, vals, nr_vals);
if (ret < 0)
goto fail_release;
}
@@ -535,7 +552,8 @@ static bool is_stat_type(struct traceeval_type *type)
static int copy_traceeval_data(struct traceeval_type *type,
struct traceeval_stat *stat,
struct traceeval_data *dst,
- const struct traceeval_data *src)
+ const struct traceeval_data *src,
+ unsigned long long ts)
{
unsigned long long val;
@@ -599,21 +617,31 @@ static int copy_traceeval_data(struct traceeval_type *type,
if (!stat->count++) {
stat->max = val;
stat->min = val;
+ stat->max_ts = ts;
+ stat->min_ts = ts;
stat->total = val;
return 0;
}
if (type->flags & TRACEEVAL_FL_SIGNED) {
- if ((long long)stat->max < (long long)val)
+ if ((long long)stat->max < (long long)val) {
stat->max = val;
- if ((long long)stat->min > (long long)val)
+ stat->max_ts = ts;
+ }
+ if ((long long)stat->min > (long long)val) {
stat->min = val;
+ stat->min_ts = ts;
+ }
stat->total += (long long)val;
} else {
- if (stat->max < val)
+ if (stat->max < val) {
+ stat->max_ts = ts;
stat->max = val;
- if (stat->min > val)
+ }
+ if (stat->min > val) {
stat->min = val;
+ stat->min_ts = ts;
+ }
stat->total += val;
}
@@ -654,7 +682,8 @@ static void data_release_and_free(size_t size, struct traceeval_data **data,
static int dup_traceeval_data_set(size_t size, struct traceeval_type *type,
struct traceeval_stat *stats,
const struct traceeval_data *orig,
- struct traceeval_data **copy)
+ struct traceeval_data **copy,
+ unsigned long long ts)
{
size_t i;
@@ -668,7 +697,7 @@ static int dup_traceeval_data_set(size_t size, struct traceeval_type *type,
for (i = 0; i < size; i++) {
if (copy_traceeval_data(type + i, stats ? stats + i : NULL,
- (*copy) + i, orig + i))
+ (*copy) + i, orig + i, ts))
goto fail;
}
@@ -754,6 +783,14 @@ static struct entry *create_hist_entry(struct traceeval *teval,
return entry;
}
+static unsigned long long get_timestamp(struct traceeval *teval,
+ const struct traceeval_data *vals)
+{
+ if (teval->timestamp_idx < 0)
+ return 0;
+ return vals[teval->timestamp_idx].number_64;
+}
+
/*
* Create a new entry in @teval with respect to @keys and @vals.
*
@@ -765,6 +802,7 @@ static int create_entry(struct traceeval *teval,
{
struct traceeval_data *new_keys;
struct traceeval_data *new_vals;
+ unsigned long long ts;
struct entry *entry;
entry = create_hist_entry(teval, keys);
@@ -775,14 +813,16 @@ static int create_entry(struct traceeval *teval,
if (!entry->val_stats)
goto fail_entry;
+ ts = get_timestamp(teval, vals);
+
/* copy keys */
if (dup_traceeval_data_set(teval->nr_key_types, teval->key_types,
- NULL, keys, &new_keys) == -1)
+ NULL, keys, &new_keys, 0) == -1)
goto fail_stats;
/* copy vals */
if (dup_traceeval_data_set(teval->nr_val_types, teval->val_types,
- entry->val_stats, vals, &new_vals) == -1)
+ entry->val_stats, vals, &new_vals, ts) == -1)
goto fail;
entry->keys = new_keys;
@@ -818,12 +858,15 @@ static int update_entry(struct traceeval *teval, struct entry *entry,
struct traceeval_type *types = teval->val_types;
struct traceeval_data *copy = entry->vals;
struct traceeval_data old[teval->nr_val_types];
+ unsigned long long ts;
size_t size = teval->nr_val_types;
ssize_t i;
if (!size)
return 0;
+ ts = get_timestamp(teval, vals);
+
for (i = 0; i < teval->nr_val_types; i++) {
if (vals[i].type != teval->val_types[i].type)
return -1;
@@ -833,7 +876,7 @@ static int update_entry(struct traceeval *teval, struct entry *entry,
old[i] = copy[i];
if (copy_traceeval_data(types + i, stats + i,
- copy + i, vals + i))
+ copy + i, vals + i, ts))
goto fail;
}
data_release(size, old, types);
@@ -844,7 +887,7 @@ static int update_entry(struct traceeval *teval, struct entry *entry,
/* Put back the old values */
for (i--; i >= 0; i--) {
copy_traceeval_data(types + i, NULL,
- copy + i, old + i);
+ copy + i, old + i, 0);
}
return -1;
}
@@ -889,6 +932,38 @@ struct traceeval_stat *traceeval_stat_size(struct traceeval *teval,
return &entry->val_stats[type->index];
}
+/**
+ * traceeval_stat_max_timestamp - return max value of stat and where it happend
+ * @stat: The stat structure that holds the stats
+ * @ts: The return value for the time stamp of where the max happened
+ *
+ * Returns the max value within @stat, and the timestamp of where that max
+ * happened in @ts.
+ */
+unsigned long long traceeval_stat_max_timestamp(struct traceeval_stat *stat,
+ unsigned long long *ts)
+{
+ if (ts)
+ *ts = stat->max_ts;
+ return stat->max;
+}
+
+/**
+ * traceeval_stat_min_timestamp - return min value of stat and where it happend
+ * @stat: The stat structure that holds the stats
+ * @ts: The return value for the time stamp of where the min happened
+ *
+ * Returns the min value within @stat, and the timestamp of where that min
+ * happened in @ts.
+ */
+unsigned long long traceeval_stat_min_timestamp(struct traceeval_stat *stat,
+ unsigned long long *ts)
+{
+ if (ts)
+ *ts = stat->min_ts;
+ return stat->min;
+}
+
/**
* traceeval_stat_max - return max value of stat
* @stat: The stat structure that holds the stats
@@ -897,7 +972,7 @@ struct traceeval_stat *traceeval_stat_size(struct traceeval *teval,
*/
unsigned long long traceeval_stat_max(struct traceeval_stat *stat)
{
- return stat->max;
+ return traceeval_stat_max_timestamp(stat, NULL);
}
/**
@@ -908,7 +983,7 @@ unsigned long long traceeval_stat_max(struct traceeval_stat *stat)
*/
unsigned long long traceeval_stat_min(struct traceeval_stat *stat)
{
- return stat->min;
+ return traceeval_stat_min_timestamp(stat, NULL);
}
/**
--
2.40.1
next reply other threads:[~2023-10-05 22:13 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-10-05 22:14 Steven Rostedt [this message]
2023-10-11 22:21 ` [PATCH] libtraceeval: Add traceeval_stat_max/min_timestamp() API Ross Zwisler
2023-10-11 23:09 ` Steven Rostedt
2023-10-17 18:47 ` Ross Zwisler
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20231005181448.176e9563@gandalf.local.home \
--to=rostedt@goodmis.org \
--cc=linux-trace-devel@vger.kernel.org \
--cc=zwisler@google.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).