[DTrace-devel] [PATCH v5 04/11] libdtrace: Add aggregation key/value computation APIs

Kris Van Hees kris.van.hees at oracle.com
Wed Oct 7 10:51:44 UTC 2026


I am lookingaand t this but there are a few issues already in terms of
safety.  Since these are API functions, we cannot count on the caller
ensuring safety.  E.g. dtrace_aggregate_keys() would result in an out of
bounds memory write if keys has room for 2 keys, num_keys is 3, and
dtagd_nkrecs >= 4.  Perhaps the function should do its own allocation
and return the number of keys upon success instead?

I would do the same for the dtrace_aggregate_value() function.

On Fri, Aug 21, 2026 at 05:10:52PM +0100, Alan Maguire wrote:
> To provide a way for consumers to emit aggregation keys
> and compute aggregation values, provide new APIs in libdtrace:
> 
> int dtrace_aggregate_value(const dtrace_aggdata_t *aggdata,
> 			   dtrace_aggvalue_t *value);
> int dtrace_aggregate_keys(const dtrace_aggdata_t *data,
> 			  dtrace_aggkey_t *keys, size_t num_keys);
> void dtrace_aggregate_key_free(dtrace_aggkey_t *key);
> 
> These provide a way to compute aggregation values from snapshots,
> print them and free the representation used to print them.
> 
> This allows consumers like python bindings to avoid having to
> understand aggregation internals.
> 
> Signed-off-by: Alan Maguire <alan.maguire at oracle.com>
> ---
>  libdtrace/dt_aggregate.c | 517 +++++++++++++++++++++++++++++++++++++++
>  libdtrace/dtrace.h       |  83 +++++++
>  libdtrace/libdtrace.ver  |   3 +
>  3 files changed, 603 insertions(+)
> 
> diff --git a/libdtrace/dt_aggregate.c b/libdtrace/dt_aggregate.c
> index 1fc8294d..afc6a01a 100644
> --- a/libdtrace/dt_aggregate.c
> +++ b/libdtrace/dt_aggregate.c
> @@ -13,11 +13,15 @@
>  #include <assert.h>
>  #include <alloca.h>
>  #include <limits.h>
> +#include <stdio.h>
> +#include <string.h>
>  
>  #include <libproc.h>
>  #include <port.h>
>  #include <dt_aggregate.h>
>  #include <dt_bpf.h>
> +#include <math.h>
> +#include <limits.h>
>  
>  typedef struct dt_ahashent {
>  	struct dt_ahashent *dtahe_prev;		/* prev on hash chain */
> @@ -44,6 +48,519 @@ struct dt_aggregate {
>  
>  #define	DTRACE_AHASHSIZE	32779		/* big 'ol prime */
>  
> +static int
> +dt_aggregate_read_signed(const char *addr, size_t size, int64_t *value)
> +{
> +	int8_t v8;
> +	int16_t v16;
> +	int32_t v32;
> +	int64_t v64;
> +
> +	switch (size) {
> +	case sizeof(v8):
> +		memcpy(&v8, addr, sizeof(v8));
> +		*value = v8;
> +		return 0;
> +	case sizeof(v16):
> +		memcpy(&v16, addr, sizeof(v16));
> +		*value = v16;
> +		return 0;
> +	case sizeof(v32):
> +		memcpy(&v32, addr, sizeof(v32));
> +		*value = v32;
> +		return 0;
> +	case sizeof(v64):
> +		memcpy(&v64, addr, sizeof(v64));
> +		*value = v64;
> +		return 0;
> +	default:
> +		return -1;
> +	}
> +}
> +
> +static int
> +dt_aggregate_read_unsigned(const char *addr, size_t size, uint64_t *value)
> +{
> +	uint8_t v8;
> +	uint16_t v16;
> +	uint32_t v32;
> +	uint64_t v64;
> +
> +	switch (size) {
> +	case sizeof(v8):
> +		memcpy(&v8, addr, sizeof(v8));
> +		*value = v8;
> +		return 0;
> +	case sizeof(v16):
> +		memcpy(&v16, addr, sizeof(v16));
> +		*value = v16;
> +		return 0;
> +	case sizeof(v32):
> +		memcpy(&v32, addr, sizeof(v32));
> +		*value = v32;
> +		return 0;
> +	case sizeof(v64):
> +		memcpy(&v64, addr, sizeof(v64));
> +		*value = v64;
> +		return 0;
> +	default:
> +		return -1;
> +	}
> +}
> +
> +int
> +dtrace_aggregate_value(const dtrace_aggdata_t *aggdata, dtrace_aggvalue_t *value)
> +{
> +	const dtrace_aggdesc_t *agg;
> +	const dtrace_recdesc_t *rec;
> +	const char *addr;
> +	uint64_t normal;
> +
> +	if (aggdata == NULL || aggdata->dtada_desc == NULL ||
> +	    aggdata->dtada_data == NULL || value == NULL)
> +		return aggdata && aggdata->dtada_hdl ?
> +			dt_set_errno(aggdata->dtada_hdl, EINVAL) : -1;
> +
> +	agg = aggdata->dtada_desc;
> +	rec = &agg->dtagd_drecs[DT_AGGDATA_RECORD];
> +	addr = aggdata->dtada_data + rec->dtrd_offset;
> +	normal = agg->dtagd_normal ? agg->dtagd_normal : 1;
> +	value->dtagv_action = rec->dtrd_action;
> +	value->dtagv_normal = normal;
> +	value->dtagv_bucket_count = 0;
> +
> +	switch (rec->dtrd_action) {
> +	case DT_AGG_AVG: {
> +		const int64_t *data = (const int64_t *)addr;
> +
> +		if (rec->dtrd_size < sizeof(*data) * 2)
> +			return dt_set_errno(aggdata->dtada_hdl, EDT_BADAGG);
> +		value->dtagv_kind = DTRACE_AGGVALUE_FLOAT;
> +		value->dtagv_value.real = data[0] == 0 ? 0.0 :
> +			(double)((long double)data[1] / normal / data[0]);
> +		return 0;
> +	}
> +	case DT_AGG_STDDEV: {
> +		uint64_t data[4];
> +
> +		if (rec->dtrd_size < sizeof(data))
> +			return dt_set_errno(aggdata->dtada_hdl, EDT_BADAGG);
> +		memcpy(data, addr, sizeof(data));
> +		value->dtagv_kind = DTRACE_AGGVALUE_UNSIGNED;
> +		value->dtagv_value.unsigned_integer =
> +			data[0] == 0 ? 0 : dt_stddev(data, normal);
> +		return 0;
> +	}
> +	case DT_AGG_SUM:
> +	case DT_AGG_MIN:
> +	case DT_AGG_MAX:
> +		value->dtagv_kind = DTRACE_AGGVALUE_INTEGER;
> +		if (dt_aggregate_read_signed(addr, rec->dtrd_size,
> +					     &value->dtagv_value.integer) != 0)
> +			return dt_set_errno(aggdata->dtada_hdl, EDT_BADAGG);
> +		value->dtagv_value.integer /= (int64_t)normal;
> +		return 0;
> +	case DT_AGG_COUNT:
> +		value->dtagv_kind = DTRACE_AGGVALUE_UNSIGNED;
> +		if (dt_aggregate_read_unsigned(addr, rec->dtrd_size,
> +			    &value->dtagv_value.unsigned_integer) != 0)
> +			return dt_set_errno(aggdata->dtada_hdl, EDT_BADAGG);
> +		value->dtagv_value.unsigned_integer /= normal;
> +		return 0;
> +	case DT_AGG_QUANTIZE: {
> +		const int64_t *data = (const int64_t *)addr;
> +		size_t i, nbins = rec->dtrd_size / sizeof(*data);
> +
> +		if (rec->dtrd_size % sizeof(*data) != 0 ||
> +		    value->dtagv_buckets == NULL ||
> +		    value->dtagv_bucket_capacity < nbins)
> +			return dt_set_errno(aggdata->dtada_hdl, EINVAL);
> +		for (i = 0; i < nbins; i++) {
> +			if (data[i] < 0)
> +				return dt_set_errno(aggdata->dtada_hdl, EDT_BADAGG);
> +		}
> +		for (i = 0; i < nbins; i++) {
> +			if (data[i] == 0)
> +				continue;
> +			value->dtagv_buckets[value->dtagv_bucket_count].bucket =
> +				DTRACE_QUANTIZE_BUCKETVAL((uint64_t)i);
> +			value->dtagv_buckets[value->dtagv_bucket_count].count =
> +				(uint64_t)(data[i] / (int64_t)normal);
> +			if (value->dtagv_buckets[value->dtagv_bucket_count].count != 0)
> +				value->dtagv_bucket_count++;
> +		}
> +		value->dtagv_kind = DTRACE_AGGVALUE_QUANTIZED;
> +		return 0;
> +	}
> +	case DT_AGG_LQUANTIZE: {
> +		uint16_t levels = DTRACE_LQUANTIZE_LEVELS(agg->dtagd_sig);
> +		uint16_t step = DTRACE_LQUANTIZE_STEP(agg->dtagd_sig);
> +		int32_t base = DTRACE_LQUANTIZE_BASE(agg->dtagd_sig);
> +		size_t i, nbins = (size_t)levels + 2;
> +		const int64_t *data = (const int64_t *)addr;
> +
> +		if (rec->dtrd_size != nbins * sizeof(*data) ||
> +		    value->dtagv_buckets == NULL ||
> +		    value->dtagv_bucket_capacity < nbins)
> +			return dt_set_errno(aggdata->dtada_hdl, EINVAL);
> +		for (i = 0; i < nbins; i++) {
> +			if (data[i] < 0)
> +				return dt_set_errno(aggdata->dtada_hdl, EDT_BADAGG);
> +		}
> +		for (i = 0; i < nbins; i++) {
> +			int64_t count = data[i] / (int64_t)normal;
> +			int64_t bucket;
> +
> +			if (count == 0)
> +				continue;
> +			if (i == 0)
> +				bucket = base - 1;
> +			else if (i == levels + 1)
> +				bucket = base + (int64_t)levels * step;
> +			else
> +				bucket = base + (int64_t)(i - 1) * step;
> +			value->dtagv_buckets[value->dtagv_bucket_count++] =
> +				(dtrace_aggvalue_bucket_t){ bucket, (uint64_t)count };
> +		}
> +		value->dtagv_kind = DTRACE_AGGVALUE_QUANTIZED;
> +		return 0;
> +	}
> +	case DT_AGG_LLQUANTIZE: {
> +		int factor = DTRACE_LLQUANTIZE_FACTOR(agg->dtagd_sig);
> +		int lmag = DTRACE_LLQUANTIZE_LMAG(agg->dtagd_sig);
> +		int hmag = DTRACE_LLQUANTIZE_HMAG(agg->dtagd_sig);
> +		int steps = DTRACE_LLQUANTIZE_STEPS(agg->dtagd_sig);
> +		int steps_factor, bin0;
> +		size_t i, nbins;
> +		const int64_t *data = (const int64_t *)addr;
> +		long double limit = (long double)INT64_MAX;
> +
> +		if (factor < 2 || steps < factor || lmag > hmag)
> +			return dt_set_errno(aggdata->dtada_hdl, EDT_BADAGG);
> +		steps_factor = steps / factor;
> +		bin0 = 1 + (hmag - lmag + 1) * (steps - steps_factor);
> +		nbins = (size_t)(hmag - lmag + 1) *
> +			(steps - steps_factor) * 2 + 3;
> +		if (!isfinite((double)powl(factor, hmag + 1)) ||
> +		    powl(factor, hmag + 1) > limit)
> +			return dt_set_errno(aggdata->dtada_hdl, EDT_BADAGG);
> +
> +		if (rec->dtrd_size != nbins * sizeof(*data) ||
> +		    value->dtagv_buckets == NULL ||
> +		    value->dtagv_bucket_capacity < nbins)
> +			return dt_set_errno(aggdata->dtada_hdl, EINVAL);
> +		for (i = 0; i < nbins; i++) {
> +			if (data[i] < 0)
> +				return dt_set_errno(aggdata->dtada_hdl, EDT_BADAGG);
> +		}
> +		for (i = 0; i < nbins; i++) {
> +			int64_t count = data[i] / (int64_t)normal;
> +			int64_t bucket;
> +			int left, negative, remaining, magnitude;
> +
> +			if (count == 0)
> +				continue;
> +			if (i == 0)
> +				bucket = -(int64_t)powl(factor, hmag + 1);
> +			else if (i == nbins - 1)
> +				bucket = (int64_t)powl(factor, hmag + 1);
> +			else if (i == (size_t)bin0)
> +				bucket = 0;
> +			else {
> +				left = (int)i - bin0;
> +				negative = left < 0;
> +				remaining = negative ? bin0 - (int)i : left;
> +				magnitude = lmag;
> +				if (lmag == 0 && steps > factor) {
> +					for (int n = 2; n <= factor && remaining > 0; n++) {
> +						remaining -= steps_factor;
> +						if (remaining == 0) {
> +							bucket = negative ? -n : n;
> +							goto ll_have_bucket;
> +						}
> +					}
> +					magnitude++;
> +				}
> +				bucket = negative ? -1 : 1;
> +				while (magnitude <= hmag) {
> +					int64_t scale = (int64_t)(powl(factor, magnitude + 1) /
> +						(double)steps);
> +					int n;
> +
> +					for (n = steps_factor + 1; n <= steps; n++) {
> +						if (--remaining == 0) {
> +							bucket = n * scale * (negative ? -1 : 1);
> +							goto ll_have_bucket;
> +						}
> +					}
> +					magnitude++;
> +				}
> +				bucket *= (int64_t)powl(factor, hmag + 1);
> +			}
> +ll_have_bucket:
> +			value->dtagv_buckets[value->dtagv_bucket_count++] =
> +				(dtrace_aggvalue_bucket_t){ bucket, (uint64_t)count };
> +		}
> +		value->dtagv_kind = DTRACE_AGGVALUE_QUANTIZED;
> +		return 0;
> +	}
> +	default:
> +		value->dtagv_kind = DTRACE_AGGVALUE_RAW;
> +		value->dtagv_value.raw.data = addr;
> +		value->dtagv_value.raw.size = rec->dtrd_size;
> +		return 0;
> +	}
> +}
> +
> +void dtrace_aggregate_key_free(dtrace_aggkey_t *key)
> +{
> +	size_t i;
> +
> +	if (!key)
> +		return;
> +	switch (key->dtagk_kind) {
> +	case DTRACE_AGGKEY_BYTES:
> +		free(key->dtagk_value.bytes.data);
> +		break;
> +	case DTRACE_AGGKEY_STRING:
> +	case DTRACE_AGGKEY_SYMBOL:
> +	case DTRACE_AGGKEY_MODULE:
> +		free(key->dtagk_value.name);
> +		break;
> +	case DTRACE_AGGKEY_ADDRESS:
> +		break;
> +	case DTRACE_AGGKEY_STACK:
> +		for (i = 0; i < key->dtagk_value.stack.depth; i++)
> +			free(key->dtagk_value.stack.frames[i]);
> +		free(key->dtagk_value.stack.frames);
> +		break;
> +	default:
> +		break;
> +	}
> +	memset(key, 0, sizeof(*key));
> +}
> +
> +int dtrace_aggregate_keys(const dtrace_aggdata_t *data,
> +			  dtrace_aggkey_t *keys, size_t num_keys)
> +{
> +	int error = EDT_NOMEM;
> +	dtrace_hdl_t *dtp;
> +	size_t i, n;
> +
> +	if (!data || !data->dtada_desc)
> +		return data && data->dtada_hdl ?
> +			dt_set_errno(data->dtada_hdl, EINVAL) : -1;
> +	dtp = data->dtada_hdl;
> +	if (data->dtada_desc->dtagd_nkrecs == 0)
> +		return 0;
> +	n = data->dtada_desc->dtagd_nkrecs - 1;
> +	if (n == 0)
> +		return 0;
> +	if (!data->dtada_key)
> +		return dt_set_errno(dtp, EDT_BADAGG);
> +	if (!keys || num_keys < n)
> +		return dt_set_errno(dtp, EINVAL);
> +	memset(keys, 0, n * sizeof(*keys));
> +
> +	for (i = 0; i < n; i++) {
> +		const dtrace_recdesc_t *rec = &data->dtada_desc->dtagd_krecs[i + 1];
> +		char *addr = data->dtada_key + rec->dtrd_offset;
> +		if ((size_t)rec->dtrd_offset > data->dtada_desc->dtagd_ksize ||
> +		    rec->dtrd_size > data->dtada_desc->dtagd_ksize -
> +		    (size_t)rec->dtrd_offset)
> +			goto badagg;
> +
> +		switch (rec->dtrd_action) {
> +		case DTRACEACT_STACK:
> +		case DTRACEACT_USTACK:
> +		case DTRACEACT_JSTACK: {
> +			uint32_t depth, max, frames, j, tgid = 0;
> +
> +			if (rec->dtrd_size < 2 * sizeof(uint64_t) ||
> +			    (rec->dtrd_action != DTRACEACT_STACK &&
> +			     rec->dtrd_size < 4 * sizeof(uint64_t)))
> +				goto badagg;
> +			memcpy(&depth, addr, sizeof(depth));
> +			max = rec->dtrd_size >= 2 * sizeof(uint64_t) ?
> +				rec->dtrd_size / sizeof(uint64_t) - 2 : 0;
> +			frames = depth < max ? depth : max;
> +			if (rec->dtrd_action != DTRACEACT_STACK) {
> +				memcpy(&tgid, addr + 3 * sizeof(uint32_t),
> +				       sizeof(tgid));
> +			}
> +
> +			keys[i].dtagk_kind = DTRACE_AGGKEY_STACK;
> +			keys[i].dtagk_value.stack.frames = frames == 0 ? NULL :
> +				calloc(frames, sizeof(char *));
> +			keys[i].dtagk_value.stack.depth = 0;
> +			if (frames != 0 && !keys[i].dtagk_value.stack.frames)
> +				goto error;
> +			for (j = 0; j < frames; j++) {
> +				char text[PATH_MAX * 2];
> +				uint64_t pc;
> +				int rc;
> +
> +				memcpy(&pc, addr + (j + 2) * sizeof(uint64_t),
> +				       sizeof(pc));
> +				if (pc == 0)
> +					break;
> +				rc = rec->dtrd_action == DTRACEACT_STACK ?
> +					dtrace_addr2str(dtp, pc, text,
> +							sizeof(text)) :
> +					dtrace_uaddr2str(dtp, tgid, pc,
> +							 text, sizeof(text));
> +				if (rc < 0) {
> +					error = EDT_BADAGG;
> +					goto error;
> +				}
> +				if (!(keys[i].dtagk_value.stack.frames[j] = strdup(text)))
> +					goto error;
> +				keys[i].dtagk_value.stack.depth++;
> +			}
> +			break;
> +		}
> +		case DTRACEACT_SYM:
> +		case DTRACEACT_MOD: {
> +			dtrace_syminfo_t info;
> +			char text[PATH_MAX * 2];
> +			uint64_t pc;
> +
> +			memcpy(&pc, addr, sizeof(pc));
> +
> +			if (dtrace_lookup_by_addr(dtp, pc,
> +						  NULL, &info) == 0) {
> +				snprintf(text, sizeof(text), "%s%s%s",
> +					 info.object,
> +					 rec->dtrd_action == DTRACEACT_SYM ? "`" : "",
> +					 rec->dtrd_action == DTRACEACT_SYM ? info.name : "");
> +			} else {
> +				snprintf(text, sizeof(text), "0x%llx",
> +					(unsigned long long)pc);
> +			}
> +			keys[i].dtagk_kind = rec->dtrd_action == DTRACEACT_SYM ?
> +				DTRACE_AGGKEY_SYMBOL : DTRACE_AGGKEY_MODULE;
> +			keys[i].dtagk_value.name = strdup(text);
> +			if (!keys[i].dtagk_value.name)
> +				goto error;
> +			break;
> +		}
> +		case DTRACEACT_UADDR:
> +		case DTRACEACT_USYM:
> +		case DTRACEACT_UMOD: {
> +			char text[PATH_MAX * 2];
> +			uint32_t tgid;
> +			uint64_t pc;
> +
> +			/*
> +			 * The target PID occupies the first word and the address
> +			 * the second; the record is aligned to 64 bits.
> +			 */
> +			if (rec->dtrd_size < 2 * sizeof(uint64_t))
> +				goto badagg;
> +			memcpy(&tgid, addr, sizeof(tgid));
> +			memcpy(&pc, addr + sizeof(uint64_t), sizeof(pc));
> +			if (rec->dtrd_action == DTRACEACT_UADDR) {
> +				keys[i].dtagk_kind = DTRACE_AGGKEY_ADDRESS;
> +				keys[i].dtagk_value.user_address.pid = tgid;
> +				keys[i].dtagk_value.user_address.address = pc;
> +				break;
> +			}
> +
> +			if (dtrace_uaddr2str(dtp, tgid, pc, text,
> +				     sizeof(text)) < 0)
> +				goto badagg;
> +			if (rec->dtrd_action == DTRACEACT_UMOD) {
> +				char *tick = strchr(text, '`');
> +				if (tick)
> +					*tick = '\0';
> +			}
> +			keys[i].dtagk_kind = rec->dtrd_action == DTRACEACT_USYM ?
> +				 DTRACE_AGGKEY_SYMBOL : DTRACE_AGGKEY_MODULE;
> +			keys[i].dtagk_value.name = strdup(text);
> +			if (!keys[i].dtagk_value.name)
> +				goto error;
> +			break;
> +		}
> +		default:
> +			keys[i].dtagk_kind = DTRACE_AGGKEY_INTEGER;
> +			switch (rec->dtrd_size) {
> +			case 1: {
> +				int8_t v;
> +
> +				memcpy(&v, addr, 1);
> +				keys[i].dtagk_value.integer = v;
> +				break;
> +			}
> +			case 2: {
> +				int16_t v;
> +
> +				memcpy(&v, addr, 2);
> +				keys[i].dtagk_value.integer = v;
> +				break;
> +			}
> +			case 4: {
> +				int32_t v;
> +
> +				memcpy(&v, addr, 4);
> +				keys[i].dtagk_value.integer = v;
> +				break;
> +			}
> +			case 8: {
> +				int64_t v;
> +
> +				memcpy(&v, addr, 8);
> +				keys[i].dtagk_value.integer = v;
> +				break;
> +			}
> +			default: {
> +				char *nul = memchr(addr, '\0', rec->dtrd_size);
> +
> +				/*
> +				 * This is a heuristic: key descriptors do not
> +				 * retain type information.  DTrace strings are
> +				 * NUL-terminated and padded with zeros to their
> +				 * fixed record size.
> +				 */
> +				if (nul != NULL) {
> +					char *p;
> +
> +					for (p = nul + 1; p < addr + rec->dtrd_size; p++) {
> +						if (*p != '\0')
> +							break;
> +					}
> +					if (p == addr + rec->dtrd_size) {
> +						keys[i].dtagk_kind = DTRACE_AGGKEY_STRING;
> +						keys[i].dtagk_value.name = strndup(addr,
> +									   rec->dtrd_size);
> +						if (!keys[i].dtagk_value.name)
> +							goto error;
> +						break;
> +					}
> +				}
> +				keys[i].dtagk_kind = DTRACE_AGGKEY_BYTES;
> +				keys[i].dtagk_value.bytes.data = malloc(rec->dtrd_size);
> +				if (!keys[i].dtagk_value.bytes.data)
> +					goto error;
> +				memcpy(keys[i].dtagk_value.bytes.data, addr,
> +				       rec->dtrd_size);
> +				keys[i].dtagk_value.bytes.size = rec->dtrd_size;
> +				break;
> +			}
> +			}
> +			break;
> +		}
> +	}
> +	return (int)n;
> +error:
> +	for (i = 0; i < n; i++)
> +		dtrace_aggregate_key_free(&keys[i]);
> +	return dt_set_errno(dtp, error);
> +badagg:
> +	for (i = 0; i < n; i++)
> +		dtrace_aggregate_key_free(&keys[i]);
> +	return dt_set_errno(dtp, EDT_BADAGG);
> +}
> +
>  /*
>   * Because qsort(3C) does not allow an argument to be passed to a comparison
>   * function, the variables that affect comparison must regrettably be global;
> diff --git a/libdtrace/dtrace.h b/libdtrace/dtrace.h
> index 82965fbd..a108080f 100644
> --- a/libdtrace/dtrace.h
> +++ b/libdtrace/dtrace.h
> @@ -379,6 +379,89 @@ struct dtrace_aggdata {
>  	caddr_t *dtada_percpu;			/* per CPU data, if avail */
>  };
>  
> +typedef enum dtrace_aggvalue_kind {
> +	DTRACE_AGGVALUE_INTEGER,
> +	DTRACE_AGGVALUE_UNSIGNED,
> +	DTRACE_AGGVALUE_FLOAT,
> +	DTRACE_AGGVALUE_RAW,
> +	DTRACE_AGGVALUE_QUANTIZED
> +} dtrace_aggvalue_kind_t;
> +
> +typedef struct dtrace_aggvalue_bucket {
> +	int64_t bucket;
> +	uint64_t count;
> +} dtrace_aggvalue_bucket_t;
> +
> +/* Quantized buckets are supplied by the caller through dtagv_buckets. */
> +typedef struct dtrace_aggvalue {
> +	dtrace_aggvalue_kind_t dtagv_kind;
> +	uint16_t dtagv_action;
> +	uint64_t dtagv_normal;
> +	dtrace_aggvalue_bucket_t *dtagv_buckets;
> +	size_t dtagv_bucket_capacity;
> +	size_t dtagv_bucket_count;
> +	union {
> +		int64_t integer;
> +		uint64_t unsigned_integer;
> +		double real;
> +		struct {
> +			const void *data;
> +			size_t size;
> +		} raw;
> +	} dtagv_value;
> +} dtrace_aggvalue_t;
> +
> +typedef enum dtrace_aggkey_kind {
> +	DTRACE_AGGKEY_INTEGER,
> +	DTRACE_AGGKEY_STRING,
> +	DTRACE_AGGKEY_BYTES,
> +	DTRACE_AGGKEY_STACK,
> +	DTRACE_AGGKEY_SYMBOL,
> +	DTRACE_AGGKEY_MODULE,
> +	DTRACE_AGGKEY_USER_ADDRESS,
> +	DTRACE_AGGKEY_ADDRESS = DTRACE_AGGKEY_USER_ADDRESS
> +} dtrace_aggkey_kind_t;
> +
> +typedef struct dtrace_aggkey {
> +	dtrace_aggkey_kind_t dtagk_kind;
> +	union {
> +		int64_t integer;
> +		struct {
> +			char *data;
> +			size_t size;
> +		} bytes;
> +		struct {
> +			char **frames;
> +			size_t depth;
> +		} stack;
> +		char *name;
> +		/* User addresses include the target PID to disambiguate ASLR. */
> +		struct {
> +			uint32_t pid;
> +			uint64_t address;
> +		} user_address;
> +	} dtagk_value;
> +} dtrace_aggkey_t;
> +
> +/*
> + * Decode all keys in an aggregation snapshot.  The caller supplies an array
> + * with capacity for dtagd_nkrecs - 1 descriptors.  Returned strings, byte
> + * buffers, and stack frames are owned by the caller and must be released
> + * with dtrace_aggregate_key_free().  Zero-key aggregations return zero.
> + */
> +extern int dtrace_aggregate_keys(const dtrace_aggdata_t *, dtrace_aggkey_t *,
> +				 size_t);
> +extern void dtrace_aggregate_key_free(dtrace_aggkey_t *);
> +
> +/*
> + * Decode one aggregation value.  Integer values can be signed or unsigned.
> + * Raw data points into the snapshot and is valid only while that snapshot
> + * remains valid.  Quantized values require a caller-provided bucket array and
> + * capacity.
> + */
> +extern int dtrace_aggregate_value(const dtrace_aggdata_t *,
> +				  dtrace_aggvalue_t *);
> +
>  typedef struct dtrace_print_aggdata {
>  	dtrace_hdl_t *dtpa_dtp;			/* handle to DTrace library */
>  	dtrace_aggid_t dtpa_id;			/* aggregation variable */
> diff --git a/libdtrace/libdtrace.ver b/libdtrace/libdtrace.ver
> index edbd5a1d..87d561b2 100644
> --- a/libdtrace/libdtrace.ver
> +++ b/libdtrace/libdtrace.ver
> @@ -8,8 +8,11 @@ LIBDTRACE_1.0 {
>      global:
>  	dtrace_addr2str;
>  	dtrace_aggregate_clear;
> +	dtrace_aggregate_keys;
> +	dtrace_aggregate_key_free;
>  	dtrace_aggregate_print;
>  	dtrace_aggregate_snap;
> +	dtrace_aggregate_value;
>  	dtrace_aggregate_walk;
>  	dtrace_aggregate_walk_joined;
>  	dtrace_aggregate_walk_keyrevsorted;
> -- 
> 2.43.5
> 



More information about the DTrace-devel mailing list