diff options
author | aiju <devnull@localhost> | 2018-11-10 13:46:16 +0000 |
---|---|---|
committer | aiju <devnull@localhost> | 2018-11-10 13:46:16 +0000 |
commit | e6d99771e5c1eef3f69fc847253d4709ffaa84be (patch) | |
tree | 16161401edc25c084ad24533519e094716965635 /sys/src/cmd | |
parent | 8c097ae84a500eae9c8e4ee21b7b3ea8f8d23259 (diff) |
adding dtracy (crude early version)
Diffstat (limited to 'sys/src/cmd')
-rw-r--r-- | sys/src/cmd/dtracy/act.c | 571 | ||||
-rw-r--r-- | sys/src/cmd/dtracy/cgen.c | 313 | ||||
-rw-r--r-- | sys/src/cmd/dtracy/dat.h | 123 | ||||
-rw-r--r-- | sys/src/cmd/dtracy/dtracy.c | 224 | ||||
-rw-r--r-- | sys/src/cmd/dtracy/fns.h | 35 | ||||
-rw-r--r-- | sys/src/cmd/dtracy/lex.c | 390 | ||||
-rwxr-xr-x | sys/src/cmd/dtracy/lint.rc | 20 | ||||
-rw-r--r-- | sys/src/cmd/dtracy/mkfile | 22 | ||||
-rw-r--r-- | sys/src/cmd/dtracy/parse.y | 123 | ||||
-rw-r--r-- | sys/src/cmd/dtracy/type.c | 529 |
10 files changed, 2350 insertions, 0 deletions
diff --git a/sys/src/cmd/dtracy/act.c b/sys/src/cmd/dtracy/act.c new file mode 100644 index 000000000..335d1bd23 --- /dev/null +++ b/sys/src/cmd/dtracy/act.c @@ -0,0 +1,571 @@ +#include <u.h> +#include <libc.h> +#include <ctype.h> +#include <dtracy.h> +#include <bio.h> +#include "dat.h" +#include "fns.h" + +/* this contains the code to prepare the kernel data structures and to parse records */ + +Clause *clause; +Clause **clauses; +int nclauses; + +/* we could just rely on the types in the expression tree but i'm paranoid */ +typedef struct Val Val; +struct Val { + enum { + VALINT, + VALSTR, + } type; + union { + vlong v; + char *s; + }; +}; + +Val +mkval(int type, ...) +{ + Val r; + va_list va; + + r.type = type; + va_start(va, type); + switch(type){ + case VALINT: r.v = va_arg(va, uvlong); break; + case VALSTR: r.s = va_arg(va, char*); break; + } + va_end(va); + return r; +} + +void +clausebegin(void) +{ + clause = emalloc(sizeof(Clause)); + clause->id = nclauses; +} + +void +addprobe(char *s) +{ + clause->probs = erealloc(clause->probs, sizeof(char *) * (clause->nprob + 1)); + clause->probs[clause->nprob++] = strdup(s); +} + +void +addstat(int type, ...) +{ + Stat *s; + va_list va; + + clause->stats = erealloc(clause->stats, sizeof(Stat) * (clause->nstats + 1)); + s = &clause->stats[clause->nstats++]; + memset(s, 0, sizeof(Stat)); + s->type = type; + va_start(va, type); + switch(type){ + case STATEXPR: + s->n = va_arg(va, Node *); + break; + case STATPRINT: + case STATPRINTF: + break; + default: + sysfatal("addstat: unknown type %d", type); + } + va_end(va); +} + +void +addarg(Node *n) +{ + Stat *s; + + assert(clause->nstats > 0); + s = &clause->stats[clause->nstats - 1]; + s->arg = erealloc(s->arg, sizeof(Node *) * (s->narg + 1)); + s->arg[s->narg++] = n; +} + +void +clauseend(void) +{ + clauses = erealloc(clauses, sizeof(Clause) * (nclauses + 1)); + clauses[nclauses++] = clause; +} + +void +actgradd(DTActGr *a, DTAct b) +{ + a->acts = erealloc(a->acts, sizeof(DTAct) * (a->nact + 1)); + a->acts[a->nact++] = b; +} + +void +addpred(DTExpr *e) +{ + clause->pred = e; +} + +static void +prepprintf(Node **arg, int narg, DTActGr *g, int *recoff) +{ + char *fmt; + int n; + Fmt f; + + if(narg <= 0) sysfatal("printf() needs an argument"); + if((*arg)->type != OSTR) sysfatal("printf() format string must be a literal"); + fmt = (*arg)->str; + fmtstrinit(&f); + n = 1; + for(; *fmt != 0; fmt++){ + fmtrune(&f, *fmt); + if(*fmt != '%') + continue; + fmt++; + again: + switch(*fmt){ + case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': + case 'u': case '+': case '-': case ',': case '#': case ' ': case '.': + fmtrune(&f, *fmt); + fmt++; + goto again; + case 'x': case 'X': case 'o': case 'b': case 'd': + if(n >= narg) sysfatal("printf() too few arguments"); + if(arg[n]->typ->type != TYPINT) sysfatal("print() %%%c with non-integer", *fmt); + arg[n] = tracegen(arg[n], g, recoff); + n++; + fmtrune(&f, 'l'); + fmtrune(&f, 'l'); + fmtrune(&f, *fmt); + break; + case 's': + if(n >= narg) sysfatal("printf() too few arguments"); + if(arg[n]->typ->type != TYPSTRING) sysfatal("print() %%s with non-string"); + arg[n] = tracegen(arg[n], g, recoff); + n++; + fmtrune(&f, *fmt); + break; + case 0: sysfatal("printf() missing verb"); + default: sysfatal("printf() unknown verb %%%c", *fmt); + } + } + if(n < narg) sysfatal("printf() too many arguments"); + (*arg)->str = fmtstrflush(&f); +} + +DTClause * +mkdtclause(Clause *c) +{ + DTClause *d; + Stat *s; + int recoff, i; + + d = emalloc(sizeof(DTClause)); + d->nprob = c->nprob; + d->probs = c->probs; + d->gr = emalloc(sizeof(DTActGr)); + d->gr->pred = c->pred; + d->gr->id = c->id; + recoff = 12; + for(s = c->stats; s < c->stats + c->nstats; s++) + switch(s->type){ + case STATEXPR: + actgradd(d->gr, (DTAct){ACTTRACE, codegen(s->n), 0}); + break; + case STATPRINT: + for(i = 0; i < s->narg; i++) + s->arg[i] = tracegen(s->arg[i], d->gr, &recoff); + break; + case STATPRINTF: + prepprintf(s->arg, s->narg, d->gr, &recoff); + break; + } + return d; +} + +void +packclauses(Fmt *f) +{ + int i; + DTClause *d; + + for(i = 0; i < nclauses; i++){ + d = mkdtclause(clauses[i]); + dtclpack(f, d); + } +} + +/* epid lookup table, filled with info from the kernel */ +Enab *enabtab[1024]; + +void +addepid(u32int epid, u32int cid, int reclen, char *p) +{ + Enab *e, **ep; + + assert(cid < nclauses); + assert((uint)reclen >= 12); + e = emalloc(sizeof(Enab)); + e->epid = epid; + e->cl = clauses[cid]; + e->reclen = reclen; + e->probe = strdup(p); + ep = &enabtab[epid % nelem(enabtab)]; + e->next = *ep; + *ep = e; +} + +Enab * +epidlookup(u32int epid) +{ + Enab *e; + + for(e = enabtab[epid % nelem(enabtab)]; e != nil; e = e->next) + if(e->epid == epid) + return e; + return nil; +} + +uchar * +unpack(uchar *p, uchar *e, char *fmt, ...) +{ + va_list va; + u64int vl; + + va_start(va, fmt); + for(;;) + switch(*fmt++){ + case 'c': + if(p + 1 > e) return nil; + *va_arg(va, u8int *) = p[0]; + p += 1; + break; + case 's': + if(p + 2 > e) return nil; + *va_arg(va, u16int *) = p[0] | p[1] << 8; + p += 2; + break; + case 'i': + if(p + 4 > e) return nil; + *va_arg(va, u32int *) = p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24; + p += 4; + break; + case 'v': + if(p + 8 > e) return nil; + vl = p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24; + vl |= (uvlong)p[4] << 32 | (uvlong)p[5] << 40 | (uvlong)p[6] << 48 | (uvlong)p[7] << 56; + *va_arg(va, u64int *) = vl; + p += 8; + break; + case 0: + return p; + default: + abort(); + } +} + +static Val +receval(Node *n, uchar *p, uchar *e, Enab *en) +{ + u8int c; + u16int s; + u32int i; + uvlong v; + char *sp; + uchar *q; + Val a, b; + + switch(n->type){ + case OSYM: + switch(n->sym->type){ + case SYMVAR: + switch(n->sym->idx){ + case DTV_TIME: + q = unpack(p + 4, e, "v", &v); + assert(q != nil); + return mkval(VALINT, v); + case DTV_PROBE: + return mkval(VALSTR, en->probe); + default: sysfatal("receval: unknown variable %d", n->type); return mkval(VALINT, 0LL); + } + break; + default: sysfatal("receval: unknown symbol type %d", n->type); return mkval(VALINT, 0LL); + } + case ONUM: return mkval(VALINT, n->num); + case OBIN: + a = receval(n->n1, p, e, en); + b = receval(n->n2, p, e, en); + assert(a.type == VALINT); + assert(b.type == VALINT); + return mkval(VALINT, evalop(n->op, n->typ->sign, a.v, b.v)); + case OLNOT: + a = receval(n->n1, p, e, en); + assert(a.type == VALINT); + return mkval(VALINT, (uvlong) !a.v); + case OTERN: + a = receval(n->n1, p, e, en); + assert(a.type == VALINT); + return a.v ? receval(n->n2, p, e, en) : receval(n->n3, p, e, en); + case ORECORD: + switch(n->typ->type){ + case TYPINT: + switch(n->typ->size){ + case 1: q = unpack(p + n->num, e, "c", &c); v = n->typ->sign ? (s8int)c : (u8int)c; break; + case 2: q = unpack(p + n->num, e, "s", &s); v = n->typ->sign ? (s16int)s : (u16int)s; break; + case 4: q = unpack(p + n->num, e, "i", &i); v = n->typ->sign ? (s32int)i : (u32int)i; break; + case 8: q = unpack(p + n->num, e, "v", &v); break; + default: q = nil; + } + assert(q != nil); + return mkval(VALINT, v); + case TYPSTRING: + assert(p + n->num + n->typ->size <= e); + sp = emalloc(n->typ->size + 1); + memcpy(sp, p + n->num, n->typ->size); + return mkval(VALSTR, sp); /* TODO: fix leak */ + default: + sysfatal("receval: don't know how to parse record for %τ", n->typ); + } + default: + sysfatal("receval: unknown type %α", n->type); + return mkval(VALINT, 0LL); + } +} + +static void +execprintf(Node **arg, int narg, uchar *p, uchar *e, Enab *en) +{ + char *x, *xp; + Val v; + int i; + + x = emalloc(sizeof(uvlong) * (narg - 1)); + xp = x; + for(i = 0; i < narg - 1; i++){ + v = receval(arg[i + 1], p, e, en); + switch(v.type){ + case VALINT: + *(uvlong*)xp = v.v; + xp += sizeof(uvlong); + break; + case VALSTR: + *(char**)xp = v.s; + xp += sizeof(char*); + break; + default: abort(); + } + } + vfprint(1, (*arg)->str, (va_list) x); + free(x); +} + +int +parseclause(Clause *cl, uchar *p, uchar *e, Enab *en, Biobuf *bp) +{ + Stat *s; + int i; + Val v; + + for(s = cl->stats; s < cl->stats + cl->nstats; s++) + switch(s->type){ + case STATEXPR: break; + case STATPRINT: + for(i = 0; i < s->narg; i++){ + v = receval(s->arg[i], p, e, en); + switch(v.type){ + case VALINT: + Bprint(bp, "%lld", v.v); + break; + case VALSTR: + Bprint(bp, "%s", v.s); + break; + default: sysfatal("parseclause: unknown val type %d", s->type); + } + Bprint(bp, "%c", i == s->narg - 1 ? '\n' : ' '); + } + break; + case STATPRINTF: + execprintf(s->arg, s->narg, p, e, en); + break; + default: + sysfatal("parseclause: unknown type %d", s->type); + } + return 0; +} + +int +parsebuf(uchar *p, int n, Biobuf *bp) +{ + uchar *e; + u32int epid; + u64int ts; + Enab *en; + + e = p + n; + while(p < e){ + p = unpack(p, e, "iv", &epid, &ts); + if(p == nil) goto err; + en = epidlookup(epid); + if(en == nil) goto err; + if(parseclause(en->cl, p - 12, p + en->reclen - 12, en, bp) < 0) return -1; + p += en->reclen - 12; + } + return 0; +err: + werrstr("buffer invalid"); + return -1; +} + +static void +dumpexpr(DTExpr *e, char *prefix) +{ + int i; + + for(i = 0; i < e->n; i++) + print("%s%.8ux %I\n", prefix, e->b[i], e->b[i]); +} + +#pragma varargck type "ε" Node* + +static void +fmtstring(Fmt *f, char *s) +{ + fmtrune(f, '"'); + for(; *s != 0; s++) + switch(*s){ + case '\n': fmtprint(f, "\\n"); break; + case '\r': fmtprint(f, "\\r"); break; + case '\t': fmtprint(f, "\\t"); break; + case '\v': fmtprint(f, "\\v"); break; + case '\b': fmtprint(f, "\\b"); break; + case '\a': fmtprint(f, "\\a"); break; + case '"': fmtprint(f, "\""); break; + case '\\': fmtprint(f, "\\"); break; + default: + if(*s < 0x20 || *s >= 0x7f) + fmtprint(f, "\\%.3o", (uchar)*s); + else + fmtrune(f, *s); + } + fmtrune(f, '"'); +} + +typedef struct Op Op; +struct Op { + char *name; + int pred; + enum { PRECRIGHT = 1 } flags; +}; +static Op optab[] = { + [OPLOR] {"||", 3, 0}, + [OPLAND] {"&&", 4, 0}, + [OPOR] {"|", 5, 0}, + [OPXNOR] {"~^", 6, 0}, + [OPXOR] {"^", 6, 0}, + [OPAND] {"&", 7, 0}, + [OPEQ] {"==", 8, }, + [OPNE] {"!=", 8, 0}, + [OPLE] {"<=", 9, 0}, + [OPLT] {"<", 9, 0}, + [OPLSH] {"<<", 10, 0}, + [OPRSH] {">>", 10, 0}, + [OPADD] {"+", 11, 0}, + [OPSUB] {"-", 11, 0}, + [OPDIV] {"/", 12, 0}, + [OPMOD] {"%", 12, 0}, + [OPMUL] {"*", 12, 0}, +}; +enum { PREDUNARY = 14 }; + +int +nodefmt(Fmt *f) +{ + Node *n; + Op *op; + int p; + + p = f->width; + n = va_arg(f->args, Node *); + switch(n->type){ + case OSYM: fmtprint(f, "%s", n->sym->name); break; + case ONUM: fmtprint(f, "%lld", n->num); break; + case OSTR: fmtstring(f, n->str); break; + case OBIN: + if(n->op >= nelem(optab) || optab[n->op].name == nil) + fmtprint(f, "(%*ε ??op%d %*ε)", PREDUNARY, n->n1, n->op, PREDUNARY, n->n2); + else{ + op = &optab[n->op]; + if(op->pred < p) fmtrune(f, '('); + fmtprint(f, "%*ε %s %*ε", op->pred + (op->flags & PRECRIGHT), n->n1, op->name, op->pred + (~op->flags & PRECRIGHT), n->n2); + if(op->pred < p) fmtrune(f, ')'); + } + break; + case OLNOT: fmtprint(f, "!%*ε", PREDUNARY, n->n1); break; + case OTERN: fmtprint(f, "%2ε ? %1ε : %1ε", n->n1, n->n2, n->n3); break; + case ORECORD: fmtprint(f, "record(%ε, %τ, %d)", n->n1, n->typ, (int)n->num); break; + case OCAST: fmtprint(f, "(%τ) %*ε", n->typ, PREDUNARY, n->n1); break; + default: fmtprint(f, "??? %α", n->type); + } + return 0; +} + +void +dump(void) +{ + int i, j; + Stat *s; + Clause *c; + DTClause *d; + DTAct *a; + + for(i = 0; i < nclauses; i++){ + c = clauses[i]; + d = mkdtclause(c); + print("clause %d:\n", c->id); + for(j = 0; j < c->nprob; j++) + print("\tprobe '%s'\n", c->probs[j]); + print("\tkernel code:\n"); + if(c->pred == nil) + print("\t\tno predicate\n"); + else{ + print("\t\tpredicate\n"); + dumpexpr(c->pred, "\t\t\t"); + } + for(a = d->gr->acts; a < d->gr->acts + d->gr->nact; a++) + switch(a->type){ + case ACTTRACE: + print("\t\ttrace (%d bytes)\n", a->size); + dumpexpr(a->p, "\t\t\t"); + break; + case ACTTRACESTR: + print("\t\ttrace string (%d bytes)\n", a->size); + dumpexpr(a->p, "\t\t\t"); + break; + default: + print("\t\t??? %d\n", a->type); + } + print("\trecord formatting:\n"); + for(s = c->stats; s < c->stats + c->nstats; s++) + switch(s->type){ + case STATEXPR: + break; + case STATPRINT: + print("\t\tprint\n"); + for(j = 0; j < s->narg; j++) + print("\t\t\targ %ε\n", s->arg[j]); + break; + case STATPRINTF: + print("\t\tprintf\n"); + for(j = 0; j < s->narg; j++) + print("\t\t\targ %ε\n", s->arg[j]); + break; + default: + print("\t\t??? %d\n", s->type); + } + } +} diff --git a/sys/src/cmd/dtracy/cgen.c b/sys/src/cmd/dtracy/cgen.c new file mode 100644 index 000000000..6b4f77d19 --- /dev/null +++ b/sys/src/cmd/dtracy/cgen.c @@ -0,0 +1,313 @@ +#include <u.h> +#include <libc.h> +#include <dtracy.h> +#include <bio.h> +#include "dat.h" +#include "fns.h" + +u16int regsused = 1; +u32int cbuf[256]; +int ncbuf; +int labtab[256]; +int nlab; + +static void +emit(u32int x) +{ + assert(ncbuf < nelem(cbuf)); + cbuf[ncbuf++] = x; +} + +static int +regalloc(void) +{ + u16int v; + int n; + + if(regsused == 0xffff){ + error("out of registers"); + return 0; + } + v = regsused + 1 & ~regsused; + regsused ^= v; + n = 0; + if((u8int)v == 0) {v >>= 8; n += 8;} + if((v & 0xf) == 0) {v >>= 4; n += 4;} + if((v & 3) == 0) {v >>= 2; n += 2;} + return n + (v >> 1); +} + +static void +regfree(int n) +{ + assert((regsused & 1<<n) != 0); + assert(n != 0); + regsused &= ~(1<<n); +} + +static int +popcount(u64int s) +{ + s = (s & 0x5555555555555555ULL) + (s >> 1 & 0x5555555555555555ULL); + s = (s & 0x3333333333333333ULL) + (s >> 2 & 0x3333333333333333ULL); + s = (s & 0x0F0F0F0F0F0F0F0FULL) + (s >> 4 & 0x0F0F0F0F0F0F0F0FULL); + s = (s & 0x00FF00FF00FF00FFULL) + (s >> 8 & 0x00FF00FF00FF00FFULL); + s = (s & 0x0000FFFF0000FFFFULL) + (s >> 16 & 0x0000FFFF0000FFFFULL); + return (u32int)s + (u32int)(s >> 32); +} + +static int +constenc(s64int val) +{ + int i, r; + s64int x; + + r = 0; + do{ + i = popcount(val ^ val - 1) - 1; + x = val << 54 - i >> 54; + if(r == 0){ + r = regalloc(); + emit(DTE_LDI << 24 | (x & 0x3ff) << 14 | i << 8 | r); + }else + emit(DTE_XORI << 24 | (x & 0x3ff) << 14 | i << 8 | r); + val ^= x << i; + }while(val != 0); + return r; +} + +static int egen(Node *); + +static void +condgen(Node *n, int invert, int truelab) +{ + int r1, r2, l1, op; + + if(n->type != OBIN) goto other; + switch(n->op){ + case OPEQ: op = DTE_SEQ; goto cmp; + case OPNE: op = DTE_SNE; goto cmp; + case OPLT: op = DTE_SLT; goto cmp; + case OPLE: op = DTE_SLE; + cmp: + r1 = egen(n->n1); + r2 = egen(n->n2); + if(invert) + emit(DTE(op ^ 1, r2, r1, truelab)); + else + emit(DTE(op, r1, r2, truelab)); + regfree(r1); + regfree(r2); + break; + case OPLOR: + case OPLAND: + if(invert ^ n->op == OPLOR){ + condgen(n->n1, invert, truelab); + condgen(n->n2, invert, truelab); + }else{ + l1 = nlab++; + condgen(n->n1, !invert, l1); + condgen(n->n2, invert, truelab); + labtab[l1] = ncbuf; + } + break; + default: + other: + r1 = egen(n); + emit(DTE(DTE_BNE ^ invert, r1, 0, truelab)); + regfree(r1); + break; + } +} + +static int +condvgen(Node *n, int invert) +{ + int r, l1, l2, op; + + if(n->type == OLNOT) + return condvgen(n->n1, !invert); + if(n->type != OBIN) goto other; + switch(n->op){ + case OPEQ: op = DTE_SEQ; goto cmp; + case OPNE: op = DTE_SNE; goto cmp; + case OPLT: op = DTE_SLT; goto cmp; + case OPLE: op = DTE_SLE; + cmp: + if(invert) + return egen(node(OBIN, op ^ 1, n->n2, n->n1)); + return egen(n); + case OPLOR: + case OPLAND: + if(invert ^ n->op == OPLOR){ + l1 = nlab++; + l2 = nlab++; + condgen(n->n1, invert, l1); + r = condvgen(n->n2, invert); + emit(DTE(DTE_BEQ, 0, 0, l2)); + labtab[l1] = ncbuf; + emit(DTE(DTE_LDI, 0, 1<<6, r)); + labtab[l2] = ncbuf; + return r; + }else{ + l1 = nlab++; + l2 = nlab++; + condgen(n->n1, invert, l1); + r = condvgen(n->n2, invert); + emit(DTE(DTE_BEQ, 0, 0, l2)); + labtab[l1] = ncbuf; + emit(DTE(DTE_LDI, 0, 0<<6, r)); + labtab[l2] = ncbuf; + return r; + } + default: + other: + r = egen(n); + emit(DTE(DTE_SNE ^ invert, r, 0, r)); + return r; + } +} + +static int +egen(Node *n) +{ + int r1, r2, rt, l1, l2, op; + + switch(/*nodetype*/n->type){ + case ONUM: + return constenc(n->num); + case OSYM: + switch(n->sym->type){ + case SYMVAR: + rt = regalloc(); + emit(DTE(DTE_LDV, n->sym->idx, rt, 0)); + return rt; + default: sysfatal("egen: unknown symbol type %d", n->sym->type); return 0; + } + case OBIN: + switch(/*oper*/n->op){ + case OPLAND: + case OPLOR: + return condvgen(n, 0); + case OPADD: op = DTE_ADD; break; + case OPSUB: op = DTE_SUB; break; + case OPMUL: op = DTE_MUL; break; + case OPDIV: op = n->typ->sign ? DTE_SDIV : DTE_UDIV; break; + case OPMOD: op = n->typ->sign ? DTE_SMOD : DTE_UMOD; break; + case OPAND: op = DTE_AND; break; + case OPOR: op = DTE_OR; break; + case OPXOR: op = DTE_XOR; break; + case OPLSH: op = DTE_LSL; break; + case OPRSH: op = n->typ->sign ? DTE_ASR : DTE_LSR; break; + case OPEQ: op = DTE_SEQ; break; + case OPNE: op = DTE_SNE; break; + case OPLT: op = DTE_SLT; break; + case OPLE: op = DTE_SLE; break; + case OPXNOR: op = DTE_XNOR; break; + default: sysfatal("egen: unknown op %d", n->op); return 0; + } + r1 = egen(n->n1); + r2 = egen(n->n2); + regfree(r1); + regfree(r2); + rt = regalloc(); + emit(DTE(op, r1, r2, rt)); + return rt; + case OTERN: + l1 = nlab++; + l2 = nlab++; + condgen(n->n1, 1, l1); + r1 = egen(n->n2); + emit(DTE(DTE_BEQ, 0, 0, l2)); + labtab[l1] = ncbuf; + r2 = egen(n->n3); + if(r1 != r2) + emit(DTE(DTE_OR, 0, r2, r1)); + labtab[l2] = ncbuf; + return r1; + case OLNOT: + return condvgen(n, 0); + case OCAST: + switch(n->typ->type){ + case TYPINT: + r1 = egen(n->n1); + emit(DTE(n->typ->sign ? DTE_SXT : DTE_ZXT, r1, n->typ->size * 8, r1)); + return r1; + case TYPSTRING: + return egen(n->n1); + default: + sysfatal("egen: don't know how to cast %τ to %τ", n->n1->typ, n->typ); + } + case ORECORD: + case OSTR: + default: sysfatal("egen: unknown type %α", n->type); return 0; + } +} + +DTExpr * +codegen(Node *n) +{ + int r, i, t; + DTExpr *ep; + + regsused = 1; + ncbuf = 0; + nlab = 0; + r = egen(n); + emit(DTE(DTE_RET, r, 0, 0)); + + for(i = 0; i < ncbuf; i++) + if((cbuf[i] >> 24 & 0xf0) == 0x30){ + t = labtab[cbuf[i] & 0xff]; + assert((uint)(t - i - 1) < 0x100); + cbuf[i] = cbuf[i] & 0xffffff00 | t - i - 1; + } + + ep = emalloc(sizeof(DTExpr) + ncbuf * sizeof(u32int)); + ep->n = ncbuf; + ep->b = (void *)(ep + 1); + memcpy(ep->b, cbuf, ncbuf * sizeof(u32int)); + return ep; +} + +Node * +tracegen(Node *n, DTActGr *g, int *recoff) +{ + switch(/*nodetype*/n->type){ + case OSYM: + case ONUM: + case OSTR: + break; + case OBIN: + n->n1 = tracegen(n->n1, g, recoff); + n->n2 = tracegen(n->n2, g, recoff); + break; + case OLNOT: + n->n1 = tracegen(n->n1, g, recoff); + break; + case OTERN: + n->n1 = tracegen(n->n1, g, recoff); + n->n2 = tracegen(n->n2, g, recoff); + n->n3 = tracegen(n->n3, g, recoff); + break; + case OCAST: + n->n1 = tracegen(n->n1, g, recoff); + break; + case ORECORD: + switch(n->typ->type){ + case TYPINT: + actgradd(g, (DTAct){ACTTRACE, codegen(n->n1), n->typ->size}); + break; + case TYPSTRING: + actgradd(g, (DTAct){ACTTRACESTR, codegen(n->n1), n->typ->size}); + break; + default: + sysfatal("tracegen: don't know how to record %τ", n->typ); + } + n->num = *recoff; + *recoff += n->typ->size; + return n; + default: sysfatal("tracegen: unknown type %α", n->type); return nil; + } + return n; +} diff --git a/sys/src/cmd/dtracy/dat.h b/sys/src/cmd/dtracy/dat.h new file mode 100644 index 000000000..fc6737b4c --- /dev/null +++ b/sys/src/cmd/dtracy/dat.h @@ -0,0 +1,123 @@ +typedef struct Node Node; +typedef struct Symbol Symbol; +typedef struct SymTab SymTab; +typedef struct Clause Clause; +typedef struct Enab Enab; +typedef struct Stat Stat; +typedef struct Type Type; + +enum { + SYMHASH = 256, +}; + +struct Type { + enum { + TYPINVAL, + TYPINT, + TYPPTR, + TYPSTRING, + } type; + int size; + uchar sign; + Type *ref; + Type *typenext; +}; + +struct Symbol { + enum { + SYMNONE, + SYMVAR, + } type; + char *name; + int idx; + Symbol *next; + Type *typ; +}; + +struct SymTab { + Symbol *sym[SYMHASH]; +}; + +struct Node { + enum { + OINVAL, + OSYM, + ONUM, + OSTR, + OBIN, + OLNOT, + OTERN, + ORECORD, + OCAST, + } type; + enum { + OPINVAL, + OPADD, + OPSUB, + OPMUL, + OPDIV, + OPMOD, + OPAND, + OPOR, + OPXOR, + OPLSH, + OPRSH, + OPEQ, + OPNE, + OPLT, + OPLE, + OPLAND, + OPLOR, + OPXNOR, + } op; + Node *n1, *n2, *n3; + Symbol *sym; + char *str; + s64int num; + + /* used by elidecasts() */ + char databits; + enum {UPZX, UPSX} upper; + + int recsize; + + Type *typ; +}; + +struct Stat { + enum { + STATEXPR, + STATPRINT, + STATPRINTF, + } type; + Node *n; + int narg; + Node **arg; +}; + +struct Clause { + int id; + Stat *stats; + int nstats; + char **probs; + int nprob; + DTExpr *pred; +}; + +struct Enab { + int epid; + int reclen; + char *probe; + Clause *cl; + Enab *next; +}; + +extern int errors; + +#pragma varargck type "α" int +#pragma varargck type "t" int +#pragma varargck type "τ" Type * +#pragma varargck type "ε" Node * +#pragma varargck argpos error 1 + +extern int dflag; diff --git a/sys/src/cmd/dtracy/dtracy.c b/sys/src/cmd/dtracy/dtracy.c new file mode 100644 index 000000000..8585180f2 --- /dev/null +++ b/sys/src/cmd/dtracy/dtracy.c @@ -0,0 +1,224 @@ +#include <u.h> +#include <libc.h> +#include <dtracy.h> +#include <bio.h> +#include "dat.h" +#include "fns.h" + +char *dtracyroot = "#Δ"; +int dtracyno; +int ctlfd, buffd; + +int +min(int a, int b) +{ + return a < b ? a : b; +} + +int +max(int a, int b) +{ + return a < b ? b : a; +} + +void * +emalloc(ulong n) +{ + void *v; + + v = malloc(n); + if(v == nil) sysfatal("malloc: %r"); + memset(v, 0, n); + setmalloctag(v, getcallerpc(&n)); + return v; +} + +void * +erealloc(void *v, ulong n) +{ + v = realloc(v, n); + if(n != 0){ + if(v == nil) sysfatal("realloc: %r"); + setrealloctag(v, getcallerpc(&v)); + } + return v; +} + +void * +dtmalloc(ulong n) +{ + return emalloc(n); +} + +void +dtfree(void *v) +{ + free(v); +} + +void +defvar(char *name, int idx, Type *ty) +{ + Symbol *s; + + s = getsym(name); + s->type = SYMVAR; + s->idx = idx; + s->typ = ty; +} + +void +globvars(void) +{ + defvar("arg0", DTV_ARG0, type(TYPINT, 8, 1)); + defvar("arg1", DTV_ARG1, type(TYPINT, 8, 1)); + defvar("arg2", DTV_ARG2, type(TYPINT, 8, 1)); + defvar("arg3", DTV_ARG3, type(TYPINT, 8, 1)); + defvar("arg4", DTV_ARG4, type(TYPINT, 8, 1)); + defvar("arg5", DTV_ARG5, type(TYPINT, 8, 1)); + defvar("arg6", DTV_ARG6, type(TYPINT, 8, 1)); + defvar("arg7", DTV_ARG7, type(TYPINT, 8, 1)); + defvar("arg8", DTV_ARG8, type(TYPINT, 8, 1)); + defvar("arg9", DTV_ARG9, type(TYPINT, 8, 1)); + defvar("pid", DTV_PID, type(TYPINT, 4, 1)); + defvar("machno", DTV_MACHNO, type(TYPINT, 4, 1)); + defvar("time", DTV_TIME, type(TYPINT, 8, 0)); + defvar("probe", DTV_PROBE, type(TYPSTRING)); +} + +int +setup(void) +{ + char buf[512]; + char *p; + int n; + + snprint(buf, sizeof(buf), "%s/clone", dtracyroot); + ctlfd = open(buf, ORDWR); + if(ctlfd < 0) return -1; + n = read(ctlfd, buf, sizeof(buf) - 1); + if(n < 0) return -1; + buf[n] = 0; + dtracyno = strtol(buf, &p, 10); + if(p == buf || *p != 0){ + werrstr("expected number reading from ctl"); + return -1; + } + snprint(buf, sizeof(buf), "%s/%d/buf", dtracyroot, dtracyno); + buffd = open(buf, OREAD); + if(buffd < 0) return -1; + return 0; +} + +int +progcopy(void) +{ + char buf[512]; + int fd; + char *prog; + Fmt f; + + fmtstrinit(&f); + packclauses(&f); + prog = fmtstrflush(&f); + snprint(buf, sizeof(buf), "%s/%d/prog", dtracyroot, dtracyno); + fd = open(buf, OWRITE); + if(fd < 0) return -1; + if(write(fd, prog, strlen(prog)) < 0){ + close(fd); + return -1; + } + close(fd); + return 0; +} + +int +epidread(void) +{ + char buf[512]; + Biobuf *bp; + char *s; + char *f[5]; + int a, b, c; + + snprint(buf, sizeof(buf), "%s/%d/epid", dtracyroot, dtracyno); + bp = Bopen(buf, OREAD); + if(bp == nil) return -1; + for(; s = Brdstr(bp, '\n', 1), s != nil; free(s)){ + if(tokenize(s, f, nelem(f)) != 4) + goto err; + a = atoi(f[0]); + b = atoi(f[1]); + c = atoi(f[2]); + addepid(a, b, c, f[3]); + } + return 0; +err: + werrstr("epidread: invalid format"); + free(s); + return -1; + +} + +void +bufread(Biobuf *bp) +{ + static uchar buf[65536]; + int n; + + n = read(buffd, buf, sizeof(buf)); + if(n < 0) sysfatal("bufread: %r"); + if(parsebuf(buf, n, bp) < 0) + sysfatal("parsebuf: %r"); + Bflush(bp); +} + +static void +usage(void) +{ + fprint(2, "usage: %s [ -cd ] script\n", argv0); + exits("usage"); +} + +int dflag; + +void +main(int argc, char **argv) +{ + Biobuf *out; + + dflag = 0; + ARGBEGIN { + case 'd': dflag = 1; break; + default: usage(); + } ARGEND; + if(argc != 1) usage(); + + fmtinstall(L'α', nodetfmt); + fmtinstall('t', typetfmt); + fmtinstall(L'I', dtefmt); + fmtinstall(L'τ', typefmt); + fmtinstall(L'ε', nodefmt); + lexinit(); + lexstring(argv[0]); + globvars(); + yyparse(); + if(errors != 0) + exits("errors"); + if(dflag) + dump(); + else{ + if(setup() < 0) + sysfatal("setup: %r"); + if(progcopy() < 0) + sysfatal("progcopy: %r"); + if(epidread() < 0) + sysfatal("epidread: %r"); + fprint(ctlfd, "go"); + out = Bfdopen(1, OWRITE); + if(out == nil) sysfatal("Bfdopen: %r"); + for(;;) + bufread(out); + } + exits(nil); +} diff --git a/sys/src/cmd/dtracy/fns.h b/sys/src/cmd/dtracy/fns.h new file mode 100644 index 000000000..f418e4f34 --- /dev/null +++ b/sys/src/cmd/dtracy/fns.h @@ -0,0 +1,35 @@ +void yyerror(char *); +int yylex(void); +int yyparse(void); +Node *node(int, ...); +int nodetfmt(Fmt *); +int typetfmt(Fmt *); +int typefmt(Fmt *); +int nodefmt(Fmt *); +void *emalloc(ulong); +void *erealloc(void *, ulong); +DTAct action(int, DTExpr *); +DTExpr *codegen(Node *); +void error(char *, ...); +Symbol *getsym(char *); +void lexinit(void); +void lexstring(char *); +void clausebegin(void); +void addstat(int, ...); +void addarg(Node *); +void addprobe(char *); +void addpred(DTExpr *); +void clauseend(void); +void packclauses(Fmt *); +void addepid(u32int, u32int, int, char*); +int parsebuf(uchar *, int, Biobuf*); +Node *tracegen(Node *, DTActGr *, int *); +void actgradd(DTActGr *, DTAct); +void needruntime(Node *); +void dump(void); +vlong evalop(int, int, vlong, vlong); +Node *exprcheck(Node *, int); +Type *type(int, ...); +int min(int, int); +int max(int, int); +Node *addtype(Type *, Node *); diff --git a/sys/src/cmd/dtracy/lex.c b/sys/src/cmd/dtracy/lex.c new file mode 100644 index 000000000..15a959ffa --- /dev/null +++ b/sys/src/cmd/dtracy/lex.c @@ -0,0 +1,390 @@ +#include <u.h> +#include <libc.h> +#include <ctype.h> +#include <dtracy.h> +#include <bio.h> +#include "dat.h" +#include "fns.h" +#include "y.tab.h" + +char *str, *strp; +int lineno = 1; +int errors; + +typedef struct Keyword Keyword; +struct Keyword { + char *name; + int tok; +}; +/* both tables must be sorted */ +Keyword kwtab[] = { + "if", TIF, + "print", TPRINT, + "printf", TPRINTF, + "s16", TS16, + "s32", TS32, + "s64", TS64, + "s8", TS8, + "string", TSTRING, + "u16", TU16, + "u32", TU32, + "u64", TU64, + "u8", TU8, +}; +Keyword optab[] = { + "!=", TNE, + "&&", TAND, + "<<", TLSL, + "<=", TLE, + "==", TEQ, + ">=", TGE, + ">>", TLSR, + "||", TOR, +}; +Keyword *kwchar[128], *opchar[128]; + +void +lexinit(void) +{ + Keyword *kw; + + for(kw = kwtab; kw < kwtab + nelem(kwtab); kw++) + if(kwchar[*kw->name] == nil) + kwchar[*kw->name] = kw; + for(kw = optab; kw < optab + nelem(optab); kw++) + if(opchar[*kw->name] == nil) + opchar[*kw->name] = kw; +} + +void +lexstring(char *s) +{ + str = strp = s; +} + +void +error(char *fmt, ...) +{ + Fmt f; + char buf[128]; + va_list va; + + fmtfdinit(&f, 2, buf, sizeof(buf)); + fmtprint(&f, "%d ", lineno); + va_start(va, fmt); + fmtvprint(&f, fmt, va); + fmtrune(&f, '\n'); + va_end(va); + fmtfdflush(&f); + errors++; +} + +void +yyerror(char *msg) +{ + error("%s", msg); +} + +static int +getch(void) +{ + if(*strp == 0) return -1; + return *strp++; +} + +static void +ungetch(void) +{ + assert(strp > str); + if(*strp != 0) + strp--; +} + +int +yylex(void) +{ + int ch; + static char buf[512]; + char *p; + Keyword *kw; + u64int v; + +again: + while(ch = getch(), ch >= 0 && isspace(ch)){ + if(ch == '\n') + lineno++; + } + if(ch < 0) + return -1; + if(ch == '/'){ + ch = getch(); + if(ch == '/'){ + while(ch = getch(), ch >= 0 && ch != '\n') + ; + if(ch == '\n') + lineno++; + goto again; + } + if(ch == '*'){ + s1: + ch = getch(); + if(ch < 0) return -1; + if(ch == '\n') lineno++; + if(ch != '*') goto s1; + s2: + ch = getch(); + if(ch < 0) return -1; + if(ch == '\n') lineno++; + if(ch == '*') goto s2; + if(ch != '/') goto s1; + goto again; + } + ungetch(); + return '/'; + } + if(isalnum(ch) || ch == '_' || ch >= 0x80 || ch == ':'){ + p = buf; + *p++ = ch; + while(ch = getch(), isalnum(ch) || ch == '_' || ch >= 0x80 || ch == ':') + if(p < buf + sizeof(buf) - 1) + *p++ = ch; + *p = 0; + ungetch(); + v = strtoull(buf, &p, 0); + if(p != buf && *p == 0){ + yylval.num = v; + return TNUM; + } + if(strcmp(buf, ":") == 0) + return ':'; + if((uchar)buf[0] < 0x80 && kwchar[buf[0]] != nil) + for(kw = kwchar[buf[0]]; kw < kwtab + nelem(kwtab) && kw->name[0] == buf[0]; kw++) + if(strcmp(kw->name, buf) == 0) + return kw->tok; + yylval.sym = getsym(buf); + return TSYM; + } + if(ch == '"'){ + p = buf; + while(ch = getch(), ch >= 0 && ch != '"'){ + if(ch == '\n') + error("unterminated string"); + if(ch == '\\') + switch(ch = getch()){ + case 'n': ch = '\n'; break; + case 'r': ch = '\r'; break; + case 't': ch = '\t'; break; + case 'v': ch = '\v'; break; + case 'b': ch = '\b'; break; + case 'a': ch = '\a'; break; + case '"': case '\\': break; + default: error("unknown escape code \\%c", ch); + } + if(p < buf + sizeof(buf) - 1) + *p++ = ch; + } + if(ch < 0) error("unterminated string"); + *p = 0; + yylval.str = strdup(buf); + return TSTR; + } + if(opchar[ch] != nil){ + buf[0] = ch; + buf[1] = getch(); + for(kw = opchar[buf[0]]; kw < optab + nelem(optab) && kw->name[0] == buf[0]; kw++) + if(buf[1] == kw->name[1]){ + buf[2] = getch(); + buf[3] = 0; + if(kw + 1 < optab + nelem(optab) && strcmp(kw[1].name, buf) == 0) + return kw[1].tok; + ungetch(); + return kw->tok; + } + ungetch(); + } + return ch; +} + +int +nodetfmt(Fmt *f) +{ + int t; + static char *nodestr[] = { + [OINVAL] "OINVAL", + [OBIN] "OBIN", + [OLNOT] "OLNOT", + [OSYM] "OSYM", + [ONUM] "ONUM", + [OSTR] "OSTR", + [OTERN] "OTERN", + [ORECORD] "ORECORD", + [OCAST] "OCAST", + }; + + t = va_arg(f->args, int); + if(t >= nelem(nodestr) || nodestr[t] == nil) + return fmtprint(f, "??? (%d)", t); + else + return fmtprint(f, "%s", nodestr[t]); +} + +Node * +node(int type, ...) +{ + va_list va; + Node *n; + + n = emalloc(sizeof(Node)); + n->type = type; + va_start(va, type); + switch(type){ + case OBIN: + n->op = va_arg(va, int); + n->n1 = va_arg(va, Node *); + n->n2 = va_arg(va, Node *); + break; + case OLNOT: + n->n1 = va_arg(va, Node *); + break; + case OSYM: + n->sym = va_arg(va, Symbol *); + break; + case ONUM: + n->num = va_arg(va, s64int); + break; + case OTERN: + n->n1 = va_arg(va, Node *); + n->n2 = va_arg(va, Node *); + n->n3 = va_arg(va, Node *); + break; + case ORECORD: + n->n1 = va_arg(va, Node *); + break; + case OCAST: + n->typ = va_arg(va, Type *); + n->n1 = va_arg(va, Node *); + break; + case OSTR: + n->str = va_arg(va, char *); + break; + default: + sysfatal("node: unknown type %α", type); + } + va_end(va); + return n; +} + +SymTab globals; + +static u64int +hash(char *s) +{ + u64int h; + + h = 0xcbf29ce484222325ULL; + for(; *s != 0; s++){ + h ^= *s; + h *= 0x100000001b3ULL; + } + return h; +} + +Symbol * +getsym(char *name) +{ + u64int h; + Symbol **sp, *s; + + h = hash(name); + for(sp = &globals.sym[h % SYMHASH]; s = *sp, s != nil; sp = &s->next) + if(strcmp(s->name, name) == 0) + return s; + *sp = s = emalloc(sizeof(Symbol)); + s->name = strdup(name); + return s; +} + +int +typetfmt(Fmt *f) +{ + int t; + static char *tstr[] = { + [TYPINVAL] "TYPINVAL", + [TYPINT] "TYPINT", + [TYPPTR] "TYPPTR", + [TYPSTRING] "TYPSTRING", + }; + + t = va_arg(f->args, int); + if(t >= nelem(tstr) || tstr[t] == nil) + return fmtprint(f, "??? (%d)", t); + else + return fmtprint(f, "%s", tstr[t]); +} + +int +typefmt(Fmt *f) +{ + Type *t; + + t = va_arg(f->args, Type *); + switch(t->type){ + case TYPINT: return fmtprint(f, "%c%d", t->sign ? 's' : 'u', t->size * 8); + case TYPSTRING: return fmtprint(f, "string"); + case TYPPTR: return fmtprint(f, "%τ*", t->ref); + default: return fmtprint(f, "%t", t->type); + } +} + +static Type typu8 = {.type TYPINT, .size 1, .sign 0}; +static Type typs8 = {.type TYPINT, .size 1, .sign 1}; +static Type typu16 = {.type TYPINT, .size 2, .sign 0}; +static Type typs16 = {.type TYPINT, .size 2, .sign 1}; +static Type typu32 = {.type TYPINT, .size 4, .sign 0}; +static Type typs32 = {.type TYPINT, .size 4, .sign 1}; +static Type typu64 = {.type TYPINT, .size 8, .sign 0}; +static Type typs64 = {.type TYPINT, .size 8, .sign 1}; +static Type typstr = {.type TYPSTRING, .size DTSTRMAX }; +static Type *typereg; + +static Type * +mkptr(Type *t) +{ + Type *s; + + for(s = typereg; s != nil; s = s->typenext) + if(s->type == TYPPTR && s->ref == t) + return s; + s = emalloc(sizeof(Type)); + s->type = TYPPTR; + s->ref = t; + return s; +} + +Type * +type(int typ, ...) +{ + int size, sign; + va_list va; + + va_start(va, typ); + switch(typ){ + case TYPINT: + size = va_arg(va, int); + sign = va_arg(va, int); + switch(size << 4 | sign){ + case 0x10: return &typu8; + case 0x11: return &typs8; + case 0x20: return &typu16; + case 0x21: return &typs16; + case 0x40: return &typu32; + case 0x41: return &typs32; + case 0x80: return &typu64; + case 0x81: return &typs64; + default: sysfatal("type: invalid (size,sign) = (%d,%d)\n", size, sign); return nil; + } + case TYPSTRING: return &typstr; + case TYPPTR: return mkptr(va_arg(va, Type *)); + default: sysfatal("type: unknown %t", typ); return nil; + } +} diff --git a/sys/src/cmd/dtracy/lint.rc b/sys/src/cmd/dtracy/lint.rc new file mode 100755 index 000000000..cafbdd835 --- /dev/null +++ b/sys/src/cmd/dtracy/lint.rc @@ -0,0 +1,20 @@ +#!/bin/rc +# check for full switch statements + +nl=' +' +nodetypes=`''{sed -n '/OINVAL/,/}/ s/,//p' dat.h | sed 's/[ ]*//g; /^$/d' | sort | grep -v '^OINVAL$'} +switches=`$nl{grep -n '/\*nodetype\*/' *.[ch]} +for(l in $switches){ + f=`:{echo $l} + a=`$nl{sed -n $f(2)^'s/[^ ].*//p' $f(1)} + comm -23 <{echo $nodetypes} <{sed -n $f(2)^',/^'^$a^'}/ s/^'^$a^'case ([^:]*):.*/\1/p' $f(1) | sort} | sed -n 's/.+/'^$f(1)^':'^$f(2)^' missing &/p' +} + +oper=`''{sed -n '/OPINVAL/,/}/ s/,//p' dat.h | sed 's/[ ]*//g; /^$/d' | sort | grep -v '^OPINVAL$'} +switches=`$nl{grep -n '/\*oper\*/' *.[ch]} +for(l in $switches){ + f=`:{echo $l} + a=`$nl{sed -n $f(2)^'s/[^ ].*//p' $f(1)} + comm -23 <{echo $oper} <{sed -n $f(2)^',/^'^$a^'}/ s/^'^$a^'case ([^:]*):.*/\1/p' $f(1) | sort} | sed -n 's/.+/'^$f(1)^':'^$f(2)^' missing &/p' +} diff --git a/sys/src/cmd/dtracy/mkfile b/sys/src/cmd/dtracy/mkfile new file mode 100644 index 000000000..7d1b4b45c --- /dev/null +++ b/sys/src/cmd/dtracy/mkfile @@ -0,0 +1,22 @@ +</$objtype/mkfile + +BIN=/$objtype/bin +TARG=dtracy +OFILES=\ + dtracy.$O\ + lex.$O\ + y.tab.$O\ + cgen.$O\ + act.$O\ + type.$O\ + +YFILES=parse.y + +HFILES=\ + dat.h\ + fns.h\ + +</sys/src/cmd/mkone + +YFLAGS=-v -d -D1 +lex.$O: y.tab.h diff --git a/sys/src/cmd/dtracy/parse.y b/sys/src/cmd/dtracy/parse.y new file mode 100644 index 000000000..9f231dbc9 --- /dev/null +++ b/sys/src/cmd/dtracy/parse.y @@ -0,0 +1,123 @@ +%{ +#include <u.h> +#include <libc.h> +#include <dtracy.h> +#include <bio.h> +#include "dat.h" +#include "fns.h" +%} + +%union{ + Node *n; + Symbol *sym; + DTExpr *e; + s64int num; + char *str; + Type *t; +} + +%type <n> expr +%type <t> type + +%token <sym> TSYM +%token <num> TNUM +%token <str> TSTR +%token TPRINT TPRINTF +%token TIF +%token TU8 TU16 TU32 TU64 +%token TS8 TS16 TS32 TS64 +%token TSTRING + +%right '?' +%left TOR +%left TAND +%left '|' +%left '^' +%left '&' +%left TEQ TNE +%left '<' '>' TLE TGE +%left TLSL TLSR +%left '+' '-' +%left '*' '/' '%' +%left unary +%right castprec + +%% + +program: | program clause + +clause: { clausebegin(); } probes optpredicate optaction { clauseend(); } + +optpredicate: | TIF expr { addpred(codegen(exprcheck($2, 1))); } + +optaction: + { + addstat(STATPRINT); + addarg(node(OSYM, getsym("probe"))); + } + | action +action: '{' stats '}' +stats: | stats0 | stats0 ';' +stats0: stat | stats0 ';' stat + +stat: expr { addstat(STATEXPR, exprcheck($1, 0)); } +| TPRINT { addstat(STATPRINT); } pelist +| TPRINTF { addstat(STATPRINTF); } pelist + + +pelist: + '(' ')' + | '(' arg ',' ')' + | '(' elist2 optcomma ')' + | arg optcomma + | elist2 optcomma +elist2: arg ',' arg | elist2 ',' arg +arg: expr { addarg(exprcheck($1, 0)); } +optcomma: | ',' + +expr: + TSYM { $$ = node(OSYM, $1); } + | TNUM { $$ = node(ONUM, $1); } + | TSTR { $$ = node(OSTR, $1); } + | expr '+' expr { $$ = node(OBIN, OPADD, $1, $3); } + | expr '-' expr { $$ = node(OBIN, OPSUB, $1, $3); } + | expr '*' expr { $$ = node(OBIN, OPMUL, $1, $3); } + | expr '/' expr { $$ = node(OBIN, OPDIV, $1, $3); } + | expr '%' expr { $$ = node(OBIN, OPMOD, $1, $3); } + | expr '&' expr { $$ = node(OBIN, OPAND, $1, $3); } + | expr '|' expr { $$ = node(OBIN, OPOR, $1, $3); } + | expr '^' expr { $$ = node(OBIN, OPXOR, $1, $3); } + | expr TLSL expr { $$ = node(OBIN, OPLSH, $1, $3); } + | expr TLSR expr { $$ = node(OBIN, OPRSH, $1, $3); } + | expr TEQ expr { $$ = node(OBIN, OPEQ, $1, $3); } + | expr TNE expr { $$ = node(OBIN, OPNE, $1, $3); } + | expr '<' expr { $$ = node(OBIN, OPLT, $1, $3); } + | expr TLE expr { $$ = node(OBIN, OPLE, $1, $3); } + | expr '>' expr { $$ = node(OBIN, OPLT, $3, $1); } + | expr TGE expr { $$ = node(OBIN, OPLE, $3, $1); } + | expr TAND expr { $$ = node(OBIN, OPLAND, $1, $3); } + | expr TOR expr { $$ = node(OBIN, OPLOR, $1, $3); } + | '-' expr %prec unary { $$ = node(OBIN, OPSUB, node(ONUM, 0LL), $2); } + | '~' expr %prec unary { $$ = node(OBIN, OPXNOR, node(ONUM, 0LL), $2); } + | '!' expr %prec unary { $$ = node(OLNOT, $2); } + | '(' expr ')' { $$ = $2; } + | expr '?' expr ':' expr %prec '?' { $$ = node(OTERN, $1, $3, $5); } + | '(' type ')' expr %prec castprec { $$ = node(OCAST, $2, $4); } + +type: + TU8 { $$ = type(TYPINT, 1, 0); } + | TS8 { $$ = type(TYPINT, 1, 1); } + | TU16 { $$ = type(TYPINT, 2, 0); } + | TS16 { $$ = type(TYPINT, 2, 1); } + | TU32 { $$ = type(TYPINT, 4, 0); } + | TS32 { $$ = type(TYPINT, 4, 1); } + | TU64 { $$ = type(TYPINT, 8, 0); } + | TS64 { $$ = type(TYPINT, 8, 1); } + | TSTRING { $$ = type(TYPSTRING); } + +probes: + TSYM { addprobe($1->name); } + | probes ',' TSYM { addprobe($3->name); } + + +%% diff --git a/sys/src/cmd/dtracy/type.c b/sys/src/cmd/dtracy/type.c new file mode 100644 index 000000000..fdf7872b6 --- /dev/null +++ b/sys/src/cmd/dtracy/type.c @@ -0,0 +1,529 @@ +#include <u.h> +#include <libc.h> +#include <ctype.h> +#include <dtracy.h> +#include <bio.h> +#include "dat.h" +#include "fns.h" + +Node * +icast(int sign, int size, Node *n) +{ + Type *t; + + t = type(TYPINT, sign, size); + return node(OCAST, t, n); +} + +/* + the type checker checks types. + the result is an expression that is correct if evaluated with 64-bit operands all the way. + to maintain c-like semantics, this means adding casts all over the place, which will get optimised later. + + note we use kencc, NOT ansi c, semantics for unsigned. +*/ + +Node * +typecheck(Node *n) +{ + int s1, s2, sign; + + switch(/*nodetype*/n->type){ + case OSYM: + switch(n->sym->type){ + case SYMNONE: error("undeclared '%s'", n->sym->name); break; + case SYMVAR: n->typ = n->sym->typ; break; + default: sysfatal("typecheck: unknown symbol type %d", n->sym->type); + } + break; + case ONUM: + if((vlong)n->num >= -0x80000000LL && (vlong)n->num <= 0x7fffffffLL) + n->typ = type(TYPINT, 4, 1); + else + n->typ = type(TYPINT, 8, 1); + break; + case OSTR: + n->typ = type(TYPSTRING); + break; + case OBIN: + n->n1 = typecheck(n->n1); + n->n2 = typecheck(n->n2); + if(n->n1->typ == nil || n->n2->typ == nil) + break; + if(n->n1->typ->type != TYPINT){ + error("%τ not allowed in operation", n->n1->typ); + break; + } + if(n->n2->typ->type != TYPINT){ + error("%τ not allowed in operation", n->n2->typ); + break; + } + s1 = n->n1->typ->size; + s2 = n->n2->typ->size; + sign = n->n1->typ->sign && n->n2->typ->sign; + switch(n->op){ + case OPADD: + case OPSUB: + case OPMUL: + case OPDIV: + case OPMOD: + case OPAND: + case OPOR: + case OPXOR: + case OPXNOR: + n->typ = type(TYPINT, 8, sign); + if(s1 > 4 || s2 > 4){ + n->n1 = icast(8, sign, n->n1); + n->n2 = icast(8, sign, n->n2); + return n; + }else{ + n->n1 = icast(4, sign, n->n1); + n->n2 = icast(4, sign, n->n2); + return icast(4, sign, n); + } + case OPEQ: + case OPNE: + case OPLT: + case OPLE: + n->typ = type(TYPINT, 4, sign); + if(s1 > 4 || s2 > 4){ + n->n1 = icast(8, sign, n->n1); + n->n2 = icast(8, sign, n->n2); + return n; + }else{ + n->n1 = icast(4, sign, n->n1); + n->n2 = icast(4, sign, n->n2); + return n; + } + case OPLAND: + case OPLOR: + n->typ = type(TYPINT, 4, sign); + return n; + case OPLSH: + case OPRSH: + if(n->n1->typ->size <= 4) + n->n1 = icast(4, n->n1->typ->sign, n->n1); + n->typ = n->n1->typ; + return icast(n->typ->size, n->typ->sign, n); + default: + sysfatal("typecheck: unknown op %d", n->op); + } + break; + case OCAST: + n->n1 = typecheck(n->n1); + if(n->n1->typ == nil) + break; + if(n->typ->type == TYPINT && n->n1->typ->type == TYPINT){ + }else if(n->typ == n->n1->typ){ + }else if(n->typ->type == TYPSTRING && n->n1->typ->type == TYPINT){ + }else + error("can't cast from %τ to %τ", n->n1->typ, n->typ); + break; + case OLNOT: + n->n1 = typecheck(n->n1); + if(n->n1->typ == nil) + break; + if(n->n1->typ->type != TYPINT){ + error("%τ not allowed in operation", n->n1->typ); + break; + } + n->typ = type(TYPINT, 4, 1); + break; + case OTERN: + n->n1 = typecheck(n->n1); + n->n2 = typecheck(n->n2); + n->n3 = typecheck(n->n3); + if(n->n1->typ == nil || n->n2->typ == nil || n->n3->typ == nil) + break; + if(n->n1->typ->type != TYPINT){ + error("%τ not allowed in operation", n->n1->typ); + break; + } + if(n->n2->typ->type == TYPINT || n->n3->typ->type == TYPINT){ + sign = n->n2->typ->sign && n->n3->typ->sign; + s1 = n->n2->typ->size; + s2 = n->n3->typ->size; + if(s1 > 4 || s2 > 4){ + n->n2 = icast(8, sign, n->n2); + n->n3 = icast(8, sign, n->n3); + n->typ = type(TYPINT, 8, sign); + return n; + }else{ + n->n2 = icast(4, sign, n->n2); + n->n3 = icast(4, sign, n->n3); + n->typ = type(TYPINT, 4, sign); + return n; + } + }else if(n->n2->typ == n->n3->typ){ + n->typ = n->n2->typ; + }else + error("don't know how to do ternary with %τ and %τ", n->n2->typ, n->n3->typ); + break; + case ORECORD: + default: sysfatal("typecheck: unknown node type %α", n->type); + } + return n; +} + +vlong +evalop(int op, int sign, vlong v1, vlong v2) +{ + switch(/*oper*/op){ + case OPADD: return v1 + v2; break; + case OPSUB: return v1 - v2; break; + case OPMUL: return v1 * v2; break; + case OPDIV: if(v2 == 0) sysfatal("division by zero"); return sign ? v1 / v2 : (uvlong)v1 / (uvlong)v2; break; + case OPMOD: if(v2 == 0) sysfatal("division by zero"); return sign ? v1 % v2 : (uvlong)v1 % (uvlong)v2; break; + case OPAND: return v1 & v2; break; + case OPOR: return v1 | v2; break; + case OPXOR: return v1 ^ v2; break; + case OPXNOR: return ~(v1 ^ v2); break; + case OPLSH: + if((u64int)v2 >= 64) + return 0; + else + return v1 << v2; + break; + case OPRSH: + if(sign){ + if((u64int)v2 >= 64) + return v1 >> 63; + else + return v1 >> v2; + }else{ + if((u64int)v2 >= 64) + return 0; + else + return (u64int)v1 >> v2; + } + break; + case OPEQ: return v1 == v2; break; + case OPNE: return v1 != v2; break; + case OPLT: return v1 < v2; break; + case OPLE: return v1 <= v2; break; + case OPLAND: return v1 && v2; break; + case OPLOR: return v1 || v2; break; + default: + sysfatal("cfold: unknown op %.2x", op); + return 0; + } + +} + +Node * +addtype(Type *t, Node *n) +{ + n->typ = t; + return n; +} + +/* fold constants */ + +static Node * +cfold(Node *n) +{ + switch(/*nodetype*/n->type){ + case ONUM: + case OSYM: + case OSTR: + return n; + case OBIN: + n->n1 = cfold(n->n1); + n->n2 = cfold(n->n2); + if(n->n1->type != ONUM || n->n2->type != ONUM) + return n; + return addtype(n->typ, node(ONUM, evalop(n->op, n->typ->sign, n->n1->num, n->n2->num))); + case OLNOT: + n->n1 = cfold(n->n1); + if(n->n1->type == ONUM) + return addtype(n->typ, node(ONUM, !n->n1->num)); + return n; + case OTERN: + n->n1 = cfold(n->n1); + n->n2 = cfold(n->n2); + n->n3 = cfold(n->n3); + if(n->n1->type == ONUM) + return n->n1->num ? n->n2 : n->n3; + return n; + case OCAST: + n->n1 = cfold(n->n1); + if(n->n1->type != ONUM || n->typ->type != TYPINT) + return n; + switch(n->typ->size << 4 | n->typ->sign){ + case 0x10: return addtype(n->typ, node(ONUM, (vlong)(u8int)n->n1->num)); + case 0x11: return addtype(n->typ, node(ONUM, (vlong)(s8int)n->n1->num)); + case 0x20: return addtype(n->typ, node(ONUM, (vlong)(u16int)n->n1->num)); + case 0x21: return addtype(n->typ, node(ONUM, (vlong)(s16int)n->n1->num)); + case 0x40: return addtype(n->typ, node(ONUM, (vlong)(u32int)n->n1->num)); + case 0x41: return addtype(n->typ, node(ONUM, (vlong)(s32int)n->n1->num)); + case 0x80: return addtype(n->typ, node(ONUM, n->n1->num)); + case 0x81: return addtype(n->typ, node(ONUM, n->n1->num)); + } + return n; + case ORECORD: + default: + fprint(2, "cfold: unknown type %α\n", n->type); + return n; + } +} + +/* calculate the minimum record size for each node of the expression */ +static Node * +calcrecsize(Node *n) +{ + switch(/*nodetype*/n->type){ + case ONUM: + case OSTR: + n->recsize = 0; + break; + case OSYM: + switch(n->sym->type){ + case SYMVAR: + switch(n->sym->idx){ + case DTV_TIME: + case DTV_PROBE: + n->recsize = 0; + break; + default: + n->recsize = n->typ->size; + break; + } + break; + default: sysfatal("calcrecsize: unknown symbol type %d", n->sym->type); return nil; + } + break; + case OBIN: + n->n1 = calcrecsize(n->n1); + n->n2 = calcrecsize(n->n2); + n->recsize = min(n->typ->size, n->n1->recsize + n->n2->recsize); + break; + case OLNOT: + n->n1 = calcrecsize(n->n1); + n->recsize = min(n->typ->size, n->n1->recsize); + break; + case OCAST: + n->n1 = calcrecsize(n->n1); + if(n->typ->type == TYPSTRING) + n->recsize = n->typ->size; + else + n->recsize = min(n->typ->size, n->n1->recsize); + break; + case OTERN: + n->n1 = calcrecsize(n->n1); + n->n2 = calcrecsize(n->n2); + n->n3 = calcrecsize(n->n3); + n->recsize = min(n->typ->size, n->n1->recsize + n->n2->recsize + n->n3->recsize); + break; + case ORECORD: + default: sysfatal("calcrecsize: unknown type %α", n->type); return nil; + } + return n; +} + +/* insert ORECORD nodes to mark the subexpression that we will pass to the kernel */ +static Node * +insrecord(Node *n) +{ + if(n->recsize == 0) + return n; + if(n->typ->size == n->recsize) + return addtype(n->typ, node(ORECORD, n)); + switch(/*nodetype*/n->type){ + case ONUM: + case OSTR: + case OSYM: + break; + case OBIN: + n->n1 = insrecord(n->n1); + n->n2 = insrecord(n->n2); + break; + case OLNOT: + case OCAST: + n->n1 = insrecord(n->n1); + break; + case OTERN: + n->n1 = insrecord(n->n1); + n->n2 = insrecord(n->n2); + n->n3 = insrecord(n->n3); + break; + case ORECORD: + default: sysfatal("insrecord: unknown type %α", n->type); return nil; + } + return n; +} + +/* + delete useless casts. + going down we determine the number of bits (m) needed to be correct at each stage. + going back up we determine the number of bits (n->databits) which can be either 0 or 1. + all other bits are either zero (n->upper == UPZX) or sign-extended (n->upper == UPSX). + note that by number of bits we always mean a consecutive block starting from the LSB. + + we can delete a cast if it either affects only bits not needed (according to m) or + if it's a no-op (according to databits, upper). +*/ +static Node * +elidecasts(Node *n, int m) +{ + switch(/*nodetype*/n->type){ + case OSTR: + return n; + case ONUM: + n->databits = n->typ->size * 8; + n->upper = n->typ->sign ? UPSX : UPZX; + break; + case OSYM: + /* TODO: make less pessimistic */ + n->databits = 64; + break; + case OBIN: + switch(/*oper*/n->op){ + case OPADD: + case OPSUB: + n->n1 = elidecasts(n->n1, m); + n->n2 = elidecasts(n->n2, m); + n->databits = min(64, max(n->n1->databits, n->n2->databits) + 1); + n->upper = n->n1->upper | n->n2->upper; + break; + case OPMUL: + n->n1 = elidecasts(n->n1, m); + n->n2 = elidecasts(n->n2, m); + n->databits = min(64, n->n1->databits + n->n2->databits); + n->upper = n->n1->upper | n->n2->upper; + break; + case OPAND: + case OPOR: + case OPXOR: + case OPXNOR: + n->n1 = elidecasts(n->n1, m); + n->n2 = elidecasts(n->n2, m); + if(n->op == OPAND && (n->n1->upper == UPZX || n->n2->upper == UPZX)){ + n->upper = UPZX; + if(n->n1->upper == UPZX && n->n2->upper == UPZX) + n->databits = min(n->n1->databits, n->n2->databits); + else if(n->n1->upper == UPZX) + n->databits = n->n1->databits; + else + n->databits = n->n2->databits; + }else{ + n->databits = max(n->n1->databits, n->n2->databits); + n->upper = n->n1->upper | n->n2->upper; + } + break; + case OPLSH: + n->n1 = elidecasts(n->n1, m); + n->n2 = elidecasts(n->n2, 64); + if(n->n2->type == ONUM && n->n2->num >= 0 && n->n1->databits + (uvlong)n->n2->num <= 64) + n->databits = n->n1->databits + n->n2->num; + else + n->databits = 64; + n->upper = n->n1->upper; + break; + case OPRSH: + n->n1 = elidecasts(n->n1, 64); + n->n2 = elidecasts(n->n2, 64); + if(n->n1->upper == n->typ->sign){ + n->databits = n->n1->databits; + n->upper = n->n1->upper; + }else{ + n->databits = 64; + n->upper = UPZX; + } + break; + case OPEQ: + case OPNE: + case OPLT: + case OPLE: + case OPLAND: + case OPLOR: + n->n1 = elidecasts(n->n1, 64); + n->n2 = elidecasts(n->n2, 64); + n->databits = 1; + n->upper = UPZX; + break; + case OPDIV: + case OPMOD: + default: + n->n1 = elidecasts(n->n1, 64); + n->n2 = elidecasts(n->n2, 64); + n->databits = 64; + n->upper = UPZX; + break; + } + break; + case OLNOT: + n->n1 = elidecasts(n->n1, 64); + n->databits = 1; + n->upper = UPZX; + break; + case OCAST: + switch(n->typ->type){ + case TYPINT: + n->n1 = elidecasts(n->n1, min(n->typ->size * 8, m)); + if(n->n1->databits < n->typ->size * 8 && n->n1->upper == n->typ->sign){ + n->databits = n->n1->databits; + n->upper = n->n1->upper; + }else{ + n->databits = n->typ->size * 8; + n->upper = n->typ->sign ? UPSX : UPZX; + } + if(n->typ->size * 8 >= m) return n->n1; + if(n->typ->size * 8 >= n->n1->databits && n->typ->sign == n->n1->upper) return n->n1; + if(n->typ->size * 8 > n->n1->databits && n->typ->sign && !n->n1->upper) return n->n1; + break; + case TYPSTRING: + n->n1 = elidecasts(n->n1, 64); + break; + default: + sysfatal("elidecasts: don't know how to cast %τ to %τ", n->n1->typ, n->typ); + } + break; + case ORECORD: + n->n1 = elidecasts(n->n1, min(n->typ->size * 8, m)); + if(n->n1->databits < n->typ->size * 8 && n->n1->upper == n->typ->sign){ + n->databits = n->n1->databits; + n->upper = n->n1->upper; + }else{ + n->databits = n->typ->size * 8; + n->upper = n->typ->sign ? UPSX : UPZX; + } + break; + case OTERN: + n->n1 = elidecasts(n->n1, 64); + n->n2 = elidecasts(n->n2, m); + n->n3 = elidecasts(n->n3, m); + if(n->n2->upper == n->n3->upper){ + n->databits = max(n->n2->databits, n->n3->databits); + n->upper = n->n2->upper; + }else{ + if(n->n3->upper == UPSX) + n->databits = max(min(64, n->n2->databits + 1), n->n3->databits); + else + n->databits = max(min(64, n->n3->databits + 1), n->n2->databits); + n->upper = UPSX; + } + break; + default: sysfatal("elidecasts: unknown type %α", n->type); + } +// print("need %d got %d%c %ε\n", n->needbits, n->databits, "ZS"[n->upper], n); + return n; +} + + +Node * +exprcheck(Node *n, int pred) +{ + if(dflag) print("start %ε\n", n); + n = typecheck(n); + if(errors) return n; + if(dflag) print("typecheck %ε\n", n); + n = cfold(n); + if(dflag) print("cfold %ε\n", n); + if(!pred){ + n = insrecord(calcrecsize(n)); + if(dflag) print("insrecord %ε\n", n); + } + n = elidecasts(n, 64); + if(dflag) print("elidecasts %ε\n", n); + return n; +} |