From 1694fc3472e6c86b7e40c27bf82eab6973fceb6b Mon Sep 17 00:00:00 2001 From: mpi Date: Thu, 9 Sep 2021 09:53:11 +0000 Subject: [PATCH] Make it possible to associate multiple probes to a single rule. 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 | 73 ++++++++++++++----- usr.sbin/btrace/bt_parser.h | 23 +++--- usr.sbin/btrace/btrace.c | 138 +++++++++++++++++++++--------------- 3 files changed, 145 insertions(+), 89 deletions(-) diff --git a/usr.sbin/btrace/bt_parse.y b/usr.sbin/btrace/bt_parse.y index 99ad7d0cf30..196edc4e28a 100644 --- a/usr.sbin/btrace/bt_parse.y +++ b/usr.sbin/btrace/bt_parse.y @@ -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 @@ -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 gvar lvar %type beginend -%type probe pname +%type plist probe pname %type filter %type action stmt stmtblck stmtlist block %type 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 }, diff --git a/usr.sbin/btrace/bt_parser.h b/usr.sbin/btrace/bt_parser.h index b7288e73675..20fde72791e 100644 --- a/usr.sbin/btrace/bt_parser.h +++ b/usr.sbin/btrace/bt_parser.h @@ -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 @@ -32,13 +32,23 @@ * "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; }; /* diff --git a/usr.sbin/btrace/btrace.c b/usr.sbin/btrace/btrace.c index 65e2a5257c0..a84ab5c40ca 100644 --- a/usr.sbin/btrace/btrace.c +++ b/usr.sbin/btrace/btrace.c @@ -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 @@ -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 { -- 2.20.1