Skip to content

feat(xdebug-trace): HTTP upload, trace files on disk, tree/search/summary API - #410

Merged
butschster merged 10 commits into
masterfrom
feat/xdebug-trace-storage
Nov 8, 2026
Merged

butschster merged 10 commits into
masterfrom
feat/xdebug-trace-storage

Conversation

@butschster

@butschster butschster commented Nov 8, 2026 •

Copy link
Copy Markdown
Member

Stacked on #407 (base feat/xdebug-trace-parser; retarget to master once #407 lands). Part of #288.

What

New module xdebug-trace: upload an Xdebug function trace (xdebug.trace_format=1, plain or .gz) over HTTP and browse it without loading it whole.

curl -H 'X-Buggregator-Event: xdebug-trace' -H 'X-Buggregator-Trace-Url: GET /users' \
     --data-binary @trace.xt.gz http://127.0.0.1:8000/
  • Upload streams the body to <uuid>.xt[.gz] (kept as sent) and summarizes it in the same pass (io.TeeReader → Summarize). Another format → 400 with the trace_format=1 hint; nothing left on disk.
  • Limits xdebug_trace.max_upload (100MB), max_size (256MB unpacked; a longer trace is cut there and marked truncated), keep (20; storing one more deletes the oldest unpinned). Env XDEBUG_TRACE_MAX_UPLOAD, XDEBUG_TRACE_MAX_SIZE, XDEBUG_TRACE_KEEP. Module flag modules.xdebug-trace (on by default).
  • Storage: files under storage.path/xdebug-traces in filesystem mode, a temp dir removed at exit otherwise. SQLite keeps the event and its summary; the call index goes to <uuid>.idx.json, so event payloads stay small. Delete / clear-all remove the files via EventDeleteListener (fix: remove attachment files when events are deleted or cleared #408); startup removes files no event refers to.
  • API (behind auth):
    • GET /api/xdebug-traces/{uuid}/summary — summary + index.
    • GET /api/xdebug-traces/{uuid}/tree?parent=&from=&limit= — one level of calls: top level, callees of the entry at byte offset parent, or the next page (from = previous next). Each node: incl/self time, memory delta, args and return value (clipped to 512 B, 20 args), direct callee count, offset.
    • GET /api/xdebug-traces/{uuid}/search?q=&from=&limit= — calls whose function or file contains q.
    • At most 1000 nodes per answer; bad offsets → 400.
  • buggregator.yaml.example documents the section.

Frontend (card + page) and MCP tools come in follow-ups. EnabledTypes() does not list xdebug-trace yet, so the UI menu does not change until the frontend has a page for it.

Tests

  • modules/xdebugtrace: TestChildren, TestChildren_BadOffset, TestChildren_CutTrace, TestSearch, TestClip, TestModule_UploadAndAPI, TestModule_IndexAndPaging (2500 callees paged through the API), TestModule_RejectsAndLeavesNoFiles, TestModule_MaxSizeCutsTheSummary, TestModule_KeepsTheNewest (pinned stays), TestModule_SweepRemovesOrphans.
  • internal/app: TestParseSize, TestModulesConfig_XdebugTrace.
  • go test -race -count=1 on modules/xdebugtrace, internal/app, internal/storage, internal/module, internal/server/http, modules/smtp, modules/httpdumps: ok. go vet clean.
  • Live run of the built binary: filesystem mode — upload gz → files + event, wrong format → 400, tree / search answer, DELETE /api/event/{uuid} → files gone; memory mode — temp dir removed on SIGTERM.

Checked on the traces recorded by a live Xdebug (#407): TestRecorded_MainTree (13 callees of {main}, closure, search paging 25 fib calls by 10, each match's path) and TestRecorded_MidTree. The live trace found one bug, fixed in cf42363: xdebug_stop_trace() and its caller never get an exit line, so tree and search reported every stopped trace as cut; with TRACE END present they no longer do (Summarize already did so). Live: both files uploaded to the built binary over HTTP; tree/search answers match the tests.

Made with harnsy

…mary API

New module xdebug-trace. POST with X-Buggregator-Event: xdebug-trace
(or xdebug-trace@host) streams the body to <uuid>.xt[.gz] in the trace
dir and summarizes it in the same pass; the trace is never held whole
in memory. Format other than trace_format=1 -> 400 with the hint.
Limits xdebug_trace.max_upload (100MB), max_size (256MB unpacked, cut
there), keep (20, oldest unpinned deleted first); env XDEBUG_TRACE_*.

Files live under storage.path/xdebug-traces in filesystem mode, in a
temp dir removed at exit otherwise. SQLite keeps the event with the
summary; the call index goes to <uuid>.idx.json. Deleting or clearing
events removes the files (EventDeleteListener); startup removes files
no event refers to.

API (behind auth): /api/xdebug-traces/{uuid}/summary, /tree
(parent=, from=, limit=: one level of calls with incl/self/memory,
args and return values clipped), /search (q=, from=, limit=).
@butschster butschster added the type: feature Net-new functionality label Nov 8, 2026
…h results carry caller path

Xdebug writes the R line after the call's exit. Search dropped a match
on its exit and Children stopped at the parent's exit, so their return
values were lost; both now take the next record's R.
Search nodes get path (callers from the top level down) so the UI can
open the tree to a hit; the scan starts at the beginning also for from.
Startup sweep also drops trace events whose file is gone.
Such a trace starts at the caller's real depth (e.g. level 3) and later
goes up to level 2. The top level is now the calls that start while no
listed call is open, not level 1, and search paths are a stack popped
by call number. from= keeps the list it pages: pass the same parent.
…UI menu

The frontend has the trace page (frontend #337), so settings now name
the module and the UI shows it.
…rted as cut in tree and search

Recorded live traces: xdebug_stop_trace() and its caller never get an exit line. With TRACE END present those open calls are not a cut; Summarize already treated it so.
@butschster
butschster marked this pull request as ready for review November 8, 2026 17:32
@butschster
butschster changed the base branch from feat/xdebug-trace-parser to master November 8, 2026 17:32
@butschster
butschster merged commit e06998a into master Nov 8, 2026
1 check passed
@butschster
butschster deleted the feat/xdebug-trace-storage branch November 8, 2026 17:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: feature Net-new functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant