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);
> }
>