Adds clep hint to time an expression and clet to print the stack trace at expression

This commit is contained in:
Dane Bettis 2021-08-27 16:44:43 +02:00 committed by DaneBettis
parent c75b9d6f48
commit 53942552f8

View File

@ -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 <inttypes.h>
#include <math.h>
#include <stdio.h>
#include <time.h>
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 <windows.h>
#elif _POSIX_C_SOURCE >= 199309L
#include <time.h> // for nanosleep
#else
#include <unistd.h> // 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)
<path>:start<line col>end<line col>
the parser handles them
dojo dissables %spots by default
we potentially can parse these against clay files
to render code in the form of
<path>:<the code between start and end>
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