diff --git a/docs/commands.md b/docs/commands.md index 785c751a..72f87dda 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -10,6 +10,10 @@ Execute the contents of the file as if it were REPL input (but allowing multilin Execute the expression, but don't print its result. +## `)profile expr` / `)profile@frequency expr` + +Profile the expression at the given sampling frequency, or 5000 samples/second by default + ## `)t expr` / `)time expr` / `)t:n expr` / `)time:n expr` Time the argument expression. `n` specifies the number of times to repeat. Exists to allow not escaping quotes and less overhead for timing very fast & small expressions. diff --git a/src/builtins/fns.c b/src/builtins/fns.c index 07b621f6..8a3364fa 100644 --- a/src/builtins/fns.c +++ b/src/builtins/fns.c @@ -339,6 +339,22 @@ B gid2str(i32 n) { return r; } +void* profiler_makeMap() { + return m_b2i(64); +} +i32 profiler_index(void** mapRaw, B comp) { + H_b2i* map = *(H_b2i**)mapRaw; + i32 r; + bool had; u64 p = mk_b2i(&map, comp, &had); + if (had) r = map->a[p].val; + else r = map->a[p].val = map->pop-1; + *(H_b2i**)mapRaw = map; + return r; +} +void profiler_freeMap(void* mapRaw) { + free_b2i((H_b2i*)mapRaw); +} + void fun_gcFn() { diff --git a/src/core/stuff.c b/src/core/stuff.c index 15337f2c..9a5eed97 100644 --- a/src/core/stuff.c +++ b/src/core/stuff.c @@ -150,7 +150,7 @@ void fprint(FILE* f, B x) { } else if (isVar(x)) fprintf(f, "(var d=%d i=%d)", (u16)(x.u>>32), (i32)x.u); else if (isExt(x)) fprintf(f, "(extvar d=%d i=%d)", (u16)(x.u>>32), (i32)x.u); - else if (x.u==bi_N.u) fprintf(f, "·"); + else if (x.u==bi_N.u) fprintf(f, "(native ·)"); else if (x.u==bi_optOut.u) fprintf(f, "(value optimized out)"); else if (x.u==bi_noVar.u) fprintf(f, "(unset variable placeholder)"); else if (x.u==bi_okHdr.u) fprintf(f, "(accepted SETH placeholder)"); diff --git a/src/jit/nvm_x86_64.c b/src/jit/nvm_x86_64.c index 46f51b43..61c51fee 100644 --- a/src/jit/nvm_x86_64.c +++ b/src/jit/nvm_x86_64.c @@ -36,7 +36,7 @@ __attribute__((optnone)) static void* mmap_nvm(u64 sz) { u64 near = (u64)&bqn_exec; u64 MAX_DIST = 1ULL<<30; - if (near < MAX_DIST) return mmap(NULL, sz, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_NORESERVE|MAP_PRIVATE|MAP_ANON|MAP_32BIT, -1, 0); + if (near < MAX_DIST) return mmap(NULL, sz, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_NORESERVE|MAP_PRIVATE|MAP_ANONYMOUS|MAP_32BIT, -1, 0); u64 ps = getPageSize(); i32 attempt = 0; @@ -50,7 +50,7 @@ static void* mmap_nvm(u64 sz) { #ifdef MAP_FIXED_NOREPLACE noreplace|= MAP_FIXED_NOREPLACE; #endif - void* c = mmap((void*)loc, sz, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_NORESERVE|MAP_PRIVATE|MAP_ANON|noreplace, -1, 0); + void* c = mmap((void*)loc, sz, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_NORESERVE|MAP_PRIVATE|MAP_ANONYMOUS|noreplace, -1, 0); if (c==NULL) continue; i64 dist = (i64)near - (i64)c; @@ -241,7 +241,7 @@ u32 perfid = 0; #endif static void* nvm_alloc(u64 sz) { - // void* r = mmap(NULL, sz, PROT_EXEC|PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON|MAP_32BIT, -1, 0); + // void* r = mmap(NULL, sz, PROT_EXEC|PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_32BIT, -1, 0); // if (r==MAP_FAILED) thrM("JIT: Failed to allocate executable memory"); // return r; I8Arr* src = mmX_allocN(fsizeof(I8Arr,a,u8,sz), t_i8arr); diff --git a/src/load.c b/src/load.c index a08cd117..e83b5916 100644 --- a/src/load.c +++ b/src/load.c @@ -116,11 +116,11 @@ Block* load_compObj(B x, B src, B path, Scope* sc) { // consumes x,src } #include "gen/src" #if RT_SRC -Block* load_compImport(B bc, B objs, B blocks, B bodies, B inds, B src) { // consumes all - return compile(bc, objs, blocks, bodies, inds, bi_N, src, m_str8l("(precompiled)"), NULL); +Block* load_compImport(char* name, B bc, B objs, B blocks, B bodies, B inds, B src) { // consumes all + return compile(bc, objs, blocks, bodies, inds, bi_N, src, m_str8l(name), NULL); } #else -Block* load_compImport(B bc, B objs, B blocks, B bodies) { // consumes all +Block* load_compImport(char* name, B bc, B objs, B blocks, B bodies) { // consumes all return compile(bc, objs, blocks, bodies, bi_N, bi_N, bi_N, bi_N, NULL); } #endif @@ -350,14 +350,14 @@ void load_init() { // very last init function #ifndef ALL_R0 B runtime_0[] = {bi_floor,bi_ceil,bi_stile,bi_lt,bi_gt,bi_ne,bi_ge,bi_rtack,bi_ltack,bi_join,bi_pair,bi_take,bi_drop,bi_select,bi_const,bi_swap,bi_each,bi_fold,bi_atop,bi_over,bi_before,bi_after,bi_cond,bi_repeat}; #else - Block* runtime0_b = load_compImport( + Block* runtime0_b = load_compImport("r0.bqn", #include "gen/runtime0" ); B r0r = evalFunBlock(runtime0_b, 0); ptr_dec(runtime0_b); B* runtime_0 = toHArr(r0r)->a; #endif - Block* runtime_b = load_compImport( + Block* runtime_b = load_compImport("r1.bqn", #include "gen/runtime1" ); @@ -458,7 +458,7 @@ void load_init() { // very last init function #else // use compiler B prevAsrt = runtime[n_asrt]; runtime[n_asrt] = bi_casrt; // horrible but GC is off so it's fiiiiiine - Block* comp_b = load_compImport( + Block* comp_b = load_compImport("c.bqn", #include "gen/compiles" ); runtime[n_asrt] = prevAsrt; @@ -469,7 +469,7 @@ void load_init() { // very last init function #if FORMATTER - Block* fmt_b = load_compImport( + Block* fmt_b = load_compImport("f.bqn", #include "gen/formatter" ); B fmtM = evalFunBlock(fmt_b, 0); ptr_dec(fmt_b); diff --git a/src/main.c b/src/main.c index f8fecf2c..3f349ce4 100644 --- a/src/main.c +++ b/src/main.c @@ -38,7 +38,22 @@ static bool isCmd(char* s, char** e, const char* cmd) { *e = s; return true; } +bool profiler_alloc(void); +bool profiler_start(i64 hz); +bool profiler_stop(void); +void profiler_free(void); +void profiler_displayResults(void); +i64 readInt(char** p) { + char* c = *p; + i64 am = 0; + while (*c>='0' & *c<='9') { + am = am*10 + (*c - '0'); + c++; + } + *p = c; + return am; +} void cbqn_runLine0(char* ln, i64 read) { if (ln[0]==10) return; if (ln[read-1]==10) ln[--read] = 0; @@ -46,6 +61,7 @@ void cbqn_runLine0(char* ln, i64 read) { B code; int output; // 0-no; 1-formatter; 2-internal i32 time = 0; + i64 profile = -1; if (ln[0] == ')') { char* cmdS = ln+1; char* cmdE; @@ -60,13 +76,16 @@ void cbqn_runLine0(char* ln, i64 read) { code = fromUTF8l(cmdE); time = -1; output = 0; + } else if (isCmd(cmdS, &cmdE, "profile ") || isCmd(cmdS, &cmdE, "profile@")) { + char* cpos = cmdE; + profile = '@'==*(cpos-1)? readInt(&cpos) : 5000; + if (profile==0) { printf("Cannot profile with 0hz sampling frequency\n"); return; } + if (profile>999999) { printf("Cannot profile with >999999hz frequency\n"); return; } + code = fromUTF8l(cmdE); + output = 0; } else if (isCmd(cmdS, &cmdE, "t:") || isCmd(cmdS, &cmdE, "time:")) { char* repE = cmdE; - i64 am = 0; - while (*repE>='0' & *repE<='9') { - am = am*10 + (*repE - '0'); - repE++; - } + i64 am = readInt(&repE); if (repE==cmdE) { printf("time command not given repetition count\n"); return; } if (am==0) { printf("repetition count was zero\n"); return; } code = fromUTF8l(repE); @@ -187,9 +206,16 @@ void cbqn_runLine0(char* ln, i64 read) { else if (t<1e6) printf("%.4gus\n", t/1e3); else if (t<1e9) printf("%.4gms\n", t/1e6); else printf("%.5gs\n", t/1e9); - } else { - res = execBlockInline(block, gsc); - } + } else if (profile>0) { + if (CATCH) { profiler_stop(); profiler_free(); rethrow(); } + if (profiler_alloc() && profiler_start(profile)) { + res = execBlockInline(block, gsc); + profiler_stop(); + profiler_displayResults(); + profiler_free(); + } + popCatch(); + } else res = execBlockInline(block, gsc); ptr_dec(block); if (output) { diff --git a/src/opt/mm_buddyTemplate.c b/src/opt/mm_buddyTemplate.c index 9d0bc247..16793d6d 100644 --- a/src/opt/mm_buddyTemplate.c +++ b/src/opt/mm_buddyTemplate.c @@ -6,7 +6,7 @@ #define alSize BN(alSize) #ifndef MMAP usz getPageSize(); - #define MMAP(SZ) mmap(NULL, (SZ)+getPageSize(), PROT_READ|PROT_WRITE, MAP_NORESERVE|MAP_PRIVATE|MAP_ANON, -1, 0) + #define MMAP(SZ) mmap(NULL, (SZ)+getPageSize(), PROT_READ|PROT_WRITE, MAP_NORESERVE|MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) #endif typedef struct AllocInfo { diff --git a/src/vm.c b/src/vm.c index bb27cf35..cccea340 100644 --- a/src/vm.c +++ b/src/vm.c @@ -996,7 +996,7 @@ static void allocStack(void** curr, void** start, void** end, i32 elSize, i32 co #if NO_MMAP void* mem = calloc(sz+ps, 1); #else - void* mem = mmap(NULL, sz+ps, PROT_READ|PROT_WRITE, MAP_NORESERVE|MAP_PRIVATE|MAP_ANON, -1, 0); + void* mem = mmap(NULL, sz+ps, PROT_READ|PROT_WRITE, MAP_NORESERVE|MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); if (*curr == MAP_FAILED) err("Failed to allocate stack"); #endif *curr = *start = mem; @@ -1231,6 +1231,180 @@ NOINLINE void vm_pstLive() { vm_pst(envStart, envCurr+1); } + +#if __has_include() && __has_include() && !NO_MMAP +#include +#include +#define PROFILE_BUFFER (1ULL<<29) + +typedef struct Profiler_ent { + Comp* comp; + usz bcPos; +} Profiler_ent; +Profiler_ent* profiler_buf_s; +Profiler_ent* profiler_buf_c; +Profiler_ent* profiler_buf_e; +bool profile_buf_full; +void profiler_sigHandler(int x) { + Profiler_ent* bn = profiler_buf_c+1; + if (RARE(bn>=profiler_buf_e)) { + profile_buf_full = true; + return; + } + + if (envCurrbody->bl->comp; + i32 bcPos = e.pos&1? ((u32)e.pos)>>1 : BCPOS(e.sc->body, (u32*)e.pos); + + *profiler_buf_c = (Profiler_ent){.comp = ptr_inc(comp), .bcPos = bcPos}; + profiler_buf_c = bn; +} + + + +static bool setHandler(bool b) { + struct sigaction act = {}; + act.sa_handler = b? profiler_sigHandler : SIG_DFL; + if (sigaction(SIGALRM/*SIGPROF*/, &act, NULL)) { + printf("Failed to set profiling signal handler\n"); + return false; + } + return true; +} +static bool setTimer(i64 us) { + struct itimerval timer; + timer.it_value.tv_sec=0; + timer.it_value.tv_usec=us; + timer.it_interval.tv_sec=0; + timer.it_interval.tv_usec=us; + if (setitimer(ITIMER_REAL/*ITIMER_PROF*/, &timer, NULL)) { + printf("Failed to start sampling timer\n"); + return false; + } + return true; +} + +void* profiler_makeMap(); +i32 profiler_index(void* mapRaw, B comp); +void profiler_freeMap(void* mapRaw); + +bool profiler_alloc() { + profiler_buf_s = profiler_buf_c = mmap(NULL, PROFILE_BUFFER, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + if (profiler_buf_s == MAP_FAILED) { + fprintf(stderr, "Failed to allocate profiler buffer\n"); + return false; + } + profiler_buf_e = profiler_buf_s+PROFILE_BUFFER; + profile_buf_full = false; + return true; +} +void profiler_free() { + munmap(profiler_buf_s, PROFILE_BUFFER); +} + +bool profiler_active; +bool profiler_start(i64 hz) { + i64 us = 999999/hz; + profiler_active = true; + return setHandler(true) && setTimer(us); +} +bool profiler_stop() { + if (!profiler_active) return false; + profiler_active = false; + if (profile_buf_full) fprintf(stderr, "Profiler buffer ran out in the middle of execution. Only timings of the start of profiling will be shown.\n"); + return setTimer(0) && setHandler(false); +} + + + +usz profiler_getResults(B* compListRes, B* mapListRes, bool keyPath) { + Profiler_ent* c = profiler_buf_s; + + B compList = emptyHVec(); + B mapList = emptyHVec(); + usz compCount = 0; + void* map = profiler_makeMap(); + + while (c!=profiler_buf_c) { + usz bcPos = c->bcPos; + Comp* comp = c->comp; + B path = comp->path; + if (!q_N(path) && !q_N(comp->src)) { + i32 idx = profiler_index(&map, q_N(path)? tag(comp, OBJ_TAG) : path); + if (idx == compCount) { + compList = vec_addN(compList, tag(comp, OBJ_TAG)); + i32* rp; + usz ia = a(comp->src)->ia; + mapList = vec_addN(mapList, m_i32arrv(&rp, ia)); + for (i32 i = 0; i < ia; i++) rp[i] = 0; + compCount++; + } + B inds = IGetU(comp->indices, 0); usz cs = o2s(IGetU(inds,bcPos)); + // B inde = IGetU(comp->indices, 1); usz ce = o2s(IGetU(inde,bcPos)); + i32* cMap = i32arr_ptr(IGetU(mapList, idx)); + // for (usz i = cs; i <= ce; i++) cMap[i]++; + cMap[cs]++; + } + c++; + } + profiler_freeMap(map); + + *compListRes = compList; + *mapListRes = mapList; + return compCount; +} + +void profiler_displayResults() { + printf("Got "N64u" samples\n", (u64)(profiler_buf_c-profiler_buf_s)); + + B compList, mapList; + usz compCount = profiler_getResults(&compList, &mapList, true); + + SGetU(compList) SGetU(mapList) + for (usz i = 0; i < compCount; i++) { + Comp* c = c(Comp, GetU(compList, i)); + i32* m = i32arr_ptr(GetU(mapList, i)); + if (!q_N(c->src)) { + if (q_N(c->path)) printf("unknown:"); + else printRaw(c->path); + printf(":\n"); + B src = c->src; + SGetU(src) + usz sia = a(src)->ia; + usz pi = 0; + i32 curr = 0; + for (usz i = 0; i < sia; i++) { + u32 c = o2cu(GetU(src, i)); + curr+= m[i]; + if (c=='\n' || i==sia-1) { + Arr* sl = TI(src,slice)(inc(src), pi, i-pi); arr_shVec(sl); + if (curr==0) printf(" │"); + else printf("%6d│", curr); + printRaw(taga(sl)); + printf("\n"); + ptr_dec(sl); + curr = 0; + pi = i+1; + } + } + } + } + dec(compList); + dec(mapList); +} +#else +bool profiler_alloc() { + printf("Profiler not supported\n"); + return false; +} +bool profiler_start(i64 hz) { return false; } +bool profiler_stop() { return false; } +void profiler_free() { thrM("Profiler not supported"); } +usz profiler_getResults(B* compListRes, B* mapListRes, bool keyPath) { thrM("Profiler not supported"); } +void profiler_displayResults() { thrM("Profiler not supported"); } +#endif + void unwindEnv(Env* envNew) { assert(envNew<=envCurr); while (envCurr!=envNew) {