Linux-Trace-Devel Archive mirror
 help / color / mirror / Atom feed
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


             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).