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