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