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  }