From 66f34ae4e8b19fd4adc84791ec8c4653b2cef115 Mon Sep 17 00:00:00 2001 From: mpi Date: Mon, 11 Sep 2023 19:01:26 +0000 Subject: [PATCH] Implement tuples, needed to export per-CPU scheduling data. It is now possible to save and print immutable arrays as below: ..$t = (1, 42, "something"); ..printf("%d %s\n", $t.1, $t.2); Also add support for evaluating builtin in order to save them in variables (required by tuples) --- usr.sbin/btrace/bt_parse.y | 23 ++++- usr.sbin/btrace/bt_parser.h | 7 +- usr.sbin/btrace/btrace.c | 174 +++++++++++++++++++++++++++++++----- usr.sbin/btrace/btrace.h | 9 +- usr.sbin/btrace/map.c | 123 ++++++++----------------- 5 files changed, 220 insertions(+), 116 deletions(-) diff --git a/usr.sbin/btrace/bt_parse.y b/usr.sbin/btrace/bt_parse.y index b2c3e65854b..0a8d9c62fb3 100644 --- a/usr.sbin/btrace/bt_parse.y +++ b/usr.sbin/btrace/bt_parse.y @@ -1,7 +1,7 @@ -/* $OpenBSD: bt_parse.y,v 1.52 2023/09/02 19:28:46 dv Exp $ */ +/* $OpenBSD: bt_parse.y,v 1.53 2023/09/11 19:01:26 mpi Exp $ */ /* - * Copyright (c) 2019-2021 Martin Pieuchot + * Copyright (c) 2019-2023 Martin Pieuchot * Copyright (c) 2019 Tobias Heider * Copyright (c) 2015 Ted Unangst * @@ -72,6 +72,8 @@ struct bt_stmt *bg_store(const char *, struct bt_arg *); struct bt_arg *bg_find(const char *); struct bt_var *bg_get(const char *); +struct bt_arg *bi_find(struct bt_arg *, unsigned long); + struct bt_var *bl_lookup(const char *); struct bt_stmt *bl_store(const char *, struct bt_arg *); struct bt_arg *bl_find(const char *); @@ -210,9 +212,11 @@ fterm : fterm '*' factor { $$ = ba_op(B_AT_OP_MULT, $1, $3); } variable: lvar { $$ = bl_find($1); } | gvar { $$ = bg_find($1); } + | variable '.' NUMBER { $$ = bi_find($1, $3); } ; factor : '(' expr ')' { $$ = $2; } + | '(' vargs ',' expr ')'{ $$ = ba_new(ba_append($2, $4), B_AT_TUPLE); } | NUMBER { $$ = ba_new($1, B_AT_LONG); } | BUILTIN { $$ = ba_new(NULL, $1); } | CSTRING { $$ = ba_new($1, B_AT_STR); } @@ -419,7 +423,7 @@ ba_new0(void *val, enum bt_argtype type) /* * Link two arguments together, to build an argument list used in - * function calls. + * operators, tuples and function calls. */ struct bt_arg * ba_append(struct bt_arg *da0, struct bt_arg *da1) @@ -592,6 +596,17 @@ bl_store(const char *vname, struct bt_arg *vval) return bs_new(B_AC_STORE, vval, bv); } +/* Create an argument that points to a tuple variable and a given index */ +struct bt_arg * +bi_find(struct bt_arg *ba, unsigned long index) +{ + struct bt_var *bv = ba->ba_value; + + ba = ba_new(bv, B_AT_TMEMBER); + ba->ba_key = (void *)index; + return ba; +} + struct bt_stmt * bm_op(enum bt_action mact, struct bt_arg *ba, struct bt_arg *mval) { @@ -610,7 +625,7 @@ bm_insert(const char *mname, struct bt_arg *mkey, struct bt_arg *mval) return bs_new(B_AC_INSERT, ba, (struct bt_var *)mval); } -/* Create an argument that points to a variable and attach a key to it. */ +/* Create an argument that points to a map variable and attach a key to it. */ struct bt_arg * bm_find(const char *vname, struct bt_arg *mkey) { diff --git a/usr.sbin/btrace/bt_parser.h b/usr.sbin/btrace/bt_parser.h index 6fe2eb43235..9cbcace6f48 100644 --- a/usr.sbin/btrace/bt_parser.h +++ b/usr.sbin/btrace/bt_parser.h @@ -1,4 +1,4 @@ -/* $OpenBSD: bt_parser.h,v 1.23 2022/12/28 21:30:16 jmc Exp $ */ +/* $OpenBSD: bt_parser.h,v 1.24 2023/09/11 19:01:26 mpi Exp $ */ /* * Copyright (c) 2019-2021 Martin Pieuchot @@ -94,7 +94,7 @@ struct bt_rule { }; /* - * Global variable representation. + * Global and local variable representation. * * Variables are untyped and also include maps and histograms. */ @@ -105,6 +105,7 @@ struct bt_var { enum bt_vartype { B_VT_STR = 1, B_VT_LONG, + B_VT_TUPLE, B_VT_MAP, B_VT_HIST, } bv_type; @@ -126,6 +127,8 @@ struct bt_arg { B_AT_VAR, /* global/local variable */ B_AT_MAP, /* global map (@map[]) */ B_AT_HIST, /* histogram */ + B_AT_TUPLE, /* tuple (1, 42, "str") */ + B_AT_TMEMBER, /* tuple member $t.2 */ B_AT_NIL, /* empty value */ B_AT_BI_PID, diff --git a/usr.sbin/btrace/btrace.c b/usr.sbin/btrace/btrace.c index f57091107e6..6ab03222eb7 100644 --- a/usr.sbin/btrace/btrace.c +++ b/usr.sbin/btrace/btrace.c @@ -1,7 +1,7 @@ -/* $OpenBSD: btrace.c,v 1.75 2023/09/02 15:16:12 dv Exp $ */ +/* $OpenBSD: btrace.c,v 1.76 2023/09/11 19:01:26 mpi Exp $ */ /* - * Copyright (c) 2019 - 2021 Martin Pieuchot + * Copyright (c) 2019 - 2023 Martin Pieuchot * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -95,6 +95,7 @@ bool stmt_test(struct bt_stmt *, struct dt_evt *); void stmt_time(struct bt_stmt *, struct dt_evt *); void stmt_zero(struct bt_stmt *); struct bt_arg *ba_read(struct bt_arg *); +struct bt_arg *baeval(struct bt_arg *, struct dt_evt *); const char *ba2hash(struct bt_arg *, struct dt_evt *); long baexpr2long(struct bt_arg *, struct dt_evt *); const char *ba2bucket(struct bt_arg *, struct bt_arg *, @@ -854,6 +855,7 @@ stmt_bucketize(struct bt_stmt *bs, struct dt_evt *dtev) struct bt_arg *brange, *bhist = SLIST_FIRST(&bs->bs_args); struct bt_arg *bval = (struct bt_arg *)bs->bs_var; struct bt_var *bv = bhist->ba_value; + struct hist *hist; const char *bucket; long step = 0; @@ -870,8 +872,17 @@ stmt_bucketize(struct bt_stmt *bs, struct dt_evt *dtev) debug("hist=%p '%s' increment bucket '%s'\n", bv->bv_value, bv_name(bv), bucket); - bv->bv_value = (struct bt_arg *) - hist_increment((struct hist *)bv->bv_value, bucket, step); + /* hist is NULL before first insert or after clear() */ + hist = (struct hist *)bv->bv_value; + if (hist == NULL) + hist = hist_new(step); + + hist_increment(hist, bucket); + + debug("hist=%p '%s' increment bucket=%p '%s' bval=%p\n", hist, + bv_name(bv), brange, bucket, bval); + + bv->bv_value = (struct bt_arg *)hist; bv->bv_type = B_VT_HIST; } @@ -943,6 +954,7 @@ stmt_insert(struct bt_stmt *bs, struct dt_evt *dtev) struct bt_var *bv = bmap->ba_value; struct map *map; const char *hash; + long val; assert(bmap->ba_type == B_AT_MAP); assert(SLIST_NEXT(bval, ba_next) == NULL); @@ -952,7 +964,36 @@ stmt_insert(struct bt_stmt *bs, struct dt_evt *dtev) /* map is NULL before first insert or after clear() */ map = (struct map *)bv->bv_value; - map = map_insert(map, hash, bval, dtev); + if (map == NULL) + map = map_new(); + + /* Operate on existring value for count(), max(), min() and sum(). */ + switch (bval->ba_type) { + case B_AT_MF_COUNT: + val = ba2long(map_get(map, hash), NULL); + val++; + bval = ba_new(val, B_AT_LONG); + break; + case B_AT_MF_MAX: + val = ba2long(map_get(map, hash), NULL); + val = MAXIMUM(val, ba2long(bval->ba_value, dtev)); + bval = ba_new(val, B_AT_LONG); + break; + case B_AT_MF_MIN: + val = ba2long(map_get(map, hash), NULL); + val = MINIMUM(val, ba2long(bval->ba_value, dtev)); + bval = ba_new(val, B_AT_LONG); + break; + case B_AT_MF_SUM: + val = ba2long(map_get(map, hash), NULL); + val += ba2long(bval->ba_value, dtev); + bval = ba_new(val, B_AT_LONG); + break; + default: + break; + } + + map_insert(map, hash, bval); debug("map=%p '%s' insert key=%p '%s' bval=%p\n", map, bv_name(bv), bkey, hash, bval); @@ -1018,7 +1059,7 @@ void stmt_store(struct bt_stmt *bs, struct dt_evt *dtev) { struct bt_arg *ba = SLIST_FIRST(&bs->bs_args); - struct bt_var *bv = bs->bs_var; + struct bt_var *bvar, *bv = bs->bs_var; assert(SLIST_NEXT(ba, ba_next) == NULL); @@ -1031,33 +1072,33 @@ stmt_store(struct bt_stmt *bs, struct dt_evt *dtev) bv->bv_value = ba; bv->bv_type = B_VT_LONG; break; - case B_AT_BI_PID: - bv->bv_value = ba_new((long)dtev->dtev_pid, B_AT_LONG); - bv->bv_type = B_VT_LONG; + case B_AT_VAR: + bvar = ba->ba_value; + bv->bv_type = bvar->bv_type; + bv->bv_value = bvar->bv_value; break; - case B_AT_BI_TID: - bv->bv_value = ba_new((long)dtev->dtev_tid, B_AT_LONG); - bv->bv_type = B_VT_LONG; + case B_AT_TUPLE: + bv->bv_value = baeval(ba, dtev); + bv->bv_type = B_VT_TUPLE; break; + case B_AT_BI_PID: + case B_AT_BI_TID: case B_AT_BI_NSECS: - bv->bv_value = ba_new(builtin_nsecs(dtev), B_AT_LONG); - bv->bv_type = B_VT_LONG; - break; case B_AT_BI_ARG0 ... B_AT_BI_ARG9: - /* FALLTHROUGH */ case B_AT_OP_PLUS ... B_AT_OP_LOR: - bv->bv_value = ba_new(ba2long(ba, dtev), B_AT_LONG); + bv->bv_value = baeval(ba, dtev); bv->bv_type = B_VT_LONG; break; case B_AT_FN_STR: - bv->bv_value = ba_new(ba2str(ba, dtev), B_AT_STR); + bv->bv_value = baeval(ba, dtev); bv->bv_type = B_VT_STR; break; default: xabort("store not implemented for type %d", ba->ba_type); } - debug("bv=%p var '%s' store (%p)\n", bv, bv_name(bv), bv->bv_value); + debug("bv=%p var '%s' store (%p)='%s'\n", bv, bv_name(bv), bv->bv_value, + ba2str(bv->bv_value, dtev)); } /* @@ -1160,7 +1201,6 @@ ba_read(struct bt_arg *ba) struct bt_var *bv = ba->ba_value; assert(ba->ba_type == B_AT_VAR); - debug("bv=%p read '%s' (%p)\n", bv, bv_name(bv), bv->bv_value); /* Handle map/hist access after clear(). */ @@ -1170,6 +1210,57 @@ ba_read(struct bt_arg *ba) return bv->bv_value; } +// XXX +extern struct bt_arg *ba_append(struct bt_arg *, struct bt_arg *); + +/* + * Return a new argument that doesn't depend on `dtev'. This is used + * when storing values in variables, maps, etc. + */ +struct bt_arg * +baeval(struct bt_arg *bval, struct dt_evt *dtev) +{ + struct bt_arg *ba, *bh = NULL; + + switch (bval->ba_type) { + case B_AT_VAR: + ba = baeval(ba_read(bval), NULL); + break; + case B_AT_LONG: + case B_AT_BI_PID: + case B_AT_BI_TID: + case B_AT_BI_CPU: + case B_AT_BI_NSECS: + case B_AT_BI_ARG0 ... B_AT_BI_ARG9: + case B_AT_BI_RETVAL: + case B_AT_OP_PLUS ... B_AT_OP_LOR: + ba = ba_new(ba2long(bval, dtev), B_AT_LONG); + break; + case B_AT_STR: + case B_AT_BI_COMM: + case B_AT_BI_KSTACK: + case B_AT_BI_USTACK: + case B_AT_BI_PROBE: + case B_AT_FN_STR: + ba = ba_new(ba2str(bval, dtev), B_AT_STR); + break; + case B_AT_TUPLE: + ba = bval->ba_value; + do { + bh = ba_append(bh, baeval(ba, dtev)); + } while ((ba = SLIST_NEXT(ba, ba_next)) != NULL); + ba = ba_new(bh, B_AT_TUPLE); + break; + default: + xabort("no eval support for type %d", bval->ba_type); + } + + return ba; +} + +/* + * Return a string of coma-separated values + */ const char * ba2hash(struct bt_arg *ba, struct dt_evt *dtev) { @@ -1556,6 +1647,7 @@ ba2str(struct bt_arg *ba, struct dt_evt *dtev) static char buf[STRLEN]; struct bt_var *bv; struct dtioc_probe_info *dtpi; + unsigned long idx; const char *str; buf[0] = '\0'; @@ -1567,6 +1659,26 @@ ba2str(struct bt_arg *ba, struct dt_evt *dtev) snprintf(buf, sizeof(buf), "%ld",(long)ba->ba_value); str = buf; break; + case B_AT_TUPLE: + snprintf(buf, sizeof(buf), "(%s)", ba2hash(ba->ba_value, dtev)); + str = buf; + break; + case B_AT_TMEMBER: + idx = (unsigned long)ba->ba_key; + bv = ba->ba_value; + /* Uninitialized tuple */ + if (bv->bv_value == NULL) { + str = buf; + break; + } + ba = bv->bv_value; + assert(ba->ba_type == B_AT_TUPLE); + ba = ba->ba_value; + while (ba != NULL && idx-- > 0) { + ba = SLIST_NEXT(ba, ba_next); + } + str = ba2str(ba, dtev); + break; case B_AT_NIL: str = ""; break; @@ -1674,6 +1786,8 @@ ba2dtflags(struct bt_arg *ba) switch (bval->ba_type) { case B_AT_STR: case B_AT_LONG: + case B_AT_TUPLE: + case B_AT_TMEMBER: case B_AT_VAR: case B_AT_HIST: case B_AT_NIL: @@ -1720,6 +1834,8 @@ ba2dtflags(struct bt_arg *ba) long bacmp(struct bt_arg *a, struct bt_arg *b) { + long val; + if (a->ba_type != b->ba_type) return a->ba_type - b->ba_type; @@ -1728,8 +1844,24 @@ bacmp(struct bt_arg *a, struct bt_arg *b) return ba2long(a, NULL) - ba2long(b, NULL); case B_AT_STR: return strcmp(ba2str(a, NULL), ba2str(b, NULL)); + case B_AT_TUPLE: + /* Compare two lists of arguments one by one. */ + do { + val = bacmp(a, b); + if (val != 0) + break; + + a = SLIST_NEXT(a, ba_next); + b = SLIST_NEXT(b, ba_next); + if (a == NULL && b != NULL) + val = -1; + else if (a != NULL && b == NULL) + val = 1; + } while (a != NULL && b != NULL); + + return val; default: - errx(1, "no compare support for type %d", a->ba_type); + xabort("no compare support for type %d", a->ba_type); } } diff --git a/usr.sbin/btrace/btrace.h b/usr.sbin/btrace/btrace.h index 78d4618d416..b5fb90f8c0a 100644 --- a/usr.sbin/btrace/btrace.h +++ b/usr.sbin/btrace/btrace.h @@ -1,4 +1,4 @@ -/* $OpenBSD: btrace.h,v 1.12 2023/05/12 14:14:16 claudio Exp $ */ +/* $OpenBSD: btrace.h,v 1.13 2023/09/11 19:01:26 mpi Exp $ */ /* * Copyright (c) 2019 - 2020 Martin Pieuchot @@ -46,14 +46,15 @@ int kelf_snprintsym(struct syms *, char *, size_t, /* map.c */ struct map; struct hist; +struct map *map_new(void); void map_clear(struct map *); void map_delete(struct map *, const char *); struct bt_arg *map_get(struct map *, const char *); -struct map *map_insert(struct map *, const char *, struct bt_arg *, - struct dt_evt *); +void map_insert(struct map *, const char *, void *); void map_print(struct map *, size_t, const char *); void map_zero(struct map *); -struct hist *hist_increment(struct hist *, const char *, long); +struct hist *hist_new(long); +void hist_increment(struct hist *, const char *); void hist_print(struct hist *, const char *); #define KLEN 1024 /* # of characters in map key, contain a stack trace */ diff --git a/usr.sbin/btrace/map.c b/usr.sbin/btrace/map.c index 0de15630c2f..1b116577b2e 100644 --- a/usr.sbin/btrace/map.c +++ b/usr.sbin/btrace/map.c @@ -1,4 +1,4 @@ -/* $OpenBSD: map.c,v 1.23 2023/09/03 10:26:35 mpi Exp $ */ +/* $OpenBSD: map.c,v 1.24 2023/09/11 19:01:26 mpi Exp $ */ /* * Copyright (c) 2020 Martin Pieuchot @@ -33,14 +33,6 @@ #include "bt_parser.h" #include "btrace.h" -#ifndef MIN -#define MIN(_a,_b) ((_a) < (_b) ? (_a) : (_b)) -#endif - -#ifndef MAX -#define MAX(_a,_b) ((_a) > (_b) ? (_a) : (_b)) -#endif - RB_HEAD(map, mentry); struct mentry { @@ -79,6 +71,18 @@ mget(struct map *map, const char *key) return mep; } +struct map * +map_new(void) +{ + struct map *map; + + map = calloc(1, sizeof(struct map)); + if (map == NULL) + err(1, "map: calloc"); + + return map; +} + void map_clear(struct map *map) { @@ -118,75 +122,14 @@ map_get(struct map *map, const char *key) return mep->mval; } -struct map * -map_insert(struct map *map, const char *key, struct bt_arg *bval, - struct dt_evt *dtev) +void +map_insert(struct map *map, const char *key, void *cookie) { struct mentry *mep; - long val; - - if (map == NULL) { - map = calloc(1, sizeof(struct map)); - if (map == NULL) - err(1, "map: calloc"); - } mep = mget(map, key); - switch (bval->ba_type) { - case B_AT_STR: - free(mep->mval); - mep->mval = ba_new(ba2str(bval, dtev), B_AT_LONG); - break; - case B_AT_LONG: - case B_AT_BI_PID: - case B_AT_BI_TID: - case B_AT_BI_CPU: - case B_AT_BI_NSECS: - case B_AT_BI_ARG0 ... B_AT_BI_ARG9: - case B_AT_BI_RETVAL: - case B_AT_BI_PROBE: - free(mep->mval); - mep->mval = ba_new(ba2long(bval, dtev), B_AT_LONG); - break; - case B_AT_MF_COUNT: - if (mep->mval == NULL) - mep->mval = ba_new(0, B_AT_LONG); - val = (long)mep->mval->ba_value; - val++; - mep->mval->ba_value = (void *)val; - break; - case B_AT_MF_MAX: - if (mep->mval == NULL) - mep->mval = ba_new(0, B_AT_LONG); - val = (long)mep->mval->ba_value; - val = MAX(val, ba2long(bval->ba_value, dtev)); - mep->mval->ba_value = (void *)val; - break; - case B_AT_MF_MIN: - if (mep->mval == NULL) - mep->mval = ba_new(0, B_AT_LONG); - val = (long)mep->mval->ba_value; - val = MIN(val, ba2long(bval->ba_value, dtev)); - mep->mval->ba_value = (void *)val; - break; - case B_AT_MF_SUM: - if (mep->mval == NULL) - mep->mval = ba_new(0, B_AT_LONG); - val = (long)mep->mval->ba_value; - val += ba2long(bval->ba_value, dtev); - mep->mval->ba_value = (void *)val; - break; - case B_AT_BI_COMM: - case B_AT_BI_KSTACK: - case B_AT_BI_USTACK: - free(mep->mval); - mep->mval = ba_new(ba2str(bval, dtev), B_AT_STR); - break; - default: - errx(1, "no insert support for type %d", bval->ba_type); - } - - return map; + free(mep->mval); + mep->mval = cookie; } static int @@ -248,26 +191,36 @@ map_zero(struct map *map) /* * Histogram implemented with map. */ - struct hist { struct map hmap; int hstep; }; struct hist * -hist_increment(struct hist *hist, const char *key, long step) +hist_new(long step) { - static struct bt_arg incba = BA_INITIALIZER(NULL, B_AT_MF_COUNT); + struct hist *hist; - if (hist == NULL) { - hist = calloc(1, sizeof(struct hist)); - if (hist == NULL) - err(1, "hist: calloc"); - hist->hstep = step; - } - assert(hist->hstep == step); + hist = calloc(1, sizeof(struct hist)); + if (hist == NULL) + err(1, "hist: calloc"); + hist->hstep = step; + + return hist; +} + +void +hist_increment(struct hist *hist, const char *bucket) +{ + struct bt_arg *ba; + long val; + + ba = map_get(&hist->hmap, bucket); - return (struct hist *)map_insert(&hist->hmap, key, &incba, NULL); + assert(ba->ba_type == B_AT_LONG); + val = (long)ba->ba_value; + val++; + ba->ba_value = (void *)val; } long -- 2.20.1