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

Reply via email to