Skip to content

cmd: add LLGo-aware LLDB launcher with schema validation - #2211

Merged
cpunion merged 14 commits into
xgo-dev:mainfrom
cpunion:codex/lldb-marker-schema-20260729
Jul 30, 2026
Merged

cmd: add LLGo-aware LLDB launcher with schema validation#2211
cpunion merged 14 commits into
xgo-dev:mainfrom
cpunion:codex/lldb-marker-schema-20260729

Conversation

@cpunion

@cpunion cpunion commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • add llgo lldb, which embeds the LLGo Python adapter and supports LLDB 18+ discovery, LLGO_LLDB, -lldb, and argument pass-through
  • keep the stock-debugger contract on DW_LANG_C, with the LLGo producer and __llgo_debugger_marker_v1 selecting the adapter
  • keep adapter commands under the llgo namespace so stock LLDB p and v remain unchanged
  • define marker v1 as debugger schema v1/runtime-layout v1 and add llgo status for the target triple, pointer size, and byte order
  • cache target inspection by module fingerprint and reject missing, unknown, or ambiguous marker versions only for LLGo-specific commands, while preserving raw LLDB debugging
  • install the real-program acceptance fixture under cmd/llgo/lldbtest and run it on primary macOS and Ubuntu CI jobs

This keeps LLGo support inside the LLGo repository and does not require DW_LANG_Go, a patched LLDB, or private LLDB APIs.

Validation

  • macOS arm64, Apple LLDB 1703, Go 1.26.5: 21 cases, 198/198 assertions
  • Linux arm64, Ubuntu 24.04 in OrbStack, LLVM/LLDB 19, Go 1.26.5: 21 cases, 198/198 assertions
  • CI also covers Ubuntu amd64 with LLDB 19
  • supported v1, ordinary C, unsupported v2, and ambiguous v1+v2 targets exercise real LLDB commands
  • go test -cover ./cmd/internal/lldb ./cmd/llgo: cmd/internal/lldb 87.3% on both locally tested platforms

Refs #2154 and #2164. Follow-up #2206.

@cpunion cpunion changed the title [Based on #2205] debug(lldb): validate marker schema and target properties cmd: add LLGo-aware LLDB launcher with schema validation Jul 29, 2026

@fennoai fennoai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review summary

Solid, well-tested PR. The llgo lldb launcher is cleanly decomposed into small unit-tested functions, lldbImportCommand correctly escapes the temp plugin path, the temp plugin is written with 0600 under os.MkdirTemp, and the migration from comment-parsed expectations to an explicit TEST_CASES table + LLDB_BREAK markers (with the stricter GetNumLocations() != 1 breakpoint check) is a real robustness improvement. runtest.sh now exercises supported-v1, plain-C, and unknown-v2 marker states end to end.

No blocking issues. Findings below; two are inline.

Minor (not inline)

  • cmd/llgo/lldbtest/test.py:593 — the __main__ fallback default plugin path is go_lldb_plugin.py, but the file is llgo_plugin.py. Pre-existing and off the runtest.sh path (which supplies the plugin via llgo lldb), so CI is unaffected, but this dead default becomes more misleading now that the plugin was relocated to cmd/internal/lldb/.
  • Usage strings divergecmd/internal/lldb/lldb.go:48 sets UsageLine: "llgo lldb [-lldb path] [--] executable [lldb arguments...]", while cmd/llgo/lldb_cmd.gox:20 registers the vaguer use "lldb [flags] executable [lldb arguments...]". The .gox line is the one users actually see (the base command is never dispatched directly), yet it omits the -lldb flag and the -- passthrough convention the README documents. Consider aligning them.
  • cmd/internal/lldb/llgo_plugin.py (inspect_target, ~L85) — a target carrying both a supported (v1) and an unknown (v2) marker collapses to supported = False because a schema is resolved only when len(marker_versions) == 1. Defensible, but undocumented and untested; a short comment on the intent would help.
  • cmd/internal/lldb/lldb.go:57-60 — on cmd.Flag.Parse error runCmd returns without a non-zero exit, so a mis-invocation like llgo lldb --batch ./out (the exact -- footgun the README warns about) exits 0. Matches sibling commands, so not a regression, but exiting non-zero here would be friendlier.

Comment thread cmd/internal/lldb/llgo_plugin.py Outdated
if target.GetNumModules() == 0:
return ()
module = target.GetModuleAtIndex(0)
for index in range(module.GetNumSymbols()):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_marker_versions walks the entire symbol table of module 0 (GetNumSymbols() × GetSymbolAtIndex().GetName() + regex) on top of the targeted FindSymbols lookups above. For a Go/LLGo binary that is an O(N) scan over tens of thousands of symbols, each iteration crossing the Python/C++ SB API boundary twice. This runs on a hot path: inspect_target_require_supported_target is invoked on every llgo print / llgo vars / llgo status, and test.py builds a fresh LLDBDebugger per test case. Consider caching LLGoTargetInfo per target (it doesn't change during a session) and/or probing only the fixed __llgo_debugger_marker_v{n} names via FindSymbols instead of a full table walk.

Separately, the if target.GetNumModules() == 0: return () guard at L59-60 sits after versions is already populated from FindSymbols, so it would discard collected results; and the regex scan only inspects module 0, so an unknown marker in a non-first module would be missed while known versions still resolve — inconsistent detection. Moving the empty-target guard to the top and scanning all modules would make the two paths consistent.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 0f9fe48. inspect_target now caches by target properties plus the loaded-module fingerprint, so the full scan runs once per stable module set and is invalidated when modules change. Marker detection now scans every module consistently, and the integration suite verifies both a cache hit and the ambiguous v1+v2 case.

Comment thread cmd/llgo/lldbtest/README.md Outdated
227 // all variables: globalInt globalStruct globalStructPtr s i err
228 // s.i8: '\x12'
-> 225 println(s.i8) // LLDB_BREAK: main_struct_updated
(lldb) v

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This example uses (lldb) v and shows the LLGo plugin's custom output (Go-typed names, []int{...} slice rendering, <variable not available>), but this same PR moved that function off the bare v alias to llgo vars and the prose just above (L43-44) now states p/v are left as stock LLDB commands. After this change, typing v invokes stock LLDB's frame variable (C framing, different output), not the plugin. The transcript should use (lldb) llgo vars to match the shown output.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 0f9fe48: the transcript now uses (lldb) llgo vars, matching the namespaced command and shown LLGo-specific output.

@codecov

codecov Bot commented Jul 29, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@cpunion

cpunion commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator Author

Addressed the remaining review notes in 0f9fe4840:

  • remove the dead standalone plugin fallback
  • align the user-visible llgo lldb usage string
  • document and test ambiguous marker rejection
  • return exit status 2 on launcher flag parse errors

Local macOS arm64 / Apple LLDB 1703 and Linux arm64 / LLDB 19 execution suites both pass 198/198 assertions; cmd/internal/lldb coverage is 87.3%.

@cpunion

cpunion commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator Author

CI follow-up in aad62ca42: the macOS primary test shard was cancelled at the job's exact 30-minute limit in two consecutive runs, while recent main runs have taken up to 29m40s. The timeout is now 45 minutes only on macOS; Ubuntu remains at 30 minutes. This does not change the matrix or test coverage.

@cpunion

cpunion commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator Author

Follow-up review findings are addressed in 0546cdcf6 and 0e17269ed:

  • use a private temporary directory for LLDB result/fixture files
  • distinguish Apple LLDB vendor build numbers from upstream LLVM majors
  • return clear errors for unloaded/running targets, missing frames, malformed expressions, and unreadable values

Validation: macOS/arm64 Apple LLDB 1703 and Linux/amd64 LLVM LLDB 19 both pass 198/198 assertions plus marker/fallback checks; cmd/internal/lldb coverage is 88.4%. Final CI: 40 passed, 1 expected skip.

@cpunion
cpunion merged commit 22fe047 into xgo-dev:main Jul 30, 2026
41 checks passed
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