On Mon, Jun 26, 2023 at 05:24:53PM +0200, Claudio Jeker wrote: > 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 .
Would it make more sense to document count() like min() and max()? Then it doesn't look like a special case. Looks like min/max functions can be used in map key assignments as well, although I'm not super familar with btrace internals yet. > .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); > } >