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

Reply via email to