diff mbox series

[v2,1/5] histograms: Initial histograms interface

Message ID 20230803225413.40697-2-stevie.6strings@gmail.com (mailing list archive)
State Superseded
Headers show
Series histograms: bug fixes and convention compliance | expand

Commit Message

Stevie Alvarez Aug. 3, 2023, 10:53 p.m. UTC
From: "Stevie Alvarez (Google)" <stevie.6strings@gmail.com>

Initial header file for libtraceeval's histogram API. The interface
provides a simple way of aggregating trace data and reading through said
data.

Signed-off-by: Stevie Alvarez (Google) <stevie.6strings@gmail.com>
---
 include/traceeval-hist.h | 128 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 128 insertions(+)
 create mode 100644 include/traceeval-hist.h

Comments

Steven Rostedt Aug. 4, 2023, 1:50 p.m. UTC | #1
On Thu,  3 Aug 2023 18:53:59 -0400
Stevie Alvarez <stevie.6strings@gmail.com> wrote:

> From: "Stevie Alvarez (Google)" <stevie.6strings@gmail.com>
> 
> Initial header file for libtraceeval's histogram API. The interface
> provides a simple way of aggregating trace data and reading through said
> data.
> 
> Signed-off-by: Stevie Alvarez (Google) <stevie.6strings@gmail.com>
> ---
>  include/traceeval-hist.h | 128 +++++++++++++++++++++++++++++++++++++++
>  1 file changed, 128 insertions(+)
>  create mode 100644 include/traceeval-hist.h
> 
> diff --git a/include/traceeval-hist.h b/include/traceeval-hist.h
> new file mode 100644
> index 0000000..4664974
> --- /dev/null
> +++ b/include/traceeval-hist.h
> @@ -0,0 +1,128 @@
> +/* SPDX-License-Identifier: MIT */
> +/*
> + * libtraceeval histogram interface.
> + *
> + * Copyright (C) 2023 Google Inc, Steven Rostedt <rostedt@goodmis.org>
> + * Copyright (C) 2023 Google Inc, Stevie Alvarez <stevie.6strings@gmail.com>
> + */
> +#ifndef __LIBTRACEEVAL_HIST_H__
> +#define __LIBTRACEEVAL_HIST_H__
> +
> +#include <stdlib.h>
> +#include <stddef.h>
> +#include <stdbool.h>
> +
> +/* Data definition interfaces */
> +
> +/* Field name/descriptor for number of hits */
> +#define TRACEEVAL_VAL_HITS ((const char *)(-1UL))
> +
> +/* Data type distinguishers */
> +enum traceeval_data_type {
> +	TRACEEVAL_TYPE_NONE,
> +	TRACEEVAL_TYPE_STRING,
> +	TRACEEVAL_TYPE_NUMBER,
> +	TRACEEVAL_TYPE_NUMBER_64,
> +	TRACEEVAL_TYPE_NUMBER_32,
> +	TRACEEVAL_TYPE_NUMBER_16,
> +	TRACEEVAL_TYPE_NUMBER_8,

Hmm, I'm thinking it would be nice to rearrange this a bit to:

enum traceeval_data_type {
	TRACEEVAL_TYPE_NONE,
	TRACEEVAL_TYPE_NUMBER_8,
	TRACEEVAL_TYPE_NUMBER_16,
	TRACEEVAL_TYPE_NUMBER_32,
	TRACEEVAL_TYPE_NUMBER_64,
	TRACEEVAL_TYPE_NUMBER,
	TRACEEVAL_TYPE_STRING,

That way NUMBER_8 will be 1, NUMBER_16 is 2, NUMBER_32 is 3 and NUMBER_64
is 4. If we ever wanted to do a trick, we can get the byte size from:

  2^(1 * (enum - 1))

  2^(1 * (NUMBER_8 - 1))  = 2^(1 * 0) = 1
  2^(1 * (NUMBER_16 - 1)) = 2^(1 * 1) = 2
  2^(1 * (NUMBER_32 - 1)) = 2^(1 * 2) = 4
  2^(1 * (NUMBER_64 - 1)) = 2^(1 * 3) = 8

I love hacks!


> +	TRACEEVAL_TYPE_DYNAMIC
> +};
> +
> +/* Statistics specification flags */
> +enum traceeval_flags {
> +	TRACEEVAL_FL_SIGNED		= (1 << 0),
> +	TRACEEVAL_FL_TIMESTAMP		= (1 << 1),
> +	TRACEEVAL_FL_STATS		= (1 << 2)

We may as well remove STATS until we decided to use it.

Maybe even get rid of this enum until we decide to use it.

Remember, once it's exposed, it is API.

> +};
> +
> +/*
> + * Trace data entry for a traceeval histogram
> + * Constitutes keys and values.
> + */
> +union traceeval_data {
> +	char				*string;
> +	struct traceeval_dynamic	*dyn_data;
> +	unsigned long long		number_64;
> +	unsigned long			number;
> +	unsigned int			number_32;
> +	unsigned short			number_16;
> +	unsigned char			number_8;
> +};
> +
> +/*
> + * struct traceeval_dynamic - Storage for dynamic traceeval_types
> + * @size: The size of the dynamic type
> + * @data: The pointer to the data of the dynamic type
> + */
> +struct traceeval_dynamic {
> +	void		*data;
> +	size_t		size;
> +};
> +

As I replied to the cover letter, you need to add:

struct traceeval_type;

to get rid of the warning.

> +/* struct traceeval_dynamic release function signature */
> +typedef void (*traceeval_dyn_release_fn)(struct traceeval_dynamic *,
> +		struct traceeval_type *);
> +
> +/* struct traceeval_dynamic compare function signature */
> +typedef int (*traceeval_dyn_cmp_fn)(struct traceeval_dynamic *,
> +		struct traceeval_dynamic *, struct traceeval_type *);
> +
> +/*
> + * struct traceeval_type - Describes the type of a traceevent_data instance
> + * @type: The enum type that describes the traceeval_data
> + * @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)
> + *
> + * The traceeval_type structure defines expectations for a corresponding
> + * traceeval_data instance for a traceeval histogram instance. Used to
> + * describe both keys and values.
> + *
> + * The @id field is an optional value in case the user has multiple struct
> + * traceeval_type instances with @type fields set to TRACEEVAL_TYPE_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.
> + *
> + * @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.
> + *
> + * 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.
> + */
> +struct traceeval_type {
> +	char				*name;
> +	traceeval_dyn_release_fn	dyn_release;
> +	traceeval_dyn_cmp_fn		dyn_cmp;
> +	enum traceeval_data_type	type;
> +	size_t				flags;
> +	size_t				id;

Let's reorder this a little. Normally function pointers come at the end of
a structure. That's more of a guideline than a rule, but let's have it here.

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;
};

Especially since dynamic types are going to be rare, we don't want it in
the hot cache.

-- Steve


> +};
> +
> +/* 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;
> +};
> +
> +/* Iterator over aggregated data */
> +struct traceeval_iterator;
> +
> +struct traceeval;
> +
> +#endif /* __LIBTRACEEVAL_HIST_H__ */
Stevie Alvarez Aug. 4, 2023, 5:41 p.m. UTC | #2
On Fri, Aug 04, 2023 at 09:50:58AM -0400, Steven Rostedt wrote:
> On Thu,  3 Aug 2023 18:53:59 -0400
> Stevie Alvarez <stevie.6strings@gmail.com> wrote:
> 
> > From: "Stevie Alvarez (Google)" <stevie.6strings@gmail.com>
> > 
> > Initial header file for libtraceeval's histogram API. The interface
> > provides a simple way of aggregating trace data and reading through said
> > data.
> > 
> > Signed-off-by: Stevie Alvarez (Google) <stevie.6strings@gmail.com>
> > ---
> >  include/traceeval-hist.h | 128 +++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 128 insertions(+)
> >  create mode 100644 include/traceeval-hist.h
> > 
> > diff --git a/include/traceeval-hist.h b/include/traceeval-hist.h
> > new file mode 100644
> > index 0000000..4664974
> > --- /dev/null
> > +++ b/include/traceeval-hist.h
> > @@ -0,0 +1,128 @@
> > +/* SPDX-License-Identifier: MIT */
> > +/*
> > + * libtraceeval histogram interface.
> > + *
> > + * Copyright (C) 2023 Google Inc, Steven Rostedt <rostedt@goodmis.org>
> > + * Copyright (C) 2023 Google Inc, Stevie Alvarez <stevie.6strings@gmail.com>
> > + */
> > +#ifndef __LIBTRACEEVAL_HIST_H__
> > +#define __LIBTRACEEVAL_HIST_H__
> > +
> > +#include <stdlib.h>
> > +#include <stddef.h>
> > +#include <stdbool.h>
> > +
> > +/* Data definition interfaces */
> > +
> > +/* Field name/descriptor for number of hits */
> > +#define TRACEEVAL_VAL_HITS ((const char *)(-1UL))
> > +
> > +/* Data type distinguishers */
> > +enum traceeval_data_type {
> > +	TRACEEVAL_TYPE_NONE,
> > +	TRACEEVAL_TYPE_STRING,
> > +	TRACEEVAL_TYPE_NUMBER,
> > +	TRACEEVAL_TYPE_NUMBER_64,
> > +	TRACEEVAL_TYPE_NUMBER_32,
> > +	TRACEEVAL_TYPE_NUMBER_16,
> > +	TRACEEVAL_TYPE_NUMBER_8,
> 
> Hmm, I'm thinking it would be nice to rearrange this a bit to:
> 
> enum traceeval_data_type {
> 	TRACEEVAL_TYPE_NONE,
> 	TRACEEVAL_TYPE_NUMBER_8,
> 	TRACEEVAL_TYPE_NUMBER_16,
> 	TRACEEVAL_TYPE_NUMBER_32,
> 	TRACEEVAL_TYPE_NUMBER_64,
> 	TRACEEVAL_TYPE_NUMBER,
> 	TRACEEVAL_TYPE_STRING,
> 
> That way NUMBER_8 will be 1, NUMBER_16 is 2, NUMBER_32 is 3 and NUMBER_64
> is 4. If we ever wanted to do a trick, we can get the byte size from:
> 
>   2^(1 * (enum - 1))
> 
>   2^(1 * (NUMBER_8 - 1))  = 2^(1 * 0) = 1
>   2^(1 * (NUMBER_16 - 1)) = 2^(1 * 1) = 2
>   2^(1 * (NUMBER_32 - 1)) = 2^(1 * 2) = 4
>   2^(1 * (NUMBER_64 - 1)) = 2^(1 * 3) = 8
> 
> I love hacks!
> 
> 
> > +	TRACEEVAL_TYPE_DYNAMIC
> > +};
> > +
> > +/* Statistics specification flags */
> > +enum traceeval_flags {
> > +	TRACEEVAL_FL_SIGNED		= (1 << 0),
> > +	TRACEEVAL_FL_TIMESTAMP		= (1 << 1),
> > +	TRACEEVAL_FL_STATS		= (1 << 2)
> 
> We may as well remove STATS until we decided to use it.
> 
> Maybe even get rid of this enum until we decide to use it.
> 
> Remember, once it's exposed, it is API.
> 
> > +};
> > +
> > +/*
> > + * Trace data entry for a traceeval histogram
> > + * Constitutes keys and values.
> > + */
> > +union traceeval_data {
> > +	char				*string;
> > +	struct traceeval_dynamic	*dyn_data;
> > +	unsigned long long		number_64;
> > +	unsigned long			number;
> > +	unsigned int			number_32;
> > +	unsigned short			number_16;
> > +	unsigned char			number_8;
> > +};
> > +
> > +/*
> > + * struct traceeval_dynamic - Storage for dynamic traceeval_types
> > + * @size: The size of the dynamic type
> > + * @data: The pointer to the data of the dynamic type
> > + */
> > +struct traceeval_dynamic {
> > +	void		*data;
> > +	size_t		size;
> > +};
> > +
> 
> As I replied to the cover letter, you need to add:
> 
> struct traceeval_type;
> 
> to get rid of the warning.
> 
> > +/* struct traceeval_dynamic release function signature */
> > +typedef void (*traceeval_dyn_release_fn)(struct traceeval_dynamic *,
> > +		struct traceeval_type *);
> > +
> > +/* struct traceeval_dynamic compare function signature */
> > +typedef int (*traceeval_dyn_cmp_fn)(struct traceeval_dynamic *,
> > +		struct traceeval_dynamic *, struct traceeval_type *);
> > +
> > +/*
> > + * struct traceeval_type - Describes the type of a traceevent_data instance
> > + * @type: The enum type that describes the traceeval_data
> > + * @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)
> > + *
> > + * The traceeval_type structure defines expectations for a corresponding
> > + * traceeval_data instance for a traceeval histogram instance. Used to
> > + * describe both keys and values.
> > + *
> > + * The @id field is an optional value in case the user has multiple struct
> > + * traceeval_type instances with @type fields set to TRACEEVAL_TYPE_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.
> > + *
> > + * @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.
> > + *
> > + * 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.
> > + */
> > +struct traceeval_type {
> > +	char				*name;
> > +	traceeval_dyn_release_fn	dyn_release;
> > +	traceeval_dyn_cmp_fn		dyn_cmp;
> > +	enum traceeval_data_type	type;
> > +	size_t				flags;
> > +	size_t				id;
> 
> Let's reorder this a little. Normally function pointers come at the end of
> a structure. That's more of a guideline than a rule, but let's have it here.
> 
> 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;
> };
> 
> Especially since dynamic types are going to be rare, we don't want it in
> the hot cache.

Does the order of the fields in a struct definition not matter? I
thought word-boundaries applied to struct definitions? Or does the
compiler take care of this?

-- Stevie

> 
> -- Steve
> 
> 
> > +};
> > +
> > +/* 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;
> > +};
> > +
> > +/* Iterator over aggregated data */
> > +struct traceeval_iterator;
> > +
> > +struct traceeval;
> > +
> > +#endif /* __LIBTRACEEVAL_HIST_H__ */
>
Steven Rostedt Aug. 4, 2023, 5:57 p.m. UTC | #3
On Fri, 4 Aug 2023 13:41:59 -0400
Stevie Alvarez <stevie.6strings@gmail.com> wrote:

> > > +struct traceeval_type {
> > > +	char				*name;
> > > +	traceeval_dyn_release_fn	dyn_release;
> > > +	traceeval_dyn_cmp_fn		dyn_cmp;
> > > +	enum traceeval_data_type	type;
> > > +	size_t				flags;
> > > +	size_t				id;  
> > 
> > Let's reorder this a little. Normally function pointers come at the end of
> > a structure. That's more of a guideline than a rule, but let's have it here.
> > 
> > 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;
> > };
> > 
> > Especially since dynamic types are going to be rare, we don't want it in
> > the hot cache.  
> 
> Does the order of the fields in a struct definition not matter? I
> thought word-boundaries applied to struct definitions? Or does the
> compiler take care of this?

They do matter. Word bounders are important, but the compiler will just
make "holes" if needed. For example, let's say on 64 bit, everything above
is 64 bits but the type. I would have created a "hole". But because the
type is more important than the id, I kept it at the top.

struct traceeval_type {
	char				*name;			// offset 0
 	enum traceeval_data_type	type;			// offset 8

[ compiler adds 4 byte "hole" or "padding" ]

 	size_t				flags;			// offset 16
 	size_t				id;			// offset 24
 	traceeval_dyn_release_fn	dyn_release;		// offset 32
 	traceeval_dyn_cmp_fn		dyn_cmp;		// offset 40
};

If a cache line is 32 bytes (most is usually 128, but let's say on an older
architecture) I don't care if the the dyn_release and dyn_cmp are in the
same cache line as name.

-- Steve
Stevie Alvarez Aug. 4, 2023, 6:25 p.m. UTC | #4
On Fri, Aug 04, 2023 at 01:57:18PM -0400, Steven Rostedt wrote:
> On Fri, 4 Aug 2023 13:41:59 -0400
> Stevie Alvarez <stevie.6strings@gmail.com> wrote:
> 
> > > > +struct traceeval_type {
> > > > +	char				*name;
> > > > +	traceeval_dyn_release_fn	dyn_release;
> > > > +	traceeval_dyn_cmp_fn		dyn_cmp;
> > > > +	enum traceeval_data_type	type;
> > > > +	size_t				flags;
> > > > +	size_t				id;  
> > > 
> > > Let's reorder this a little. Normally function pointers come at the end of
> > > a structure. That's more of a guideline than a rule, but let's have it here.
> > > 
> > > 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;
> > > };
> > > 
> > > Especially since dynamic types are going to be rare, we don't want it in
> > > the hot cache.  
> > 
> > Does the order of the fields in a struct definition not matter? I
> > thought word-boundaries applied to struct definitions? Or does the
> > compiler take care of this?
> 
> They do matter. Word bounders are important, but the compiler will just
> make "holes" if needed. For example, let's say on 64 bit, everything above
> is 64 bits but the type. I would have created a "hole". But because the
> type is more important than the id, I kept it at the top.
> 
> struct traceeval_type {
> 	char				*name;			// offset 0
>  	enum traceeval_data_type	type;			// offset 8
> 
> [ compiler adds 4 byte "hole" or "padding" ]
> 
>  	size_t				flags;			// offset 16
>  	size_t				id;			// offset 24
>  	traceeval_dyn_release_fn	dyn_release;		// offset 32
>  	traceeval_dyn_cmp_fn		dyn_cmp;		// offset 40
> };
> 
> If a cache line is 32 bytes (most is usually 128, but let's say on an older
> architecture) I don't care if the the dyn_release and dyn_cmp are in the
> same cache line as name.
> 
> -- Steve

This makes sense, I appreciate the explination! I was treating size_t as
an int in my head by accident.... oops!

-- Stevie
diff mbox series

Patch

diff --git a/include/traceeval-hist.h b/include/traceeval-hist.h
new file mode 100644
index 0000000..4664974
--- /dev/null
+++ b/include/traceeval-hist.h
@@ -0,0 +1,128 @@ 
+/* SPDX-License-Identifier: MIT */
+/*
+ * libtraceeval histogram interface.
+ *
+ * Copyright (C) 2023 Google Inc, Steven Rostedt <rostedt@goodmis.org>
+ * Copyright (C) 2023 Google Inc, Stevie Alvarez <stevie.6strings@gmail.com>
+ */
+#ifndef __LIBTRACEEVAL_HIST_H__
+#define __LIBTRACEEVAL_HIST_H__
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+/* Data definition interfaces */
+
+/* Field name/descriptor for number of hits */
+#define TRACEEVAL_VAL_HITS ((const char *)(-1UL))
+
+/* Data type distinguishers */
+enum traceeval_data_type {
+	TRACEEVAL_TYPE_NONE,
+	TRACEEVAL_TYPE_STRING,
+	TRACEEVAL_TYPE_NUMBER,
+	TRACEEVAL_TYPE_NUMBER_64,
+	TRACEEVAL_TYPE_NUMBER_32,
+	TRACEEVAL_TYPE_NUMBER_16,
+	TRACEEVAL_TYPE_NUMBER_8,
+	TRACEEVAL_TYPE_DYNAMIC
+};
+
+/* Statistics specification flags */
+enum traceeval_flags {
+	TRACEEVAL_FL_SIGNED		= (1 << 0),
+	TRACEEVAL_FL_TIMESTAMP		= (1 << 1),
+	TRACEEVAL_FL_STATS		= (1 << 2)
+};
+
+/*
+ * Trace data entry for a traceeval histogram
+ * Constitutes keys and values.
+ */
+union traceeval_data {
+	char				*string;
+	struct traceeval_dynamic	*dyn_data;
+	unsigned long long		number_64;
+	unsigned long			number;
+	unsigned int			number_32;
+	unsigned short			number_16;
+	unsigned char			number_8;
+};
+
+/*
+ * struct traceeval_dynamic - Storage for dynamic traceeval_types
+ * @size: The size of the dynamic type
+ * @data: The pointer to the data of the dynamic type
+ */
+struct traceeval_dynamic {
+	void		*data;
+	size_t		size;
+};
+
+/* struct traceeval_dynamic release function signature */
+typedef void (*traceeval_dyn_release_fn)(struct traceeval_dynamic *,
+		struct traceeval_type *);
+
+/* struct traceeval_dynamic compare function signature */
+typedef int (*traceeval_dyn_cmp_fn)(struct traceeval_dynamic *,
+		struct traceeval_dynamic *, struct traceeval_type *);
+
+/*
+ * struct traceeval_type - Describes the type of a traceevent_data instance
+ * @type: The enum type that describes the traceeval_data
+ * @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)
+ *
+ * The traceeval_type structure defines expectations for a corresponding
+ * traceeval_data instance for a traceeval histogram instance. Used to
+ * describe both keys and values.
+ *
+ * The @id field is an optional value in case the user has multiple struct
+ * traceeval_type instances with @type fields set to TRACEEVAL_TYPE_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.
+ *
+ * @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.
+ *
+ * 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.
+ */
+struct traceeval_type {
+	char				*name;
+	traceeval_dyn_release_fn	dyn_release;
+	traceeval_dyn_cmp_fn		dyn_cmp;
+	enum traceeval_data_type	type;
+	size_t				flags;
+	size_t				id;
+};
+
+/* 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;
+};
+
+/* Iterator over aggregated data */
+struct traceeval_iterator;
+
+struct traceeval;
+
+#endif /* __LIBTRACEEVAL_HIST_H__ */