Debugging Wrapper
The /lib/dbug.hoon
agent wrapper adds support to view the state of a Gall agent. It is applied to an existing Gall agent as a single drop-in line, %- agent:dbug
.
Before we look at the code, let's consider the functionality it exposes. By supplying %- agent:dbug
, an associated +dbug
generator can be invoked against the agent state.
For instance, using the %azimuth
agent, we can expose the current state of the agent:
> :azimuth +dbug
[ %7
url=~.
net=%default
refresh=~m5
whos={}
nas=[%0 points={} operators={} dns=<||>]
own={}
spo={}
logs=~
sap=[%0 id=[hash=0x0 number=0] nas=[%0 points={} operators={} dns=<||>] owners={} sponsors={}]
]
> :azimuth +dbug %bowl
> [ [our=~zod src=~zod dap=%azimuth]
[wex={} sup={}]
act=3
eny
0v1rn.n49dr.2u8t5.h7be5.6dcq7.9hon5.6m3pr.3hcb8.u7tmv.qddpq.kent7.1ftc7.9tao6.hfsht.4i0c3.ak3t7.t8d8j.nn4eb.b7eh3.4d5pr.t8ftg
now=~2023.2.3..20.03.23..f60e
byk=[p=~zod q=%base r=[%da p=~2023.1.26..02.41.25..926a]]
]
> :azimuth +dbug [%incoming ~]
> no matching subscriptions
> :azimuth +dbug [%state '(lent whos)']
> 0
There are four actions exposed by the wrapper via the +dbug
generator:
:app +dbug
exposes the entire state, just dumping the current agent state.:app +dbug %bowl
shows the agent's$bowl
. The Gall$bowl
consists of:+$ bowl :: standard app state $: $: our=ship :: host src=ship :: guest dap=term :: agent == :: $: wex=boat :: outgoing subs sup=bitt :: incoming subs == :: $: act=@ud :: change number eny=@uvJ :: entropy now=@da :: current time byk=beak :: load source == == ::
:app +dbug [%state 'hoon']
exposes data in the state, including evaluated Hoon like(lent values)
.:app +dbug [?(%incoming outgoing) specifics]
reveals details about the subscriptions.
The Code
As we examine this code, there are two particularly interesting aspects:
How
/lib/dbug.hoon
modifies an agent's arms by adding functionality over the top of them.How
/gen/dbug.hoon
utilizes the modified arms with an elegant and simple invocation.
There is also extensive use of $tank
/$tang
formatted error messaging.
How the library works
By applying this door builder using %-
censig, the +on-poke
and +on-peek
arms can be modified. (In fact, all of the arms can be modified but most of the arms are pass-throughs to the modified agent.)
+on-poke
+on-poke
The +on-poke
arm has several branches added to it after a check to see whether it is being used through the +dbug
generator. If it isn't (as determined by the associated $mark
), then the poke is passed through to the base agent.
?. ?=(%dbug mark)
=^ cards agent (on-poke:ag mark vase)
[cards this]
The following ?-
wuthep handles the input arguments: %state
is the most interesting code in this library. The code first checks whether the base agent has a /dbug/state
peek endpoint already (in which case it passes it through), otherwise it evaluates the requested Hoon expression against the agent's state (obtained via +on-save:ag
).
%state
=? grab.dbug =('' grab.dbug) '-'
=; product=^vase
[(sell product)]~
=/ state=^vase
:: if the underlying app has implemented a /dbug/state scry endpoint,
:: use that vase in place of +on-save's.
::
=/ result=(each ^vase tang)
(mule |.(q:(need (need (on-peek:ag /x/dbug/state)))))
?:(?=(%& -.result) p.result on-save:ag)
%+ slap
(slop state !>([bowl=bowl ..zuse]))
(ream grab.dbug)
This branch includes the use of a rare =?
tiswut conditional leg change and the reversed =/
tisfas, =;
tismic. There is also some direct compilation of $cord
s taking place:
+sell
is a$vase
pretty-printer.+slop
conses two$vase
s together as a cell.+slap
compiles a Hoon expression and produces a$vase
of the result.+ream
parses a$cord
to a Hoon expression.
+on-peek
+on-peek
The +on-peek
arm adds several peek endpoints which expose the state (via +on-save:ag
) and the subscriptions.
> .^(noun %gx /(scot %p our)/azimuth/(scot %da now)/dbug/subscriptions/noun)
[0 0]
How the generator works
The generator explicitly injects the %dbug
mark in its return +cask
([mark noun]
). This is a valid if uncommon operation, and it works here because the mark is never used as a transforming gate but only as a marker to see whether the arms need to pass through the values. The no-argument input is routed through the %state
with an empty $cord
.
:- %dbug
?- args
~ [%state '']
[@ ~] ?-(what.args %bowl [%bowl ~], %state [%state ''])
[[@ *] ~] poke.args
==
Library authors should consider augmenting developer capabilities by exposing appropriate functionality using a wrapper agent similar to /lib/dbug
.
Last updated