I created this simple btrace script to help find malloc(9) leaks but it did not work. First step was adding kstack support to the map implementation. But then it still did not work because btrace did not enable the kstack reporting for the probe. This turned out to be an issue with the if () condition I added to filter out uninteresting events. The problem is that the statements in the if block are not scanned for possible extra probe arguments. The below diff fixes all of that.
Here is the btrace script: tracepoint:uvm:malloc { if (arg0 == 127 && arg2 <= 64) { @mem[arg1] = kstack } } tracepoint:uvm:free { if (arg0 == 127 && arg2 <= 64) { delete(@mem[arg1]) } } END { printf("Possible memory leaks\n"); print(@mem) } -- :wq Claudio Index: bt.5 =================================================================== RCS file: /cvs/src/usr.sbin/btrace/bt.5,v retrieving revision 1.14 diff -u -p -r1.14 bt.5 --- bt.5 31 Mar 2022 17:27:29 -0000 1.14 +++ bt.5 26 Jun 2023 15:16:25 -0000 @@ -120,6 +120,11 @@ Functions: .It Fn clear "@map" Delete all (key, value) pairs from .Va @map . +.It "@map[key]" = Fn count +Increment the value of +.Va key +in +.Va @map . .It Fn delete "@map[key]" Delete the pair indexed by .Va key Index: btrace.c =================================================================== RCS file: /cvs/src/usr.sbin/btrace/btrace.c,v retrieving revision 1.70 diff -u -p -r1.70 btrace.c --- btrace.c 12 May 2023 14:14:16 -0000 1.70 +++ btrace.c 26 Jun 2023 15:12:18 -0000 @@ -450,6 +450,37 @@ rules_do(int fd) } } +static uint64_t +rules_action_scan(struct bt_stmt *bs) +{ + struct bt_arg *ba; + uint64_t evtflags = 0; + + while (bs != NULL) { + SLIST_FOREACH(ba, &bs->bs_args, ba_next) + evtflags |= ba2dtflags(ba); + + /* Also check the value for map/hist insertion */ + switch (bs->bs_act) { + case B_AC_BUCKETIZE: + case B_AC_INSERT: + ba = (struct bt_arg *)bs->bs_var; + evtflags |= ba2dtflags(ba); + break; + case B_AC_TEST: + evtflags |= rules_action_scan( + (struct bt_stmt *)bs->bs_var); + break; + default: + break; + } + + bs = SLIST_NEXT(bs, bs_next); + } + + return evtflags; +} + void rules_setup(int fd) { @@ -474,21 +505,7 @@ rules_setup(int fd) evtflags |= ba2dtflags(ba); } - SLIST_FOREACH(bs, &r->br_action, bs_next) { - SLIST_FOREACH(ba, &bs->bs_args, ba_next) - evtflags |= ba2dtflags(ba); - - /* Also check the value for map/hist insertion */ - switch (bs->bs_act) { - case B_AC_BUCKETIZE: - case B_AC_INSERT: - ba = (struct bt_arg *)bs->bs_var; - evtflags |= ba2dtflags(ba); - break; - default: - break; - } - } + evtflags |= rules_action_scan(SLIST_FIRST(&r->br_action)); SLIST_FOREACH(bp, &r->br_probes, bp_next) { debug("parsed probe '%s'", debug_probe_name(bp)); @@ -1685,10 +1702,17 @@ ba2dtflags(struct bt_arg *ba) long bacmp(struct bt_arg *a, struct bt_arg *b) { - assert(a->ba_type == b->ba_type); - assert(a->ba_type == B_AT_LONG); + if (a->ba_type != b->ba_type) + return a->ba_type - b->ba_type; - return ba2long(a, NULL) - ba2long(b, NULL); + switch (a->ba_type) { + case B_AT_LONG: + return ba2long(a, NULL) - ba2long(b, NULL); + case B_AT_STR: + return strcmp(ba2str(a, NULL), ba2str(b, NULL)); + default: + errx(1, "no compare support for type %d", a->ba_type); + } } __dead void Index: map.c =================================================================== RCS file: /cvs/src/usr.sbin/btrace/map.c,v retrieving revision 1.20 diff -u -p -r1.20 map.c --- map.c 30 Apr 2022 01:29:05 -0000 1.20 +++ map.c 24 Jun 2023 10:27:59 -0000 @@ -176,6 +176,11 @@ map_insert(struct map *map, const char * val += ba2long(bval->ba_value, dtev); mep->mval->ba_value = (void *)val; break; + 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); }