From 84f783ae589a0def1a12603f15f30ab1bf82fc5c Mon Sep 17 00:00:00 2001 From: Mark Williamson Date: Wed, 5 Mar 2025 18:27:00 +0000 Subject: [PATCH] Add a --symbol-file flag to "delve replay" for external symbols. This allows a separate debug symbol file to be specified when replaying a recording with the Undo backend. This can be used to supply debug information when the main binary has been stripped. --- cmd/dlv/cmds/commands.go | 16 +++++++++++++--- pkg/proc/gdbserial/gdbserver.go | 2 +- pkg/proc/gdbserial/undo.go | 6 +++--- service/debugger/debugger.go | 2 +- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/cmd/dlv/cmds/commands.go b/cmd/dlv/cmds/commands.go index 313d67fad..be140a540 100644 --- a/cmd/dlv/cmds/commands.go +++ b/cmd/dlv/cmds/commands.go @@ -100,6 +100,9 @@ var ( loadConfErr error rrOnProcessPid int + // External unstripped executable to use for symbols while replaying an Undo recording. + // TODO: Support this for rr backend. + replaySymbolFile string attachWaitFor string attachWaitForInterval float64 @@ -427,7 +430,7 @@ Currently supports linux/amd64 and linux/arm64 core files, windows/amd64 minidum if rrAvailable || undoAvailable { replayCommand := &cobra.Command{ - Use: "replay [trace directory or LiveRecorder recording]", + Use: "replay ", Short: "Replays a rr trace or LiveRecorder recording.", Long: `Replays a rr trace or LiveRecorder recording. @@ -442,12 +445,18 @@ Either Mozilla rr (https://github.com/mozilla/rr) or UDB (https://undo.io) must return nil }, Run: func(cmd *cobra.Command, args []string) { - if isUndo, _ := gdbserial.UndoIsRecording(args[0]); isUndo { + recording := args[0] + + if isUndo, _ := gdbserial.UndoIsRecording(recording); isUndo { backend = "undo" } else { backend = "rr" + if replaySymbolFile != "" { + fmt.Fprintf(os.Stderr, "Separate symbol file is not supported with rr backend.\n") + os.Exit(1) + } } - os.Exit(execute(0, []string{}, conf, args[0], debugger.ExecutingOther, args, buildFlags)) + os.Exit(execute(0, []string{replaySymbolFile}, conf, recording, debugger.ExecutingOther, args, buildFlags)) }, ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) > 2 { @@ -456,6 +465,7 @@ Either Mozilla rr (https://github.com/mozilla/rr) or UDB (https://undo.io) must return nil, cobra.ShellCompDirectiveDefault }, } + replayCommand.Flags().StringVarP(&replaySymbolFile, "symbol-file", "", "", "External unstripped executable for debug symbol lookups while replaying a recording.") replayCommand.Flags().IntVarP(&rrOnProcessPid, "onprocess", "p", 0, "Pass onprocess pid to rr.") diff --git a/pkg/proc/gdbserial/gdbserver.go b/pkg/proc/gdbserial/gdbserver.go index c6fb44dc0..8de716997 100644 --- a/pkg/proc/gdbserial/gdbserver.go +++ b/pkg/proc/gdbserial/gdbserver.go @@ -687,7 +687,7 @@ func (p *gdbProcess) EntryPoint() (uint64, error) { func (p *gdbProcess) initialize(path, cmdline string, debugInfoDirs []string, stopReason proc.StopReason) (*proc.TargetGroup, error) { var err error - if p.conn.undoSession != nil { + if path == "" && p.conn.undoSession != nil { // Always use the path from the recording file, so that we have consistent debug // symbols. Allowing the user to specify other paths is out of scope for now. path, err = undoGetExePath(&p.conn) diff --git a/pkg/proc/gdbserial/undo.go b/pkg/proc/gdbserial/undo.go index 73f532040..c6108286c 100644 --- a/pkg/proc/gdbserial/undo.go +++ b/pkg/proc/gdbserial/undo.go @@ -597,7 +597,7 @@ func UndoRecord(cmd []string, wd string, quiet bool, stdin string, stdout proc.O return recording, err } -func UndoReplay(recording string, quiet bool, debugInfoDirs []string, cmdline string) (tgt *proc.TargetGroup, err error) { +func UndoReplay(recording string, exePath string, quiet bool, debugInfoDirs []string, cmdline string) (tgt *proc.TargetGroup, err error) { if err := UndoIsAvailable(); err != nil { return nil, err } @@ -641,7 +641,7 @@ func UndoReplay(recording string, quiet bool, debugInfoDirs []string, cmdline st p.conn.undoSession = newUndoSession() p.tracedir = recording - tgt, err = p.Dial(port, "", cmdline, 0, debugInfoDirs, proc.StopAttached) + tgt, err = p.Dial(port, exePath, cmdline, 0, debugInfoDirs, proc.StopAttached) if err != nil { servercmd.Process.Kill() return nil, err @@ -659,7 +659,7 @@ func UndoRecordAndReplay(cmd []string, wd string, quiet bool, debugInfoDirs []st if err != nil || recording == "" { return nil, "", err } - tgt, err = UndoReplay(recording, quiet, debugInfoDirs, strings.Join(cmd, " ")) + tgt, err = UndoReplay(recording, "", quiet, debugInfoDirs, strings.Join(cmd, " ")) return tgt, recording, err } diff --git a/service/debugger/debugger.go b/service/debugger/debugger.go index 4dbe1288d..85a3b1d73 100644 --- a/service/debugger/debugger.go +++ b/service/debugger/debugger.go @@ -202,7 +202,7 @@ func New(config *Config, processArgs []string) (*Debugger, error) { d.target, err = gdbserial.Replay(d.config.CoreFile, false, false, d.config.DebugInfoDirectories, d.config.RrOnProcessPid, "") case "undo": d.log.Infof("opening recording %s", d.config.CoreFile) - d.target, err = gdbserial.UndoReplay(d.config.CoreFile, false, d.config.DebugInfoDirectories, "") + d.target, err = gdbserial.UndoReplay(d.config.CoreFile, d.processArgs[0], false, d.config.DebugInfoDirectories, "") default: d.log.Infof("opening core file %s (executable %s)", d.config.CoreFile, d.processArgs[0]) d.target, err = core.OpenCore(d.config.CoreFile, d.processArgs[0], d.config.DebugInfoDirectories)