xref: /openbsd-src/usr.sbin/btrace/map.c (revision 66f34ae4e8b19fd4adc84791ec8c4653b2cef115)
1*66f34ae4Smpi /*	$OpenBSD: map.c,v 1.24 2023/09/11 19:01:26 mpi Exp $ */
223160851Smpi 
323160851Smpi /*
423160851Smpi  * Copyright (c) 2020 Martin Pieuchot <mpi@openbsd.org>
523160851Smpi  *
623160851Smpi  * Permission to use, copy, modify, and distribute this software for any
723160851Smpi  * purpose with or without fee is hereby granted, provided that the above
823160851Smpi  * copyright notice and this permission notice appear in all copies.
923160851Smpi  *
1023160851Smpi  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1123160851Smpi  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1223160851Smpi  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1323160851Smpi  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1423160851Smpi  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1523160851Smpi  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1623160851Smpi  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1723160851Smpi  */
1823160851Smpi 
1923160851Smpi /*
2023160851Smpi  * Associative array implemented with RB-Tree.
2123160851Smpi  */
2223160851Smpi 
2323160851Smpi #include <sys/queue.h>
2423160851Smpi #include <sys/tree.h>
2523160851Smpi 
2623160851Smpi #include <assert.h>
2723160851Smpi #include <err.h>
2823160851Smpi #include <limits.h>
2923160851Smpi #include <stdlib.h>
3023160851Smpi #include <stdio.h>
3123160851Smpi #include <string.h>
3223160851Smpi 
3323160851Smpi #include "bt_parser.h"
3423160851Smpi #include "btrace.h"
3523160851Smpi 
36244912e4Smpi RB_HEAD(map, mentry);
3723160851Smpi 
3823160851Smpi struct mentry {
3923160851Smpi 	RB_ENTRY(mentry)	 mlink;
4023160851Smpi 	char			 mkey[KLEN];
4123160851Smpi 	struct bt_arg		*mval;
4223160851Smpi };
4323160851Smpi 
441e8bdbf0Sclaudio int		 mcmp(const struct mentry *, const struct mentry *);
45244912e4Smpi struct mentry	*mget(struct map *, const char *);
4623160851Smpi 
47244912e4Smpi RB_GENERATE(map, mentry, mlink, mcmp);
4823160851Smpi 
4923160851Smpi int
mcmp(const struct mentry * me0,const struct mentry * me1)501e8bdbf0Sclaudio mcmp(const struct mentry *me0, const struct mentry *me1)
5123160851Smpi {
5223160851Smpi 	return strncmp(me0->mkey, me1->mkey, KLEN - 1);
5323160851Smpi }
5423160851Smpi 
5588be68dbSmpi struct mentry *
mget(struct map * map,const char * key)56244912e4Smpi mget(struct map *map, const char *key)
5788be68dbSmpi {
5888be68dbSmpi 	struct mentry me, *mep;
5988be68dbSmpi 
6088be68dbSmpi 	strlcpy(me.mkey, key, KLEN);
61244912e4Smpi 	mep = RB_FIND(map, map, &me);
6288be68dbSmpi 	if (mep == NULL) {
6388be68dbSmpi 		mep = calloc(1, sizeof(struct mentry));
6488be68dbSmpi 		if (mep == NULL)
6588be68dbSmpi 			err(1, "mentry: calloc");
6688be68dbSmpi 
6788be68dbSmpi 		strlcpy(mep->mkey, key, KLEN);
68244912e4Smpi 		RB_INSERT(map, map, mep);
6988be68dbSmpi 	}
7088be68dbSmpi 
7188be68dbSmpi 	return mep;
7288be68dbSmpi }
7388be68dbSmpi 
74*66f34ae4Smpi struct map *
map_new(void)75*66f34ae4Smpi map_new(void)
76*66f34ae4Smpi {
77*66f34ae4Smpi 	struct map *map;
78*66f34ae4Smpi 
79*66f34ae4Smpi 	map = calloc(1, sizeof(struct map));
80*66f34ae4Smpi 	if (map == NULL)
81*66f34ae4Smpi 		err(1, "map: calloc");
82*66f34ae4Smpi 
83*66f34ae4Smpi 	return map;
84*66f34ae4Smpi }
85*66f34ae4Smpi 
8623160851Smpi void
map_clear(struct map * map)87244912e4Smpi map_clear(struct map *map)
8823160851Smpi {
8923160851Smpi 	struct mentry *mep;
9023160851Smpi 
91244912e4Smpi 	while ((mep = RB_MIN(map, map)) != NULL) {
92244912e4Smpi 		RB_REMOVE(map, map, mep);
9323160851Smpi 		free(mep);
9423160851Smpi 	}
9523160851Smpi 
9623160851Smpi 	assert(RB_EMPTY(map));
9723160851Smpi 	free(map);
9823160851Smpi }
9923160851Smpi 
10023160851Smpi void
map_delete(struct map * map,const char * key)101244912e4Smpi map_delete(struct map *map, const char *key)
10223160851Smpi {
10323160851Smpi 	struct mentry me, *mep;
10423160851Smpi 
10523160851Smpi 	strlcpy(me.mkey, key, KLEN);
106244912e4Smpi 	mep = RB_FIND(map, map, &me);
10788be68dbSmpi 	if (mep != NULL) {
108244912e4Smpi 		RB_REMOVE(map, map, mep);
10923160851Smpi 		free(mep);
11023160851Smpi 	}
11188be68dbSmpi }
11223160851Smpi 
11388be68dbSmpi struct bt_arg *
map_get(struct map * map,const char * key)114244912e4Smpi map_get(struct map *map, const char *key)
11588be68dbSmpi {
11688be68dbSmpi 	struct mentry *mep;
11788be68dbSmpi 
11888be68dbSmpi 	mep = mget(map, key);
11988be68dbSmpi 	if (mep->mval == NULL)
12088be68dbSmpi 		mep->mval = ba_new(0, B_AT_LONG);
12188be68dbSmpi 
12288be68dbSmpi 	return mep->mval;
12388be68dbSmpi }
124244912e4Smpi 
125*66f34ae4Smpi void
map_insert(struct map * map,const char * key,void * cookie)126*66f34ae4Smpi map_insert(struct map *map, const char *key, void *cookie)
12723160851Smpi {
12888be68dbSmpi 	struct mentry *mep;
12923160851Smpi 
13088be68dbSmpi 	mep = mget(map, key);
1310dac42ecSmpi 	free(mep->mval);
132*66f34ae4Smpi 	mep->mval = cookie;
13323160851Smpi }
13423160851Smpi 
1351e8bdbf0Sclaudio static int
map_cmp(const void * a,const void * b)1361e8bdbf0Sclaudio map_cmp(const void *a, const void *b)
1371e8bdbf0Sclaudio {
1381e8bdbf0Sclaudio 	const struct mentry *ma = *(const struct mentry **)a;
1391e8bdbf0Sclaudio 	const struct mentry *mb = *(const struct mentry **)b;
1401e8bdbf0Sclaudio 	long rv;
1411e8bdbf0Sclaudio 
1421e8bdbf0Sclaudio 	rv = bacmp(ma->mval, mb->mval);
1431e8bdbf0Sclaudio 	if (rv != 0)
1441e8bdbf0Sclaudio 		return (rv > 0 ? -1 : 1);
1451e8bdbf0Sclaudio 	return mcmp(ma, mb);
1461e8bdbf0Sclaudio }
1471e8bdbf0Sclaudio 
14823160851Smpi /* Print at most `top' entries of the map ordered by value. */
14923160851Smpi void
map_print(struct map * map,size_t top,const char * name)1509c84395dSmpi map_print(struct map *map, size_t top, const char *name)
15123160851Smpi {
1521e8bdbf0Sclaudio 	struct mentry **elms, *mep;
1531e8bdbf0Sclaudio 	size_t i, count = 0;
15423160851Smpi 
15523160851Smpi 	if (map == NULL)
15623160851Smpi 		return;
15723160851Smpi 
1581e8bdbf0Sclaudio 	RB_FOREACH(mep, map, map)
1591e8bdbf0Sclaudio 		count++;
1601e8bdbf0Sclaudio 
1611e8bdbf0Sclaudio 	elms = calloc(count, sizeof(*elms));
1621e8bdbf0Sclaudio 	if (elms == NULL)
1631e8bdbf0Sclaudio 		err(1, NULL);
1641e8bdbf0Sclaudio 
1651e8bdbf0Sclaudio 	count = 0;
1661e8bdbf0Sclaudio 	RB_FOREACH(mep, map, map)
1671e8bdbf0Sclaudio 		elms[count++] = mep;
1681e8bdbf0Sclaudio 
1691e8bdbf0Sclaudio 	qsort(elms, count, sizeof(*elms), map_cmp);
1701e8bdbf0Sclaudio 
1711e8bdbf0Sclaudio 	for (i = 0; i < top && i < count; i++) {
1721e8bdbf0Sclaudio 		mep = elms[i];
1731e8bdbf0Sclaudio 		printf("@%s[%s]: %s\n", name, mep->mkey,
1741e8bdbf0Sclaudio 		    ba2str(mep->mval, NULL));
17523160851Smpi 	}
1761e8bdbf0Sclaudio 
1771e8bdbf0Sclaudio 	free(elms);
17823160851Smpi }
17923160851Smpi 
18023160851Smpi void
map_zero(struct map * map)181244912e4Smpi map_zero(struct map *map)
18223160851Smpi {
18323160851Smpi 	struct mentry *mep;
18423160851Smpi 
185244912e4Smpi 	RB_FOREACH(mep, map, map) {
18623160851Smpi 		mep->mval->ba_value = 0;
18723160851Smpi 		mep->mval->ba_type = B_AT_LONG;
18823160851Smpi 	}
18923160851Smpi }
1909c84395dSmpi 
1919c84395dSmpi /*
192ca7c1b32Sjasper  * Histogram implemented with map.
1939c84395dSmpi  */
1949c84395dSmpi struct hist {
1959c84395dSmpi 	struct map	hmap;
1969c84395dSmpi 	int		hstep;
1979c84395dSmpi };
1989c84395dSmpi 
1999c84395dSmpi struct hist *
hist_new(long step)200*66f34ae4Smpi hist_new(long step)
2019c84395dSmpi {
202*66f34ae4Smpi 	struct hist *hist;
2039c84395dSmpi 
2049c84395dSmpi 	hist = calloc(1, sizeof(struct hist));
2059c84395dSmpi 	if (hist == NULL)
2069c84395dSmpi 		err(1, "hist: calloc");
2079c84395dSmpi 	hist->hstep = step;
2089c84395dSmpi 
209*66f34ae4Smpi 	return hist;
210*66f34ae4Smpi }
211*66f34ae4Smpi 
212*66f34ae4Smpi void
hist_increment(struct hist * hist,const char * bucket)213*66f34ae4Smpi hist_increment(struct hist *hist, const char *bucket)
214*66f34ae4Smpi {
215*66f34ae4Smpi 	struct bt_arg *ba;
216*66f34ae4Smpi 	long val;
217*66f34ae4Smpi 
218*66f34ae4Smpi 	ba = map_get(&hist->hmap, bucket);
219*66f34ae4Smpi 
220*66f34ae4Smpi 	assert(ba->ba_type == B_AT_LONG);
221*66f34ae4Smpi 	val = (long)ba->ba_value;
222*66f34ae4Smpi 	val++;
223*66f34ae4Smpi 	ba->ba_value = (void *)val;
2249c84395dSmpi }
2259c84395dSmpi 
2269c84395dSmpi long
hist_get_bin_suffix(long bin,char ** suffix)2279c84395dSmpi hist_get_bin_suffix(long bin, char **suffix)
2289c84395dSmpi {
22961184c00Stedu #define EXA	(PETA * 1024)
23061184c00Stedu #define PETA	(TERA * 1024)
23161184c00Stedu #define TERA	(GIGA * 1024)
2329c84395dSmpi #define GIGA	(MEGA * 1024)
2339c84395dSmpi #define MEGA	(KILO * 1024)
23461184c00Stedu #define KILO	(1024LL)
2359c84395dSmpi 
2369c84395dSmpi 	*suffix = "";
23761184c00Stedu 	if (bin >= EXA) {
23861184c00Stedu 		bin /= EXA;
23961184c00Stedu 		*suffix = "E";
24061184c00Stedu 	}
24161184c00Stedu 	if (bin >= PETA) {
24261184c00Stedu 		bin /= PETA;
24361184c00Stedu 		*suffix = "P";
24461184c00Stedu 	}
24561184c00Stedu 	if (bin >= TERA) {
24661184c00Stedu 		bin /= TERA;
24761184c00Stedu 		*suffix = "T";
24861184c00Stedu 	}
2499c84395dSmpi 	if (bin >= GIGA) {
2509c84395dSmpi 		bin /= GIGA;
2519c84395dSmpi 		*suffix = "G";
2529c84395dSmpi 	}
2539c84395dSmpi 	if (bin >= MEGA) {
2549c84395dSmpi 		bin /= MEGA;
2559c84395dSmpi 		*suffix = "M";
2569c84395dSmpi 	}
2579c84395dSmpi 	if (bin >= KILO) {
2589c84395dSmpi 		bin /= KILO;
2599c84395dSmpi 		*suffix = "K";
2609c84395dSmpi 	}
2619c84395dSmpi 	return bin;
2629c84395dSmpi }
2639c84395dSmpi 
2649c84395dSmpi /*
2659c84395dSmpi  * Print bucket header where `upb' is the upper bound of the interval
2669c84395dSmpi  * and `hstep' the width of the interval.
2679c84395dSmpi  */
2689c84395dSmpi static inline int
hist_print_bucket(char * buf,size_t buflen,long upb,long hstep)2699c84395dSmpi hist_print_bucket(char *buf, size_t buflen, long upb, long hstep)
2709c84395dSmpi {
2719c84395dSmpi 	int l;
2729c84395dSmpi 
2739c84395dSmpi 	if (hstep != 0) {
2749c84395dSmpi 		/* Linear histogram */
2759c84395dSmpi 		l = snprintf(buf, buflen, "[%lu, %lu)", upb - hstep, upb);
2769c84395dSmpi 	} else {
2779c84395dSmpi 		/* Power-of-two histogram */
2789c84395dSmpi 		if (upb < 0) {
2799c84395dSmpi 			l = snprintf(buf, buflen, "(..., 0)");
28076f89280Smpi 		} else if (upb == 0) {
2819c84395dSmpi 			l = snprintf(buf, buflen, "[%lu]", upb);
2829c84395dSmpi 		} else {
2839c84395dSmpi 			long lob = upb / 2;
2849c84395dSmpi 			char *lsuf, *usuf;
2859c84395dSmpi 
2869c84395dSmpi 			upb = hist_get_bin_suffix(upb, &usuf);
2879c84395dSmpi 			lob = hist_get_bin_suffix(lob, &lsuf);
2889c84395dSmpi 
2899c84395dSmpi 			l = snprintf(buf, buflen, "[%lu%s, %lu%s)",
2909c84395dSmpi 			    lob, lsuf, upb, usuf);
2919c84395dSmpi 		}
2929c84395dSmpi 	}
2939c84395dSmpi 
2949c84395dSmpi 	if (l < 0 || (size_t)l > buflen)
2959c84395dSmpi 		warn("string too long %d > %lu", l, sizeof(buf));
2969c84395dSmpi 
2979c84395dSmpi 	return l;
2989c84395dSmpi }
2999c84395dSmpi 
3009c84395dSmpi void
hist_print(struct hist * hist,const char * name)3019c84395dSmpi hist_print(struct hist *hist, const char *name)
3029c84395dSmpi {
3039c84395dSmpi 	struct map *map = &hist->hmap;
3049c84395dSmpi 	static char buf[80];
3059c84395dSmpi 	struct mentry *mep, *mcur;
3069c84395dSmpi 	long bmin, bprev, bin, val, max = 0;
3079c84395dSmpi 	int i, l, length = 52;
3089c84395dSmpi 
3099c84395dSmpi 	if (map == NULL)
3109c84395dSmpi 		return;
3119c84395dSmpi 
3129c84395dSmpi 	bprev = 0;
3139c84395dSmpi 	RB_FOREACH(mep, map, map) {
3149c84395dSmpi 		val = ba2long(mep->mval, NULL);
3159c84395dSmpi 		if (val > max)
3169c84395dSmpi 			max = val;
3179c84395dSmpi 	}
3189c84395dSmpi 	printf("@%s:\n", name);
3199c84395dSmpi 
3209c84395dSmpi 	/*
3219c84395dSmpi 	 * Sort by ascending key.
3229c84395dSmpi 	 */
3239c84395dSmpi 	bprev = -1;
3249c84395dSmpi 	for (;;) {
3259c84395dSmpi 		mcur = NULL;
3269c84395dSmpi 		bmin = LONG_MAX;
3279c84395dSmpi 
3289c84395dSmpi 		RB_FOREACH(mep, map, map) {
3299c84395dSmpi 			bin = atol(mep->mkey);
3309c84395dSmpi 			if ((bin <= bmin) && (bin > bprev)) {
3319c84395dSmpi 				mcur = mep;
3329c84395dSmpi 				bmin = bin;
3339c84395dSmpi 			}
3349c84395dSmpi 		}
3359c84395dSmpi 		if (mcur == NULL)
3369c84395dSmpi 			break;
3379c84395dSmpi 
3389c84395dSmpi 		bin = atol(mcur->mkey);
3399c84395dSmpi 		val = ba2long(mcur->mval, NULL);
3409c84395dSmpi 		i = (length * val) / max;
3419c84395dSmpi 		l = hist_print_bucket(buf, sizeof(buf), bin, hist->hstep);
3429c84395dSmpi 		snprintf(buf + l, sizeof(buf) - l, "%*ld |%.*s%*s|",
3439c84395dSmpi 		    20 - l, val,
3449c84395dSmpi 		    i, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",
3459c84395dSmpi 		    length - i, "");
3469c84395dSmpi 		printf("%s\n", buf);
3479c84395dSmpi 
3489c84395dSmpi 		bprev = bin;
3499c84395dSmpi 	}
3509c84395dSmpi }
351