xref: /openbsd-src/usr.sbin/btrace/bt_parse.y (revision 139d07b5d2b54520acbf93ed1ea9df2b42a216fe)
1*139d07b5Smpi /*	$OpenBSD: bt_parse.y,v 1.62 2025/01/23 11:17:32 mpi Exp $	*/
223160851Smpi 
323160851Smpi /*
466f34ae4Smpi  * Copyright (c) 2019-2023 Martin Pieuchot <mpi@openbsd.org>
523160851Smpi  * Copyright (c) 2019 Tobias Heider <tobhe@openbsd.org>
623160851Smpi  * Copyright (c) 2015 Ted Unangst <tedu@openbsd.org>
723160851Smpi  *
823160851Smpi  * Permission to use, copy, modify, and distribute this software for any
923160851Smpi  * purpose with or without fee is hereby granted, provided that the above
1023160851Smpi  * copyright notice and this permission notice appear in all copies.
1123160851Smpi  *
1223160851Smpi  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1323160851Smpi  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1423160851Smpi  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1523160851Smpi  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1623160851Smpi  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1723160851Smpi  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1823160851Smpi  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1923160851Smpi  */
2023160851Smpi 
2123160851Smpi /*
2223160851Smpi  * B tracing language parser.
2323160851Smpi  *
2423160851Smpi  * The dialect of the language understood by this parser aims to be
25891bd5a4Smpi  * compatible with the one understood by bpftrace(8), see:
2623160851Smpi  *
2723160851Smpi  * https://github.com/iovisor/bpftrace/blob/master/docs/reference_guide.md
2823160851Smpi  *
2923160851Smpi  */
3023160851Smpi 
3123160851Smpi %{
3223160851Smpi #include <sys/queue.h>
3323160851Smpi 
3423160851Smpi #include <assert.h>
3523160851Smpi #include <ctype.h>
3623160851Smpi #include <err.h>
37bf2cda35Sdv #include <errno.h>
3823160851Smpi #include <limits.h>
3923160851Smpi #include <stdarg.h>
4023160851Smpi #include <stdint.h>
4123160851Smpi #include <stdio.h>
4223160851Smpi 
4323160851Smpi #include "bt_parser.h"
4423160851Smpi 
45477314cbSmpi /* Name for the default map @[], hopefully nobody will use this one ;) */
46477314cbSmpi #define UNNAMED_MAP	"___unnamed_map_doesnt_have_any_name"
47477314cbSmpi 
4823160851Smpi /* Number of rules to evaluate. */
4923160851Smpi struct bt_ruleq		g_rules = TAILQ_HEAD_INITIALIZER(g_rules);
5023160851Smpi 
5123160851Smpi /* Number of probes except BEGIN/END. */
5223160851Smpi int		 	g_nprobes;
5323160851Smpi 
54477314cbSmpi /* List of global variables, including maps. */
5523160851Smpi SLIST_HEAD(, bt_var)	 g_variables;
5623160851Smpi 
577aa3827dSmpi /* List of local variables, cleaned for each new rule. */
587aa3827dSmpi SLIST_HEAD(, bt_var)	l_variables;
597aa3827dSmpi 
6089f39301Smpi struct bt_arg 		g_nullba = BA_INITIALIZER(0, B_AT_LONG);
6189f39301Smpi struct bt_arg		g_maxba = BA_INITIALIZER(LONG_MAX, B_AT_LONG);
6289f39301Smpi 
631694fc34Smpi struct bt_rule	*br_new(struct bt_probe *, struct bt_filter *,
641694fc34Smpi 		     struct bt_stmt *);
65*139d07b5Smpi struct bt_probe	*bp_new(const char *, const char *, const char *, long);
66a710f35bSmpi struct bt_arg	*ba_append(struct bt_arg *, struct bt_arg *);
67822ace7eSmpi struct bt_arg	*ba_op(enum bt_argtype, struct bt_arg *, struct bt_arg *);
6823160851Smpi struct bt_stmt	*bs_new(enum bt_action, struct bt_arg *, struct bt_var *);
69a710f35bSmpi struct bt_stmt	*bs_append(struct bt_stmt *, struct bt_stmt *);
7023160851Smpi 
717aa3827dSmpi struct bt_var	*bg_lookup(const char *);
727aa3827dSmpi struct bt_stmt	*bg_store(const char *, struct bt_arg *);
737aa3827dSmpi struct bt_arg	*bg_find(const char *);
747aa3827dSmpi struct bt_var	*bg_get(const char *);
7523160851Smpi 
7666f34ae4Smpi struct bt_arg	*bi_find(struct bt_arg *, unsigned long);
7766f34ae4Smpi 
787aa3827dSmpi struct bt_var	*bl_lookup(const char *);
797aa3827dSmpi struct bt_stmt	*bl_store(const char *, struct bt_arg *);
807aa3827dSmpi struct bt_arg	*bl_find(const char *);
817aa3827dSmpi 
827aa3827dSmpi struct bt_arg	*bm_find(const char *, struct bt_arg *);
837aa3827dSmpi struct bt_stmt	*bm_insert(const char *, struct bt_arg *, struct bt_arg *);
84adc7d920Smpi struct bt_stmt	*bm_op(enum bt_action, struct bt_arg *, struct bt_arg *);
8523160851Smpi 
869c84395dSmpi struct bt_stmt	*bh_inc(const char *, struct bt_arg *, struct bt_arg *);
879c84395dSmpi 
8823160851Smpi /*
8923160851Smpi  * Lexer
9023160851Smpi  */
9123160851Smpi const char	*pbuf;
9223160851Smpi size_t		 plen;
9323160851Smpi size_t		 pindex;
9423160851Smpi int		 perrors = 0;
9523160851Smpi 
9623160851Smpi typedef struct {
9723160851Smpi 	union {
9823160851Smpi 		long			 number;
9923160851Smpi 		int			 i;
10023160851Smpi 		const char		*string;
10123160851Smpi 		struct bt_probe		*probe;
10223160851Smpi 		struct bt_filter	*filter;
10323160851Smpi 		struct bt_stmt		*stmt;
10423160851Smpi 		struct bt_arg		*arg;
10523160851Smpi 	} v;
10623160851Smpi 	const char			*filename;
10723160851Smpi 	int				 lineno;
10823160851Smpi 	int				 colno;
10923160851Smpi } yystype;
11023160851Smpi #define YYSTYPE yystype
11123160851Smpi 
11223160851Smpi static void	 yyerror(const char *, ...);
11323160851Smpi static int	 yylex(void);
11485e665dcSanton 
115fe7ae692Sdv static int 	 pflag = 0;		/* probe parsing context flag */
116fe7ae692Sdv static int 	 beflag = 0;		/* BEGIN/END parsing context flag */
11723160851Smpi %}
11823160851Smpi 
11925efc3b0Smpi %token	<v.i>		ERROR ENDFILT
12025efc3b0Smpi %token	<v.i>		OP_EQ OP_NE OP_LE OP_LT OP_GE OP_GT OP_LAND OP_LOR
12123160851Smpi /* Builtins */
122*139d07b5Smpi %token	<v.i>		BUILTIN BEGIN ELSE END IF STR
123adc7d920Smpi /* Functions and Map operators */
1240e0170acSmpi %token  <v.i>		F_DELETE F_PRINT
125b005393aSdv %token	<v.i>		MFUNC FUNC0 FUNC1 FUNCN OP1 OP2 OP4 MOP0 MOP1
1261045066fSclaudio %token	<v.string>	STRING CSTRING GVAR LVAR
1271045066fSclaudio %token	<v.arg>		PVAR PNUM
12823160851Smpi %token	<v.number>	NUMBER
129adc7d920Smpi 
13037bd7b46Smpi %type	<v.i>		beginend
1311694fc34Smpi %type	<v.probe>	plist probe pname
1325fd09555Smpi %type	<v.filter>	filter
1334846dae3Smpi %type	<v.stmt>	action stmt stmtblck stmtlist block
1341045066fSclaudio %type	<v.arg>		vargs mentry mpat pargs
135b005393aSdv %type	<v.arg>		expr term fterm variable factor func
13623160851Smpi %%
13723160851Smpi 
13823160851Smpi grammar	: /* empty */
13923160851Smpi 	| grammar '\n'
14023160851Smpi 	| grammar rule
14123160851Smpi 	| grammar error
14223160851Smpi 	;
14323160851Smpi 
144fe7ae692Sdv rule	: plist filter action		{ br_new($1, $2, $3); beflag = 0; }
14523160851Smpi 	;
14623160851Smpi 
147f864534aSmpi beginend: BEGIN	| END ;
14823160851Smpi 
1491694fc34Smpi plist	: plist ',' probe		{ $$ = bp_append($1, $3); }
1501694fc34Smpi 	| probe
1511694fc34Smpi 	;
1521694fc34Smpi 
1535fd09555Smpi probe	: { pflag = 1; } pname		{ $$ = $2; pflag = 0; }
154fe7ae692Sdv 	| { beflag = 1; } beginend	{ $$ = bp_new(NULL, NULL, NULL, $2); }
1551694fc34Smpi 	;
15685e665dcSanton 
1575fd09555Smpi pname	: STRING ':' STRING ':' STRING	{ $$ = bp_new($1, $3, $5, 0); }
158*139d07b5Smpi 	| STRING ':' STRING ':' NUMBER	{ $$ = bp_new($1, $3, NULL, $5); }
15923160851Smpi 	;
16023160851Smpi 
1611045066fSclaudio mentry	: GVAR '[' vargs ']'		{ $$ = bm_find($1, $3); }
1627aa3827dSmpi 	;
1637aa3827dSmpi 
16489f39301Smpi mpat	: MOP0 '(' ')'			{ $$ = ba_new(NULL, $1); }
1656201e9b1Sdv 	| MOP1 '(' expr ')'		{ $$ = ba_new($3, $1); }
1666ca6ae3fSmpi 	| expr
16723160851Smpi 	;
16823160851Smpi 
16989f39301Smpi filter	: /* empty */			{ $$ = NULL; }
17089f39301Smpi 	| '/' expr ENDFILT		{ $$ = bc_new(NULL, B_AT_OP_NE, $2); }
17189f39301Smpi 	;
17289f39301Smpi 
17389f39301Smpi /*
17489f39301Smpi  * Give higher precedence to:
17589f39301Smpi  *  1. && and ||
17689f39301Smpi  *  2. ==, !=, <<, <, >=, >, +, =, &, ^, |
177b9c158acScheloha  *  3. *, /, %
17889f39301Smpi  */
17989f39301Smpi expr	: expr OP_LAND term	{ $$ = ba_op(B_AT_OP_LAND, $1, $3); }
18089f39301Smpi 	| expr OP_LOR term	{ $$ = ba_op(B_AT_OP_LOR, $1, $3); }
181adc7d920Smpi 	| term
1820dac42ecSmpi 	;
1830dac42ecSmpi 
18489f39301Smpi term	: term OP_EQ fterm	{ $$ = ba_op(B_AT_OP_EQ, $1, $3); }
18589f39301Smpi 	| term OP_NE fterm	{ $$ = ba_op(B_AT_OP_NE, $1, $3); }
18689f39301Smpi 	| term OP_LE fterm	{ $$ = ba_op(B_AT_OP_LE, $1, $3); }
18789f39301Smpi 	| term OP_LT fterm	{ $$ = ba_op(B_AT_OP_LT, $1, $3); }
18889f39301Smpi 	| term OP_GE fterm	{ $$ = ba_op(B_AT_OP_GE, $1, $3); }
18989f39301Smpi 	| term OP_GT fterm	{ $$ = ba_op(B_AT_OP_GT, $1, $3); }
19089f39301Smpi 	| term '+' fterm	{ $$ = ba_op(B_AT_OP_PLUS, $1, $3); }
19189f39301Smpi 	| term '-' fterm	{ $$ = ba_op(B_AT_OP_MINUS, $1, $3); }
19289f39301Smpi 	| term '&' fterm	{ $$ = ba_op(B_AT_OP_BAND, $1, $3); }
19389f39301Smpi 	| term '^' fterm	{ $$ = ba_op(B_AT_OP_XOR, $1, $3); }
19489f39301Smpi 	| term '|' fterm	{ $$ = ba_op(B_AT_OP_BOR, $1, $3); }
19589f39301Smpi 	| fterm
1965fe2fdbfSmpi 	;
1975fe2fdbfSmpi 
19889f39301Smpi fterm	: fterm '*' factor	{ $$ = ba_op(B_AT_OP_MULT, $1, $3); }
19989f39301Smpi 	| fterm '/' factor	{ $$ = ba_op(B_AT_OP_DIVIDE, $1, $3); }
200b9c158acScheloha 	| fterm '%' factor	{ $$ = ba_op(B_AT_OP_MODULO, $1, $3); }
20189f39301Smpi 	| factor
20289f39301Smpi 	;
20389f39301Smpi 
2041045066fSclaudio variable: LVAR			{ $$ = bl_find($1); }
2051045066fSclaudio 	| GVAR			{ $$ = bg_find($1); }
20666f34ae4Smpi 	| variable '.' NUMBER	{ $$ = bi_find($1, $3); }
2070e0170acSmpi 	;
2080e0170acSmpi 
20989f39301Smpi factor : '(' expr ')'		{ $$ = $2; }
21066f34ae4Smpi 	| '(' vargs ',' expr ')'{ $$ = ba_new(ba_append($2, $4), B_AT_TUPLE); }
21158afdee7Sdv 	| NUMBER		{ $$ = ba_new($1, B_AT_LONG); }
212f864534aSmpi 	| BUILTIN		{ $$ = ba_new(NULL, $1); }
2136f84e5f7Smpi 	| CSTRING		{ $$ = ba_new($1, B_AT_STR); }
2141045066fSclaudio 	| PVAR
2151045066fSclaudio 	| PNUM
2160e0170acSmpi 	| variable
2175fd09555Smpi 	| mentry
218b005393aSdv 	| func
2195fe2fdbfSmpi 	;
22088be68dbSmpi 
2211045066fSclaudio func	: STR '(' PVAR ')'		{ $$ = ba_new($3, B_AT_FN_STR); }
2221045066fSclaudio 	| STR '(' PVAR ',' expr ')'	{ $$ = ba_op(B_AT_FN_STR, $3, $5); }
223b005393aSdv 	;
22423160851Smpi 
2256201e9b1Sdv vargs	: expr
2266201e9b1Sdv 	| vargs ',' expr		{ $$ = ba_append($1, $3); }
22723160851Smpi 	;
22823160851Smpi 
22989f39301Smpi pargs	: expr
2301045066fSclaudio 	| GVAR ',' expr			{ $$ = ba_append(bg_find($1), $3); }
2318c6b709bSanton 	;
2328c6b709bSanton 
2331694fc34Smpi NL	: /* empty */
2341694fc34Smpi 	| '\n'
2351d6f9355Smpi 	;
2361d6f9355Smpi 
2371d6f9355Smpi stmt	: ';' NL			{ $$ = NULL; }
2381045066fSclaudio 	| GVAR '=' expr			{ $$ = bg_store($1, $3); }
2391045066fSclaudio 	| LVAR '=' expr			{ $$ = bl_store($1, $3); }
2401045066fSclaudio 	| GVAR '[' vargs ']' '=' mpat	{ $$ = bm_insert($1, $3, $6); }
241adc7d920Smpi 	| FUNCN '(' vargs ')'		{ $$ = bs_new($1, $3, NULL); }
2426201e9b1Sdv 	| FUNC1 '(' expr ')'		{ $$ = bs_new($1, $3, NULL); }
2430e0170acSmpi 	| MFUNC '(' variable ')'	{ $$ = bs_new($1, $3, NULL); }
244adc7d920Smpi 	| FUNC0 '(' ')'			{ $$ = bs_new($1, NULL, NULL); }
2457aa3827dSmpi 	| F_DELETE '(' mentry ')'	{ $$ = bm_op($1, $3, NULL); }
2465fd09555Smpi 	| F_PRINT '(' pargs ')'		{ $$ = bs_new($1, $3, NULL); }
2471045066fSclaudio 	| GVAR '=' OP1 '(' expr ')'	{ $$ = bh_inc($1, $5, NULL); }
2481045066fSclaudio 	| GVAR '=' OP4 '(' expr ',' vargs ')'	{ $$ = bh_inc($1, $5, $7); }
24923160851Smpi 	;
25023160851Smpi 
251ca41b749Smpi stmtblck: IF '(' expr ')' block			{ $$ = bt_new($3, $5, NULL); }
252ca41b749Smpi 	| IF '(' expr ')' block ELSE block	{ $$ = bt_new($3, $5, $7); }
253ca41b749Smpi 	| IF '(' expr ')' block ELSE stmtblck	{ $$ = bt_new($3, $5, $7); }
2544846dae3Smpi 	;
2554846dae3Smpi 
2564846dae3Smpi stmtlist: stmtlist stmtblck		{ $$ = bs_append($1, $2); }
257a710f35bSmpi 	| stmtlist stmt			{ $$ = bs_append($1, $2); }
2584846dae3Smpi 	| stmtblck
2594846dae3Smpi 	| stmt
2604846dae3Smpi 	;
2614846dae3Smpi 
262dd9124f8Smpi block	: action
2634846dae3Smpi 	| stmt ';'
26423160851Smpi 	;
26523160851Smpi 
26623160851Smpi action	: '{' stmtlist '}'		{ $$ = $2; }
267e817eab9Sdv 	| '{' '}'			{ $$ = NULL; }
26823160851Smpi 	;
26923160851Smpi 
27023160851Smpi %%
27123160851Smpi 
27258afdee7Sdv struct bt_arg*
2736ca6ae3fSmpi get_varg(int index)
2746ca6ae3fSmpi {
27558afdee7Sdv 	extern int nargs;
27658afdee7Sdv 	extern char **vargs;
27758afdee7Sdv 	const char *errstr = NULL;
27858afdee7Sdv 	long val;
2796ca6ae3fSmpi 
2801045066fSclaudio 	if (1 <= index && index <= nargs) {
28158afdee7Sdv 		val = (long)strtonum(vargs[index-1], LONG_MIN, LONG_MAX,
28258afdee7Sdv 		    &errstr);
28358afdee7Sdv 		if (errstr == NULL)
28458afdee7Sdv 			return ba_new(val, B_AT_LONG);
28558afdee7Sdv 		return ba_new(vargs[index-1], B_AT_STR);
28658afdee7Sdv 	}
2876ca6ae3fSmpi 
28858afdee7Sdv 	return ba_new(0L, B_AT_NIL);
28958afdee7Sdv }
29058afdee7Sdv 
29158afdee7Sdv struct bt_arg*
29258afdee7Sdv get_nargs(void)
29358afdee7Sdv {
29458afdee7Sdv 	extern int nargs;
29558afdee7Sdv 
29658afdee7Sdv 	return ba_new((long) nargs, B_AT_LONG);
2976ca6ae3fSmpi }
2986ca6ae3fSmpi 
29923160851Smpi /* Create a new rule, representing  "probe / filter / { action }" */
30023160851Smpi struct bt_rule *
3011694fc34Smpi br_new(struct bt_probe *probe, struct bt_filter *filter, struct bt_stmt *head)
30223160851Smpi {
30323160851Smpi 	struct bt_rule *br;
30423160851Smpi 
305ea0c567aSmpi 	br = calloc(1, sizeof(*br));
30623160851Smpi 	if (br == NULL)
30723160851Smpi 		err(1, "bt_rule: calloc");
3081694fc34Smpi 	/* SLIST_INSERT_HEAD() nullify the next pointer. */
3091694fc34Smpi 	SLIST_FIRST(&br->br_probes) = probe;
31023160851Smpi 	br->br_filter = filter;
31123160851Smpi 	/* SLIST_INSERT_HEAD() nullify the next pointer. */
31223160851Smpi 	SLIST_FIRST(&br->br_action) = head;
31323160851Smpi 
3147aa3827dSmpi 	SLIST_FIRST(&br->br_variables) = SLIST_FIRST(&l_variables);
3157aa3827dSmpi 	SLIST_INIT(&l_variables);
3167aa3827dSmpi 
3171694fc34Smpi 	do {
3181694fc34Smpi 		if (probe->bp_type != B_PT_PROBE)
3191694fc34Smpi 			continue;
32023160851Smpi 		g_nprobes++;
3211694fc34Smpi 	} while ((probe = SLIST_NEXT(probe, bp_next)) != NULL);
3221694fc34Smpi 
32323160851Smpi 	TAILQ_INSERT_TAIL(&g_rules, br, br_next);
32423160851Smpi 
32523160851Smpi 	return br;
32623160851Smpi }
32723160851Smpi 
328ea0c567aSmpi /* Create a new condition */
329ea0c567aSmpi struct bt_filter *
330822ace7eSmpi bc_new(struct bt_arg *term, enum bt_argtype op, struct bt_arg *ba)
331ea0c567aSmpi {
332ea0c567aSmpi 	struct bt_filter *bf;
333ea0c567aSmpi 
334ea0c567aSmpi 	bf = calloc(1, sizeof(*bf));
335ea0c567aSmpi 	if (bf == NULL)
336ea0c567aSmpi 		err(1, "bt_filter: calloc");
337ea0c567aSmpi 
338822ace7eSmpi 	bf->bf_condition = bs_new(B_AC_TEST, ba_op(op, term, ba), NULL);
339ea0c567aSmpi 
340ea0c567aSmpi 	return bf;
34123160851Smpi }
34223160851Smpi 
3434846dae3Smpi /* Create a new if/else test */
3444846dae3Smpi struct bt_stmt *
345ca41b749Smpi bt_new(struct bt_arg *ba, struct bt_stmt *condbs, struct bt_stmt *elsebs)
3464846dae3Smpi {
3474846dae3Smpi 	struct bt_arg *bop;
348ca41b749Smpi 	struct bt_cond *bc;
3494846dae3Smpi 
3504846dae3Smpi 	bop = ba_op(B_AT_OP_NE, NULL, ba);
3514846dae3Smpi 
352ca41b749Smpi 	bc = calloc(1, sizeof(*bc));
353ca41b749Smpi 	if (bc == NULL)
354ca41b749Smpi 		err(1, "bt_cond: calloc");
355ca41b749Smpi 	bc->bc_condbs = condbs;
356ca41b749Smpi 	bc->bc_elsebs = elsebs;
3574846dae3Smpi 
358ca41b749Smpi 	return bs_new(B_AC_TEST, bop, (struct bt_var *)bc);
3594846dae3Smpi }
360ca41b749Smpi 
361*139d07b5Smpi /*
362*139d07b5Smpi  * interval and profile support the same units.
363*139d07b5Smpi  */
364*139d07b5Smpi static uint64_t
365*139d07b5Smpi bp_unit_to_nsec(const char *unit, long value)
366*139d07b5Smpi {
367*139d07b5Smpi 	static const struct {
368*139d07b5Smpi 		const char *name;
369*139d07b5Smpi 		enum { UNIT_HZ, UNIT_US, UNIT_MS, UNIT_S } id;
370*139d07b5Smpi 		long long max;
371*139d07b5Smpi 	} units[] = {
372*139d07b5Smpi 		{ .name = "hz", .id = UNIT_HZ, .max = 1000000LL },
373*139d07b5Smpi 		{ .name = "us", .id = UNIT_US, .max = LLONG_MAX / 1000 },
374*139d07b5Smpi 		{ .name = "ms", .id = UNIT_MS, .max = LLONG_MAX / 1000000 },
375*139d07b5Smpi 		{ .name = "s", .id = UNIT_S, .max = LLONG_MAX / 1000000000 },
376*139d07b5Smpi 	};
377*139d07b5Smpi 	size_t i;
378*139d07b5Smpi 
379*139d07b5Smpi 	for (i = 0; i < nitems(units); i++) {
380*139d07b5Smpi 		if (strcmp(units[i].name, unit) == 0) {
381*139d07b5Smpi 			if (value < 1)
382*139d07b5Smpi 				yyerror("Number is invalid: %ld", value);
383*139d07b5Smpi 			if (value > units[i].max)
384*139d07b5Smpi 				yyerror("Number is too large: %ld", value);
385*139d07b5Smpi 			switch (units[i].id) {
386*139d07b5Smpi 			case UNIT_HZ:
387*139d07b5Smpi 				return (1000000000LLU / value);
388*139d07b5Smpi 			case UNIT_US:
389*139d07b5Smpi 				return (value * 1000LLU);
390*139d07b5Smpi 			case UNIT_MS:
391*139d07b5Smpi 				return (value * 1000000LLU);
392*139d07b5Smpi 			case UNIT_S:
393*139d07b5Smpi 				return (value * 1000000000LLU);
394*139d07b5Smpi 			}
395*139d07b5Smpi 		}
396*139d07b5Smpi 	}
397*139d07b5Smpi 	yyerror("Invalid unit: %s", unit);
398*139d07b5Smpi 	return 0;
399*139d07b5Smpi }
400*139d07b5Smpi 
40123160851Smpi /* Create a new probe */
40223160851Smpi struct bt_probe *
403*139d07b5Smpi bp_new(const char *prov, const char *func, const char *name, long number)
40423160851Smpi {
40523160851Smpi 	struct bt_probe *bp;
4061694fc34Smpi 	enum bt_ptype ptype;
40723160851Smpi 
4081694fc34Smpi 	if (prov == NULL && func == NULL && name == NULL)
409*139d07b5Smpi 		ptype = number; /* BEGIN or END */
4101694fc34Smpi 	else
4111694fc34Smpi 		ptype = B_PT_PROBE;
4121694fc34Smpi 
413ea0c567aSmpi 	bp = calloc(1, sizeof(*bp));
41423160851Smpi 	if (bp == NULL)
41523160851Smpi 		err(1, "bt_probe: calloc");
41623160851Smpi 	bp->bp_prov = prov;
41723160851Smpi 	bp->bp_func = func;
41823160851Smpi 	bp->bp_name = name;
419*139d07b5Smpi 	if (ptype == B_PT_PROBE && name == NULL)
420*139d07b5Smpi 		bp->bp_nsecs = bp_unit_to_nsec(func, number);
4211694fc34Smpi 	bp->bp_type = ptype;
42223160851Smpi 
42323160851Smpi 	return bp;
42423160851Smpi }
42523160851Smpi 
4261694fc34Smpi /*
4271694fc34Smpi  * Link two probes together, to build a probe list attached to
4281694fc34Smpi  * a single action.
4291694fc34Smpi  */
4301694fc34Smpi struct bt_probe *
4311694fc34Smpi bp_append(struct bt_probe *bp0, struct bt_probe *bp1)
4321694fc34Smpi {
4331694fc34Smpi 	struct bt_probe *bp = bp0;
4341694fc34Smpi 
4351694fc34Smpi 	assert(bp1 != NULL);
4361694fc34Smpi 
4371694fc34Smpi 	if (bp0 == NULL)
4381694fc34Smpi 		return bp1;
4391694fc34Smpi 
4401694fc34Smpi 	while (SLIST_NEXT(bp, bp_next) != NULL)
4411694fc34Smpi 		bp = SLIST_NEXT(bp, bp_next);
4421694fc34Smpi 
4431694fc34Smpi 	SLIST_INSERT_AFTER(bp, bp1, bp_next);
4441694fc34Smpi 
4451694fc34Smpi 	return bp0;
4461694fc34Smpi }
4471694fc34Smpi 
44823160851Smpi /* Create a new argument */
44923160851Smpi struct bt_arg *
45023160851Smpi ba_new0(void *val, enum bt_argtype type)
45123160851Smpi {
45223160851Smpi 	struct bt_arg *ba;
45323160851Smpi 
454ea0c567aSmpi 	ba = calloc(1, sizeof(*ba));
45523160851Smpi 	if (ba == NULL)
45623160851Smpi 		err(1, "bt_arg: calloc");
45723160851Smpi 	ba->ba_value = val;
45823160851Smpi 	ba->ba_type = type;
45923160851Smpi 
46023160851Smpi 	return ba;
46123160851Smpi }
46223160851Smpi 
46323160851Smpi /*
46423160851Smpi  * Link two arguments together, to build an argument list used in
46566f34ae4Smpi  * operators, tuples and function calls.
46623160851Smpi  */
46723160851Smpi struct bt_arg *
468a710f35bSmpi ba_append(struct bt_arg *da0, struct bt_arg *da1)
46923160851Smpi {
47023160851Smpi 	struct bt_arg *ba = da0;
47123160851Smpi 
47223160851Smpi 	assert(da1 != NULL);
47323160851Smpi 
47423160851Smpi 	if (da0 == NULL)
47523160851Smpi 		return da1;
47623160851Smpi 
47723160851Smpi 	while (SLIST_NEXT(ba, ba_next) != NULL)
47823160851Smpi 		ba = SLIST_NEXT(ba, ba_next);
47923160851Smpi 
48023160851Smpi 	SLIST_INSERT_AFTER(ba, da1, ba_next);
48123160851Smpi 
48223160851Smpi 	return da0;
48323160851Smpi }
48423160851Smpi 
48523160851Smpi /* Create an operator argument */
48623160851Smpi struct bt_arg *
487822ace7eSmpi ba_op(enum bt_argtype op, struct bt_arg *da0, struct bt_arg *da1)
48823160851Smpi {
489822ace7eSmpi 	return ba_new(ba_append(da0, da1), op);
49023160851Smpi }
49123160851Smpi 
49223160851Smpi /* Create a new statement: function call or assignment. */
49323160851Smpi struct bt_stmt *
49423160851Smpi bs_new(enum bt_action act, struct bt_arg *head, struct bt_var *var)
49523160851Smpi {
49623160851Smpi 	struct bt_stmt *bs;
49723160851Smpi 
498ea0c567aSmpi 	bs = calloc(1, sizeof(*bs));
49923160851Smpi 	if (bs == NULL)
50023160851Smpi 		err(1, "bt_stmt: calloc");
50123160851Smpi 	bs->bs_act = act;
50223160851Smpi 	bs->bs_var = var;
50323160851Smpi 	/* SLIST_INSERT_HEAD() nullify the next pointer. */
50423160851Smpi 	SLIST_FIRST(&bs->bs_args) = head;
50523160851Smpi 
50623160851Smpi 	return bs;
50723160851Smpi }
50823160851Smpi 
50923160851Smpi /* Link two statements together, to build an 'action'. */
51023160851Smpi struct bt_stmt *
511a710f35bSmpi bs_append(struct bt_stmt *ds0, struct bt_stmt *ds1)
51223160851Smpi {
51323160851Smpi 	struct bt_stmt *bs = ds0;
51423160851Smpi 
51523160851Smpi 	if (ds0 == NULL)
51623160851Smpi 		return ds1;
51723160851Smpi 
5181d6f9355Smpi 	if (ds1 == NULL)
5191d6f9355Smpi 		return ds0;
5201d6f9355Smpi 
52123160851Smpi 	while (SLIST_NEXT(bs, bs_next) != NULL)
52223160851Smpi 		bs = SLIST_NEXT(bs, bs_next);
52323160851Smpi 
52423160851Smpi 	SLIST_INSERT_AFTER(bs, ds1, bs_next);
52523160851Smpi 
52623160851Smpi 	return ds0;
52723160851Smpi }
52823160851Smpi 
529477314cbSmpi const char *
530477314cbSmpi bv_name(struct bt_var *bv)
531477314cbSmpi {
532477314cbSmpi 	if (strncmp(bv->bv_name, UNNAMED_MAP, strlen(UNNAMED_MAP)) == 0)
533477314cbSmpi 		return "";
534477314cbSmpi 	return bv->bv_name;
535477314cbSmpi }
536477314cbSmpi 
5377aa3827dSmpi /* Allocate a variable. */
5387aa3827dSmpi struct bt_var *
5397aa3827dSmpi bv_new(const char *vname)
5407aa3827dSmpi {
5417aa3827dSmpi 	struct bt_var *bv;
5427aa3827dSmpi 
5437aa3827dSmpi 	bv = calloc(1, sizeof(*bv));
5447aa3827dSmpi 	if (bv == NULL)
5457aa3827dSmpi 		err(1, "bt_var: calloc");
5467aa3827dSmpi 	bv->bv_name = vname;
5477aa3827dSmpi 
5487aa3827dSmpi 	return bv;
5497aa3827dSmpi }
5507aa3827dSmpi 
55123160851Smpi /* Return the global variable corresponding to `vname'. */
55223160851Smpi struct bt_var *
5537aa3827dSmpi bg_lookup(const char *vname)
55423160851Smpi {
55523160851Smpi 	struct bt_var *bv;
55623160851Smpi 
55723160851Smpi 	SLIST_FOREACH(bv, &g_variables, bv_next) {
55823160851Smpi 		if (strcmp(vname, bv->bv_name) == 0)
55923160851Smpi 			break;
56023160851Smpi 	}
56123160851Smpi 
56223160851Smpi 	return bv;
56323160851Smpi }
56423160851Smpi 
5657aa3827dSmpi /* Find or allocate a global variable corresponding to `vname' */
56623160851Smpi struct bt_var *
5677aa3827dSmpi bg_get(const char *vname)
56823160851Smpi {
56923160851Smpi 	struct bt_var *bv;
57023160851Smpi 
5717aa3827dSmpi 	bv = bg_lookup(vname);
5727aa3827dSmpi 	if (bv == NULL) {
5737aa3827dSmpi 		bv = bv_new(vname);
57423160851Smpi 		SLIST_INSERT_HEAD(&g_variables, bv, bv_next);
5757aa3827dSmpi 	}
57623160851Smpi 
57723160851Smpi 	return bv;
57823160851Smpi }
57923160851Smpi 
5807aa3827dSmpi /* Create an "argument" that points to an existing untyped variable. */
58123160851Smpi struct bt_arg *
5827aa3827dSmpi bg_find(const char *vname)
58323160851Smpi {
584122a1b33Smpi 	return ba_new(bg_get(vname), B_AT_VAR);
58523160851Smpi }
58623160851Smpi 
5877aa3827dSmpi /* Create a 'store' statement to assign a value to a global variable. */
5887aa3827dSmpi struct bt_stmt *
5897aa3827dSmpi bg_store(const char *vname, struct bt_arg *vval)
5907aa3827dSmpi {
5917aa3827dSmpi 	return bs_new(B_AC_STORE, vval, bg_get(vname));
5927aa3827dSmpi }
5937aa3827dSmpi 
5947aa3827dSmpi /* Return the local variable corresponding to `vname'. */
5957aa3827dSmpi struct bt_var *
5967aa3827dSmpi bl_lookup(const char *vname)
5977aa3827dSmpi {
5987aa3827dSmpi 	struct bt_var *bv;
5997aa3827dSmpi 
6007aa3827dSmpi 	SLIST_FOREACH(bv, &l_variables, bv_next) {
6017aa3827dSmpi 		if (strcmp(vname, bv->bv_name) == 0)
6027aa3827dSmpi 			break;
6037aa3827dSmpi 	}
6047aa3827dSmpi 
6057aa3827dSmpi 	return bv;
6067aa3827dSmpi }
6077aa3827dSmpi 
6087aa3827dSmpi /* Find or create a local variable corresponding to `vname' */
6097aa3827dSmpi struct bt_arg *
6107aa3827dSmpi bl_find(const char *vname)
6117aa3827dSmpi {
6127aa3827dSmpi 	struct bt_var *bv;
6137aa3827dSmpi 
6147aa3827dSmpi 	bv = bl_lookup(vname);
6157aa3827dSmpi 	if (bv == NULL) {
6167aa3827dSmpi 		bv = bv_new(vname);
6177aa3827dSmpi 		SLIST_INSERT_HEAD(&l_variables, bv, bv_next);
6187aa3827dSmpi 	}
6197aa3827dSmpi 
6207aa3827dSmpi 	return ba_new(bv, B_AT_VAR);
6217aa3827dSmpi }
6227aa3827dSmpi 
6237aa3827dSmpi /* Create a 'store' statement to assign a value to a local variable. */
6247aa3827dSmpi struct bt_stmt *
6257aa3827dSmpi bl_store(const char *vname, struct bt_arg *vval)
6267aa3827dSmpi {
6277aa3827dSmpi 	struct bt_var *bv;
6287aa3827dSmpi 
6297aa3827dSmpi 	bv = bl_lookup(vname);
6307aa3827dSmpi 	if (bv == NULL) {
6317aa3827dSmpi 		bv = bv_new(vname);
6327aa3827dSmpi 		SLIST_INSERT_HEAD(&l_variables, bv, bv_next);
6337aa3827dSmpi 	}
6347aa3827dSmpi 
6357aa3827dSmpi 	return bs_new(B_AC_STORE, vval, bv);
6367aa3827dSmpi }
6377aa3827dSmpi 
63866f34ae4Smpi /* Create an argument that points to a tuple variable and a given index */
63966f34ae4Smpi struct bt_arg *
64066f34ae4Smpi bi_find(struct bt_arg *ba, unsigned long index)
64166f34ae4Smpi {
64266f34ae4Smpi 	struct bt_var *bv = ba->ba_value;
64366f34ae4Smpi 
64466f34ae4Smpi 	ba = ba_new(bv, B_AT_TMEMBER);
64566f34ae4Smpi 	ba->ba_key = (void *)index;
64666f34ae4Smpi 	return ba;
64766f34ae4Smpi }
64866f34ae4Smpi 
64923160851Smpi struct bt_stmt *
650adc7d920Smpi bm_op(enum bt_action mact, struct bt_arg *ba, struct bt_arg *mval)
65123160851Smpi {
65288be68dbSmpi 	return bs_new(mact, ba, (struct bt_var *)mval);
65323160851Smpi }
65423160851Smpi 
65523160851Smpi /* Create a 'map store' statement to assign a value to a map entry. */
65623160851Smpi struct bt_stmt *
6577aa3827dSmpi bm_insert(const char *mname, struct bt_arg *mkey, struct bt_arg *mval)
65823160851Smpi {
65988be68dbSmpi 	struct bt_arg *ba;
66088be68dbSmpi 
661273c317dSmpi 	if (mkey->ba_type == B_AT_TUPLE)
662273c317dSmpi 		yyerror("tuple cannot be used as map key");
663273c317dSmpi 
6647aa3827dSmpi 	ba = ba_new(bg_get(mname), B_AT_MAP);
66588be68dbSmpi 	ba->ba_key = mkey;
6667aa3827dSmpi 
66788be68dbSmpi 	return bs_new(B_AC_INSERT, ba, (struct bt_var *)mval);
66888be68dbSmpi }
66988be68dbSmpi 
67066f34ae4Smpi /* Create an argument that points to a map variable and attach a key to it. */
67188be68dbSmpi struct bt_arg *
6727aa3827dSmpi bm_find(const char *vname, struct bt_arg *mkey)
67388be68dbSmpi {
67488be68dbSmpi 	struct bt_arg *ba;
67588be68dbSmpi 
676122a1b33Smpi 	ba = ba_new(bg_get(vname), B_AT_MAP);
67788be68dbSmpi 	ba->ba_key = mkey;
67888be68dbSmpi 	return ba;
67923160851Smpi }
68023160851Smpi 
6819c84395dSmpi /*
6829c84395dSmpi  * Histograms implemented using associative arrays (maps).  In the case
6839c84395dSmpi  * of linear histograms `ba_key' points to a list of (min, max, step)
6849c84395dSmpi  * necessary to "bucketize" any value.
6859c84395dSmpi  */
6869c84395dSmpi struct bt_stmt *
6879c84395dSmpi bh_inc(const char *hname, struct bt_arg *hval, struct bt_arg *hrange)
6889c84395dSmpi {
6899c84395dSmpi 	struct bt_arg *ba;
6909c84395dSmpi 
6919c84395dSmpi 	if (hrange == NULL) {
6929c84395dSmpi 		/* Power-of-2 histogram */
6939c84395dSmpi 	} else {
6949f622a61Smpi 		long min = 0, max;
6959c84395dSmpi 		int count = 0;
6969c84395dSmpi 
6979c84395dSmpi 		/* Linear histogram */
6989c84395dSmpi 		for (ba = hrange; ba != NULL; ba = SLIST_NEXT(ba, ba_next)) {
6999c84395dSmpi 			if (++count > 3)
7009c84395dSmpi 				yyerror("too many arguments");
7019c84395dSmpi 			if (ba->ba_type != B_AT_LONG)
7029c84395dSmpi 				yyerror("type invalid");
7039c84395dSmpi 
7049c84395dSmpi 			switch (count) {
7059c84395dSmpi 			case 1:
7069c84395dSmpi 				min = (long)ba->ba_value;
7079c84395dSmpi 				if (min >= 0)
7089c84395dSmpi 					break;
7093a50f0a9Sjmc 				yyerror("negative minimum");
7109c84395dSmpi 			case 2:
7119c84395dSmpi 				max = (long)ba->ba_value;
7129c84395dSmpi 				if (max > min)
7139c84395dSmpi 					break;
7143a50f0a9Sjmc 				yyerror("maximum smaller than minimum (%d < %d)",
7159c84395dSmpi 				    max,  min);
7169c84395dSmpi 			case 3:
7179c84395dSmpi 				break;
7189c84395dSmpi 			default:
7199c84395dSmpi 				assert(0);
7209c84395dSmpi 			}
7219c84395dSmpi 		}
7229c84395dSmpi 		if (count < 3)
7239c84395dSmpi 			yyerror("%d missing arguments", 3 - count);
7249c84395dSmpi 	}
7259c84395dSmpi 
7267aa3827dSmpi 	ba = ba_new(bg_get(hname), B_AT_HIST);
7279c84395dSmpi 	ba->ba_key = hrange;
7289c84395dSmpi 	return bs_new(B_AC_BUCKETIZE, ba, (struct bt_var *)hval);
7299c84395dSmpi }
7309c84395dSmpi 
73123160851Smpi struct keyword {
73223160851Smpi 	const char	*word;
73323160851Smpi 	int		 token;
734adc7d920Smpi 	int		 type;
73523160851Smpi };
73623160851Smpi 
73723160851Smpi int
73823160851Smpi kw_cmp(const void *str, const void *xkw)
73923160851Smpi {
74023160851Smpi 	return (strcmp(str, ((const struct keyword *)xkw)->word));
74123160851Smpi }
74223160851Smpi 
743adc7d920Smpi struct keyword *
74423160851Smpi lookup(char *s)
74523160851Smpi {
74623160851Smpi 	static const struct keyword kws[] = {
7471694fc34Smpi 		{ "BEGIN",	BEGIN,		B_PT_BEGIN },
7481694fc34Smpi 		{ "END",	END,		B_PT_END },
749adc7d920Smpi 		{ "arg0",	BUILTIN,	B_AT_BI_ARG0 },
750adc7d920Smpi 		{ "arg1",	BUILTIN,	B_AT_BI_ARG1 },
751adc7d920Smpi 		{ "arg2",	BUILTIN,	B_AT_BI_ARG2 },
752adc7d920Smpi 		{ "arg3",	BUILTIN,	B_AT_BI_ARG3 },
753adc7d920Smpi 		{ "arg4",	BUILTIN,	B_AT_BI_ARG4 },
754adc7d920Smpi 		{ "arg5",	BUILTIN,	B_AT_BI_ARG5 },
755adc7d920Smpi 		{ "arg6",	BUILTIN,	B_AT_BI_ARG6 },
756adc7d920Smpi 		{ "arg7",	BUILTIN,	B_AT_BI_ARG7 },
757adc7d920Smpi 		{ "arg8",	BUILTIN,	B_AT_BI_ARG8 },
758adc7d920Smpi 		{ "arg9",	BUILTIN,	B_AT_BI_ARG9 },
7590e0170acSmpi 		{ "clear",	MFUNC,		B_AC_CLEAR },
760adc7d920Smpi 		{ "comm",	BUILTIN,	B_AT_BI_COMM },
761adc7d920Smpi 		{ "count",	MOP0, 		B_AT_MF_COUNT },
762adc7d920Smpi 		{ "cpu",	BUILTIN,	B_AT_BI_CPU },
763adc7d920Smpi 		{ "delete",	F_DELETE,	B_AC_DELETE },
764ca41b749Smpi 		{ "else",	ELSE,		0 },
765adc7d920Smpi 		{ "exit",	FUNC0,		B_AC_EXIT },
7669c84395dSmpi 		{ "hist",	OP1,		0 },
7674846dae3Smpi 		{ "if",		IF,		0 },
768adc7d920Smpi 		{ "kstack",	BUILTIN,	B_AT_BI_KSTACK },
7699c84395dSmpi 		{ "lhist",	OP4,		0 },
770adc7d920Smpi 		{ "max",	MOP1,		B_AT_MF_MAX },
771adc7d920Smpi 		{ "min",	MOP1,		B_AT_MF_MIN },
772adc7d920Smpi 		{ "nsecs",	BUILTIN,	B_AT_BI_NSECS },
773f864534aSmpi 		{ "pid",	BUILTIN,	B_AT_BI_PID },
7748c6b709bSanton 		{ "print",	F_PRINT,	B_AC_PRINT },
775adc7d920Smpi 		{ "printf",	FUNCN,		B_AC_PRINTF },
776ca210abeSclaudio 		{ "probe",	BUILTIN,	B_AT_BI_PROBE },
777adc7d920Smpi 		{ "retval",	BUILTIN,	B_AT_BI_RETVAL },
778b005393aSdv 		{ "str",	STR,		B_AT_FN_STR },
779adc7d920Smpi 		{ "sum",	MOP1,		B_AT_MF_SUM },
780f864534aSmpi 		{ "tid",	BUILTIN,	B_AT_BI_TID },
781adc7d920Smpi 		{ "time",	FUNC1,		B_AC_TIME },
782adc7d920Smpi 		{ "ustack",	BUILTIN,	B_AT_BI_USTACK },
7830e0170acSmpi 		{ "zero",	MFUNC,		B_AC_ZERO },
78423160851Smpi 	};
78523160851Smpi 
786adc7d920Smpi 	return bsearch(s, kws, nitems(kws), sizeof(kws[0]), kw_cmp);
78723160851Smpi }
78823160851Smpi 
78923160851Smpi int
79023160851Smpi peek(void)
79123160851Smpi {
79223160851Smpi 	if (pbuf != NULL) {
79323160851Smpi 		if (pindex < plen)
79423160851Smpi 			return pbuf[pindex];
79523160851Smpi 	}
79623160851Smpi 	return EOF;
79723160851Smpi }
79823160851Smpi 
79923160851Smpi int
80023160851Smpi lgetc(void)
80123160851Smpi {
80223160851Smpi 	if (pbuf != NULL) {
80323160851Smpi 		if (pindex < plen) {
80423160851Smpi 			yylval.colno++;
80523160851Smpi 			return pbuf[pindex++];
80623160851Smpi 		}
80723160851Smpi 	}
80823160851Smpi 	return EOF;
80923160851Smpi }
81023160851Smpi 
81123160851Smpi void
81223160851Smpi lungetc(void)
81323160851Smpi {
81423160851Smpi 	if (pbuf != NULL && pindex > 0) {
81523160851Smpi 		yylval.colno--;
81623160851Smpi 		pindex--;
81723160851Smpi 	}
81823160851Smpi }
81923160851Smpi 
8201045066fSclaudio static inline int
8211045066fSclaudio allowed_to_end_number(int x)
8221045066fSclaudio {
8231045066fSclaudio 	return (isspace(x) || x == ')' || x == '/' || x == '{' || x == ';' ||
8241045066fSclaudio 	    x == ']' || x == ',' || x == '=');
8251045066fSclaudio }
8261045066fSclaudio 
8271045066fSclaudio static inline int
8281045066fSclaudio allowed_in_string(int x)
8291045066fSclaudio {
8301045066fSclaudio 	return (isalnum(x) || x == '_');
8311045066fSclaudio }
8321045066fSclaudio 
83353407cb0Smpi static int
83453407cb0Smpi skip(void)
83523160851Smpi {
836adc7d920Smpi 	int c;
83723160851Smpi 
83823160851Smpi again:
83923160851Smpi 	/* skip whitespaces */
84023160851Smpi 	for (c = lgetc(); isspace(c); c = lgetc()) {
84123160851Smpi 		if (c == '\n') {
84223160851Smpi 			yylval.lineno++;
84323160851Smpi 			yylval.colno = 0;
84423160851Smpi 		}
84523160851Smpi 	}
84623160851Smpi 
84770d67bf8Smpi 	/* skip single line comments and shell magic */
84870d67bf8Smpi 	if ((c == '/' && peek() == '/') ||
84970d67bf8Smpi 	    (yylval.lineno == 1 && yylval.colno == 1 && c == '#' &&
85070d67bf8Smpi 	     peek() == '!')) {
85123160851Smpi 		for (c = lgetc(); c != EOF; c = lgetc()) {
85223160851Smpi 			if (c == '\n') {
85323160851Smpi 				yylval.lineno++;
85423160851Smpi 				yylval.colno = 0;
85523160851Smpi 				goto again;
85623160851Smpi 			}
85723160851Smpi 		}
85823160851Smpi 	}
85923160851Smpi 
86023160851Smpi 	/* skip multi line comments */
86123160851Smpi 	if (c == '/' && peek() == '*') {
86223160851Smpi 		int pc;
86323160851Smpi 
86423160851Smpi 		for (pc = 0, c = lgetc(); c != EOF; c = lgetc()) {
86523160851Smpi 			if (pc == '*' && c == '/')
86623160851Smpi 				goto again;
867e9c69d2bSmpi 			else if (c == '\n')
868e9c69d2bSmpi 				yylval.lineno++;
86923160851Smpi 			pc = c;
87023160851Smpi 		}
87123160851Smpi 	}
87223160851Smpi 
87353407cb0Smpi 	return c;
87453407cb0Smpi }
87553407cb0Smpi 
87653407cb0Smpi int
87753407cb0Smpi yylex(void)
87853407cb0Smpi {
87953407cb0Smpi 	unsigned char	 buf[1024];
88053407cb0Smpi 	unsigned char	*ebuf, *p, *str;
88153407cb0Smpi 	int		 c;
88253407cb0Smpi 
88353407cb0Smpi 	ebuf = buf + sizeof(buf);
88453407cb0Smpi 	p = buf;
88553407cb0Smpi 
88653407cb0Smpi again:
88753407cb0Smpi 	c = skip();
88853407cb0Smpi 
88923160851Smpi 	switch (c) {
890a31d03f7Smpi 	case '!':
89109b6fe41Smpi 	case '=':
892a31d03f7Smpi 		if (peek() == '=') {
893a31d03f7Smpi 			lgetc();
894a31d03f7Smpi 			return (c == '=') ? OP_EQ : OP_NE;
895a31d03f7Smpi 		}
89625efc3b0Smpi 		return c;
89725efc3b0Smpi 	case '<':
89825efc3b0Smpi 		if (peek() == '=') {
89925efc3b0Smpi 			lgetc();
90025efc3b0Smpi 			return OP_LE;
90125efc3b0Smpi 		}
90225efc3b0Smpi 		return OP_LT;
90325efc3b0Smpi 	case '>':
90425efc3b0Smpi 		if (peek() == '=') {
90525efc3b0Smpi 			lgetc();
90625efc3b0Smpi 			return OP_GE;
90725efc3b0Smpi 		}
90825efc3b0Smpi 		return OP_GT;
909a31d03f7Smpi 	case '&':
910a31d03f7Smpi 		if (peek() == '&') {
911a31d03f7Smpi 			lgetc();
912a31d03f7Smpi 			return OP_LAND;
913a31d03f7Smpi 		}
91425efc3b0Smpi 		return c;
915a31d03f7Smpi 	case '|':
916a31d03f7Smpi 		if (peek() == '|') {
917a31d03f7Smpi 			lgetc();
918a31d03f7Smpi 			return OP_LOR;
919a31d03f7Smpi 		}
92025efc3b0Smpi 		return c;
9215fe2fdbfSmpi 	case '/':
9223a789416Sdv 		while (isspace(peek())) {
9233a789416Sdv 			if (lgetc() == '\n') {
9243a789416Sdv 				yylval.lineno++;
9253a789416Sdv 				yylval.colno = 0;
9265fe2fdbfSmpi 			}
9273a789416Sdv 		}
9283a789416Sdv 		if (peek() == '{' || peek() == '/' || peek() == '\n')
9293a789416Sdv 			return ENDFILT;
9305fe2fdbfSmpi 		/* FALLTHROUGH */
93123160851Smpi 	case ',':
93223160851Smpi 	case '(':
93323160851Smpi 	case ')':
93423160851Smpi 	case '{':
93523160851Smpi 	case '}':
93623160851Smpi 	case ':':
93723160851Smpi 	case ';':
93823160851Smpi 		return c;
9391045066fSclaudio 	case '$':
9401045066fSclaudio 		c = lgetc();
9411045066fSclaudio 		if (c == '#') {
9421045066fSclaudio 			yylval.v.arg = get_nargs();
9431045066fSclaudio 			return PNUM;
9441045066fSclaudio 		} else if (isdigit(c)) {
9451045066fSclaudio 			do {
9461045066fSclaudio 				*p++ = c;
9471045066fSclaudio 				if (p == ebuf) {
9481045066fSclaudio 					yyerror("line too long");
9491045066fSclaudio 					return ERROR;
9501045066fSclaudio 				}
9511045066fSclaudio 			} while ((c = lgetc()) != EOF && isdigit(c));
9521045066fSclaudio 			lungetc();
9531045066fSclaudio 			*p = '\0';
9541045066fSclaudio 			if (c == EOF || allowed_to_end_number(c)) {
9551045066fSclaudio 				const char *errstr = NULL;
9561045066fSclaudio 				int num;
9571045066fSclaudio 
9581045066fSclaudio 				num = strtonum(buf, 1, INT_MAX, &errstr);
9591045066fSclaudio 				if (errstr) {
9601045066fSclaudio 					yyerror("'$%s' is %s", buf, errstr);
9611045066fSclaudio 					return ERROR;
9621045066fSclaudio 				}
9631045066fSclaudio 
9641045066fSclaudio 				yylval.v.arg = get_varg(num);
9651045066fSclaudio 				return PVAR;
9661045066fSclaudio 			}
9671045066fSclaudio 		} else if (isalpha(c)) {
9681045066fSclaudio 			do {
9691045066fSclaudio 				*p++ = c;
9701045066fSclaudio 				if (p == ebuf) {
9711045066fSclaudio 					yyerror("line too long");
9721045066fSclaudio 					return ERROR;
9731045066fSclaudio 				}
9741045066fSclaudio 			} while ((c = lgetc()) != EOF && allowed_in_string(c));
9751045066fSclaudio 			lungetc();
9761045066fSclaudio 			*p = '\0';
9771045066fSclaudio 			if ((yylval.v.string = strdup(buf)) == NULL)
9781045066fSclaudio 				err(1, "%s", __func__);
9791045066fSclaudio 			return LVAR;
9801045066fSclaudio 		}
9811045066fSclaudio 		yyerror("'$%s%c' is an invalid variable name", buf, c);
9821045066fSclaudio 		return ERROR;
9831045066fSclaudio 		break;
9841045066fSclaudio 	case '@':
9851045066fSclaudio 		c = lgetc();
9861045066fSclaudio 		/* check for unnamed map '@' */
9871045066fSclaudio 		if (isalpha(c)) {
9881045066fSclaudio 			do {
9891045066fSclaudio 				*p++ = c;
9901045066fSclaudio 				if (p == ebuf) {
9911045066fSclaudio 					yyerror("line too long");
9921045066fSclaudio 					return ERROR;
9931045066fSclaudio 				}
9941045066fSclaudio 			} while ((c = lgetc()) != EOF && allowed_in_string(c));
9951045066fSclaudio 			lungetc();
9961045066fSclaudio 			*p = '\0';
9971045066fSclaudio 			if ((yylval.v.string = strdup(buf)) == NULL)
9981045066fSclaudio 				err(1, "%s", __func__);
9991045066fSclaudio 			return GVAR;
10001045066fSclaudio 		} else if (allowed_to_end_number(c) || c == '[') {
10011045066fSclaudio 			lungetc();
10021045066fSclaudio 			*p = '\0';
10031045066fSclaudio 			yylval.v.string = UNNAMED_MAP;
10041045066fSclaudio 			return GVAR;
10051045066fSclaudio 		}
10061045066fSclaudio 		yyerror("'@%s%c' is an invalid variable name", buf, c);
10071045066fSclaudio 		return ERROR;
10081045066fSclaudio 		break;
100923160851Smpi 	case EOF:
101023160851Smpi 		return 0;
101123160851Smpi 	case '"':
101223160851Smpi 		/* parse C-like string */
101353407cb0Smpi 		while ((c = lgetc()) != EOF) {
101453407cb0Smpi 			if (c == '"') {
101553407cb0Smpi 				/* handle multi-line strings */
101653407cb0Smpi 				c = skip();
101753407cb0Smpi 				if (c == '"')
101853407cb0Smpi 					continue;
101953407cb0Smpi 				else
102053407cb0Smpi 					lungetc();
102153407cb0Smpi 				break;
102253407cb0Smpi 			}
102323160851Smpi 			if (c == '\\') {
102423160851Smpi 				c = lgetc();
102523160851Smpi 				switch (c) {
102623160851Smpi 				case '\\':	c = '\\';	break;
102723160851Smpi 				case '\'':	c = '\'';	break;
102823160851Smpi 				case '"':	c = '"';	break;
102923160851Smpi 				case 'a':	c = '\a';	break;
103023160851Smpi 				case 'b':	c = '\b';	break;
103123160851Smpi 				case 'e':	c = 033;	break;
103223160851Smpi 				case 'f':	c = '\f';	break;
103323160851Smpi 				case 'n':	c = '\n';	break;
103423160851Smpi 				case 'r':	c = '\r';	break;
103523160851Smpi 				case 't':	c = '\t';	break;
103623160851Smpi 				case 'v':	c = '\v';	break;
103723160851Smpi 				default:
10383a50f0a9Sjmc 					yyerror("'%c' unsupported escape", c);
103923160851Smpi 					return ERROR;
104023160851Smpi 				}
104123160851Smpi 			}
104223160851Smpi 			*p++ = c;
104323160851Smpi 			if (p == ebuf) {
104468aa3067Sdv 				yyerror("line too long");
104523160851Smpi 				return ERROR;
104623160851Smpi 			}
104723160851Smpi 		}
104823160851Smpi 		if (c == EOF) {
104923160851Smpi 			yyerror("\"%s\" invalid EOF", buf);
105023160851Smpi 			return ERROR;
105123160851Smpi 		}
105223160851Smpi 		*p++ = '\0';
105323160851Smpi 		if ((str = strdup(buf)) == NULL)
105423160851Smpi 			err(1, "%s", __func__);
105523160851Smpi 		yylval.v.string = str;
105623160851Smpi 		return CSTRING;
105723160851Smpi 	default:
105823160851Smpi 		break;
105923160851Smpi 	}
106023160851Smpi 
106123160851Smpi 	/* parsing number */
106223160851Smpi 	if (isdigit(c)) {
106323160851Smpi 		do {
106423160851Smpi 			*p++ = c;
106523160851Smpi 			if (p == ebuf) {
106668aa3067Sdv 				yyerror("line too long");
106723160851Smpi 				return ERROR;
106823160851Smpi 			}
1069bf2cda35Sdv 		} while ((c = lgetc()) != EOF &&
1070bf2cda35Sdv 		    (isxdigit(c) || c == 'x' || c == 'X'));
107123160851Smpi 		lungetc();
107223160851Smpi 		if (c == EOF || allowed_to_end_number(c)) {
107323160851Smpi 			*p = '\0';
1074bf2cda35Sdv 			errno = 0;
1075bf2cda35Sdv 			yylval.v.number = strtol(buf, NULL, 0);
1076bf2cda35Sdv 			if (errno == ERANGE) {
1077bf2cda35Sdv 				/*
1078bf2cda35Sdv 				 * Characters are already validated, so only
1079bf2cda35Sdv 				 * check ERANGE.
1080bf2cda35Sdv 				 */
1081bf2cda35Sdv 				yyerror("%sflow", (yylval.v.number == LONG_MIN)
1082bf2cda35Sdv 				    ? "under" : "over");
108323160851Smpi 				return ERROR;
108423160851Smpi 			}
108523160851Smpi 			return NUMBER;
108623160851Smpi 		} else {
108723160851Smpi 			while (p > buf + 1) {
108823160851Smpi 				--p;
108923160851Smpi 				lungetc();
109023160851Smpi 			}
109123160851Smpi 			c = *--p;
109223160851Smpi 		}
109323160851Smpi 	}
109423160851Smpi 
109523160851Smpi 	/* parsing next word */
109623160851Smpi 	if (allowed_in_string(c)) {
1097adc7d920Smpi 		struct keyword *kwp;
109823160851Smpi 		do {
109923160851Smpi 			*p++ = c;
110023160851Smpi 			if (p == ebuf) {
110168aa3067Sdv 				yyerror("line too long");
110223160851Smpi 				return ERROR;
110323160851Smpi 			}
110423160851Smpi 		} while ((c = lgetc()) != EOF && (allowed_in_string(c)));
110523160851Smpi 		lungetc();
110623160851Smpi 		*p = '\0';
1107adc7d920Smpi 		kwp = lookup(buf);
1108adc7d920Smpi 		if (kwp == NULL) {
110923160851Smpi 			if ((yylval.v.string = strdup(buf)) == NULL)
111023160851Smpi 				err(1, "%s", __func__);
1111adc7d920Smpi 			return STRING;
1112adc7d920Smpi 		}
111385e665dcSanton 		if (pflag) {
111485e665dcSanton 			/*
111585e665dcSanton 			 * Probe lexer backdoor, interpret the token as a string
111685e665dcSanton 			 * rather than a keyword. Otherwise, reserved keywords
1117*139d07b5Smpi 			 * would conflict with syscall names.
111885e665dcSanton 			 */
111985e665dcSanton 			yylval.v.string = kwp->word;
112085e665dcSanton 			return STRING;
1121fe7ae692Sdv 		} else if (beflag) {
1122fe7ae692Sdv 			/* Interpret tokens in a BEGIN/END context. */
1123fe7ae692Sdv 			if (kwp->type >= B_AT_BI_ARG0 &&
1124fe7ae692Sdv 			    kwp->type <= B_AT_BI_ARG9)
1125fe7ae692Sdv 				yyerror("the %s builtin cannot be used with "
1126fe7ae692Sdv 				    "BEGIN or END probes", kwp->word);
1127676820edSjmatthew 		}
1128adc7d920Smpi 		yylval.v.i = kwp->type;
1129adc7d920Smpi 		return kwp->token;
113023160851Smpi 	}
113123160851Smpi 
113223160851Smpi 	if (c == '\n') {
113323160851Smpi 		yylval.lineno++;
113423160851Smpi 		yylval.colno = 0;
113523160851Smpi 	}
113623160851Smpi 	if (c == EOF)
113723160851Smpi 		return 0;
113823160851Smpi 	return c;
113923160851Smpi }
114023160851Smpi 
114123160851Smpi void
114223160851Smpi pprint_syntax_error(void)
114323160851Smpi {
114423160851Smpi 	char line[BUFSIZ];
114523160851Smpi 	int c, indent = yylval.colno;
114623160851Smpi 	size_t i;
114723160851Smpi 
114823160851Smpi 	strlcpy(line, &pbuf[pindex - yylval.colno], sizeof(line));
114923160851Smpi 
115023160851Smpi 	for (i = 0; line[i] != '\0' && (c = line[i]) != '\n'; i++) {
115123160851Smpi 		if (c == '\t')
115223160851Smpi 			indent += (8 - 1);
115323160851Smpi 		fputc(c, stderr);
115423160851Smpi 	}
115523160851Smpi 
115623160851Smpi 	fprintf(stderr, "\n%*c\n", indent, '^');
115723160851Smpi }
115823160851Smpi 
115923160851Smpi void
116023160851Smpi yyerror(const char *fmt, ...)
116123160851Smpi {
116223160851Smpi 	const char *prefix;
116323160851Smpi 	va_list	va;
116423160851Smpi 
116523160851Smpi 	prefix = (yylval.filename != NULL) ? yylval.filename : getprogname();
116623160851Smpi 
116723160851Smpi 	fprintf(stderr, "%s:%d:%d: ", prefix, yylval.lineno, yylval.colno);
116823160851Smpi 	va_start(va, fmt);
116923160851Smpi 	vfprintf(stderr, fmt, va);
117023160851Smpi 	va_end(va);
117123160851Smpi 	fprintf(stderr, ":\n");
117223160851Smpi 
117323160851Smpi 	pprint_syntax_error();
117423160851Smpi 
117523160851Smpi 	perrors++;
117623160851Smpi }
117723160851Smpi 
117823160851Smpi int
117923160851Smpi btparse(const char *str, size_t len, const char *filename, int debug)
118023160851Smpi {
118123160851Smpi 	if (debug > 0)
118223160851Smpi 		yydebug = 1;
118323160851Smpi 	pbuf = str;
118423160851Smpi 	plen = len;
118523160851Smpi 	pindex = 0;
118623160851Smpi 	yylval.filename = filename;
118723160851Smpi 	yylval.lineno = 1;
118823160851Smpi 
118923160851Smpi 	yyparse();
11906e5d64e6Smpi 	if (perrors)
11916e5d64e6Smpi 		return perrors;
119223160851Smpi 
11937aa3827dSmpi 	assert(SLIST_EMPTY(&l_variables));
11947aa3827dSmpi 
11956e5d64e6Smpi 	return 0;
119623160851Smpi }
1197