From ea0c567a8e96fb7f14327c7b9b0bc447c6c64606 Mon Sep 17 00:00:00 2001 From: mpi Date: Mon, 1 Feb 2021 11:26:28 +0000 Subject: [PATCH] Start implementing conditionals for filters. 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 | 52 +++++++++++++++++++++++++------------ usr.sbin/btrace/bt_parser.h | 30 ++++++++++++++------- usr.sbin/btrace/btrace.c | 51 ++++++++++++++++++++++++++++-------- 3 files changed, 97 insertions(+), 36 deletions(-) diff --git a/usr.sbin/btrace/bt_parse.y b/usr.sbin/btrace/bt_parse.y index 187db9f6d1d..607d6dc5241 100644 --- a/usr.sbin/btrace/bt_parse.y +++ b/usr.sbin/btrace/bt_parse.y @@ -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 + * Copyright (c) 2019-2021 Martin Pieuchot * Copyright (c) 2019 Tobias Heider * Copyright (c) 2015 Ted Unangst * @@ -117,7 +117,7 @@ static int pflag; %type probe probeval %type predicate %type action stmt stmtlist -%type expr vargs map mexpr printargs term +%type expr vargs map mexpr printargs term condition %type 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; diff --git a/usr.sbin/btrace/bt_parser.h b/usr.sbin/btrace/bt_parser.h index c3a078b3638..c873b8a6c25 100644 --- a/usr.sbin/btrace/bt_parser.h +++ b/usr.sbin/btrace/bt_parser.h @@ -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 + * Copyright (c) 2019-2021 Martin Pieuchot * * 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; diff --git a/usr.sbin/btrace/btrace.c b/usr.sbin/btrace/btrace.c index 3eda0a638fd..9be7bbd849f 100644 --- a/usr.sbin/btrace/btrace.c +++ b/usr.sbin/btrace/btrace.c @@ -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 @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -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"); } -- 2.20.1