/
client_custom.go
133 lines (116 loc) · 2.65 KB
/
client_custom.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package rpc2
import (
"errors"
"fmt"
"io"
"net"
"net/rpc"
"net/rpc/jsonrpc"
"sync"
"github.com/aarzilli/gdlv/internal/dlvclient/service/api"
)
// Client is a RPC service.Client.
type RPCClient struct {
addr string
client *rpc.Client
mu sync.Mutex
running, recording bool
retValLoadCfg *api.LoadConfig
recordedCache *bool
}
// NewClient creates a new RPCClient.
func NewClient(addr string, logFile io.Writer) (*RPCClient, error) {
netclient, err := net.Dial("tcp", addr)
if err != nil {
return nil, err
}
var rwc io.ReadWriteCloser = netclient
if logFile != nil {
rwc = &LogClient{netclient, logFile}
}
client := jsonrpc.NewClient(rwc)
c := &RPCClient{addr: addr, client: client}
c.call("SetApiVersion", api.SetAPIVersionIn{2}, &api.SetAPIVersionOut{})
return c, nil
}
func (c *RPCClient) Running() bool {
if c == nil {
return false
}
c.mu.Lock()
defer c.mu.Unlock()
return c.running || c.recording
}
var errRunning = errors.New("running")
func (c *RPCClient) call(method string, args, reply interface{}) error {
argsAsCmd := func() api.DebuggerCommand {
cmd, ok := args.(api.DebuggerCommand)
if !ok {
pcmd := args.(*api.DebuggerCommand)
cmd = *pcmd
}
return cmd
}
switch method {
case "Command":
cmd := argsAsCmd()
switch cmd.Name {
case api.SwitchThread, api.SwitchGoroutine, api.Halt:
// those don't start the process
default:
c.mu.Lock()
c.running = true
c.mu.Unlock()
defer func() {
c.mu.Lock()
c.running = false
c.mu.Unlock()
}()
}
case "Restart":
c.mu.Lock()
c.running = true
c.mu.Unlock()
defer func() {
c.mu.Lock()
c.running = false
c.mu.Unlock()
}()
}
return c.client.Call("RPCServer."+method, args, reply)
}
func (c *RPCClient) WaitForRecordingDone() {
c.mu.Lock()
c.recording = true
c.mu.Unlock()
c.GetState()
c.mu.Lock()
c.recording = false
c.mu.Unlock()
}
type ProcessExitedError struct {
pid, exitStatus int
}
func (err *ProcessExitedError) Error() string {
return fmt.Sprintf("Process %d has exited with status %d", err.pid, err.exitStatus)
}
// exitedToError returns an error if out.State says that the process exited.
func (c *RPCClient) exitedToError(out *CommandOut, err error) (*api.DebuggerState, error) {
if err != nil {
return nil, err
}
if out.State.Exited {
return nil, &ProcessExitedError{c.ProcessPid(), out.State.ExitStatus}
}
return &out.State, nil
}
// Recorded returns true if the debugger target is a recording.
func (c *RPCClient) Recorded() bool {
if c.recordedCache != nil {
return *c.recordedCache
}
out := new(RecordedOut)
c.call("Recorded", RecordedIn{}, out)
c.recordedCache = &out.Recorded
return out.Recorded
}