代码拉取完成,页面将自动刷新
/*
* Expression compiler and simulator.
* https://github.com/rswier/c4.git
*
* Instruction Set
* pc: instruction register
* sp: stack pointer
* bp: frame pointer
* a : general purpose register
*
* Opcode Instruction #Description
* LEA imm64 LEA a, imm64[bp] #case LEA: a = (long)(bp + *pc++); break; // load local address
* IMM imm64 IMM a, imm64 #case IMM: a = *pc++; break; // load global address or immediate
* JMP imm64 JMP imm64 #case JMP: pc = (long *)*pc; break; // jump
* JSR imm64 JSR imm64 #case JSR: { *--sp = (long)(pc + 1); pc = (long *)*pc; } break; // jump to subroutine
* BZ imm64 BZ imm64 #case BZ: pc = a ? pc + 1 : (long *)*pc; break; // branch if zero
* BNZ imm64 BNZ imm64 #case BNZ: pc = a ? (long *)*pc : pc + 1; break; // branch if not zero
* ENT imm64 ENT imm64 #case ENT: { *--sp = (long)bp; bp = sp; sp = sp - *pc++; } break; // enter subroutine
* ADJ imm64 ADJ imm64 #case ADJ: sp = sp + *pc++; break; // stack adjust
* LI imm64 LI imm64 #case LI: switch(*pc++) { case CHAR: a = *(char *)a; ...} // load int
* SI imm64 SI imm64 #case SI: switch(*pc++) { case CHAR: *(char *)*sp++ = a; ...} // store int
* LEV LEV #case LEV: { sp = bp; bp = (long *)*sp++; pc = (long *)*sp++; } break; // leave subroutine
* PSH PSH a #case PSH: *--sp = a; break; // push
* #
* OR OR a, [sp] #case OR: a = *sp++ | a; break;
* XOR XOR a, [sp] #case XOR: a = *sp++ ^ a; break;
* AND AND a, [sp] #case AND: a = *sp++ & a; break;
* EQ EQ a, [sp] #case EQ: a = *sp++ == a; break;
* NE NE a, [sp] #case NE: a = *sp++ != a; break;
* LT LT a, [sp] #case LT: a = *sp++ < a; break;
* GT GT a, [sp] #case GT: a = *sp++ > a; break;
* LE LE a, [sp] #case LE: a = *sp++ <= a; break;
* GE GE a, [sp] #case GE: a = *sp++ >= a; break;
* SHL SHL a, [sp] #case SHL: a = *sp++ << a; break;
* SHR SHR a, [sp] #case SHR: a = *sp++ >> a; break;
* ADD ADD a, [sp] #case ADD: a = *sp++ + a; break;
* SUB SUB a, [sp] #case SUB: a = *sp++ - a; break;
* MUL MUL a, [sp] #case MUL: a = *sp++ * a; break;
* DIV DIV a, [sp] #case DIV: a = *sp++ / a; break;
* MOD MOD a, [sp] #case MOD: a = *sp++ % a; break;
* #
* PRTF PRTF a, [sp] #case PRTF: t = sp + pc[1]; a = printf((char *)t[-1], t[-2], ...); break;
* KSYM KSYM a, [sp] #case KSYM: a = ksymbol(*sp); break;
* #case EXIT: return a;
**/
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <string.h>
#include <unistd.h>
#include <setjmp.h>
#include <arpa/inet.h>
#include <monitor.h>
#include <tep.h>
#include <expr.h>
#include <stack_helpers.h>
extern const char *syscalls_table[];
extern const int syscalls_table_size;
char *p, *lp, // current position in source code
*data, *str; // data/string pointer
long *e, *le, // current position in emitted code
tk, // current token
ival, // current token value
ty, // current expression type
loc; // local variable offset
struct symbol_table *id; // currently parsed identifier
struct symbol_table *symtab; // symbol table (simple list of identifiers)
int n_syms, nr_syms;
jmp_buf synerr_jmp;
// tokens and classes (operators last and in precedence order)
enum {
Num = 128, Fun, Sys, Glo, Loc, Id,
Int, Sizeof,
Assign, Cond, Lor, Lan, Or, Xor, And, Eq, Ne, Lt, Gt, Le, Ge, Match, Shl, Shr, Add, Sub, Mul, Div, Mod, Inc, Dec, Brak
};
// opcodes
enum { LEA ,IMM ,JMP ,JSR ,BZ ,BNZ ,ENT ,ADJ ,LI ,SI ,LEV ,PSH ,LTu ,GTu ,LEu, GEu ,
OR ,XOR ,AND ,EQ ,NE ,LT ,GT ,LE ,GE ,SHL ,SHR ,SAR, ADD ,SUB ,MUL ,DIV ,MOD ,DIVu,MODu,
PRTF, KSYM, NTHL, NTHS, STRNCMP, COMM, MATCH, STREQ, STRNE, SYSCALL, KVMEXIT, SYSTEM, EXIT };
// types
enum { CHAR, SHORT, INT, LONG, ARRAY = 0x4, UNSIGNED = 0x8, PTR = 0x10 };
#define INSN "LEA ,IMM ,JMP ,JSR ,BZ ,BNZ ,ENT ,ADJ ,LI ,SI ,LEV ,PSH ,LTu ,GTu ,LEu, GEu ," \
"OR ,XOR ,AND ,EQ ,NE ,LT ,GT ,LE ,GE ,SHL ,SHR ,SAR ,ADD ,SUB ,MUL ,DIV ,MOD ,DIVu,MODu," \
"PRTF,KSYM,NTHL,NTHS,SCMP,COMM,MACH,SSEQ,SSNE,SCAL,VMXT,SYST,EXIT,"
#define ADD_KEY(name, _token, _type) \
{ p = (char *)name; { next(); id->token = _token; id->class = 0; id->type = _type; id->value = 0; } }
#define ADD_LIB(name, _type, insn) \
{ p = (char *)name; { next(); id->class = Sys; id->type = _type; id->value = insn; } }
#define ADD_GLO(name, _type, _value) \
{ p = (char *)name; { next(); id->class = Glo; id->type = _type; id->nr_elm = 1; id->value = (long)_value; } }
#define TK_SHIFT 6
#define HASH(tk, len) (((tk) << TK_SHIFT) + (len))
#define LEN(hash) ((hash) & ((1 << TK_SHIFT) - 1))
static void synerr(const char *s)
{
printf("%s\n", lp);
printf("%*s%s\n", (int)(p-lp+1), "^ ", s);
longjmp(synerr_jmp, -1);
}
static int typeop(int op)
{
switch (op) {
case Sizeof: return (ty >= PTR) ? sizeof(void *) : (1 << (ty & ~UNSIGNED));
case Assign: return (ty >= PTR) ? PTR : ty;
case Inc:
case Dec:
return (ty>=PTR+PTR) ? sizeof(void *) : (ty>=PTR ? (1<<((ty-PTR) & ~UNSIGNED)) : 1);
case Add:
case Sub:
case Brak:
return (ty>=PTR+PTR) ? sizeof(void *) : (1<<((ty-PTR) & ~UNSIGNED));
default: synerr("bad typeop");
}
return -1;
}
static void next(void)
{
char *pp;
while ((tk = *p)) {
++p;
if (tk == '\n') ;
else if (tk == '#') {
while (*p != 0 && *p != '\n') ++p;
}
else if ((tk >= 'a' && tk <= 'z') || (tk >= 'A' && tk <= 'Z') || tk == '_') {
pp = p - 1;
while ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9') || *p == '_')
tk = tk * 147 + *p++;
tk = HASH(tk, p - pp);
id = symtab;
while (id < symtab+nr_syms && id->token) {
if (tk == id->hash && !memcmp(id->name, pp, p - pp)) { tk = id->token; return; }
id ++;
}
if (id == symtab+nr_syms) {
nr_syms += 16;
symtab = realloc(symtab, nr_syms*sizeof(*symtab));
memset(&symtab[nr_syms-16], 0, 16*sizeof(*symtab));
id = &symtab[n_syms];
}
n_syms ++;
id->name = pp;
id->hash = tk;
tk = id->token = Id;
return;
}
else if (tk >= '0' && tk <= '9') {
if ((ival = tk - '0')) {
while (*p >= '0' && *p <= '9') ival = ival * 10 + *p++ - '0';
}
else if (*p == 'x' || *p == 'X') {
while ((tk = *++p) && ((tk >= '0' && tk <= '9') || (tk >= 'a' && tk <= 'f') || (tk >= 'A' && tk <= 'F')))
ival = ival * 16 + (tk & 15) + (tk >= 'A' ? 9 : 0);
} else {
while (*p >= '0' && *p <= '7')
ival = ival * 8 + *p++ - '0';
}
tk = Num;
return;
}
else if (tk == '/') {
if (*p == '/') {
++p;
while (*p != 0 && *p != '\n') ++p;
} else {
tk = Div;
return;
}
}
else if (tk == '\'' || tk == '"') {
pp = str;
while (*p != 0 && *p != tk) {
if ((ival = *p++) == '\\') {
if ((ival = *p++) == 'n') ival = '\n';
}
if (tk == '"') *str++ = ival;
}
++p; *str++ = '\0';
if (tk == '"') ival = (long)pp;
else tk = Num;
return;
}
else if (tk == '=') { if (*p == '=') { ++p; tk = Eq; } else tk = Assign; return; }
else if (tk == '+') { if (*p == '+') { ++p; tk = Inc; } else tk = Add; return; }
else if (tk == '-') { if (*p == '-') { ++p; tk = Dec; } else tk = Sub; return; }
else if (tk == '!') { if (*p == '=') { ++p; tk = Ne; } return; }
else if (tk == '<') { if (*p == '=') { ++p; tk = Le; } else if (*p == '<') { ++p; tk = Shl; } else tk = Lt; return; }
else if (tk == '>') { if (*p == '=') { ++p; tk = Ge; } else if (*p == '>') { ++p; tk = Shr; } else tk = Gt; return; }
else if (tk == '|') { if (*p == '|') { ++p; tk = Lor; } else tk = Or; return; }
else if (tk == '&') { if (*p == '&') { ++p; tk = Lan; } else tk = And; return; }
else if (tk == '^') { tk = Xor; return; }
else if (tk == '%') { tk = Mod; return; }
else if (tk == '*') { tk = Mul; return; }
else if (tk == '[') { tk = Brak; return; }
else if (tk == '?') { tk = Cond; return; }
else if (tk == '~') { tk = Match; return; }
else if (tk == ';' || tk == '{' || tk == '}' || tk == '(' || tk == ')' || tk == ']' || tk == ',' || tk == ':') return;
}
}
static void expr(int lev)
{
long t, *d;
if (!tk) { synerr("unexpected eof in expression"); }
else if (tk == Num) { *++e = IMM; *++e = ival; next(); ty = INT; }
else if (tk == '"') {
*++e = IMM; *++e = ival; next();
while (tk == '"') next();
ty = (CHAR|PTR);
}
else if (tk == Sizeof) {
next(); if (tk == '(') next(); else { synerr("open paren expected in sizeof"); }
if (tk == Int) { ty = id->type; if (ty == UNSIGNED) ty |= INT; next(); } else { synerr("wrong type"); }
if (tk == Int) { ty = (ty&UNSIGNED) | id->type; next(); }
while (tk == Mul) { next(); ty = ty + PTR; }
if (tk == ')') next(); else { synerr("close paren expected in sizeof"); }
*++e = IMM; *++e = typeop(Sizeof);
ty = INT;
}
else if (tk == Id) {
struct symbol_table *s = id;
next();
if (tk == '(') {
next();
t = 0;
while (tk != ')') { expr(Assign); *++e = PSH; ++t; if (tk == ',') next(); }
next();
if (s->class == Sys) { *++e = s->value; s->ref++; }
else if (s->class == Fun) { *++e = JSR; *++e = s->value; s->ref++; }
else { synerr("bad function call"); }
if (t) { *++e = ADJ; *++e = t; }
ty = s->type;
}
else if (s->class == Num) { *++e = IMM; *++e = s->value; ty = INT; }
else {
if (s->class == Loc) { *++e = LEA; *++e = loc - s->value; s->ref++; }
else if (s->class == Glo) { *++e = IMM; *++e = s->value; s->ref++; }
else { synerr("undefined variable"); }
ty = s->type;
if (ty & ARRAY) { ty &= ~ARRAY; ty |= PTR; }
else { *++e = LI; *++e = typeop(Assign); }
}
}
else if (tk == '(') {
next();
if (tk == Int) {
t = id->type; if (t == UNSIGNED) t |= INT; next();
if (tk == Int) { t = (t&UNSIGNED) | id->type; next(); }
while (tk == Mul) { next(); t = t + PTR; }
if (tk == ')') next(); else { synerr("bad cast"); }
expr(Inc);
ty = t;
if (*(e-1) == LI) *e = typeop(Assign);
}
else {
expr(Assign);
if (tk == ')') next(); else { synerr("close paren expected"); }
}
}
else if (tk == Mul) {
next(); expr(Inc);
if (ty >= PTR) ty = ty - PTR; else { synerr("bad dereference"); }
*++e = LI; *++e = typeop(Assign);
}
else if (tk == And) {
next(); expr(Inc);
if (*(e-1) == LI) e -= 2; else { synerr("bad address-of"); }
ty = ty + PTR;
}
else if (tk == '!') { next(); expr(Inc); *++e = PSH; *++e = IMM; *++e = 0; *++e = EQ; ty = INT; }
else if (tk == Match) { next(); expr(Inc); *++e = PSH; *++e = IMM; *++e = -1; *++e = XOR; ty = INT; }
else if (tk == Add) { next(); expr(Inc); ty = INT; }
else if (tk == Sub) {
next(); *++e = IMM;
if (tk == Num) { *++e = -ival; next(); } else { *++e = -1; *++e = PSH; expr(Inc); *++e = MUL; }
ty = INT;
}
else if (tk == Inc || tk == Dec) {
t = tk; next(); expr(Inc);
if (*(e-1) == LI) { *(e-1) = PSH; ++e; *e = *(e-1); *(e-1) = LI; }
else { synerr("bad lvalue in pre-increment"); }
*++e = PSH;
*++e = IMM; *++e = typeop(t);
*++e = (t == Inc) ? ADD : SUB;
*++e = SI; *++e = typeop(Assign);
}
else { synerr("bad expression"); }
while (tk >= lev) { // "precedence climbing" or "Top Down Operator Precedence" method
t = ty;
if (tk == Assign) {
next();
if (*(e-1) == LI) *--e = PSH; else { synerr("bad lvalue in assignment"); }
expr(Assign); ty = t; *++e = SI; *++e = typeop(Assign);
}
else if (tk == Cond) {
next();
*++e = BZ; d = ++e;
expr(Assign);
if (tk == ':') next(); else { synerr("conditional missing colon"); }
*d = (long)(e + 3); *++e = JMP; d = ++e;
expr(Cond);
*d = (long)(e + 1);
}
else if (tk == Lor) { next(); *++e = BNZ; d = ++e; expr(Lan); *d = (long)(e + 1); ty = INT; }
else if (tk == Lan) { next(); *++e = BZ; d = ++e; expr(Or); *d = (long)(e + 1); ty = INT; }
else if (tk == Or) { next(); *++e = PSH; expr(Xor); *++e = OR; ty = INT; }
else if (tk == Xor) { next(); *++e = PSH; expr(And); *++e = XOR; ty = INT; }
else if (tk == And) { next(); *++e = PSH; expr(Eq); *++e = AND; ty = INT; }
else if (tk == Eq) {
next(); *++e = PSH; expr(Lt);
if (t == (CHAR|PTR) && ty == (CHAR|PTR)) *++e = STREQ;
else *++e = EQ;
ty = INT;
}
else if (tk == Ne) {
next(); *++e = PSH; expr(Lt);
if (t == (CHAR|PTR) && ty == (CHAR|PTR)) *++e = STRNE;
else *++e = NE;
ty = INT;
}
else if (tk == Lt) { next(); *++e = PSH; expr(Shl); *++e = (t>=PTR || ty>=PTR || ((t|ty)&UNSIGNED))?LTu:LT; ty = INT; }
else if (tk == Gt) { next(); *++e = PSH; expr(Shl); *++e = (t>=PTR || ty>=PTR || ((t|ty)&UNSIGNED))?GTu:GT; ty = INT; }
else if (tk == Le) { next(); *++e = PSH; expr(Shl); *++e = (t>=PTR || ty>=PTR || ((t|ty)&UNSIGNED))?LEu:LE; ty = INT; }
else if (tk == Ge) { next(); *++e = PSH; expr(Shl); *++e = (t>=PTR || ty>=PTR || ((t|ty)&UNSIGNED))?GEu:GE; ty = INT; }
else if (tk == Match) {
if ( t != (CHAR|PTR)) synerr("~ operator requires char * operand");
next(); *++e = PSH; expr(Shl);
if (ty != (CHAR|PTR)) synerr("~ operator requires char * operand");
*++e = MATCH; ty = INT;
}
else if (tk == Shl) { next(); *++e = PSH; expr(Add); *++e = SHL; ty = INT; }
else if (tk == Shr) { next(); *++e = PSH; expr(Add); *++e = (t>=PTR || ty>=PTR || ((t|ty)&UNSIGNED))?SHR:SAR; ty = INT; }
else if (tk == Add) {
next(); *++e = PSH; expr(Mul);
if ((ty = t) > PTR) { *++e = PSH; *++e = IMM; *++e = typeop(Add); *++e = MUL; }
*++e = ADD;
}
else if (tk == Sub) {
next(); *++e = PSH; expr(Mul);
if (t > PTR && t == ty) { *++e = SUB; *++e = PSH; *++e = IMM; *++e = typeop(Sub); *++e = DIV; ty = INT; }
else if ((ty = t) > PTR) { *++e = PSH; *++e = IMM; *++e = typeop(Sub); *++e = MUL; *++e = SUB; }
else *++e = SUB;
}
else if (tk == Mul) { next(); *++e = PSH; expr(Inc); *++e = MUL; ty = INT; }
else if (tk == Div) { next(); *++e = PSH; expr(Inc); *++e = (t>=PTR || ty>=PTR || ((t|ty)&UNSIGNED))?DIVu:DIV; ty = INT; }
else if (tk == Mod) { next(); *++e = PSH; expr(Inc); *++e = (t>=PTR || ty>=PTR || ((t|ty)&UNSIGNED))?MODu:MOD; ty = INT; }
else if (tk == Inc || tk == Dec) {
if (*(e-1) == LI) { *(e-1) = PSH; ++e; *e = *(e-1); *(e-1) = LI; }
else { synerr("bad lvalue in post-increment"); }
*++e = PSH;
*++e = IMM; *++e = typeop(tk); //*++e = (ty > PTR) ? sizeof(long) : sizeof(char);
*++e = (tk == Inc) ? ADD : SUB;
*++e = SI; *++e = typeop(Assign);
*++e = PSH;
*++e = IMM; *++e = typeop(tk); //(ty > PTR) ? sizeof(long) : sizeof(char);
*++e = (tk == Inc) ? SUB : ADD;
next();
}
else if (tk == Brak) {
next(); *++e = PSH; expr(Assign);
if (tk == ']') next(); else { synerr("close bracket expected"); }
if (t > PTR) { *++e = PSH; *++e = IMM; ty = t; *++e = typeop(Brak); *++e = MUL; }
else if (t < PTR) { synerr("pointer type expected"); }
*++e = ADD;
ty = t - PTR;
*++e = LI; *++e = typeop(Assign);
}
else { synerr("compiler error"); }
}
}
static const char *ksymbol(unsigned long func)
{
char *name = function_resolver(NULL, (unsigned long long *)&func, NULL);
return name ? : "Unknown";
}
/* Wildcard matching function that implements trace event filter's ~ operator
* Supports:
* * - matches any number of characters
* ? - matches exactly one character
* [abc] - matches any character in the set
* [a-z] - matches any character in the range
* [^abc]|[!abc] - matches any character NOT in the set
* Returns: 1 if string matches pattern, 0 otherwise
*/
static int wildcard_match(const char *str, const char *pattern)
{
const char *s, *p, *star = NULL, *str_backup = NULL;
int invert, match;
if (!str || !pattern)
return 0;
s = str;
p = pattern;
while (*s) {
if (*p == '*') {
/* Skip consecutive stars */
while (*p == '*')
p++;
/* If star is at the end, it matches everything */
if (!*p)
return 1;
/* Save positions for backtracking */
star = p;
str_backup = s;
} else if (*p == '?') {
/* ? matches exactly one character */
s++;
p++;
} else if (*p == '[') {
/* Character class matching */
p++;
invert = 0;
if (*p == '^' || *p == '!') {
invert = 1;
p++;
}
match = 0;
while (*p && *p != ']') {
if (*(p+1) == '-' && *(p+2) != ']' && *(p+2)) {
/* Range: [a-z] */
if (*s >= *p && *s <= *(p+2))
match = 1;
p += 3;
} else {
/* Single character */
if (*s == *p)
match = 1;
p++;
}
}
if (*p == ']')
p++;
if (invert)
match = !match;
if (!match) {
/* Backtrack if we have a star */
if (star) {
p = star;
s = ++str_backup;
continue;
}
return 0;
}
s++;
} else if (*p == *s) {
/* Exact character match */
s++;
p++;
} else {
/* No match, try to backtrack */
if (star) {
p = star;
s = ++str_backup;
} else {
return 0;
}
}
}
/* Skip any trailing stars */
while (*p == '*')
p++;
/* Match if we've consumed both strings */
return !*p;
}
static void dump_symtab(struct symbol_table *symtab, int nr, bool print_value);
struct expr_prog *expr_compile(char *expr_str, struct global_var_declare *declare)
{
int i, err;
int nr_insn = 1024;
int datasize = 256;
int strsize = strlen(expr_str);
char *d, *s;
struct expr_prog *prog = NULL;
// reset
loc = 0;
n_syms = 0;
// Default keywords (6) + library symbols (9) + default global (2) + tracepoint headers (4) = 21 symbols
// Original nr_syms = 16 is too small, memory reallocations needed
nr_syms = 32;
symtab = calloc(nr_syms, sizeof(struct symbol_table));
data = d = malloc(datasize);
str = s = malloc(strsize);
le = e = calloc(nr_insn, sizeof(long));
if (!symtab || !data || !str || !le)
goto err_return;
prog = calloc(1, sizeof(*prog));
if (!prog)
goto err_return;
// add keywords to symbol table
ADD_KEY("char", Int, CHAR);
ADD_KEY("short", Int, SHORT);
ADD_KEY("int", Int, INT);
ADD_KEY("long", Int, LONG);
ADD_KEY("unsigned", Int, UNSIGNED);
ADD_KEY("sizeof", Sizeof, INT);
// add library to symbol table
// int printf(const char *format, ...);
ADD_LIB("printf", INT, PRTF);
// char *ksymbol(unsigned long func);
ADD_LIB("ksymbol", CHAR | PTR, KSYM);
// uint32_t ntohl(uint32_t netlong);
ADD_LIB("ntohl", INT, NTHL);
// uint16_t ntohs(uint16_t netshort);
ADD_LIB("ntohs", SHORT, NTHS);
// int strncmp(const char *s1, const char *s2, long n);
ADD_LIB("strncmp", INT, STRNCMP);
// char *comm_get(int pid);
ADD_LIB("comm_get", CHAR | PTR, COMM);
// char *syscall_name(int id);
ADD_LIB("syscall_name", CHAR | PTR, SYSCALL);
// char *exit_reason_str(int isa, int val);
ADD_LIB("exit_reason_str", CHAR | PTR, KVMEXIT);
// int system(const char *format, ...);
ADD_LIB("system", INT, SYSTEM);
// add default global var to symbol table
ADD_GLO("_cpu", INT, &prog->glo._cpu);
ADD_GLO("_pid", INT, &prog->glo._pid);
data = d; // reset data
memset(d, 0, datasize);
str = s; // reset str
memset(s, 0, strsize);
// global variable declaration
if (declare) {
struct global_var_declare *save = declare;
int max_offset, name_size = 0;
while (declare->name) {
name_size += strlen(declare->name) + 1;
declare ++;
}
s = realloc(s, name_size + strsize);
if (!s) goto err_return;
memset(s + name_size, 0, strsize);
restart:
max_offset = 0;
declare = save;
str = s;
while (declare->name) {
// FIX: Copy declare->name to safe buffer to prevent access after free
// declare structure and its name fields (e.g., "_offset", "_len") are temporary
// released after expr_compile, but expr_dump needs to access later
// Solution: Copy names to str buffer for safe long-term access by expr_dump
p = strcpy(str, declare->name);
str += strlen(declare->name) + 1;
next();
id->class = Glo;
switch (declare->elementsize) {
case sizeof(char) : id->type = CHAR; break;
case sizeof(short) : id->type = SHORT; break;
case sizeof(int) : id->type = INT; break;
case sizeof(long) : id->type = LONG; break;
default: goto err_return;
}
if (declare->size != declare->elementsize)
id->type |= ARRAY;
if (declare->is_unsigned)
id->type |= UNSIGNED;
id->nr_elm = declare->size / declare->elementsize;
id->value = (long)data + declare->offset;
if (declare->offset + declare->size > max_offset)
max_offset = declare->offset + declare->size;
declare ++;
}
if (max_offset > datasize) {
datasize += 256;
data = d = realloc(d, datasize);
memset(d, 0, datasize);
goto restart;
}
data += max_offset;
}
lp = p = expr_str;
err = setjmp(synerr_jmp);
if (err == 0) {
do {
next();
expr(Assign);
} while (tk == ',');
*++e = EXIT;
} else
goto err_return;
nr_insn = e - le + 1;
prog->symtab = realloc(symtab, n_syms*sizeof(*symtab));
prog->nr_syms = n_syms;
if (data - d) { prog->data = d; prog->datasize = datasize; }
else { free(d); prog->data = NULL; prog->datasize = 0; }
if (str - s) prog->str = s; else { free(s); prog->str = NULL; }
prog->insn = realloc(le, nr_insn*sizeof(long));
prog->nr_insn = nr_insn;
// FIX: Instruction relocation mechanism for jump targets after realloc
// For JMP/JSR/BZ/BNZ instructions, adjust target address by offset
if (prog->insn != le) {
for (i=1; i<nr_insn; i++) {
switch (prog->insn[i]) {
case JMP: case JSR: case BZ: case BNZ:
prog->insn[i+1] -= (void *)le - (void *)prog->insn;
default: break;
}
if (prog->insn[i] <= SI) i++;
}
}
for (i=0; i<prog->nr_syms; i++) {
struct symbol_table *sym = &prog->symtab[i];
if (sym->class == Sys && sym->value == KSYM && sym->ref)
function_resolver_ref();
if (sym->class == Sys && sym->value == COMM && sym->ref)
global_comm_ref();
}
return prog;
err_return:
printf("Available variables:\n");
dump_symtab(symtab, nr_syms, 0);
if (symtab) free(symtab);
if (d) free(d);
if (s) free(s);
if (le) free(le);
if (prog) free(prog);
return NULL;
}
long expr_run(struct expr_prog *prog)
{
long *pc, *sp, *bp, a, cycle; // vm registers
long i, *t; // temps
long stack[512];
pc = prog->insn + 1;
bp = sp = stack + 512;
a = 0;
cycle = 0;
if (prog->debug) printf("Program running:\n");
while (1) {
i = *pc++;
++cycle;
if (prog->debug) {
if (cycle > 1) printf("; a: 0x%lx\n", a);
printf("%ld> %.4s", cycle, &INSN[i * 5]);
if (i <= SI) printf(" 0x%-16lx", *pc); else printf(" %-18s", "");
}
switch (i) {
case LEA: a = (long)(bp + *pc++); break; // load local address
case IMM: a = *pc++; break; // load global address or immediate
case JMP: pc = (long *)*pc; break; // jump
case JSR: { *--sp = (long)(pc + 1); pc = (long *)*pc; } break; // jump to subroutine
case BZ: pc = a ? pc + 1 : (long *)*pc; break; // branch if zero
case BNZ: pc = a ? (long *)*pc : pc + 1; break; // branch if not zero
case ENT: { *--sp = (long)bp; bp = sp; sp = sp - *pc++; } break; // enter subroutine
case ADJ: sp = sp + *pc++; break; // stack adjust
case LI: switch(*pc++) { // load int
case CHAR: a = *(char *)a; break;
case UNSIGNED|CHAR: a = *(unsigned char *)a; break;
case SHORT: a = *(short *)a; break;
case UNSIGNED|SHORT: a = *(unsigned short *)a; break;
case INT: a = *(int *)a; break;
case UNSIGNED|INT: a = *(unsigned int *)a; break;
case LONG: a = *(long *)a; break;
case UNSIGNED|LONG: a = *(unsigned long *)a; break;
case PTR: a = *(unsigned long *)a; break;
default: printf("wrong instruction\n"); return -1;
} break;
case SI: switch(*pc++) { // store int
case CHAR: *(char *)*sp++ = a; break;
case UNSIGNED|CHAR: *(unsigned char *)*sp++ = a; break;
case SHORT: *(short *)*sp++ = a; break;
case UNSIGNED|SHORT: *(unsigned short *)*sp++ = a; break;
case INT: *(int *)*sp++ = a; break;
case UNSIGNED|INT: *(unsigned int *)*sp++ = a; break;
case LONG: *(long *)*sp++ = a; break;
case UNSIGNED|LONG: *(unsigned long *)*sp++ = a; break;
case PTR: *(unsigned long *)*sp++ = a; break;
default: printf("wrong instruction\n"); return -1;
} break;
case LEV: { sp = bp; bp = (long *)*sp++; pc = (long *)*sp++; } break; // leave subroutin
case PSH: *--sp = a; break; // push
case OR: a = *sp++ | a; break;
case XOR: a = *sp++ ^ a; break;
case AND: a = *sp++ & a; break;
case EQ: a = *sp++ == a; break;
case NE: a = *sp++ != a; break;
case LT: a = *sp++ < a; break;
case GT: a = *sp++ > a; break;
case LE: a = *sp++ <= a; break;
case GE: a = *sp++ >= a; break;
case LTu: a = (unsigned long)(*sp++) < (unsigned long)a; break;
case GTu: a = (unsigned long)(*sp++) > (unsigned long)a; break;
case LEu: a = (unsigned long)(*sp++) <= (unsigned long)a; break;
case GEu: a = (unsigned long)(*sp++) >= (unsigned long)a; break;
case SHL: a = *sp++ << a; break;
case SHR: a = (unsigned long)(*sp++) >> (unsigned long)a; break;
case SAR: a = *sp++ >> a; break;
case ADD: a = *sp++ + a; break;
case SUB: a = *sp++ - a; break;
case MUL: a = *sp++ * a; break;
case DIV: a = *sp++ / a; break;
case DIVu:a = (unsigned long)(*sp++) / (unsigned long)a; break;
case MOD: a = *sp++ % a; break;
case MODu:a = (unsigned long)(*sp++) % (unsigned long)a; break;
case PRTF: t = sp + pc[1]; a = printf((char *)t[-1], t[-2], t[-3], t[-4], t[-5], t[-6], t[-7]); break;
case KSYM: a = (long)(void *)ksymbol(*sp); break;
case NTHL: a = (int)ntohl((int)*sp); break;
case NTHS: a = (short)ntohs((short)*sp); break;
case STRNCMP: t = sp + pc[1]; a = strncmp((const char *)t[-1], (const char *)t[-2], (long)t[-3]); break;
case COMM: a = (long)(void *)(global_comm_get((int)*sp) ?: "<...>"); break;
case MATCH: a = wildcard_match((const char *)*sp++, (const char *)a); break;
case STREQ: a = (strcmp((const char *)*sp++, (const char *)a) == 0); break;
case STRNE: a = (strcmp((const char *)*sp++, (const char *)a) != 0); break;
case SYSCALL: {
int id = (int)*sp;
if (id >= 0 && id < syscalls_table_size && syscalls_table[id])
a = (long)(void *)syscalls_table[id];
else
a = (long)(void *)"Unknown";
} break;
case KVMEXIT: t = sp + pc[1];
a = (long)(void *)find_exit_reason((unsigned int)t[-1], (unsigned int)t[-2]); break;
case SYSTEM: t = sp + pc[1]; {
char cmd[4096];
int ret = snprintf(cmd, sizeof(cmd), (char *)t[-1], t[-2], t[-3], t[-4], t[-5], t[-6], t[-7]);
a = (ret < 0 || ret >= sizeof(cmd)) ? -1 : system(cmd);
} break;
case EXIT: if (prog->debug) printf("exit(0x%lx) cycle = %ld\n", a, cycle); return a;
default: printf("unknown instruction = %ld! cycle = %ld\n", i, cycle); return -1;
}
}
}
int expr_load_glo(struct expr_prog *prog, const char *name, long value)
{
int i;
if (!prog) return 0;
for (i=prog->nr_syms-1; i>=0; i--) {
struct symbol_table *s = &prog->symtab[i];
if (s->token == Id && s->class == Glo && !memcmp(s->name, name, LEN(s->hash))) {
switch (s->type) {
case CHAR: *(char *)s->value = (char)value; break;
case UNSIGNED|CHAR: *(unsigned char *)s->value = (unsigned char)value; break;
case SHORT: *(short *)s->value = (short)value; break;
case UNSIGNED|SHORT: *(unsigned short *)s->value = (unsigned short)value; break;
case INT: *(int *)s->value = (int)value; break;
case UNSIGNED|INT: *(unsigned int *)s->value = (unsigned int)value; break;
case LONG: *(long *)s->value = (long)value; break;
case UNSIGNED|LONG: *(unsigned long *)s->value = (unsigned long)value; break;
default: return -1;
}
return 0;
}
}
return -1;
}
int expr_load_data(struct expr_prog *prog, void *d, int size)
{
if (!prog || size > prog->datasize) return -1;
memcpy(prog->data, d, size);
prog->data[size] = 0;
return 0;
}
int expr_load_global(struct expr_prog *prog, struct expr_global *global)
{
if (!prog || !global) return -1;
// Load tracepoint raw data
if (global->data && global->size > 0) {
if (expr_load_data(prog, global->data, global->size) != 0)
return -1;
}
// Set default global variables
prog->glo._cpu = global->_cpu;
prog->glo._pid = global->_pid;
return 0;
}
void expr_destroy(struct expr_prog *prog)
{
int i;
if (!prog) return;
for (i=0; i<prog->nr_syms; i++) {
struct symbol_table *s = &prog->symtab[i];
if (s->class == Sys && s->value == KSYM && s->ref)
function_resolver_unref();
if (s->class == Sys && s->value == COMM && s->ref)
global_comm_unref();
}
if (prog->symtab) free(prog->symtab);
if (prog->data) free(prog->data);
if (prog->str) free(prog->str);
if (prog->insn) free(prog->insn);
free(prog);
}
static void dump_symtab(struct symbol_table *symtab, int nr, bool print_value)
{
int i;
for (i = 0; i < nr; i++) {
struct symbol_table *s = &symtab[i];
if (s->token == Id && s->class == Glo) {
if (print_value)
printf(" %16p%s", (void *)s->value, s->ref ? " +" : " ");
else
printf(" ");
if (s->type & UNSIGNED) printf(" unsigned");
switch (s->type & 0x3) {
case CHAR: printf(" char"); break;
case SHORT: printf(" short"); break;
case INT: printf(" int"); break;
case LONG: printf(" long"); break;
default: break;
}
if (s->type >= PTR) {
int t = s->type;
printf(" "); do { printf("*"); t -= PTR; } while (t >= PTR);
}
if (s->type & ARRAY) {
/* Display array dimensions - show empty brackets for
* flexible array members where nr_elm is 0 */
if (s->nr_elm) printf(" [%d]", s->nr_elm);
else printf(" []");
}
printf(" %.*s\n", (int)LEN(s->hash), s->name);
}
}
}
void expr_dump(struct expr_prog *prog)
{
long *insn_end, *insn;
if (!prog) return;
insn = prog->insn;
insn_end = insn+prog->nr_insn-1;
printf("Instruction:\n");
while (insn < insn_end) {
printf(" %p:", insn+1);
printf("%8.4s", &INSN[*++insn * 5]);
if (*insn <= SI) printf(" 0x%lx\n", *++insn); else printf("\n");
}
if (prog->data) {
printf("Global variable:\n");
dump_symtab(prog->symtab, prog->nr_syms, 1);
}
if (prog->str) {
char *c = prog->str;
printf("Strings:\n");
while (*c) {
printf(" %p:", c);
c += printf(" %s\n", c) - 4;
}
}
}
/*
* expr profiler
*
**/
char *expression = NULL;
struct expression_info {
char *expression;
struct expr_prog *prog;
struct tp_list *tp_list;
};
static int expr_argc_init(int argc, char *argv[])
{
if (argc < 1) {
fprintf(stderr, " {expression} needs to be specified.\n");
help();
} else {
expression = strdup(argv[0]);
}
return 0;
}
static void expr_deinit(struct prof_dev *dev);
static int expr_init(struct prof_dev *dev)
{
struct perf_evlist *evlist = dev->evlist;
struct env *env = dev->env;
struct expression_info *info;
struct global_var_declare *declare;
struct perf_event_attr attr = {
.type = PERF_TYPE_TRACEPOINT,
.config = 0,
.size = sizeof(struct perf_event_attr),
.sample_period = 1,
.sample_type = PERF_SAMPLE_TID | PERF_SAMPLE_TIME | PERF_SAMPLE_ID | PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD |
PERF_SAMPLE_RAW,
.read_format = 0,
.pinned = 1,
.disabled = 1,
.wakeup_events = 1,
};
struct perf_evsel *evsel;
struct tp *tp;
int i;
if (!env->event)
return -1;
info = zalloc(sizeof(*info));
if (!info)
return -1;
dev->private = info;
info->expression = expression;
expression = NULL;
tep__ref();
info->tp_list = tp_list_new(dev, env->event);
if (!info->tp_list)
goto failed;
if (info->tp_list->nr_tp != 1) {
fprintf(stderr, "Only a single event is allowed to be specified.\n");
goto failed;
}
declare = tep__event_fields(info->tp_list->tp[0].id);
if (!declare)
goto failed;
printf("expression: %s\n", info->expression);
info->prog = expr_compile(info->expression, declare);
free(declare);
if (!info->prog)
goto failed;
info->prog->debug = env->verbose;
expr_dump(info->prog);
for_each_real_tp(info->tp_list, tp, i) {
evsel = tp_evsel_new(tp, &attr);
if (!evsel) {
goto failed;
}
perf_evlist__add(evlist, evsel);
}
// Only test a small number of events.
if (env->exit_n == 0) env->exit_n = 5;
return 0;
failed:
expr_deinit(dev);
return -1;
}
static int expr_filter(struct prof_dev *dev)
{
struct expression_info *info = dev->private;
return tp_list_apply_filter(dev, info->tp_list);
}
static void expr_deinit(struct prof_dev *dev)
{
struct expression_info *info = dev->private;
if (info->expression)
free(info->expression);
expr_destroy(info->prog);
tp_list_free(info->tp_list);
tep__unref();
free(info);
}
// in linux/perf_event.h
// PERF_SAMPLE_TID | PERF_SAMPLE_TIME | PERF_SAMPLE_ID | PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD | PERF_SAMPLE_RAW
struct sample_type_header {
struct {
__u32 pid;
__u32 tid;
} tid_entry;
__u64 time;
__u64 id;
struct {
__u32 cpu;
__u32 reserved;
} cpu_entry;
__u64 period;
struct {
__u32 size;
__u8 data[0];
} raw;
};
static long expr_ftrace_filter(struct prof_dev *dev, union perf_event *event, int instance)
{
struct expression_info *info = dev->private;
struct sample_type_header *raw = (void *)event->sample.array;
struct expr_global *glo = GLOBAL(raw->cpu_entry.cpu, raw->tid_entry.pid, raw->raw.data, raw->raw.size);
return tp_list_ftrace_filter(dev, info->tp_list, glo);
}
static void expr_sample(struct prof_dev *dev, union perf_event *event, int instance)
{
struct expression_info *info = dev->private;
struct sample_type_header *raw = (void *)event->sample.array;
long result;
prof_dev_print_time(dev, raw->time, stdout);
tp_print_marker(&info->tp_list->tp[0]);
tp_print_event(&info->tp_list->tp[0], raw->time, raw->cpu_entry.cpu, raw->raw.data, raw->raw.size);
info->prog->glo._cpu = raw->cpu_entry.cpu;
info->prog->glo._pid = raw->tid_entry.pid;
expr_load_data(info->prog, raw->raw.data, raw->raw.size);
result = expr_run(info->prog);
printf("result: 0x%lx\n", result);
}
static void expr_help(struct help_ctx *hctx)
{
struct tp *tp = &hctx->tp_list[0]->tp[0];
printf(PROGRAME " expr ");
printf("-e \"");
printf("%s:%s/%s/", tp->sys, tp->name, tp->filter&&tp->filter[0]?tp->filter:".");
printf("\" {expression} ");
common_help(hctx, true, true, true, false, false, true, true);
common_help(hctx, false, true, true, false, false, true, true);
printf("\n");
}
static const char *expr_desc[] = PROFILER_DESC("expr",
"[OPTION...] {expression}",
"Expression compiler and simulator.", "",
"SYNOPSIS",
" Expressions are first compiled into assembly instructions, which can then be",
" simulated and executed multiple times. Expressions can use global variables,",
" which come from tracepoint fields.",
"",
"SYNTAX",
" Supported types: char, short, int, long, unsigned, and pointer types.",
" Most operators are supported. See Operators.",
" Supports multiple built-in functions. See Built-in Functions.",
" Global variables _cpu and _pid are available. See Global Variables.",
"",
" Operators",
" Precedence Operator Description",
" 1 ++ -- Suffix/postfix increment and decrement",
" () Function call",
" [] Array subscripting",
" 2 ++ -- Prefix increment and decrement",
" + - Unary plus and minus",
" ! ~ Logical NOT and bitwise NOT",
" (type) Cast",
" * Indirection (dereference)",
" & Address-of",
" sizeof Size-of",
" 3 * / % Multiplication, division, and remainder",
" 4 + - Addition and subtraction",
" 5 << >> Bitwise left shift and right shift",
" 6 < <= For relational operators < and <= respectively",
" > >= For relational operators > and >= respectively",
" ~ Extended Operator: Wildcard pattern matching",
" 7 == != For relational = and != respectively",
" Supports both numeric and string comparison",
" 8 & Bitwise AND",
" 9 ^ Bitwise XOR (exclusive or)",
" 10 | Bitwise OR (inclusive or)",
" 11 && Logical AND",
" 12 || Logical OR",
" 13 ?: Ternary conditional",
" 14 = Simple assignment",
" 15 , Comma",
"",
" Built-in Functions",
" int printf(char *fmt, args...)",
" Prints args according to fmt, and return the number of characters",
" printed, args can take up to 6 variable parameters.",
"",
" char *ksymbol(long addr)",
" Get the kernel symbol name according to addr, and return a string.",
"",
" int ntohl(int netlong)",
" short ntohs(short netshort)",
" These functions convert network byte order to host byte order.",
"",
" int strncmp(const char *s1, const char *s2, long n)",
" Compare two strings.",
"",
" char *comm_get(int pid)",
" Get the comm string of pid",
"",
" char *syscall_name(int id)",
" Get the syscall name by id, and return a string.",
"",
" char *exit_reason_str(int isa, int val)",
" Get the KVM exit reason string according to ISA and exit reason value.",
" ISA: 1=VMX, 2=SVM, 3=ARM. Returns a string describing the exit reason.",
"",
" int system(const char *format, ...)",
" Format a command string using printf-style formatting and execute it via system().",
" Returns the command's exit status. Supports up to 6 additional parameters.",
" WARNING: Be cautious with untrusted input to avoid command injection.",
"",
" Global Variables",
" int _cpu",
" The CPU number where the event occurred.",
"",
" int _pid",
" The process ID (not thread ID) of the event.",
"",
" Other variables come from the tracepoint event fields. Use 'help' to see",
" available fields for a specific event, e.g.:",
" "PROGRAME" trace -e sched:sched_wakeup help",
"",
" Extended Operator (C++-style operator overloading)",
" ~ (const char *str, const char *pattern)",
" This operator overloads the bitwise NOT operator to provide trace event filter's ~ functionality.",
" Supports *, ?, and character classes [abc], [a-z], [^abc], [!abc].",
" Returns 1 if str matches pattern, 0 otherwise.",
"",
" ==(const char *s1, const char *s2)",
" !=(const char *s1, const char *s2)",
" When both operands are string pointers (char *), == and != operators",
" perform string comparison instead of pointer comparison.",
" Returns 1 for true, 0 for false.",
" Examples:",
" comm == \"systemd\" - Check if process name equals systemd",
" prev_comm == next_comm - Compare two string fields",
"",
"EXAMPLES",
" "PROGRAME" expr -e sched:sched_wakeup help",
" "PROGRAME" expr -e sched:sched_wakeup '&pid'",
" "PROGRAME" expr -e 'kmem:mm_page_alloc/order>0/' '1<<order' -v",
" "PROGRAME" expr -e workqueue:workqueue_execute_start 'printf(\"%s \", ksymbol(function))' -v",
" "PROGRAME" expr -e sched:sched_process_exec 'printf(\"%s \", filename)'",
" "PROGRAME" expr -e sched:sched_wakeup 'comm ~ \"*sh\"' -v",
" "PROGRAME" expr -e sched:sched_switch 'prev_comm ~ \"systemd*\" || next_comm ~ \"systemd*\"'",
" "PROGRAME" expr -e sched:sched_process_exec 'filename != \"/bin/sh\"'"
);
static const char *expr_argv[] = PROFILER_ARGV("expr",
"OPTION:",
"cpus", "pids", "tids", "output", "mmap-pages", "exit-N",
"version", "verbose", "quiet", "help",
PROFILER_ARGV_PROFILER, "event"
);
static profiler _expr = {
.name = "expr",
.desc = expr_desc,
.argv = expr_argv,
.pages = 2,
.help = expr_help,
.argc_init = expr_argc_init,
.init = expr_init,
.filter = expr_filter,
.deinit = expr_deinit,
.ftrace_filter = expr_ftrace_filter,
.sample = expr_sample,
};
PROFILER_REGISTER(_expr);
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。