retracesoftware-proxy 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- retracesoftware/__init__.py +0 -0
- retracesoftware/config.json +382 -0
- retracesoftware/install/__init__.py +0 -0
- retracesoftware/install/config.py +53 -0
- retracesoftware/install/edgecases.py +220 -0
- retracesoftware/install/globals.py +17 -0
- retracesoftware/install/install.py +172 -0
- retracesoftware/install/patcher.py +494 -0
- retracesoftware/install/predicate.py +92 -0
- retracesoftware/install/record.py +128 -0
- retracesoftware/install/references.py +65 -0
- retracesoftware/install/replay.py +151 -0
- retracesoftware/install/tracer.py +144 -0
- retracesoftware/install/typeutils.py +72 -0
- retracesoftware/proxy/__init__.py +3 -0
- retracesoftware/proxy/gateway.py +100 -0
- retracesoftware/proxy/proxyfactory.py +357 -0
- retracesoftware/proxy/proxysystem.py +20 -0
- retracesoftware/proxy/proxytype.py +280 -0
- retracesoftware/proxy/record.py +133 -0
- retracesoftware/proxy/replay.py +110 -0
- retracesoftware/proxy/thread.py +11 -0
- retracesoftware_proxy-0.1.0.dist-info/METADATA +12 -0
- retracesoftware_proxy-0.1.0.dist-info/RECORD +26 -0
- retracesoftware_proxy-0.1.0.dist-info/WHEEL +5 -0
- retracesoftware_proxy-0.1.0.dist-info/top_level.txt +1 -0
|
File without changes
|
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
{
|
|
2
|
+
"states": [
|
|
3
|
+
"disabled", {"comment": "Default state when retrace is disabled for a thread"},
|
|
4
|
+
"internal", {"comment": "Default state when retrace is disabled for a thread"},
|
|
5
|
+
"external", {"comment": "When target thread is running outside the python system to be recorded"},
|
|
6
|
+
"retrace", {"comment": "When target thread is running outside the retrace system"},
|
|
7
|
+
"bootstrap", {"comment": "When the target thread is running in the low-level module load machinery"},
|
|
8
|
+
"gc", {"comment": "When the target thread is running inside the pyton garbage collector"}
|
|
9
|
+
],
|
|
10
|
+
|
|
11
|
+
"recording_path": "recordings/%Y%m%d_%H%M%S_%f",
|
|
12
|
+
|
|
13
|
+
"tracing_levels": {
|
|
14
|
+
"none": [],
|
|
15
|
+
"all": [
|
|
16
|
+
"proxy.ext.disabled.call",
|
|
17
|
+
"proxy.int.disabled.call",
|
|
18
|
+
"proxy.wrapping.new",
|
|
19
|
+
"proxy.wrapping.new.method",
|
|
20
|
+
"proxy.int.handler",
|
|
21
|
+
"proxy.ext.handler",
|
|
22
|
+
"proxy.int.newref",
|
|
23
|
+
"proxy.ext.call",
|
|
24
|
+
"proxy.ext.result",
|
|
25
|
+
"proxy.ext.error",
|
|
26
|
+
"proxy.int.call",
|
|
27
|
+
"proxy.int.result",
|
|
28
|
+
"proxy.int.error",
|
|
29
|
+
"proxy.ext.new.extended",
|
|
30
|
+
"proxy.ext.new.extended.method",
|
|
31
|
+
"proxy.ext.new.extended.member",
|
|
32
|
+
"proxy.int.new.extended",
|
|
33
|
+
"proxy.int.new.extended.method",
|
|
34
|
+
"install.module",
|
|
35
|
+
"install.module.phase",
|
|
36
|
+
"install.module.phase.results"
|
|
37
|
+
],
|
|
38
|
+
"debug": [
|
|
39
|
+
"proxy.wrapping.new",
|
|
40
|
+
"proxy.wrapping.new.method",
|
|
41
|
+
"proxy.int_to_ext.stack",
|
|
42
|
+
"proxy.ext_to_int.wrap",
|
|
43
|
+
"proxy.int.handler",
|
|
44
|
+
"proxy.int.newref",
|
|
45
|
+
"proxy.ext.call",
|
|
46
|
+
"proxy.ext.result",
|
|
47
|
+
"proxy.ext.error",
|
|
48
|
+
"proxy.int.result",
|
|
49
|
+
"proxy.int.error",
|
|
50
|
+
"proxy.ext.new.extended",
|
|
51
|
+
"proxy.ext.new.extended.method",
|
|
52
|
+
"proxy.ext.new.extended.member",
|
|
53
|
+
"proxy.int.new.extended",
|
|
54
|
+
"proxy.int.new.extended.method",
|
|
55
|
+
"install.module",
|
|
56
|
+
"install.module.phase",
|
|
57
|
+
"install.module.phase.results"
|
|
58
|
+
]
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
"default_tracing_level": "debug",
|
|
62
|
+
|
|
63
|
+
"exclude_paths": [
|
|
64
|
+
"^.*\\.py$",
|
|
65
|
+
"^.*\\.pyc$",
|
|
66
|
+
"^/tmp$",
|
|
67
|
+
"^/tmp/.*$"
|
|
68
|
+
],
|
|
69
|
+
|
|
70
|
+
"preload": [
|
|
71
|
+
|
|
72
|
+
],
|
|
73
|
+
|
|
74
|
+
"predicates": {
|
|
75
|
+
"path": {
|
|
76
|
+
"and": [
|
|
77
|
+
{
|
|
78
|
+
"not": "^.*\\.py$"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"not": "^.*\\.pyc$"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"not": "^recordings/.*$"
|
|
85
|
+
}
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
"type_attribute_filter": {
|
|
91
|
+
"and": [
|
|
92
|
+
".*",
|
|
93
|
+
{
|
|
94
|
+
"not": [
|
|
95
|
+
"__hash__",
|
|
96
|
+
"__getattribute__",
|
|
97
|
+
"__init_subclass__",
|
|
98
|
+
"__subclasshook__",
|
|
99
|
+
"__class__",
|
|
100
|
+
"__sizeof__",
|
|
101
|
+
"__new__",
|
|
102
|
+
"__del__",
|
|
103
|
+
"__dict__"
|
|
104
|
+
]
|
|
105
|
+
}
|
|
106
|
+
]
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
"filters": {
|
|
110
|
+
"functions": [
|
|
111
|
+
{
|
|
112
|
+
"or": [
|
|
113
|
+
{
|
|
114
|
+
"type": "types.BuiltinFunctionType"
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"type": "types.FunctionType"
|
|
118
|
+
}
|
|
119
|
+
]
|
|
120
|
+
}
|
|
121
|
+
],
|
|
122
|
+
"all": {
|
|
123
|
+
"and": [
|
|
124
|
+
{
|
|
125
|
+
"or": [
|
|
126
|
+
{
|
|
127
|
+
"type": "type"
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"type": "types.BuiltinFunctionType"
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"type": "types.FunctionType"
|
|
134
|
+
}
|
|
135
|
+
]
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"noneof": [
|
|
139
|
+
{
|
|
140
|
+
"subtype": "BaseException"
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"value": "$immutable"
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"name": "__spec__"
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"name": "__package__"
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"name": "__loader__"
|
|
153
|
+
}
|
|
154
|
+
]
|
|
155
|
+
}
|
|
156
|
+
]
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
"modules": {
|
|
161
|
+
"_imp": {
|
|
162
|
+
"patch_extension_exec": ["exec_dynamic", "exec_builtin"]
|
|
163
|
+
},
|
|
164
|
+
"sys": {
|
|
165
|
+
"with_state": {
|
|
166
|
+
"disabled": ["excepthook"]
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
"importlib._bootstrap": {
|
|
171
|
+
"with_state": {
|
|
172
|
+
"bootstrap": ["_load_unlocked", "_find_spec"]
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
|
|
176
|
+
"encodings": {
|
|
177
|
+
"with_state": {
|
|
178
|
+
"disabled": ["search_function"]
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
"_retrace_utils": {
|
|
182
|
+
"immutable_types": [
|
|
183
|
+
"PyCFunctionProxy"
|
|
184
|
+
]
|
|
185
|
+
},
|
|
186
|
+
"os": {
|
|
187
|
+
"immutable_types": [
|
|
188
|
+
"stat_result",
|
|
189
|
+
"terminal_size",
|
|
190
|
+
"statvfs_result"
|
|
191
|
+
]
|
|
192
|
+
},
|
|
193
|
+
"mmap": {
|
|
194
|
+
"proxy": [
|
|
195
|
+
"mmapXXX"
|
|
196
|
+
]
|
|
197
|
+
},
|
|
198
|
+
"types": {
|
|
199
|
+
"immutable_types": [
|
|
200
|
+
"TracebackType"
|
|
201
|
+
]
|
|
202
|
+
},
|
|
203
|
+
"builtins": {
|
|
204
|
+
"replace": {
|
|
205
|
+
"set": "retracesoftware_utils.set",
|
|
206
|
+
"frozenset": "retracesoftware_utils.frozenset"
|
|
207
|
+
},
|
|
208
|
+
"immutable_types": [
|
|
209
|
+
"BaseException",
|
|
210
|
+
"memoryview",
|
|
211
|
+
"int",
|
|
212
|
+
"float",
|
|
213
|
+
"complex",
|
|
214
|
+
"str",
|
|
215
|
+
"bytes",
|
|
216
|
+
"bool",
|
|
217
|
+
"bytearray",
|
|
218
|
+
"type",
|
|
219
|
+
"slice"],
|
|
220
|
+
|
|
221
|
+
"patch_exec": "exec"
|
|
222
|
+
},
|
|
223
|
+
"_thread": {
|
|
224
|
+
"proxy": [
|
|
225
|
+
"allocate",
|
|
226
|
+
"allocate_lock",
|
|
227
|
+
"RLock"
|
|
228
|
+
],
|
|
229
|
+
"patch_start_new_thread": ["start_new_thread", "start_new"]
|
|
230
|
+
},
|
|
231
|
+
"_datetime": {
|
|
232
|
+
"immutable_types": [
|
|
233
|
+
"datetime",
|
|
234
|
+
"tzinfo",
|
|
235
|
+
"timezone"
|
|
236
|
+
],
|
|
237
|
+
"proxy_type_attributes": {
|
|
238
|
+
"datetime": [
|
|
239
|
+
"now",
|
|
240
|
+
"utcnow",
|
|
241
|
+
"today"
|
|
242
|
+
]
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
"time": {
|
|
246
|
+
"immutable_types": [
|
|
247
|
+
"struct_time"
|
|
248
|
+
],
|
|
249
|
+
"proxy": [
|
|
250
|
+
"perf_counter",
|
|
251
|
+
"time",
|
|
252
|
+
"gmtime",
|
|
253
|
+
"localtime",
|
|
254
|
+
"monotonic",
|
|
255
|
+
"monotonic_ns",
|
|
256
|
+
"time_ns"
|
|
257
|
+
]
|
|
258
|
+
},
|
|
259
|
+
"io": {
|
|
260
|
+
"proxy": ["open_code", "open"],
|
|
261
|
+
"path_predicates": {
|
|
262
|
+
"open_code": "path",
|
|
263
|
+
"open": "file"
|
|
264
|
+
},
|
|
265
|
+
"wrap": {
|
|
266
|
+
"FileIO.readinfo": "retracesoftware.install.edgecases.readinto",
|
|
267
|
+
"BufferedReader.readinfo": "retracesoftware.install.edgecases.readinto",
|
|
268
|
+
"BufferedRandom.readinfo": "retracesoftware.install.edgecases.readinto"
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
"pathlib": {
|
|
272
|
+
"immutable_types": [
|
|
273
|
+
"PosixPath"
|
|
274
|
+
]
|
|
275
|
+
},
|
|
276
|
+
"posix": {
|
|
277
|
+
"immutable_types": [
|
|
278
|
+
"times_result",
|
|
279
|
+
"statvfs_result",
|
|
280
|
+
"uname_result",
|
|
281
|
+
"stat_result",
|
|
282
|
+
"terminal_size",
|
|
283
|
+
"DirEntry"
|
|
284
|
+
],
|
|
285
|
+
|
|
286
|
+
"proxy_all_except": [
|
|
287
|
+
"fork",
|
|
288
|
+
"register_at_fork",
|
|
289
|
+
"basename",
|
|
290
|
+
"readlink",
|
|
291
|
+
"strerror",
|
|
292
|
+
"listdir",
|
|
293
|
+
"_path_normpath"
|
|
294
|
+
],
|
|
295
|
+
|
|
296
|
+
"wrappers": {
|
|
297
|
+
"fork_exec": "retracesoftware.install.edgecases.fork_exec",
|
|
298
|
+
"posix_spawn": "retracesoftware.install.edgecases.posix_spawn"
|
|
299
|
+
}
|
|
300
|
+
},
|
|
301
|
+
"_posixsubprocess": {
|
|
302
|
+
"proxy_all_except": []
|
|
303
|
+
},
|
|
304
|
+
"fcntl": {
|
|
305
|
+
"proxy_all_except": []
|
|
306
|
+
},
|
|
307
|
+
"_signal": {
|
|
308
|
+
"proxy_all_except": []
|
|
309
|
+
},
|
|
310
|
+
"_socket": {
|
|
311
|
+
"proxy_all_except": [
|
|
312
|
+
"CAPI"
|
|
313
|
+
],
|
|
314
|
+
"immutable_types": [
|
|
315
|
+
"error",
|
|
316
|
+
"herror",
|
|
317
|
+
"gaierror",
|
|
318
|
+
"timeout"
|
|
319
|
+
],
|
|
320
|
+
"wrap": {
|
|
321
|
+
"socket.recvfrom_into": "retracesoftware.install.edgecases.recvfrom_into",
|
|
322
|
+
"socket.recv_into": "retracesoftware.install.edgecases.recv_into",
|
|
323
|
+
"socket.recvmsg_into": "retracesoftware.install.edgecases.recvmsg_into"
|
|
324
|
+
}
|
|
325
|
+
},
|
|
326
|
+
"select": {
|
|
327
|
+
"proxy_all_except": []
|
|
328
|
+
},
|
|
329
|
+
"_sqlite3": {
|
|
330
|
+
"proxy_all_except": []
|
|
331
|
+
},
|
|
332
|
+
"_ssl": {
|
|
333
|
+
"proxy_all_except": [],
|
|
334
|
+
|
|
335
|
+
"proxy_functions": ".*",
|
|
336
|
+
"comment": {
|
|
337
|
+
"proxy_types": [
|
|
338
|
+
"MemoryBIO",
|
|
339
|
+
"_SSLSocket",
|
|
340
|
+
"_SSLContext"
|
|
341
|
+
],
|
|
342
|
+
"wrap": {
|
|
343
|
+
"_SSLSocket.write": "write"
|
|
344
|
+
}
|
|
345
|
+
},
|
|
346
|
+
"wrap": {
|
|
347
|
+
"_SSLSocket.read": "retracesoftware.install.edgecases.read"
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
"_random": {
|
|
351
|
+
"proxy_all_except": [],
|
|
352
|
+
"proxy_types": [
|
|
353
|
+
"Random"
|
|
354
|
+
],
|
|
355
|
+
"proxy_functions": ".*"
|
|
356
|
+
},
|
|
357
|
+
"_multiprocessing": {
|
|
358
|
+
"proxy_functions": ".*"
|
|
359
|
+
},
|
|
360
|
+
"multiprocessing.context": {
|
|
361
|
+
"proxy": ["_default_context"]
|
|
362
|
+
},
|
|
363
|
+
|
|
364
|
+
"PIL._imaging": {
|
|
365
|
+
"proxy_all_except": ["map_buffer"]
|
|
366
|
+
},
|
|
367
|
+
"psycopg2._psycopg": {
|
|
368
|
+
"proxy_all_except": ["Error"]
|
|
369
|
+
},
|
|
370
|
+
|
|
371
|
+
"_collections": {
|
|
372
|
+
"sync_types": [
|
|
373
|
+
"deque"
|
|
374
|
+
]
|
|
375
|
+
},
|
|
376
|
+
"_queue": {
|
|
377
|
+
"sync_types": [
|
|
378
|
+
"SimpleQueue"
|
|
379
|
+
]
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import pkgutil
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
import datetime
|
|
5
|
+
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
def debug_level(config):
|
|
9
|
+
if 'RETRACE_DEBUG' in os.environ:
|
|
10
|
+
debug = env_int('RETRACE_DEBUG')
|
|
11
|
+
else:
|
|
12
|
+
debug = config.get('record', {}).get('debug', 1)
|
|
13
|
+
|
|
14
|
+
return debug
|
|
15
|
+
|
|
16
|
+
def env_truthy(key, default=False):
|
|
17
|
+
value = os.getenv(key)
|
|
18
|
+
if value is None:
|
|
19
|
+
return default
|
|
20
|
+
return value.strip().lower() in ("1", "true", "yes", "on")
|
|
21
|
+
|
|
22
|
+
def env_int(key, default = 0):
|
|
23
|
+
value = os.getenv(key)
|
|
24
|
+
if value is None:
|
|
25
|
+
return default
|
|
26
|
+
return int(value)
|
|
27
|
+
|
|
28
|
+
def get_recording_path(config):
|
|
29
|
+
return Path(datetime.datetime.now().strftime(config.format(pid = os.getpid())))
|
|
30
|
+
|
|
31
|
+
def recording_path(config):
|
|
32
|
+
if 'RETRACE_RECORDING_PATH' in os.environ:
|
|
33
|
+
return Path(os.environ['RETRACE_RECORDING_PATH'])
|
|
34
|
+
else:
|
|
35
|
+
recording_path = get_recording_path(config.get('record_path', 'recordings'))
|
|
36
|
+
os.environ['RETRACE_RECORDING_PATH'] = str(recording_path)
|
|
37
|
+
return recording_path
|
|
38
|
+
|
|
39
|
+
def load_config(filename):
|
|
40
|
+
|
|
41
|
+
data = pkgutil.get_data("retracesoftware", filename)
|
|
42
|
+
assert data is not None
|
|
43
|
+
|
|
44
|
+
config = json.loads(data.decode("utf-8"))
|
|
45
|
+
|
|
46
|
+
config['debug_level'] = debug_level(config)
|
|
47
|
+
|
|
48
|
+
# config['recording_path'] = recording_path(config)
|
|
49
|
+
|
|
50
|
+
config['verbose'] = env_truthy('RETRACE_VERBOSE')
|
|
51
|
+
|
|
52
|
+
return config
|
|
53
|
+
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# from .proxytype import *
|
|
2
|
+
|
|
3
|
+
import functools
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
from retracesoftware.install import globals
|
|
7
|
+
|
|
8
|
+
def recvfrom_into(target):
|
|
9
|
+
@functools.wraps(target)
|
|
10
|
+
def wrapper(self, buffer, nbytes = 0, flags = 0):
|
|
11
|
+
data, address = self.recvfrom(len(buffer) if nbytes == 0 else nbytes, flags)
|
|
12
|
+
buffer[0:len(data)] = data
|
|
13
|
+
return len(data), address
|
|
14
|
+
return wrapper
|
|
15
|
+
|
|
16
|
+
def recv_into(target):
|
|
17
|
+
@functools.wraps(target)
|
|
18
|
+
def wrapper(self, buffer, nbytes = 0, flags = 0):
|
|
19
|
+
data = self.recv(len(buffer) if nbytes == 0 else nbytes, flags)
|
|
20
|
+
buffer[0:len(data)] = data
|
|
21
|
+
return len(data)
|
|
22
|
+
return wrapper
|
|
23
|
+
|
|
24
|
+
def recvmsg_into(target):
|
|
25
|
+
@functools.wraps(target)
|
|
26
|
+
def wrapper(self, buffers, ancbufsize = 0, flags = 0):
|
|
27
|
+
raise NotImplementedError('TODO')
|
|
28
|
+
return wrapper
|
|
29
|
+
|
|
30
|
+
def read(target):
|
|
31
|
+
@functools.wraps(target)
|
|
32
|
+
def wrapper(self, *args):
|
|
33
|
+
# super_type = super(type(self), self)
|
|
34
|
+
|
|
35
|
+
if len(args) == 0:
|
|
36
|
+
return target(self)
|
|
37
|
+
else:
|
|
38
|
+
buflen = args[0]
|
|
39
|
+
|
|
40
|
+
# pdb.set_trace()
|
|
41
|
+
|
|
42
|
+
data = target(self, buflen)
|
|
43
|
+
|
|
44
|
+
if len(args) == 1:
|
|
45
|
+
return data
|
|
46
|
+
else:
|
|
47
|
+
buffer = args[1]
|
|
48
|
+
|
|
49
|
+
buffer[0:len(data)] = data
|
|
50
|
+
|
|
51
|
+
return len(data)
|
|
52
|
+
return wrapper
|
|
53
|
+
|
|
54
|
+
def write(target):
|
|
55
|
+
@functools.wraps(target)
|
|
56
|
+
def wrapper(self, byteslike):
|
|
57
|
+
return target(byteslike.tobytes())
|
|
58
|
+
|
|
59
|
+
return wrapper
|
|
60
|
+
|
|
61
|
+
def readinto(target):
|
|
62
|
+
@functools.wraps(target)
|
|
63
|
+
def wrapper(self, buffer):
|
|
64
|
+
bytes = self.read(buffer.nbytes)
|
|
65
|
+
buffer[:len(bytes)] = bytes
|
|
66
|
+
return len(bytes)
|
|
67
|
+
return wrapper
|
|
68
|
+
|
|
69
|
+
typewrappers = {
|
|
70
|
+
'_socket': {
|
|
71
|
+
'socket': {
|
|
72
|
+
'recvfrom_into': recvfrom_into,
|
|
73
|
+
'recv_into': recv_into,
|
|
74
|
+
'recvmsg_into': recvmsg_into
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
'_ssl': {
|
|
78
|
+
'_SSLSocket': {
|
|
79
|
+
'read': read,
|
|
80
|
+
# 'write': write
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
'io': {
|
|
84
|
+
'FileIO': {
|
|
85
|
+
'readinto': readinto
|
|
86
|
+
},
|
|
87
|
+
'BufferedReader': {
|
|
88
|
+
'readinto': readinto
|
|
89
|
+
},
|
|
90
|
+
'BufferedRandom': {
|
|
91
|
+
'readinto': readinto
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
def patchtype(module, name, cls : type):
|
|
97
|
+
if module in typewrappers:
|
|
98
|
+
if name in typewrappers[module]:
|
|
99
|
+
for method,patcher in typewrappers[module][name].items():
|
|
100
|
+
setattr(cls, method, patcher(getattr(cls, method)))
|
|
101
|
+
|
|
102
|
+
def transform_argument(target : callable, position : int, name : str, transform : callable, default = None):
|
|
103
|
+
assert callable(transform)
|
|
104
|
+
assert callable(target)
|
|
105
|
+
|
|
106
|
+
@functools.wraps(target)
|
|
107
|
+
def wrapper(*args, **kwargs):
|
|
108
|
+
if name in kwargs:
|
|
109
|
+
kwargs[name] = transform(kwargs[name])
|
|
110
|
+
elif len(args) > position:
|
|
111
|
+
args = list(args)
|
|
112
|
+
args[position] = transform(args[position])
|
|
113
|
+
else:
|
|
114
|
+
kwargs[name] = transform(default)
|
|
115
|
+
|
|
116
|
+
# print(f'Running: {args} {kwargs}')
|
|
117
|
+
return target(*args, **kwargs)
|
|
118
|
+
|
|
119
|
+
return wrapper
|
|
120
|
+
|
|
121
|
+
def retrace_env():
|
|
122
|
+
import threading
|
|
123
|
+
|
|
124
|
+
env = {
|
|
125
|
+
'RETRACE_RECORDING_PATH': globals.recording_path.next_subprocess_path(),
|
|
126
|
+
# 'RETRACE_PARENT_THREAD_ID': threading.current_thread().__retrace_thread_id__,
|
|
127
|
+
# 'RETRACE_PARENT_EXEC_COUNTER': str(next_exec_counter()
|
|
128
|
+
'RETRACE_MODE': 'record'
|
|
129
|
+
# 'RETRACE_MODE': 'proxy' if os.getenv('RETRACE_MODE') == 'proxy' else 'record'
|
|
130
|
+
}
|
|
131
|
+
# a timestamped directory off current directory
|
|
132
|
+
# controlled by config
|
|
133
|
+
# 1. if recording_dir is set use that
|
|
134
|
+
# 2. if recording_dir is not set look for
|
|
135
|
+
|
|
136
|
+
# env['RETRACE_EXECUTION_ID'] = hashlib.md5(json.dumps(env).encode('UTF8')).hexdigest()
|
|
137
|
+
|
|
138
|
+
return env
|
|
139
|
+
|
|
140
|
+
def fork_exec(target):
|
|
141
|
+
def transform(env):
|
|
142
|
+
r = [f'{k}={v}'.encode('utf-8') for k,v in retrace_env().items()]
|
|
143
|
+
return env + r if env else r
|
|
144
|
+
|
|
145
|
+
return transform_argument(target = target,
|
|
146
|
+
position = 5,
|
|
147
|
+
name = 'env',
|
|
148
|
+
transform = transform, default = os.environ)
|
|
149
|
+
|
|
150
|
+
def posix_spawn(target):
|
|
151
|
+
def transform(env):
|
|
152
|
+
r = retrace_env()
|
|
153
|
+
return {**env, **r}
|
|
154
|
+
|
|
155
|
+
return transform_argument(target = target,
|
|
156
|
+
position = 2,
|
|
157
|
+
name = 'env',
|
|
158
|
+
transform = transform, default = os.environ)
|
|
159
|
+
|
|
160
|
+
function_patchers = {
|
|
161
|
+
'_posixsubprocess': {
|
|
162
|
+
'fork_exec': fork_exec
|
|
163
|
+
},
|
|
164
|
+
'posix': {
|
|
165
|
+
'posix_spawn': posix_spawn
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
# import traceback
|
|
170
|
+
|
|
171
|
+
def typepatcher(cls : type):
|
|
172
|
+
|
|
173
|
+
# print(f'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! type: {cls} created')
|
|
174
|
+
# traceback.print_stack()
|
|
175
|
+
|
|
176
|
+
return typewrappers.get(cls.__module__, {}).get(cls.__name__, {})
|
|
177
|
+
|
|
178
|
+
# if cls.__module__ in typewrappers:
|
|
179
|
+
# # print(f'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! TYPEWRAPPER for {cls}')
|
|
180
|
+
|
|
181
|
+
# mod = typewrappers[cls.__module__]
|
|
182
|
+
|
|
183
|
+
# return mod.get(cls.__name__, {})
|
|
184
|
+
|
|
185
|
+
# if cls.__name__ in mod:
|
|
186
|
+
# # if cls.__name__ == '_SSLSocket':
|
|
187
|
+
# # breakpoint()
|
|
188
|
+
|
|
189
|
+
# log.info("Applying specialized typewrapper to %s, updated slots: %s", cls, list(mod[cls.__name__].keys()))
|
|
190
|
+
|
|
191
|
+
# for name,value in mod[cls.__name__].items():
|
|
192
|
+
# setattr(cls, name, value(getattr(cls, name)))
|
|
193
|
+
|
|
194
|
+
# # slots = {'__module__': cls.__module__, '__slots__': ()}
|
|
195
|
+
# # slots.update(mod[cls.__name__])
|
|
196
|
+
# # return type(cls.__name__, (cls, ), slots)
|
|
197
|
+
|
|
198
|
+
# return cls
|
|
199
|
+
|
|
200
|
+
def typewrapper(cls : type):
|
|
201
|
+
|
|
202
|
+
# print(f'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! type: {cls} created')
|
|
203
|
+
# traceback.print_stack()
|
|
204
|
+
|
|
205
|
+
if cls.__module__ in typewrappers:
|
|
206
|
+
# print(f'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! TYPEWRAPPER for {cls}')
|
|
207
|
+
|
|
208
|
+
mod = typewrappers[cls.__module__]
|
|
209
|
+
|
|
210
|
+
if cls.__name__ in mod:
|
|
211
|
+
# if cls.__name__ == '_SSLSocket':
|
|
212
|
+
# breakpoint()
|
|
213
|
+
|
|
214
|
+
log.info("Applying specialized typewrapper to %s, updated slots: %s", classname(cls), list(mod[cls.__name__].keys()))
|
|
215
|
+
|
|
216
|
+
slots = {'__module__': cls.__module__, '__slots__': ()}
|
|
217
|
+
slots.update(mod[cls.__name__])
|
|
218
|
+
return type(cls.__name__, (cls, ), slots)
|
|
219
|
+
|
|
220
|
+
return cls
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
class RecordingPath:
|
|
2
|
+
def __init__(self, path):
|
|
3
|
+
self.path = path
|
|
4
|
+
self.subprocess_counter = 0
|
|
5
|
+
self.fork_counter = 0
|
|
6
|
+
|
|
7
|
+
def next_subprocess_path(self):
|
|
8
|
+
path = self.path / f'subprocess-{self.subprocess_counter}'
|
|
9
|
+
self.subprocess_counter = self.subprocess_counter + 1
|
|
10
|
+
return path
|
|
11
|
+
|
|
12
|
+
def next_fork_path(self):
|
|
13
|
+
path = self.path / f'fork-{self.fork_counter}'
|
|
14
|
+
self.fork_counter = self.fork_counter + 1
|
|
15
|
+
return path
|
|
16
|
+
|
|
17
|
+
recording_path = None
|