Analysis pulled every export, import and symbol across the wire
and then kept the handful that name code -- 87k rows for a
kernel, each carrying a type and an address as text.
Ask for what it uses instead: the offset, name and source of each
function the module knows about, chosen where the rows already
are.
Every lookup built all three categories, gave each row a lowered
key to search by, and sorted each with a callback -- 14 seconds
before the first row of a kernel's 87k exports reached the view,
and most of it for rows nobody asked to see.
Keep what the runtime hands back, put it in order with the
engine's own sort, and report a row only when it goes out. Build
the text to search by when a query first arrives.
Serve symbol pages by offset and step through them with prev/next
controls showing the current range. Reset to the first page when
the filter or category changes. Clamp the page index at the
mutation site and floor the offset in the agent so hammering
prev/next can't run past the ends into a negative range.
Bind Cmd+F (Ctrl+F on GTK) to focus the filter input.
Modules like YouTube Kids export 100k+ symbols. Enumerating and
marshalling the whole set per open stalled the UI, and both
frontends re-filtered the full arrays on the main thread on every
keystroke.
Build a cached, pre-sorted, pre-lowercased index in the agent and
serve filtered, capped pages. Add queryModuleSymbols returning a
window plus match and category counts. Wire both frontends to it
with a debounced filter; drop the client-side filtering.
Identify a process instance by a deterministic identity returned
from the agent (per-OS, with a Process.mainModule + pid fallback)
and stash it on ProcessSession.processInfo. Rekey memory_page
into memory_page_module (module UUID + offset) so cached bytes
survive ASLR, and memory_page_anon (process identity + address)
for JIT / heap regions that only survive within one process
instance. Purge stale anon pages on every fresh attach.
Add updateProcessInfo, updateModuleAnalysis and updateInsight
SessionOps plus a side-channel publish-memory-page op and a
+fetch-memory-page request. The driver write-throughs every
page it fetches; non-drivers pull on miss and persist locally.
Module-analysis rows flow the same way so non-drivers can key
their cache by module UUID. The lab-join snapshot now carries
processInfo and module_analyses too.
Refresh actions on the insight detail view: Reread Bytes drops
the pages overlapping the visible range; Reanalyze Module drops
the module's analysis and pages, re-running r2 on next access.
The macOS detail view now reads the insight live from
insightsBySession via observation; GTK forwards .insightUpdated
into the detail and reschedules a refresh when the resolved
address changes.
Persist per-session module analysis (executable ranges + known
functions, keyed by module name + LC_UUID) so on-demand
analysis runs once per binary and rebases on reattach. The
agent gains enumerateModuleRanges and getModuleIdentity; the
store gains a module_analysis table.
Disassembler queries $FB/$FE for the request address; when it
hits a function start, fetch via pDJ bounded to the function's
byte range. Otherwise fall back to pdJ N. Filter pdJ
decoration entries (xrefs, fcn headers, var summaries) that
share the addr of an instruction.
DisassemblyPage carries a scope (.function/.span) so the
SwiftUI and GTK insight views can disable loadMore when the
page is function-bounded. The disassemble AI tool surfaces the
scope too, so the model knows whether it has the whole
function or just an N-instruction span.
Go-to-Function-Start now opens (or selects) the insight rooted
at the function start, which then renders via the bounded
path.
Add agent-side observers for the live module list and thread list
plus an RPC to enumerate a module's exports/imports/symbols. Mirror
in LumaCore as ProcessThread and ModuleSymbolBundle.