diff mbox series

[v2,10/17] libtraceeval histogram: Add updating of stats

Message ID 20230811053940.1408424-11-rostedt@goodmis.org (mailing list archive)
State Superseded
Headers show
Series libtraceeval histogram: Updates | expand

Commit Message

Steven Rostedt Aug. 11, 2023, 5:39 a.m. UTC
From: "Steven Rostedt (Google)" <rostedt@goodmis.org>

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) <rostedt@goodmis.org>
---
 include/traceeval-hist.h |  17 ++--
 src/eval-local.h         |   9 ++
 src/histograms.c         | 182 +++++++++++++++++++++++++++++++++++----
 3 files changed, 183 insertions(+), 25 deletions(-)

Comments

Ross Zwisler Aug. 15, 2023, 8:25 p.m. UTC | #1
On Fri, Aug 11, 2023 at 01:39:33AM -0400, Steven Rostedt wrote:
> From: "Steven Rostedt (Google)" <rostedt@goodmis.org>
> 
> 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) <rostedt@goodmis.org>
> ---
>  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 1c02f3039809..d061a4532b06 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 820d7ad096e8..190b19db14d2 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;

I'm confused about why 'val_stats' is in 'struct entry', instead of in
'struct traceeval'?

I would think that we'd want to keep our stats on a per-histogram basis, where
we have an array of 'struct traceeval_stat' structs with the same size as our
'val_types' array, so that for a given compound value, say 

{ TRACEVAL_TYPE_NUMBER_64, TRACEEVAL_TYPE_NUMBER_16, TRACEEVAL_NUMBER_8}

we'd have stats for each of these entries, like min, max, average, etc.?

With the stats associated with each entry, I don't see how we keep all the
stats for all the entries up to date as we do insertions & removals.
Steven Rostedt Aug. 15, 2023, 8:55 p.m. UTC | #2
On Tue, 15 Aug 2023 14:25:54 -0600
Ross Zwisler <zwisler@google.com> wrote:

> On Fri, Aug 11, 2023 at 01:39:33AM -0400, Steven Rostedt wrote:
> > From: "Steven Rostedt (Google)" <rostedt@goodmis.org>
> > 
> > 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) <rostedt@goodmis.org>
> > ---
> >  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 1c02f3039809..d061a4532b06 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 820d7ad096e8..190b19db14d2 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;  
> 
> I'm confused about why 'val_stats' is in 'struct entry', instead of in
> 'struct traceeval'?
> 
> I would think that we'd want to keep our stats on a per-histogram basis, where
> we have an array of 'struct traceeval_stat' structs with the same size as our
> 'val_types' array, so that for a given compound value, say 
> 
> { TRACEVAL_TYPE_NUMBER_64, TRACEEVAL_TYPE_NUMBER_16, TRACEEVAL_NUMBER_8}
> 
> we'd have stats for each of these entries, like min, max, average, etc.?
> 
> With the stats associated with each entry, I don't see how we keep all the
> stats for all the entries up to date as we do insertions & removals.

Because the stats are for each key, not the total of the traceeval. If you
look at the code in task-eval, I want to know the max,min,total,count,avg
of the sleep time for a task when it is blocked, when it is sleeping, when
it is running, etc:

Task: migrate
      Total run time (us):       4645364
      Total blocked time (us):         0
      Total preempt time (us):     98540
      Total sleep time (us):      115559

    thread id: 1884
      Total run time (us):           808

    thread id: 1885
      Total run time (us):       2920763
      Total preempt time (us):    245570
      Total sleep time (us):   224289277

    thread id: 1886
      Total run time (us):       3601375
      Total preempt time (us):   1308185
      Total sleep time (us):    12692630

    thread id: 1887
      Total run time (us):       3740300
      Total preempt time (us):   2620102
      Total sleep time (us):     4806722

That "Task: migrate" is just one entry in the traceeval, the threads are
entries in the nested traceeval that is saved per task.

Actually, task-eval is only looking at the total time, but I could easily
add the max/min/avg times too. If I were to write a latency program, and
was tracing the wake up latency of a task, you probably want these stats
for each key, so I can see the max latency per task, and not just the total
for all tasks.

-- Steve
diff mbox series

Patch

diff --git a/include/traceeval-hist.h b/include/traceeval-hist.h
index 1c02f3039809..d061a4532b06 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 820d7ad096e8..190b19db14d2 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 a1a2daaa2e9b..9a8ec4d85301 100644
--- a/src/histograms.c
+++ b/src/histograms.c
@@ -506,21 +506,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;
@@ -549,6 +613,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;
@@ -562,7 +627,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;
 	}
 
@@ -605,7 +671,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);
 }
 
 /*
@@ -660,18 +726,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;
@@ -680,10 +750,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;
 }
 
@@ -700,7 +773,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);
@@ -708,6 +781,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