github.com/undoio/delve@v1.9.0/pkg/proc/gdbserial/undo.go (about) 1 package gdbserial 2 3 import ( 4 "bytes" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "os/exec" 9 "runtime" 10 11 "github.com/undoio/delve/pkg/proc" 12 ) 13 14 func serverFile() (string, error) { 15 switch runtime.GOARCH { 16 case "amd64": 17 return "udbserver_x64", nil 18 case "arm64": 19 return "udbserver_arm64", nil 20 case "386": 21 return "udbserver_x32", nil 22 default: 23 return "", &ErrBackendUnavailable{} 24 } 25 } 26 27 func UndoIsAvailable() error { 28 server, err := serverFile() 29 if err != nil { 30 return err 31 } 32 33 cmds := []string{server, "live-record"} 34 35 for _, cmd := range cmds { 36 if _, err := exec.LookPath(cmd); err != nil { 37 return &ErrBackendUnavailable{} 38 } 39 } 40 return nil 41 } 42 43 func UndoRecord(cmd []string, wd string, quiet bool, redirects [3]string) (recording string, err error) { 44 if err := UndoIsAvailable(); err != nil { 45 return "", err 46 } 47 48 file, err := ioutil.TempFile("/tmp", "undo") 49 if err != nil { 50 return "", err 51 } 52 53 recording = file.Name() 54 args := make([]string, 0) 55 args = append(args, "-o", recording) 56 args = append(args, cmd...) 57 lrcmd := exec.Command("live-record", args...) 58 var closefn func() 59 // FIXME: pass quiet to openRedirects(), not false. 60 lrcmd.Stdin, lrcmd.Stdout, lrcmd.Stderr, closefn, err = openRedirects(redirects, false) 61 if err != nil { 62 return "", err 63 } 64 if wd != "" { 65 lrcmd.Dir = wd 66 } 67 lrcmd.Env = os.Environ() 68 69 // Ignore failures from Run - it could be the target failing 70 _ = lrcmd.Run() 71 closefn() 72 73 if isRecording, err := UndoIsRecording(recording); !isRecording { 74 // Recording apparently failed to put anything in the file 75 os.Remove(recording) 76 if err == nil { 77 err = fmt.Errorf("Recording failed") 78 } 79 return "", err 80 } 81 82 return recording, err 83 } 84 85 func UndoReplay(recording string, path string, quiet bool, debugInfoDirs []string) (tgt *proc.Target, err error) { 86 if err := UndoIsAvailable(); err != nil { 87 return nil, err 88 } 89 90 if isRecording, err := UndoIsRecording(recording); !isRecording || err != nil { 91 if err == nil { 92 err = fmt.Errorf("%s is not a LiveRecorder recording", recording) 93 } 94 return nil, err 95 } 96 97 port := unusedPort() 98 99 args := make([]string, 0) 100 args = append(args, "--load-file", recording, "--connect-port", port[1:]) 101 server, err := serverFile() 102 if err != nil { 103 return nil, err 104 } 105 servercmd := exec.Command(server, args...) 106 107 if !quiet { 108 servercmd.Env = os.Environ() 109 // servercmd.Env = append(servercmd.Env, "UNDO_debug_filename=/dev/stderr") 110 // servercmd.Env = append(servercmd.Env, "UNDO_debug_level=1") 111 servercmd.Stdout = os.Stdout 112 servercmd.Stderr = os.Stderr 113 } 114 115 if err := servercmd.Start(); err != nil { 116 return nil, err 117 } 118 119 p := newProcess(servercmd.Process) 120 p.tracedir = recording 121 tgt, err = p.Dial(port, path, 0, debugInfoDirs, proc.StopAttached) 122 if err != nil { 123 servercmd.Process.Kill() 124 return nil, err 125 } 126 127 // set to cause gdbserver.go to treat incoming signal numbers according to the GDB 128 // mapping, not the Linux mapping (see signal2native and native2signal in gdbserial.cpp) 129 p.conn.isUndoServer = true 130 return tgt, nil 131 } 132 133 // RecordAndReplay acts like calling Record and then Replay. 134 func UndoRecordAndReplay(cmd []string, wd string, quiet bool, debugInfoDirs []string, redirects [3]string) (tgt *proc.Target, recording string, err error) { 135 recording, err = UndoRecord(cmd, wd, quiet, redirects) 136 if err != nil || recording == "" { 137 return nil, "", err 138 } 139 tgt, err = UndoReplay(recording, cmd[0], quiet, debugInfoDirs) 140 return tgt, recording, err 141 } 142 143 func UndoIsRecording(recordingFile string) (result bool, err error) { 144 marker := []byte("HD\x10\x00\x00\x00UndoDB recording") 145 146 f, err := os.Open(recordingFile) 147 if err != nil { 148 return false, err 149 } 150 defer f.Close() 151 152 data := make([]byte, len(marker)) 153 c, err := f.Read(data) 154 if err != nil || c != len(marker) { 155 return false, err 156 } 157 158 return bytes.Equal(marker, data), nil 159 }