-
Notifications
You must be signed in to change notification settings - Fork 2
/
sdworker.py
220 lines (203 loc) · 10 KB
/
sdworker.py
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
import sublime
import threading
import traceback
import sys
import queue
from GoDebug.jsonrpctcp_client import JsonRpcTcpClient
from GoDebug.jsonrpctcp_client import JsonRpcTcpProtocolError
def __start(connect, const, logger):
logger.debug("Start worker")
try:
connect._open(const.HOST, const.PORT)
return True
except:
traceback.print_exc(file=(sys.stdout if logger.get_file() == const.STDOUT else open(logger.get_file(),"a")))
logger.error("Exception thrown, details in file: %s" % logger.get_file())
return False
def __stop(connect, const, logger):
try:
if connect._is_open():
connect._close()
except:
traceback.print_exc(file=(sys.stdout if logger.get_file() == const.STDOUT else open(logger.get_file(),"a")))
logger.error("Exception thrown, details in file: %s" % logger.get_file())
logger.debug("Stop worker")
def __default_cfg():
return {
'followPointers': True,
'maxVariableRecurse': 1,
'maxStringLen': 64,
'maxArrayValues': 64,
'maxStructFields': -1
}
def __get_eval_parms(goroutine_id, frame, expr):
return {"Scope": {"GoroutineID": goroutine_id, "Frame": frame}, "Expr": expr, "Cfg": __default_cfg()}
def __get_variable_parms(goroutine_id, frame):
return {"Scope": {"GoroutineID": goroutine_id, "Frame": frame}, "Cfg": __default_cfg()}
def __get_stacktrace_parms(goroutine_id):
# return {"Id": goroutine_id, "Depth": 20, "Full": False, "Cfg": __default_cfg()}
return {"Id": goroutine_id, "Depth": 20}
def __get_current_goroutine(response):
if type(response) is dict:
if 'State' in response:
if not response['State']['exited'] and 'currentThread' in response['State']:
return response['State']['currentThread']['goroutineID']
return None
def __get_error_response(cmd, parms):
return {"cmd": cmd, "parms": parms, "result": False}
def __get_error_response_ex(cmd, parms, e):
return {"cmd": cmd, "parms": parms, "result": False, "error_code": e.code, "error_message": e.message}
def _do_method(alive, queue, prj, worker_callback=None):
const = prj.const
logger = prj.logger
connect = JsonRpcTcpClient(const, logger)
if __start(connect, const, logger):
alive.set()
while alive.isSet():
requests = queue.get()
if requests is None:
alive.clear()
continue
responses = []
errors = False
goroutine_id = None
goroutines = False
frame = 0
watches = None
for request in requests:
cmd = request["cmd"]
parms = request["parms"]
if parms is None:
parms = {}
try:
if cmd in const.RUNTIME_COMMANDS:
parms['name'] = cmd
response = connect.RPCServer.Command(parms)
goroutine_id = __get_current_goroutine(response)
elif cmd == const.STATE_COMMAND:
if errors:
errors = False
response = connect.RPCServer.State(parms)
goroutine_id = __get_current_goroutine(response)
elif cmd == const.CREATE_BREAKPOINT_COMMAND:
response = connect.RPCServer.CreateBreakpoint(parms)
elif cmd == const.CLEAR_BREAKPOINT_COMMAND:
response = connect.RPCServer.ClearBreakpoint({"Id": parms['bkpt_id'], "Name": parms['bkpt_name']})
elif cmd == const.RESTART_COMMAND:
response = connect.RPCServer.Restart(parms)
elif cmd == const.CANCEL_NEXT_COMMAND:
response = connect.RPCServer.CancelNext(parms)
elif cmd == const.STACKTRACE_COMMAND:
response = connect.RPCServer.Stacktrace(__get_stacktrace_parms(parms['goroutine_id']))
elif cmd == const.BREAKPOINT_COMMAND:
response = connect.RPCServer.ListBreakpoints(parms)
elif cmd == const.VARIABLE_COMMAND:
call_parms = __get_variable_parms(parms['goroutine_id'], parms['frame'])
response_locals = connect.RPCServer.ListLocalVars(call_parms)
response_args = connect.RPCServer.ListFunctionArgs(call_parms)
response = {"Locals": response_locals['Variables'], "Arguments": response_args['Args']}
elif cmd == const.WATCH_COMMAND:
if 'goroutine_id' in parms:
goroutine_id = parms['goroutine_id']
frame = parms['frame']
watches = parms['watches']
continue
elif cmd == const.GOROUTINE_COMMAND:
if not goroutines:
goroutines = True
continue
else:
raise ValueError("Unknown worker command: %s" % cmd)
responses.append({"cmd": cmd, "result": True, "response": response})
except JsonRpcTcpProtocolError as e:
traceback.print_exc(file=(sys.stdout if logger.get_file() == const.STDOUT else open(logger.get_file(),"a")))
logger.error("Exception thrown, details in file: %s" % logger.get_file())
responses.append(__get_error_response_ex(cmd, parms, e))
if cmd not in [const.STATE_COMMAND, const.CREATE_BREAKPOINT_COMMAND, const.CLEAR_BREAKPOINT_COMMAND]:
errors = True
except:
traceback.print_exc(file=(sys.stdout if logger.get_file() == const.STDOUT else open(logger.get_file(),"a")))
logger.error("Exception thrown, details in file: %s" % logger.get_file())
responses.append(__get_error_response(cmd, parms))
if cmd not in [const.STATE_COMMAND, const.CREATE_BREAKPOINT_COMMAND, const.CLEAR_BREAKPOINT_COMMAND]:
errors = True
parms = {}
if errors:
errors = False
cmd = const.STATE_COMMAND
try:
response = connect.RPCServer.State(parms)
goroutine_id = __get_current_goroutine(response)
responses.append({"cmd": cmd, "result": True, "response": response})
except JsonRpcTcpProtocolError as e:
responses.append(__get_error_response_ex(cmd, parms, e))
errors = True
except:
responses.append(__get_error_response(cmd, parms))
errors = True
if not errors and goroutines:
cmd = const.GOROUTINE_COMMAND
try:
response = connect.RPCServer.ListGoroutines(parms)
found = False
for gr in response['Goroutines']:
if gr['id'] == goroutine_id:
found = True
break
if not found:
goroutine_id = 0
errors = True
responses.append({"cmd": const.GOROUTINE_COMMAND, "result": True, "response": response, "current_goroutine_id": goroutine_id})
except JsonRpcTcpProtocolError as e:
responses.append(__get_error_response_ex(cmd, parms, e))
errors = True
except:
responses.append(__get_error_response(cmd, parms))
errors = True
if not errors and watches is not None and goroutine_id > 0:
cmd = const.WATCH_COMMAND
response_watches = []
for element in watches:
try:
value = connect.RPCServer.Eval(__get_eval_parms(goroutine_id, frame, element['expr']))
response_watches.append({"watch_id": element['watch_id'], "result": True, "eval": value})
except JsonRpcTcpProtocolError as e:
response_watches.append(__get_error_response_ex(cmd, element, e))
except:
response_watches.append(__get_error_response(cmd, element))
responses.append({"cmd": const.WATCH_COMMAND, "result": True, "response": response_watches})
if worker_callback is not None:
# callback
sublime.set_timeout(worker_callback(prj, responses), 0)
__stop(connect, const, logger)
class DlvWorker(object):
def __init__(self, prj, worker_callback = None):
self.__prj = prj
self.__worker_callback = worker_callback
self.__alive = threading.Event()
self.__queue = None
self.__stoped = True
def __start(self):
self.__stoped = False
self.__queue = queue.Queue()
t = threading.Thread(name='worker', target=_do_method, args=(self.__alive, self.__queue, self.__prj, self.__worker_callback))
t.start()
def stop(self):
if self.__queue is not None:
self.__queue.put(None)
self.__stoped = True
def do(self, cmd, parms=None):
self.do_batch([{"cmd": cmd, "parms": parms}])
def do_batch(self, requests):
logger = self.__prj.logger
if not self.__alive.isSet():
logger.warning("Worker not started, put requests to the queue")
if self.__stoped:
self.__start()
if type(requests) is not list:
logger.error("Wrong requests type %s on worker call, list expected" % type(requests))
return
elif len(requests) == 0:
logger.error("Call worker with empty request")
return
self.__queue.put(requests)