From 53942552f8121033826051c7085fff58d72d3ca5 Mon Sep 17 00:00:00 2001 From: Dane Bettis Date: Fri, 27 Aug 2021 16:44:43 +0200 Subject: [PATCH] Adds clep hint to time an expression and clet to print the stack trace at expression --- pkg/urbit/noun/nock.c | 433 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 429 insertions(+), 4 deletions(-) diff --git a/pkg/urbit/noun/nock.c b/pkg/urbit/noun/nock.c index 195eb3463..3d3b55ecb 100644 --- a/pkg/urbit/noun/nock.c +++ b/pkg/urbit/noun/nock.c @@ -1024,10 +1024,24 @@ _n_bint(u3_noun* ops, u3_noun hif, u3_noun nef, c3_o los_o, c3_o tel_o) // no currently recognized static hints // case c3__clep: { + fprintf(stderr," \r\n"); fprintf(stderr, "compiling clep\r\n"); u3_noun fen = u3_nul; c3_w nef_w = _n_comp(&fen, nef, los_o, tel_o); + // HILB overflows to HILS + // + ++tot_w; _n_emit(ops, u3nc(HILB, u3nc(u3k(hif), u3k(nef)))); + ++tot_w; _n_emit(ops, u3nc(SBIN, nef_w + 1)); + tot_w += nef_w; _n_apen(ops, fen); + ++tot_w; _n_emit(ops, ( c3y == los_o ) ? HILL : HILK); + } break; + case c3__clet: { + fprintf(stderr," \r\n"); + fprintf(stderr, "compiling clet\r\n"); + u3_noun fen = u3_nul; + c3_w nef_w = _n_comp(&fen, nef, los_o, tel_o); + // HILB overflows to HILS // ++tot_w; _n_emit(ops, u3nc(HILB, u3nc(u3k(hif), u3k(nef)))); @@ -1677,6 +1691,295 @@ u3n_find(u3_noun key, u3_noun fol) return pog_p; } +//----------------------------------------------------- +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +static u3_cell +current_epoc_time_ms_as_cell (void) +{ + long ms; // Milliseconds + time_t s; // Seconds + struct timespec spec; + clock_gettime(CLOCK_REALTIME, &spec); + s = spec.tv_sec; + // Convert nanoseconds to milliseconds + ms = round(spec.tv_nsec / 1.0e6); + if (ms > 999) { + s++; + ms = 0; + } + return u3i_cell(s, ms); +} + +/*TODO: move time functions + so that noun does not + cycle-depend upon vere + */ +#include <../include/vere/vere.h> +static u3_atom +urbit_epocnow_atom (void) +{ + struct timespec spec; + clock_gettime(CLOCK_REALTIME, &spec); + return u3_time_in_ts(&spec); +} + +#ifdef WIN32 +#include +#elif _POSIX_C_SOURCE >= 199309L +#include // for nanosleep +#else +#include // for usleep +#endif + +void +sleep_ms(int milliseconds) +{ // cross-platform sleep function +#ifdef WIN32 + Sleep(milliseconds); +#elif _POSIX_C_SOURCE >= 199309L + struct timespec ts; + ts.tv_sec = milliseconds / 1000; + ts.tv_nsec = (milliseconds % 1000) * 1000000; + nanosleep(&ts, NULL); +#else + if (milliseconds >= 1000) + sleep(milliseconds / 1000); + usleep((milliseconds % 1000) * 1000); +#endif +} +//----------------------------------------------------- +/* urbit dev process notes 2021-08-11: + project setup: + install nix + install git + install git-lfs + git clone urbit + cd urbit + git config credential.helper store + git config pull.rebase true + git lfs install + git lfs pull + cd pkg/urbit + nix-shell + ./configure && make -j 6 + + pills: + - brass: formal bootstrap + - solid: fast boot for development (logically, partially-evaluated brass) + - ivory: runtime support + my pill locations: + urbit/bin/brass.pill + urbit/bin/ivory.pill + urbit/bin/solid.pill + urbit/pkg/hs/urbit-king/test/gold/hoontree.pill + + boot a fake ship: + build/urbit -F name-of-any-ship-except-comets + - + + these are all the same: + build/urbit -F zod + build/urbit -F zod -c zod + build/urbit -F zod -u bootstrap.urbit.org/$URBIT_VERSION.pill + + this is the standard local dev command: + build/urbit -l -F zod -B path/to/pill -A path/to/arvo -c name-of-fake-ship + my version: + # create a fake ship zod with a peir named fakezod, lite boot using fast boot pill and the local arvo path + build/urbit -c ../../fakezod -F zod -l -B ../../bin/solid.pill -A ../arvo + + to restart: + build/urbit name-of-fake-ship + my version: + # this should resume the above ship with all the settings + build/urbit fakezod + + notable urbit binary flags: + * -L: local-only networking (automatically set for fake ships) + * -u: download pill from url + * -B: boot from local pill at path + * -l: lite-boot (faster, limited userspace) + * -A: load filesystem from local arvo directory + + to test clep: + # in the fake zod dojo type and enter this: + ~> %clep (add 2 2) + + bootstrap formula (see media.urbit.org/whitepaper.pdf): + [%2 [%0 3] %0 2] + - + + >>>>> commands to use during main dev cycle: + [urbit git root]$ cd pkg/urbit/ && nix-shell && cd ../.. + [nix-shell urbit/pkg/urbit]$ ./configure && make -j 6 && build/urbit ../../fakezod + ~zod:dojo> !: ~> %clep (add 1 2) + ~zod:dojo> !: ~> %clet (add 4 1) + + dynamic hint protocol: + commonly used hints are directly represented in the u3 bytecode for performance, and adding new opcodes is an involved task. + + in urbit-v1.0, we introduced a protocol for implementing dynamic hint handlers. hints must be whitelisted in the nock->bytecode compiler, and handlers are implemented in before/after callback functions: + + https://github.com/urbit/urbit/pull/3979 + + there are two kinds of dynamic hint handlers, mirroring the two hint reduction rules (opcode 11) in the nock spec. the first is an atomic hint, which is merely a label; the second is a labeled nock formula, which can convey arbitrary data to the runtime. these are compiled into different bytecodes, and handled in different callbacks. + + hints are whitelisted in _n_bint() (the "hint-processing helper" for the bytecode compiler in _n_comp()). there are separate case statements for the separate hint types, and whitelisting a given hint merely involves adding a fallthru case (the default case discards the hint while preserving nock semantics). to simplify memory management, hint labels should be "motes" -- compile-time constants of up to 4 letters, defined in motes.h. + + atomic hint handlers are added to _n_hilt_fore()/_n_hilt_hind(), arbitrary hint handlers in _n_hint_fore()/_n_hint_hind() + */ + +/* urbit grant notes 2021-08-18: + Here's a quick rundown of u3's stack trace production mechanism: + + - the bytecode interpreter maintains a trace stack via + u3t_push()/u3t_mean() and u3t_drop() + + - the frames in that stack are unrendered + Many frames are just data, like the %spot hints that record source locations. But some contain code, like the %mean hints from ~|. + + - the stack is stored in the current road, at u3R->bug.tax + + - traces are currently only produced for failed/interrupted virtual computations + + - Virtualization is performed by u3m_soft() and related variants. + It involves a setjmp() call to trap for "exceptions", and a new inner road (u3m_leap()) to establish an isolated memory arena. + "Exceptions" are "thrown" via longjmp(), in u3m_bail() or a signal handler. + + In some more detail: + - Every event is run virtualized via u3m_soft. see _serf_poke() in worker/serf.c + - Inside an event, further levels of virtualization are established via the +mink jet, which eventually calls u3m_soft_run() + - Successful computations, namespace blocks, and deterministic bails, such as u3m_bail(c3__exit), are all returned directly to the parent context + - Non-deterministic bails cannot be trapped, and unwind the entire execution context by re-bailing at each layer. + - In the case of a deterministic bail, the product of +mink / u3m_soft_run() is a proto-stack trace + - To render that to (list tank), ie $tang, we call `+mook`, which formats each entry in urbit's print format ($tank), and evaluates any lazy/deferred frames + - There's no C implementation, so we just make a dynamic call into nock + - A representative call-site is in u3m_soft() + ------- + - At any point time, the logical current trace stack may be spread across many roads, if we're deeply nested in virtual contexts + - There is not currently any code that combines those traces directly; they're currently combined by unwinding/re-bailing + - For an initial implementation, I would only print the trace from the current road. + - Eventually, we'll want to gather the trace stack from all roads, and probably virtualize the call to +mook. + + */ + +/* urbit grant todo/notes 2021-08-25: + - u3t_init()/u3t_boot() are called on loom/inner-road init + - u3t_on()/u3t_off() establishing modes/labels for -P sampling profiler (and no-op without the compile-time MEMORY_DEBUG option) + + >>>> Brad jump to THIS! <<<<<< + 1. be able to session drive next time + 2. DONE: poc of print traces without crashing + 3. look into concating parent stacks into my trace + - trace is most recent at top + - (weld a b) / u3ck_weld + - u3kc_weld() + - u3kb_weld() + - best bet (zing a) + - u3kb_zing() + - nock.hoon: _n_find() + - trace.c: _t_samp_process() + - post is an offset into loom: u3to / u3of + - rod_u = u3tn(u3_road, rod_u->par_p); + + correct code example: + u3_noun lit = u3t(u3dc("mook", 2, u3k(u3R->bug.tax))); + while ( u3_nul != lit ) { + u3t_slog(u3nc(0,u3k(u3h(lit)))); + lit = u3t(lit); + } + + notes: + print the trace during 'fore' + spot hints + src code locations + assumed to be paths to hoon file in clay + wraps expressions (or tries to) + :startend + the parser handles them + dojo dissables %spots by default + we potentially can parse these against clay files + to render code in the form of + : + aka what perl, python, and everything else does + mean hints + also push/pop per expression + not locations, these are data (to pretty print?) + ~| and ~! will push a %mean hint in hoon or dojo + an example: + [ %gall-call-failed + ~[/gall/use/hood/0w2.ihwUB/out/~zod/dojo/drum/phat/~zod/dojo /dill //term/1] + [ %deal + p=[p=~zod q=~zod] + q=%dojo + r + [ %poke + cage + [ p=%sole-action + q + [ #t/ + [ id=@ta + dat + ?( + [%clr %~] + [ %det + ler=[own=@ud his=@ud] + haw=@uvH + ted + ^#2 + ?( + [%del p=@ud] + [%ins p=@ud q=@c] + [%mor p=it(#2)] + [%nop %~] + [%set p=it(@c)] + ) + ] + [%ret %~] + [%tab pos=@ud] + ) + ] + q=[1.852.707.279.204.647.268.964 7.628.146 0] + ] + ] + ] + ] + ] + to re-enable dojo spot hints do this (without the backticks): + turn on hints `!: ` + turn off hints `!. ` + + this should push %mean hint before clet or add + ~| %clet ~> %clet (add 1 1) + + ~! is a hint for compile time crashes + ~| is for runtime crash hints + + tall: + ~| %foo ~% %clet (add 2 2) + wide: + ~|(%foo ~%(%clet (add 2 2))) + + tall can contain wide but not the other way. + + road ref counts: + (expect more info from Joe on this soon) + read only access to roads above me + on road exit we copy out our data + usually some big tree + as we copy things out we inc ref count on stuff that is already in parent road + walk up the parent roads to find the + stacks to join together + + event:(timestamp, ovom:(wire, card:(tree of cells))) + */ +//----------------------------------------------------- + + /* _n_hilt_fore(): literal (atomic) dynamic hint, before formula evaluation. ** hin: [hint-atom, formula]. TRANSFER ** bus: subject. RETAIN @@ -1689,8 +1992,54 @@ static c3_o _n_hilt_fore(u3_noun hin, u3_noun bus, u3_noun* out) { if ( c3__clep == u3h(hin) ) { - fprintf(stderr, "fore: got clep\r\n"); - *out = c3__clep; + u3_cell now = current_epoc_time_ms_as_cell(); + *out = u3i_cell(u3h(hin), now); + //// TODO: this should work, but does not + //u3_atom now = urbit_epocnow_atom(); + //*out = u3i_cell(u3h(hin), now); + sleep_ms(512); + return c3y; + } + else if ( c3__clet == u3h(hin) ) { + u3_cell now = current_epoc_time_ms_as_cell(); + *out = u3i_cell(u3h(hin), now); + //// TODO: this should work, but does not + //u3_atom now = urbit_epocnow_atom(); + //*out = u3i_cell(u3h(hin), now); + + u3_road* road = u3R; + u3_noun tax = road->bug.tax; + + /*TODO: something in this loop causes u3t_slog to fail + perhaps its the zero at the end of one of the tax lists + perhaps its on of the tax refs that IS zero + perhaps its a pointer/noun mismatch + the result is that I get what seems to be a stacktrace-on-fail + when I just want a printout of the stack... + */ + // while there is a parent road ref ... + while ( &(u3H->rod_u) != road ) { + // ... point at the next road and append its stack to tax + road = u3tn(u3_road, road->par_p); + //if (0 == road->bug.tax) { + // continue; + //} + //if (0 == tax) { + // tax = road->bug.tax; + // continue; + //} + tax = u3qb_weld(tax, road->bug.tax); + } + // render and print the stack + u3_noun lit = u3t(u3dc("mook", 2, u3k(tax))); + while ( u3_nul != lit ) { + u3t_slog(u3nc(1,u3k(u3th(lit)))); + lit = u3t(lit); + } + + //u3_noun mok = u3dc("mook", 2, tax); + //u3v_punt(blu, tab_l, u3k(u3h(mok))); + return c3y; } @@ -1706,8 +2055,83 @@ _n_hilt_fore(u3_noun hin, u3_noun bus, u3_noun* out) static void _n_hilt_hind(u3_noun tok, u3_noun pro) { - if ( c3__clep == tok ) { - fprintf(stderr, "hind: got clep\r\n"); + /* TODO: first attempt to unpack tok + so that we can test for clep-ness + */ + u3_noun h_tok, t_tok; + u3r_cell(tok, &h_tok, &t_tok); + if ( c3__clep == h_tok ) { + // get and unpack current time + u3_noun h_now, t_now; + u3_cell timenow = current_epoc_time_ms_as_cell(); + u3r_cell(timenow, &h_now, &t_now); + + // unpack old time + u3_noun h_then, t_then; + u3r_cell(t_tok, &h_then, &t_then); + + // subtract seconds + u3_atom s = h_now - h_then; + + // subtract milliseconds + u3_atom ms; + if ( s != 0 ) { + ms = (1000+t_now) - t_then; + s -= 1; + } + else { + ms = t_now - t_then; + } + fprintf(stderr, " \r\n"); + fprintf(stderr, "clep: took sec.ms %u.%u\r\n", s, ms); + /* TODO: we need to fix this; + both u3_time_gap_double and u3_time_gap_ms crash when mem managing + and yeild wrong results when I comment out the u3z calls + */ + //u3_atom now = urbit_epocnow_atom(); + //u3_atom wen = t_tok; + //c3_c* lab_a = u3m_pretty(now); + //c3_c* lab_b = u3m_pretty(wen); + //fprintf(stderr, " \r\n"); + //fprintf(stderr, "clep: result is now:%s wen:%s\r\n", lab_a, lab_b); + //c3_d delta_time = u3_time_gap_double(u3k(now), u3k(wen)); + //fprintf(stderr, "clep: took %lu microseconds\r\n", delta_time); + } + else if ( c3__clet == h_tok ) { + // get and unpack current time + u3_noun h_now, t_now; + u3_cell timenow = current_epoc_time_ms_as_cell(); + u3r_cell(timenow, &h_now, &t_now); + + // unpack old time + u3_noun h_then, t_then; + u3r_cell(t_tok, &h_then, &t_then); + + // subtract seconds + u3_atom s = h_now - h_then; + + // subtract milliseconds + u3_atom ms; + if ( s != 0 ) { + ms = (1000+t_now) - t_then; + s -= 1; + } + else { + ms = t_now - t_then; + } + fprintf(stderr, " \r\n"); + fprintf(stderr, "clet: took sec.ms %u.%u\r\n", s, ms); + /* TODO: we need to fix this; + both u3_time_gap_double and u3_time_gap_ms crash when mem managing + and yeild wrong results when I comment out the u3z calls + */ + //u3_atom now = urbit_epocnow_atom(); + //u3_atom wen = t_tok; + //c3_c* lab_a = u3m_pretty(now); + //c3_c* lab_b = u3m_pretty(wen); + //fprintf(stderr, "clet: result is now:%s wen:%s\r\n", lab_a, lab_b); + //c3_d delta_time = u3_time_gap_double(u3k(now), u3k(wen)); + //fprintf(stderr, "clet: took %lu microseconds\r\n", delta_time); } else { c3_assert( u3_nul == tok ); @@ -1715,6 +2139,7 @@ _n_hilt_hind(u3_noun tok, u3_noun pro) u3z(tok); } +//----------------------------------------------------- /* _n_hint_fore(): arbitrary dynamic hint, before formula evaluation ** hin: [hint-atom, formula]. TRANSFER