-/* $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>
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 *);
%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
| 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); }
| gvar ',' pat { $$ = ba_append(bg_find($1), $3); }
;
-NL : /* empty */ | '\n'
- ;
+NL : /* empty */
+ | '\n'
+ ;
stmt : ';' NL { $$ = NULL; }
| gvar '=' pat { $$ = bg_store($1, $3); }
/* 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;
}
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");
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)
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 },
-/* $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 */
};
*/
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;
};
/*
-/* $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>
__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 *);
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)
/* 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) {
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);
+ }
}
}
rules_teardown(int fd)
{
struct dtioc_req *dtrq;
+ struct bt_probe *bp;
struct bt_rule *r, *rend = NULL;
int dokstack = 0, off = 0;
}
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)
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) {
{
struct bt_stmt *bs;
+ if (verbose < 2)
+ return;
+
if (r->br_filter == NULL) {
debugx("\n");
return;
}
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 {