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[ %7url=~.net=%defaultrefresh=~m5whos={}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=3eny0v1rn.n49dr.2u8t5.h7be5.6dcq7.9hon5.6m3pr.3hcb8.u7tmv.qddpq.kent7.1ftc7.9tao6.hfsht.4i0c3.ak3t7.t8d8j.nn4eb.b7eh3.4d5pr.t8ftgnow=~2023.2.3..20.03.23..f60ebyk=[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'sbowl
. The Gallbowl
consists of:+$ bowl :: standard app state$: $: our=ship :: hostsrc=ship :: guestdap=term :: agent== ::$: wex=boat :: outgoing subssup=bitt :: incoming subs== ::$: act=@ud :: change numbereny=@uvJ :: entropynow=@da :: current timebyk=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
/gen/dbug.hoon
:
Click to expand
/+ *dbug:- %say|= $: :: environment*:: inline argumentsargs=?(~ [what=?(%bowl %state) ~] [=poke ~]):: named arguments~==:- %dbug?- args~ [%state ''][@ ~] ?-(what.args %bowl [%bowl ~], %state [%state ''])[[@ *] ~] poke.args==
/lib/dbug.hoon
:
Click to expand
:: dbug: agent wrapper for generic debugging tools:::: usage: %-(agent:dbug your-agent)::|%+$ poke$% [%bowl ~][%state grab=cord][%incoming =about][%outgoing =about]==::+$ about$@ ~$% [%ship =ship][%path =path][%wire =wire][%term =term]==::++ agent|= =agent:gall^- agent:gall!.|_ =bowl:gall+* this .ag ~(. agent bowl)::++ on-poke|= [=mark =vase]^- (quip card:agent:gall agent:gall)?. ?=(%dbug mark)=^ cards agent (on-poke:ag mark vase)[cards this]=/ dbug!<(poke vase)=; =tang((%*(. slog pri 1) tang) [~ this])?- -.dbug%bowl [(sell !>(bowl))]~::%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)::%incoming=; =tang?^ tang tang[%leaf "no matching subscriptions"]~%+ murn%+ sort ~(tap by sup.bowl)|= [[* a=[=ship =path]] [* b=[=ship =path]]](aor [path ship]:a [path ship]:b)|= [=duct [=ship =path]]^- (unit tank)=; relevant=??. relevant ~`>[path=path from=ship duct=duct]<?: ?=(~ about.dbug) &?- -.about.dbug%ship =(ship ship.about.dbug)%path ?=(^ (find path.about.dbug path))%wire %+ lien duct|=(=wire ?=(^ (find wire.about.dbug wire)))%term !!==::%outgoing=; =tang?^ tang tang[%leaf "no matching subscriptions"]~%+ murn%+ sort ~(tap by wex.bowl)|= [[[a=wire *] *] [[b=wire *] *]](aor a b)|= [[=wire =ship =term] [acked=? =path]]^- (unit tank)=; relevant=??. relevant ~`>[wire=wire agnt=[ship term] path=path ackd=acked]<?: ?=(~ about.dbug) &?- -.about.dbug%ship =(ship ship.about.dbug)%path ?=(^ (find path.about.dbug path))%wire ?=(^ (find wire.about.dbug wire))%term =(term term.about.dbug)====::++ on-peek|= =path^- (unit (unit cage))?. ?=([@ %dbug *] path)(on-peek:ag path)?+ path [~ ~][%u %dbug ~] ``noun+!>(&)[%x %dbug %state ~] ``noun+!>(on-save:ag)[%x %dbug %subscriptions ~] ``noun+!>([wex sup]:bowl)==::++ on-init^- (quip card:agent:gall agent:gall)=^ cards agent on-init:ag[cards this]::++ on-save on-save:ag::++ on-load|= old-state=vase^- (quip card:agent:gall agent:gall)=^ cards agent (on-load:ag old-state)[cards this]::++ on-watch|= =path^- (quip card:agent:gall agent:gall)=^ cards agent (on-watch:ag path)[cards this]::++ on-leave|= =path^- (quip card:agent:gall agent:gall)=^ cards agent (on-leave:ag path)[cards this]::++ on-agent|= [=wire =sign:agent:gall]^- (quip card:agent:gall agent:gall)=^ cards agent (on-agent:ag wire sign)[cards this]::++ on-arvo|= [=wire =sign-arvo]^- (quip card:agent:gall agent:gall)=^ cards agent (on-arvo:ag wire sign-arvo)[cards this]::++ on-fail|= [=term =tang]^- (quip card:agent:gall agent:gall)=^ cards agent (on-fail:ag term tang)[cards this]----
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
Click to expand
++ on-poke|= [=mark =vase]^- (quip card:agent:gall agent:gall)?. ?=(%dbug mark)=^ cards agent (on-poke:ag mark vase)[cards this]=/ dbug!<(poke vase)=; =tang((%*(. slog pri 1) tang) [~ this])?- -.dbug%bowl [(sell !>(bowl))]~::%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)::%incoming=; =tang?^ tang tang[%leaf "no matching subscriptions"]~%+ murn%+ sort ~(tap by sup.bowl)|= [[* a=[=ship =path]] [* b=[=ship =path]]](aor [path ship]:a [path ship]:b)|= [=duct [=ship =path]]^- (unit tank)=; relevant=??. relevant ~`>[path=path from=ship duct=duct]<?: ?=(~ about.dbug) &?- -.about.dbug%ship =(ship ship.about.dbug)%path ?=(^ (find path.about.dbug path))%wire %+ lien duct|=(=wire ?=(^ (find wire.about.dbug wire)))%term !!==::%outgoing=; =tang?^ tang tang[%leaf "no matching subscriptions"]~%+ murn%+ sort ~(tap by wex.bowl)|= [[[a=wire *] *] [[b=wire *] *]](aor a b)|= [[=wire =ship =term] [acked=? =path]]^- (unit tank)=; relevant=??. relevant ~`>[wire=wire agnt=[ship term] path=path ackd=acked]<?: ?=(~ about.dbug) &?- -.about.dbug%ship =(ship ship.about.dbug)%path ?=(^ (find path.about.dbug path))%wire ?=(^ (find wire.about.dbug wire))%term =(term term.about.dbug)====
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 avase
pretty-printer.++slop
conses twovase
s together as a cell.++slap
compiles a Hoon expression and produces avase
of the result.++ream
parses acord
to a Hoon expression.
++on-peek
Click to expand
++ on-peek|= =path^- (unit (unit cage))?. ?=([@ %dbug *] path)(on-peek:ag path)?+ path [~ ~][%u %dbug ~] ``noun+!>(&)[%x %dbug %state ~] ``noun+!>(on-save:ag)[%x %dbug %subscriptions ~] ``noun+!>([wex sup]:bowl)==
The ++on-peek
arm adds several peek endpoints which expose the state (via ++onsave: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
.