Emit.redefinition has taken ~debug since it was written and was tested
with it; Session.eval never passed it, so every body installed by C-c C-c
lost its debug info in the running process.
Passing it alone would have been half a fix. Build.shared is what forces
-O0, and dev.ml built modules at -O2, so the llvm.dbg.declares would have
been emitted and then deleted by mem2reg: a line table, and no locals.
And a module with DWARF loaded into a host without it lines up against
nothing. So it is one flag — flan dev --debug and flan reload --debug —
and it sets the host build, the module builds and the emitted metadata
together. Off by default: a debug build is an -O0 build, and quietly
making every reloaded body -O0 changes the frame time of the one function
you are iterating on, in the loop whose point is watching that number.
What a dlopen'd module does to a breakpoint, measured against the reload
fixture rather than reasoned about:
- lldb reads the new module's DWARF on the dlopen and says so: "1
location added to breakpoint 3".
- A breakpoint set by NAME gains a second location either way, so
dlopen was never the difficulty. What the line table buys is that it
stops with source instead of disassembly.
- A FILE AND LINE breakpoint on the new body resolves only with it;
without, it sits at locations = 0 (pending) forever.
- A FILE AND LINE breakpoint on the HOST's copy stays pinned at
locations = 1. That is correct, not stale: the old body is still
mapped and every call site that has not gone through its cell again
still reaches it.
- The stack crosses intact — a frame in the reloaded .so and the one
below it in the host each name their own .flan file.
(lldb) frame variable
(long) step = 10
(long) prior = 11
The transcripts are in flan-dape.el, replacing the note that said the
module carries no DWARF yet.
flan-cnr.el's stack pane was refusing for the wrong reason. DWARF was
never its gap; nothing is attached to the stopped program, and a socket
cannot read another process's frames. Reworded to say that.
Source interleaving in the disassembly buffer is unblocked and not done:
objdump -dS interleaves a --debug module's Flan source correctly, so
Dev.asm_of needs the -S and a parse_listing that tolerates source lines.
No DAP implementation here, and there should not be one. `flan build --debug'
puts DWARF in the executable, lldb reads it, lldb-dap speaks the protocol — so
what was actually missing was a dape-configs entry that knows to build a .flan
file first and where the binary lands.
The build goes through dape's own `compile' key rather than a shell-out, so a
rejected program lands in a compilation buffer and next-error walks it. Flan's
diagnostics are already file:line:col.
`flan-debug' goes through `dape--config-eval' and not `alist-get'. `dape'
takes a config whose forms are already evaluated — that is what M-x dape does
after reading one — and handing it the stored entry would pass the list
(flan-dape--binary (flan-dape--source)) to lldb as a program name. Driven
headlessly to prove it: a breakpoint set by line in the .flan buffer, hit,
reported as flan.tick at debug.flan:19 with c and n in scope.
The keybinding is registered from here rather than in flan-mode.el, so this
file is the only thing anyone has to load to get it and flan-mode keeps
working for someone who never installs dape.
The two frictions are written down at the bottom of flan-dape.el from lldb
transcripts, not from reasoning about what ought to happen, because the guess
I started from was wrong. Across a reload a breakpoint set by *name* gains a
second location and both stay live — the old body is still mapped and still
what old call sites reach. One set by *file and line* stops firing, and not
because dape pinned it to an address: the redefinition module has no line
table to resolve against. Given one, lldb does re-resolve on dlopen.
Which names the gap: Emit.redefinition takes ~debug and Session.eval does not
pass it, so `flan reload' and the `flan dev' daemon build modules without
DWARF. lib/session.ml is the dev loop's file, not this lane's.
test-flan-dape.el is not in dune test. It wants Emacs, dape, lldb-dap and a
built flan at once, and wiring four optional things into the acceptance table
would make that table's failures mean less, not more.