Start implementing conditionals for filters.
authormpi <mpi@openbsd.org>
Mon, 1 Feb 2021 11:26:28 +0000 (11:26 +0000)
committermpi <mpi@openbsd.org>
Mon, 1 Feb 2021 11:26:28 +0000 (11:26 +0000)
Allows to check the existence of a variable in predicates, making it
possible to trace syscall latency, as follow:

syscall:select:entry
{
  @start[pid] = nsecs;
}

syscall:select:return
/@start[pid]/
{
  @usecs = hist((nsecs - @start[pid]) / 1000);
  delete(@start[pid]);
}

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

index 187db9f..607d6dc 100644 (file)
@@ -1,7 +1,7 @@
-/*     $OpenBSD: bt_parse.y,v 1.21 2021/01/10 11:06:08 jmatthew Exp $  */
+/*     $OpenBSD: bt_parse.y,v 1.22 2021/02/01 11:26:28 mpi Exp $       */
 
 /*
- * Copyright (c) 2019 - 2020 Martin Pieuchot <mpi@openbsd.org>
+ * Copyright (c) 2019-2021 Martin Pieuchot <mpi@openbsd.org>
  * Copyright (c) 2019 Tobias Heider <tobhe@openbsd.org>
  * Copyright (c) 2015 Ted Unangst <tedu@openbsd.org>
  *
@@ -117,7 +117,7 @@ static int pflag;
 %type  <v.probe>       probe probeval
 %type  <v.filter>      predicate
 %type  <v.stmt>        action stmt stmtlist
-%type  <v.arg>         expr vargs map mexpr printargs term
+%type  <v.arg>         expr vargs map mexpr printargs term condition
 %type  <v.rtype>       beginend
 
 %left  '|'
@@ -158,6 +158,11 @@ oper               : OP_EQ                         { $$ = B_OP_EQ; }
 predicate      : /* empty */                   { $$ = NULL; }
                | '/' filterval oper NUMBER '/' { $$ = bf_new($3, $2, $4); }
                | '/' NUMBER oper filterval '/' { $$ = bf_new($3, $4, $2); }
+               | '/' condition '/'             { $$ = bc_new($2); }
+               ;
+
+condition      : gvar                          { $$ = bv_get($1); }
+               | map                           { $$ = $1; }
                ;
 
 builtin                : PID                           { $$ = B_AT_BI_PID; }
@@ -232,7 +237,7 @@ br_new(struct bt_probe *probe, struct bt_filter *filter, struct bt_stmt *head,
 {
        struct bt_rule *br;
 
-       br = calloc(1, sizeof(struct bt_rule));
+       br = calloc(1, sizeof(*br));
        if (br == NULL)
                err(1, "bt_rule: calloc");
        br->br_probe = probe;
@@ -251,23 +256,38 @@ br_new(struct bt_probe *probe, struct bt_filter *filter, struct bt_stmt *head,
        return br;
 }
 
-/* Create a new filter */
+/* Create a new event filter */
 struct bt_filter *
 bf_new(enum bt_operand op, enum bt_filtervar var, int val)
 {
-       struct bt_filter *df;
+       struct bt_filter *bf;
 
        if (val < 0 || val > INT_MAX)
                errx(1, "invalid pid '%d'", val);
 
-       df = calloc(1, sizeof(struct bt_filter));
-       if (df == NULL)
+       bf = calloc(1, sizeof(*bf));
+       if (bf == NULL)
                err(1, "bt_filter: calloc");
-       df->bf_op = op;
-       df->bf_var = var;
-       df->bf_val = val;
+       bf->bf_evtfilter.bf_op = op;
+       bf->bf_evtfilter.bf_var = var;
+       bf->bf_evtfilter.bf_val = val;
+
+       return bf;
+}
+
+/* Create a new condition */
+struct bt_filter *
+bc_new(struct bt_arg *ba)
+{
+       struct bt_filter *bf;
+
+       bf = calloc(1, sizeof(*bf));
+       if (bf == NULL)
+               err(1, "bt_filter: calloc");
+
+       bf->bf_condition = bs_new(B_AC_TEST, ba, NULL);
 
-       return df;
+       return bf;
 }
 
 /* Create a new probe */
@@ -279,7 +299,7 @@ bp_new(const char *prov, const char *func, const char *name, int32_t rate)
        if (rate < 0 || rate > INT32_MAX)
                errx(1, "only positive values permitted");
 
-       bp = calloc(1, sizeof(struct bt_probe));
+       bp = calloc(1, sizeof(*bp));
        if (bp == NULL)
                err(1, "bt_probe: calloc");
        bp->bp_prov = prov;
@@ -296,7 +316,7 @@ ba_new0(void *val, enum bt_argtype type)
 {
        struct bt_arg *ba;
 
-       ba = calloc(1, sizeof(struct bt_arg));
+       ba = calloc(1, sizeof(*ba));
        if (ba == NULL)
                err(1, "bt_arg: calloc");
        ba->ba_value = val;
@@ -365,7 +385,7 @@ bs_new(enum bt_action act, struct bt_arg *head, struct bt_var *var)
 {
        struct bt_stmt *bs;
 
-       bs = calloc(1, sizeof(struct bt_stmt));
+       bs = calloc(1, sizeof(*bs));
        if (bs == NULL)
                err(1, "bt_stmt: calloc");
        bs->bs_act = act;
@@ -424,7 +444,7 @@ bv_new(const char *vname)
 {
        struct bt_var *bv;
 
-       bv = calloc(1, sizeof(struct bt_var));
+       bv = calloc(1, sizeof(*bv));
        if (bv == NULL)
                err(1, "bt_var: calloc");
        bv->bv_name = vname;
index c3a078b..c873b8a 100644 (file)
@@ -1,7 +1,7 @@
-/*     $OpenBSD: bt_parser.h,v 1.11 2021/01/27 07:19:54 deraadt Exp $  */
+/*     $OpenBSD: bt_parser.h,v 1.12 2021/02/01 11:26:29 mpi Exp $      */
 
 /*
- * Copyright (c) 2019-2020 Martin Pieuchot <mpi@openbsd.org>
+ * Copyright (c) 2019-2021 Martin Pieuchot <mpi@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -41,19 +41,17 @@ struct bt_probe {
 #define bp_unit        bp_func
 };
 
+
 /*
- * Filters correspond to predicates performed in kernel.
- *
- * When a probe fires the check is performed, if it isn't true no event
- * is recorded.
+ * Event filters correspond to checks performed in-kernel.
  */
-struct bt_filter {
+struct bt_evtfilter {
        enum bt_operand {
                B_OP_NONE = 1,
                B_OP_EQ,
                B_OP_NE,
-       }                        bf_op;
-       enum  bt_filtervar {
+       }                       bf_op;
+       enum bt_filtervar {
                B_FV_NONE = 1,
                B_FV_PID,
                B_FV_TID
@@ -61,6 +59,19 @@ struct bt_filter {
        uint32_t                 bf_val;
 };
 
+/*
+ * Filters, also known as predicates, describe under which set of
+ * conditions a rule is executed.
+ *
+ * Depending on their type they are performed in-kernel or when a rule
+ * is evaluated.  In the first case they might prevent the recording of
+ * events, in the second case events might be discarded at runtime.
+ */
+struct bt_filter {
+       struct bt_evtfilter       bf_evtfilter; /* in-kernel event filter */
+       struct bt_stmt           *bf_condition; /* per event condition */
+};
+
 TAILQ_HEAD(bt_ruleq, bt_rule);
 
 /*
@@ -170,6 +181,7 @@ struct bt_stmt {
                B_AC_PRINT,                     /* print(@map, 10) */
                B_AC_PRINTF,                    /* printf("hello!\n") */
                B_AC_STORE,                     /* @a = 3 */
+               B_AC_TEST,                      /* if (@a) */
                B_AC_TIME,                      /* time("%H:%M:%S  ") */
                B_AC_ZERO,                      /* zero(@map) */
        }                        bs_act;
index 3eda0a6..9be7bbd 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: btrace.c,v 1.27 2021/01/21 13:19:25 mpi Exp $ */
+/*     $OpenBSD: btrace.c,v 1.28 2021/02/01 11:26:29 mpi Exp $ */
 
 /*
  * Copyright (c) 2019 - 2020 Martin Pieuchot <mpi@openbsd.org>
@@ -29,6 +29,7 @@
 #include <locale.h>
 #include <signal.h>
 #include <stdarg.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -86,6 +87,7 @@ void                   stmt_delete(struct bt_stmt *, struct dt_evt *);
 void                    stmt_insert(struct bt_stmt *, struct dt_evt *);
 void                    stmt_print(struct bt_stmt *, struct dt_evt *);
 void                    stmt_store(struct bt_stmt *, struct dt_evt *);
+bool                    stmt_test(struct bt_stmt *, struct dt_evt *);
 void                    stmt_time(struct bt_stmt *, struct dt_evt *);
 void                    stmt_zero(struct bt_stmt *);
 struct bt_arg          *ba_read(struct bt_arg *);
@@ -431,8 +433,9 @@ rules_setup(int fd, int tracepid)
 
                r->br_pbn = dtpi->dtpi_pbn;
                dtrq->dtrq_pbn = dtpi->dtpi_pbn;
-               if (r->br_filter) {
-                       struct bt_filter *df = r->br_filter;
+               if (r->br_filter != NULL &&
+                   r->br_filter->bf_condition == NULL) {
+                       struct bt_evtfilter *df = &r->br_filter->bf_evtfilter;
 
                        dtrq->dtrq_filter.dtf_operand = dop2dt(df->bf_op);
                        dtrq->dtrq_filter.dtf_variable = dvar2dt(df->bf_var);
@@ -533,6 +536,11 @@ rule_eval(struct bt_rule *r, struct dt_evt *dtev)
 
        debug("eval rule '%s'\n", debug_rule_name(r));
 
+       if (r->br_filter != NULL && r->br_filter->bf_condition != NULL) {
+               if (stmt_test(r->br_filter->bf_condition, dtev) == false)
+                       return;
+       }
+
        SLIST_FOREACH(bs, &r->br_action, bs_next) {
                switch (bs->bs_act) {
                case B_AC_BUCKETIZE:
@@ -802,7 +810,7 @@ stmt_print(struct bt_stmt *bs, struct dt_evt *dtev)
 }
 
 /*
- * Variable store:     { var = 3; }
+ * Variable store:     { @var = 3; }
  *
  * In this case '3' is represented by `ba', the argument of a STORE
  * action.
@@ -835,6 +843,27 @@ stmt_store(struct bt_stmt *bs, struct dt_evt *dtev)
        debug("bv=%p var '%s' store (%p) \n", bv, bv_name(bv), bv->bv_value);
 }
 
+/*
+ * Expression test:    { if (expr) stmt; }
+ */
+bool
+stmt_test(struct bt_stmt *bs, struct dt_evt *dtev)
+{
+       struct bt_arg *ba;
+       long val;
+
+       if (bs == NULL)
+               return true;
+
+       assert(bs->bs_var == NULL);
+       ba = SLIST_FIRST(&bs->bs_args);
+       val = ba2long(ba, dtev);
+
+       debug("ba=%p test (%ld != 0)\n", ba, val);
+
+       return val != 0;
+}
+
 /*
  * Print time:                 { time("%H:%M:%S"); }
  */
@@ -1241,7 +1270,7 @@ debugx(const char *fmt, ...)
 }
 
 static inline const char *
-debug_getfiltervar(struct bt_filter *df)
+debug_getfiltervar(struct bt_evtfilter *df)
 {
        switch (df->bf_var) {
        case B_FV_PID:  return "pid";
@@ -1250,12 +1279,10 @@ debug_getfiltervar(struct bt_filter *df)
        default:
                xabort("invalid filtervar %d", df->bf_var);
        }
-
-
 }
 
 static inline const char *
-debug_getfilterop(struct bt_filter *df)
+debug_getfilterop(struct bt_evtfilter *df)
 {
        switch (df->bf_op) {
        case B_OP_EQ:   return "==";
@@ -1269,9 +1296,11 @@ debug_getfilterop(struct bt_filter *df)
 void
 debug_dump_filter(struct bt_rule *r)
 {
-       if (r->br_filter) {
-               debugx(" / %s %s %u /", debug_getfiltervar(r->br_filter),
-                   debug_getfilterop(r->br_filter), r->br_filter->bf_val);
+       if (r->br_filter != NULL && r->br_filter->bf_condition == NULL) {
+               struct bt_evtfilter *df = &r->br_filter->bf_evtfilter;
+
+               debugx(" / %s %s %u /", debug_getfiltervar(df),
+                   debug_getfilterop(df), df->bf_val);
        }
        debugx("\n");
 }