Skip to content

feat(rsp): QTDP tracepoint vocabulary (#26 phase-2)#16

Merged
zachgenius merged 2 commits into
masterfrom
feat/v1.6-26-qtdp
May 12, 2026
Merged

feat(rsp): QTDP tracepoint vocabulary (#26 phase-2)#16
zachgenius merged 2 commits into
masterfrom
feat/v1.6-26-qtdp

Conversation

@zachgenius

Copy link
Copy Markdown
Owner

Summary

  • Adds the gdb-remote tracepoint packet vocabulary (QTinit, QTDP define + condition, QTStart/QTStop, qTStatus, qTBuffer, QTFrame) + parse_tstatus_reply + TracepointWire carrier to src/transport/rsp/packets.{h,cpp}.
  • 7 new unit test cases (39 assertions) pin exact wire bytes against the gdb-remote spec; ctest 70/70 still green.
  • Design note docs/34-tracepoints-in-target.md documents wire shapes, the phase-2.5 dispatcher integration plan, capability negotiation (qSupported flags + empty-reply fallback), and the failure-mode matrix.

Vocabulary-only PR — dispatcher integration is intentionally phase-2.5 (the integration adds capability detection, per-channel state, and a fallback path large enough to fight TDD if landed alongside the vocabulary).

Why phase-2

Phase-1 (#26) shipped daemon-side tracepoint evaluation: every hit fires the LLDB breakpoint callback, evaluates the predicate daemon-side, decides whether to log. That's the high-frequency path tracepoints exist to optimise, so phase-2 pushes the bytecode into the inferior's agent (lldb-server / gdbserver) via gdb-remote's QTDP family. The agent runs the predicate without round-tripping to the daemon.

What's deferred to phase-2.5

  • Dispatcher integration: tracepoint.create emits QTinit + QTDP:T... + QTDP:-T... + QTStart against RspChannel-backed targets, with daemon-side fallback on E NN or empty-reply.
  • has_feature("tracepoints+") helper + per-channel capability cache on RspChannel.
  • Live smoke test against lldb-server gdbserver (outlined in §5 of the design note).

Test plan

  • build/bin/ldb_unit_tests "[qtdp]" — 7 cases, 39 assertions
  • ctest --test-dir build --output-on-failure — 70/70 green
  • Warning-clean build (the one pre-existing -Wsign-conversion in append_hex_bytes is on master; not introduced here)
  • Phase-2.5 will add the live smoke test via target.connect_remote_rsptracepoint.createqTStatustracepoint.delete

🤖 Generated with Claude Code

zachgenius and others added 2 commits May 12, 2026 21:24
Phase-2 of the v1.6 tracepoints chain wires the wire vocabulary for
pushing tracepoints into the inferior via gdb-remote's tracepoint
family (QTDP / QTStart / QTStop / QTFrame / qTStatus / qTBuffer /
QTinit). Phase-1 (#26) shipped daemon-side tracepoint evaluation;
this PR ships the packet builders + parser + carrier struct so a
later phase-2.5 PR can route RspChannel-backed tracepoints through
the in-target agent without daemon round-trips on every hit.

Scoped to vocabulary only — dispatcher integration is intentionally
phase-2.5. The vocabulary is reusable on its own (a custom-agent
bpf/perf path could speak QTDP-like packets), and integration is
large enough to fight TDD if landed here. docs/34 explains the
phase-2.5 wiring + capability negotiation + failure fallbacks.

Builders pin step=0 (no single-step collection; phase-3 territory)
and treat pass_count==0 as unlimited (the orchestrator's rate-limit
already covers that case daemon-side). QTDP_condition uses the spec's
continuation `-T<id>` form so condition bytecode lands in a separate
packet from the primary define.

7 new TEST_CASEs, 39 assertions. Builders get exact-wire-bytes
goldens against the spec; parse_tstatus_reply covers T0/T1 +
trailing kv pairs + malformed-shape rejection. ctest 70/70 green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The QTDP continuation packet (`build_QTDP_condition`) emitted
`QTDP:-T<id>:...` — a leading `-T`. Per the gdb-remote spec
(remote.c, `remote_add_target_side_condition` formats it as
`"QTDP:-%x:%s:%s"`) the continuation drops the `T`; only the PRIMARY
define packet (`QTDP:T<id>:...`) carries it. A real lldb-server or
gdbserver would silently NAK the malformed packet, so this would
have failed the first time we wired a real condition over RSP.

The existing test golden pinned the buggy value, so the unit test
passed against the wrong spec — a textbook silent-corruption failure
mode for a debugger. Updated the golden to the spec-compliant form
(`QTDP:-1:401000:X1,27`).

Three drive-by hardenings while touching the file:

  * `build_QTDP_condition` now throws `backend::Error` on empty
    bytecode. `X0,` is undefined by the agent-expression spec and
    some servers NAK it; reject at the source so the caller gets a
    typed error instead of a transport-level mystery.
  * `parse_tstatus_reply` caps kv-pair allocation at 64. The parser
    runs against untrusted remote-debug-server bytes; unbounded
    allocation on a hostile/buggy server was a real attack surface.
  * `append_hex_bytes` no longer warns under -Wsign-conversion. The
    range-for over `std::string_view` was implicit-narrowing
    `char → unsigned char`; switched to indexed iteration with an
    explicit cast.

Tests:
  * Updated QTDP continuation golden to the spec-correct bytes.
  * Added `QTDP condition rejects empty bytecode` (throws).
  * Added `parse_tstatus_reply caps kv-pair allocation` (64 OK, 65 nullopt).
  * Net +2 TEST_CASE blocks in test_rsp_packets.cpp.

70/70 ctest green, warning-clean build.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@zachgenius zachgenius force-pushed the feat/v1.6-26-qtdp branch from b408a12 to 43734ca Compare May 12, 2026 11:24
@zachgenius zachgenius merged commit 1ef6e73 into master May 12, 2026
4 checks passed
@zachgenius zachgenius deleted the feat/v1.6-26-qtdp branch May 12, 2026 11:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant