Make it possible to associate multiple probes to a single rule.
authormpi <mpi@openbsd.org>
Thu, 9 Sep 2021 09:53:11 +0000 (09:53 +0000)
committermpi <mpi@openbsd.org>
Thu, 9 Sep 2021 09:53:11 +0000 (09:53 +0000)
The following syntax, reducing duplication, is now allowed:

END,
interval:hz:2
{
...
}

Rule descriptors are now linked to a list of probe descriptors instead of
a single one.  Enabled kernel probes are now linked to btrace(8) probe
descriptors.

While here stop parsing filter and probe if debug is not enabled.

usr.sbin/btrace/bt_parse.y
usr.sbin/btrace/bt_parser.h
usr.sbin/btrace/btrace.c

index 99ad7d0..196edc4 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: bt_parse.y,v 1.41 2021/09/08 15:34:01 mpi Exp $       */
+/*     $OpenBSD: bt_parse.y,v 1.42 2021/09/09 09:53:11 mpi Exp $       */
 
 /*
  * Copyright (c) 2019-2021 Martin Pieuchot <mpi@openbsd.org>
@@ -59,8 +59,8 @@ SLIST_HEAD(, bt_var)  l_variables;
 struct bt_arg          g_nullba = BA_INITIALIZER(0, B_AT_LONG);
 struct bt_arg          g_maxba = BA_INITIALIZER(LONG_MAX, B_AT_LONG);
 
-struct bt_rule *br_new(struct bt_probe *, struct bt_filter *, struct bt_stmt *,
-                    enum bt_rtype);
+struct bt_rule *br_new(struct bt_probe *, struct bt_filter *,
+                    struct bt_stmt *);
 struct bt_probe        *bp_new(const char *, const char *, const char *, int32_t);
 struct bt_arg  *ba_append(struct bt_arg *, struct bt_arg *);
 struct bt_arg  *ba_op(enum bt_argtype, struct bt_arg *, struct bt_arg *);
@@ -124,7 +124,7 @@ static int pflag;
 
 %type  <v.string>      gvar lvar
 %type  <v.i>           beginend
-%type  <v.probe>       probe pname
+%type  <v.probe>       plist probe pname
 %type  <v.filter>      filter
 %type  <v.stmt>        action stmt stmtblck stmtlist block
 %type  <v.arg>         pat vargs mentry mpat pargs staticv
@@ -137,13 +137,18 @@ grammar   : /* empty */
        | grammar error
        ;
 
-rule   : beginend action               { br_new(NULL, NULL, $2, $1); }
-       | probe filter action           { br_new($1, $2, $3, B_RT_PROBE); }
+rule   : plist filter action           { br_new($1, $2, $3); }
        ;
 
 beginend: BEGIN        | END ;
 
+plist  : plist ',' probe               { $$ = bp_append($1, $3); }
+       | probe
+       ;
+
 probe  : { pflag = 1; } pname          { $$ = $2; pflag = 0; }
+       | beginend                      { $$ = bp_new(NULL, NULL, NULL, $1); }
+       ;
 
 pname  : STRING ':' STRING ':' STRING  { $$ = bp_new($1, $3, $5, 0); }
        | STRING ':' HZ ':' NUMBER      { $$ = bp_new($1, "hz", NULL, $5); }
@@ -227,8 +232,9 @@ pargs       : expr
        | gvar ',' pat                  { $$ = ba_append(bg_find($1), $3); }
        ;
 
-NL     : /* empty */ | '\n'
-               ;
+NL     : /* empty */
+       | '\n'
+       ;
 
 stmt   : ';' NL                        { $$ = NULL; }
        | gvar '=' pat                  { $$ = bg_store($1, $3); }
@@ -291,29 +297,29 @@ get_nargs(void)
 
 /* Create a new rule, representing  "probe / filter / { action }" */
 struct bt_rule *
-br_new(struct bt_probe *probe, struct bt_filter *filter, struct bt_stmt *head,
-    enum bt_rtype rtype)
+br_new(struct bt_probe *probe, struct bt_filter *filter, struct bt_stmt *head)
 {
        struct bt_rule *br;
 
        br = calloc(1, sizeof(*br));
        if (br == NULL)
                err(1, "bt_rule: calloc");
-       br->br_probe = probe;
+       /* SLIST_INSERT_HEAD() nullify the next pointer. */
+       SLIST_FIRST(&br->br_probes) = probe;
        br->br_filter = filter;
        /* SLIST_INSERT_HEAD() nullify the next pointer. */
        SLIST_FIRST(&br->br_action) = head;
-       br->br_type = rtype;
 
        SLIST_FIRST(&br->br_variables) = SLIST_FIRST(&l_variables);
        SLIST_INIT(&l_variables);
 
-       if (rtype == B_RT_PROBE) {
+       do {
+               if (probe->bp_type != B_PT_PROBE)
+                       continue;
                g_nprobes++;
-               TAILQ_INSERT_TAIL(&g_rules, br, br_next);
-       } else {
-               TAILQ_INSERT_HEAD(&g_rules, br, br_next);
-       }
+       } while ((probe = SLIST_NEXT(probe, bp_next)) != NULL);
+
+       TAILQ_INSERT_TAIL(&g_rules, br, br_next);
 
        return br;
 }
@@ -349,10 +355,16 @@ struct bt_probe *
 bp_new(const char *prov, const char *func, const char *name, int32_t rate)
 {
        struct bt_probe *bp;
+       enum bt_ptype ptype;
 
        if (rate < 0 || rate > INT32_MAX)
                errx(1, "only positive values permitted");
 
+       if (prov == NULL && func == NULL && name == NULL)
+               ptype = rate; /* BEGIN or END */
+       else
+               ptype = B_PT_PROBE;
+
        bp = calloc(1, sizeof(*bp));
        if (bp == NULL)
                err(1, "bt_probe: calloc");
@@ -360,10 +372,33 @@ bp_new(const char *prov, const char *func, const char *name, int32_t rate)
        bp->bp_func = func;
        bp->bp_name = name;
        bp->bp_rate = rate;
+       bp->bp_type = ptype;
 
        return bp;
 }
 
+/*
+ * Link two probes together, to build a probe list attached to
+ * a single action.
+ */
+struct bt_probe *
+bp_append(struct bt_probe *bp0, struct bt_probe *bp1)
+{
+       struct bt_probe *bp = bp0;
+
+       assert(bp1 != NULL);
+
+       if (bp0 == NULL)
+               return bp1;
+
+       while (SLIST_NEXT(bp, bp_next) != NULL)
+               bp = SLIST_NEXT(bp, bp_next);
+
+       SLIST_INSERT_AFTER(bp, bp1, bp_next);
+
+       return bp0;
+}
+
 /* Create a new argument */
 struct bt_arg *
 ba_new0(void *val, enum bt_argtype type)
@@ -660,8 +695,8 @@ struct keyword *
 lookup(char *s)
 {
        static const struct keyword kws[] = {
-               { "BEGIN",      BEGIN,          B_RT_BEGIN },
-               { "END",        END,            B_RT_END },
+               { "BEGIN",      BEGIN,          B_PT_BEGIN },
+               { "END",        END,            B_PT_END },
                { "arg0",       BUILTIN,        B_AT_BI_ARG0 },
                { "arg1",       BUILTIN,        B_AT_BI_ARG1 },
                { "arg2",       BUILTIN,        B_AT_BI_ARG2 },
index b7288e7..20fde72 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: bt_parser.h,v 1.19 2021/09/08 13:29:51 dv Exp $       */
+/*     $OpenBSD: bt_parser.h,v 1.20 2021/09/09 09:53:11 mpi Exp $      */
 
 /*
  * Copyright (c) 2019-2021 Martin Pieuchot <mpi@openbsd.org>
  *     "provider:function:name"
  * or
  *     "provider:time_unit:rate"
+ *
+ * Multiple probes can be associated to the same action.
  */
 struct bt_probe {
+       SLIST_ENTRY(bt_probe)    bp_next;       /* next probe for this rule */
        const char              *bp_prov;       /* provider */
        const char              *bp_func;       /* function or time unit */
        const char              *bp_name;
        uint32_t                 bp_rate;
 #define bp_unit        bp_func
+       enum bt_ptype {
+                B_PT_BEGIN = 1,
+                B_PT_END,
+                B_PT_PROBE,
+       }                        bp_type;       /* BEGIN, END or 'probe' */
+       void                    *bp_cookie;     /* ioctl request */
+       uint32_t                 bp_pbn;        /* ID assigned by the kernel */
 };
 
 
@@ -77,19 +87,10 @@ TAILQ_HEAD(bt_ruleq, bt_rule);
  */
 struct bt_rule {
        TAILQ_ENTRY(bt_rule)     br_next;       /* linkage in global list */
-       struct bt_probe         *br_probe;
+       SLIST_HEAD(, bt_probe)   br_probes;     /* list of probes */
        struct bt_filter        *br_filter;
        SLIST_HEAD(, bt_stmt)    br_action;
        SLIST_HEAD(, bt_var)     br_variables;  /* local variables */
-
-       enum bt_rtype {
-                B_RT_BEGIN = 1,
-                B_RT_END,
-                B_RT_PROBE,
-       }                        br_type;       /* BEGIN, END or 'probe' */
-
-       uint32_t                 br_pbn;        /* ID assigned by the kernel */
-       void                    *br_cookie;
 };
 
 /*
index 65e2a52..a84ab5c 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: btrace.c,v 1.53 2021/09/09 09:43:49 mpi Exp $ */
+/*     $OpenBSD: btrace.c,v 1.54 2021/09/09 09:53:11 mpi Exp $ */
 
 /*
  * Copyright (c) 2019 - 2021 Martin Pieuchot <mpi@openbsd.org>
@@ -105,7 +105,7 @@ int                  ba2dtflags(struct bt_arg *);
 __dead void             xabort(const char *, ...);
 void                    debug(const char *, ...);
 void                    debugx(const char *, ...);
-const char             *debug_rule_name(struct bt_rule *);
+const char             *debug_probe_name(struct bt_probe *);
 void                    debug_dump_term(struct bt_arg *);
 void                    debug_dump_expr(struct bt_arg *);
 void                    debug_dump_filter(struct bt_rule *);
@@ -398,54 +398,58 @@ rules_setup(int fd)
        struct bt_probe *bp;
        struct bt_stmt *bs;
        int dokstack = 0, on = 1;
+       uint64_t evtflags;
 
        TAILQ_FOREACH(r, &g_rules, br_next) {
-               debug("parsed probe '%s'", debug_rule_name(r));
-               debug_dump_filter(r);
-
-               if (r->br_type != B_RT_PROBE) {
-                       if (r->br_type == B_RT_BEGIN)
-                               rbegin = r;
-                       continue;
-               }
-
-               bp = r->br_probe;
-               dtpi_cache(fd);
-               dtpi = dtpi_get_by_value(bp->bp_prov, bp->bp_func, bp->bp_name);
-               if (dtpi == NULL) {
-                       errx(1, "probe '%s:%s:%s' not found", bp->bp_prov,
-                           bp->bp_func, bp->bp_name);
-               }
-
-               dtrq = calloc(1, sizeof(*dtrq));
-               if (dtrq == NULL)
-                       err(1, "dtrq: 1alloc");
-
-               r->br_pbn = dtpi->dtpi_pbn;
-               dtrq->dtrq_pbn = dtpi->dtpi_pbn;
-               dtrq->dtrq_rate = r->br_probe->bp_rate;
-
+               evtflags = 0;
                SLIST_FOREACH(bs, &r->br_action, bs_next) {
                        struct bt_arg *ba;
 
                        SLIST_FOREACH(ba, &bs->bs_args, ba_next)
-                               dtrq->dtrq_evtflags |= ba2dtflags(ba);
+                               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;
-                               dtrq->dtrq_evtflags |= ba2dtflags(ba);
+                               evtflags |= ba2dtflags(ba);
                                break;
                        default:
                                break;
                        }
                }
 
-               if (dtrq->dtrq_evtflags & DTEVT_KSTACK)
-                       dokstack = 1;
-               r->br_cookie = dtrq;
+               SLIST_FOREACH(bp, &r->br_probes, bp_next) {
+                       debug("parsed probe '%s'", debug_probe_name(bp));
+                       debug_dump_filter(r);
+
+                       if (bp->bp_type != B_PT_PROBE) {
+                               if (bp->bp_type == B_PT_BEGIN)
+                                       rbegin = r;
+                               continue;
+                       }
+
+                       dtpi_cache(fd);
+                       dtpi = dtpi_get_by_value(bp->bp_prov, bp->bp_func,
+                           bp->bp_name);
+                       if (dtpi == NULL) {
+                               errx(1, "probe '%s:%s:%s' not found",
+                                   bp->bp_prov, bp->bp_func, bp->bp_name);
+                       }
+
+                       dtrq = calloc(1, sizeof(*dtrq));
+                       if (dtrq == NULL)
+                               err(1, "dtrq: 1alloc");
+
+                       bp->bp_pbn = dtpi->dtpi_pbn;
+                       dtrq->dtrq_pbn = dtpi->dtpi_pbn;
+                       dtrq->dtrq_rate = bp->bp_rate;
+                       dtrq->dtrq_evtflags = evtflags;
+                       if (dtrq->dtrq_evtflags & DTEVT_KSTACK)
+                               dokstack = 1;
+                       bp->bp_cookie = dtrq;
+               }
        }
 
        if (dokstack)
@@ -463,12 +467,14 @@ rules_setup(int fd)
 
        /* Enable all probes */
        TAILQ_FOREACH(r, &g_rules, br_next) {
-               if (r->br_type != B_RT_PROBE)
-                       continue;
+               SLIST_FOREACH(bp, &r->br_probes, bp_next) {
+                       if (bp->bp_type != B_PT_PROBE)
+                               continue;
 
-               dtrq = r->br_cookie;
-               if (ioctl(fd, DTIOCPRBENABLE, dtrq))
-                       err(1, "DTIOCPRBENABLE");
+                       dtrq = bp->bp_cookie;
+                       if (ioctl(fd, DTIOCPRBENABLE, dtrq))
+                               err(1, "DTIOCPRBENABLE");
+               }
        }
 
        if (g_nprobes > 0) {
@@ -481,12 +487,16 @@ void
 rules_apply(struct dt_evt *dtev)
 {
        struct bt_rule *r;
+       struct bt_probe *bp;
 
        TAILQ_FOREACH(r, &g_rules, br_next) {
-               if (r->br_type != B_RT_PROBE || r->br_pbn != dtev->dtev_pbn)
-                       continue;
+               SLIST_FOREACH(bp, &r->br_probes, bp_next) {
+                       if (bp->bp_type != B_PT_PROBE ||
+                           bp->bp_pbn != dtev->dtev_pbn)
+                               continue;
 
-               rule_eval(r, dtev);
+                       rule_eval(r, dtev);
+               }
        }
 }
 
@@ -494,6 +504,7 @@ void
 rules_teardown(int fd)
 {
        struct dtioc_req *dtrq;
+       struct bt_probe *bp;
        struct bt_rule *r, *rend = NULL;
        int dokstack = 0, off = 0;
 
@@ -503,18 +514,19 @@ rules_teardown(int fd)
        }
 
        TAILQ_FOREACH(r, &g_rules, br_next) {
-               dtrq = r->br_cookie;
-               if (r->br_type != B_RT_PROBE) {
-                       if (r->br_type == B_RT_END)
-                               rend = r;
-                       continue;
-        } else {
-            if (ioctl(fd, DTIOCPRBDISABLE, dtrq))
-                err(1, "DTIOCPRBDISABLE");
-        }
+               SLIST_FOREACH(bp, &r->br_probes, bp_next) {
+                       if (bp->bp_type != B_PT_PROBE) {
+                               if (bp->bp_type == B_PT_END)
+                                       rend = r;
+                               continue;
+                       }
 
-       if (dtrq->dtrq_evtflags & DTEVT_KSTACK)
-               dokstack = 1;
+                       dtrq = bp->bp_cookie;
+                       if (ioctl(fd, DTIOCPRBDISABLE, dtrq))
+                               err(1, "DTIOCPRBDISABLE");
+                       if (dtrq->dtrq_evtflags & DTEVT_KSTACK)
+                               dokstack = 1;
+               }
        }
 
        if (dokstack)
@@ -535,9 +547,12 @@ void
 rule_eval(struct bt_rule *r, struct dt_evt *dtev)
 {
        struct bt_stmt *bs;
+       struct bt_probe *bp;
 
-       debug("eval rule '%s'", debug_rule_name(r));
-       debug_dump_filter(r);
+       SLIST_FOREACH(bp, &r->br_probes, bp_next) {
+               debug("eval rule '%s'", debug_probe_name(bp));
+               debug_dump_filter(r);
+       }
 
        if (r->br_filter != NULL && r->br_filter->bf_condition != NULL) {
                if (stmt_test(r->br_filter->bf_condition, dtev) == false) {
@@ -1542,6 +1557,9 @@ debug_dump_filter(struct bt_rule *r)
 {
        struct bt_stmt *bs;
 
+       if (verbose < 2)
+               return;
+
        if (r->br_filter == NULL) {
                debugx("\n");
                return;
@@ -1555,20 +1573,22 @@ debug_dump_filter(struct bt_rule *r)
 }
 
 const char *
-debug_rule_name(struct bt_rule *r)
+debug_probe_name(struct bt_probe *bp)
 {
-       struct bt_probe *bp = r->br_probe;
        static char buf[64];
 
-       if (r->br_type == B_RT_BEGIN)
+       if (verbose < 2)
+               return "";
+
+       if (bp->bp_type == B_PT_BEGIN)
                return "BEGIN";
 
-       if (r->br_type == B_RT_END)
+       if (bp->bp_type == B_PT_END)
                return "END";
 
-       assert(r->br_type == B_RT_PROBE);
+       assert(bp->bp_type == B_PT_PROBE);
 
-       if (r->br_probe->bp_rate) {
+       if (bp->bp_rate) {
                snprintf(buf, sizeof(buf), "%s:%s:%u", bp->bp_prov,
                    bp->bp_unit, bp->bp_rate);
        } else {