retracesoftware-proxy 0.1.21__py3-none-any.whl → 0.2.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/__main__.py +266 -0
- retracesoftware/autoenable.py +53 -0
- retracesoftware/config.json +19 -295
- retracesoftware/install/config.py +6 -0
- retracesoftware/install/edgecases.py +23 -1
- retracesoftware/install/patcher.py +93 -649
- retracesoftware/install/patchfindspec.py +117 -0
- retracesoftware/install/phases.py +338 -0
- retracesoftware/install/record.py +97 -37
- retracesoftware/install/replace.py +28 -0
- retracesoftware/install/replay.py +55 -11
- retracesoftware/install/tracer.py +87 -77
- retracesoftware/modules.toml +384 -0
- retracesoftware/proxy/messagestream.py +204 -0
- retracesoftware/proxy/proxysystem.py +283 -64
- retracesoftware/proxy/proxytype.py +34 -10
- retracesoftware/proxy/record.py +97 -64
- retracesoftware/proxy/replay.py +62 -219
- retracesoftware/proxy/serializer.py +28 -0
- retracesoftware/proxy/startthread.py +40 -0
- retracesoftware/proxy/stubfactory.py +42 -19
- retracesoftware/replay.py +104 -0
- retracesoftware/run.py +373 -0
- retracesoftware/stackdifference.py +133 -0
- {retracesoftware_proxy-0.1.21.dist-info → retracesoftware_proxy-0.2.0.dist-info}/METADATA +2 -1
- retracesoftware_proxy-0.2.0.dist-info/RECORD +41 -0
- retracesoftware_proxy-0.1.21.dist-info/RECORD +0 -29
- {retracesoftware_proxy-0.1.21.dist-info → retracesoftware_proxy-0.2.0.dist-info}/WHEEL +0 -0
- {retracesoftware_proxy-0.1.21.dist-info → retracesoftware_proxy-0.2.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import runpy
|
|
3
|
+
import os
|
|
4
|
+
import argparse
|
|
5
|
+
from typing import Tuple, List
|
|
6
|
+
import retracesoftware.utils as utils
|
|
7
|
+
import retracesoftware.functional as functional
|
|
8
|
+
from retracesoftware.stackdifference import on_stack_difference
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from retracesoftware.proxy.record import RecordProxySystem
|
|
11
|
+
from retracesoftware.proxy.replay import ReplayProxySystem
|
|
12
|
+
import retracesoftware.stream as stream
|
|
13
|
+
from retracesoftware.proxy.startthread import thread_id
|
|
14
|
+
import datetime
|
|
15
|
+
import json
|
|
16
|
+
from shutil import copy2
|
|
17
|
+
import threading
|
|
18
|
+
import time
|
|
19
|
+
import gc
|
|
20
|
+
import hashlib
|
|
21
|
+
|
|
22
|
+
from retracesoftware.run import install, run_with_retrace, ImmutableTypes, thread_states
|
|
23
|
+
|
|
24
|
+
def expand_recording_path(path):
|
|
25
|
+
return datetime.datetime.now().strftime(path.format(pid = os.getpid()))
|
|
26
|
+
|
|
27
|
+
def load_json(file):
|
|
28
|
+
with open(file, "r", encoding="utf-8") as f:
|
|
29
|
+
return json.load(f)
|
|
30
|
+
|
|
31
|
+
def dump_as_json(path, obj):
|
|
32
|
+
with open(path, 'w') as f:
|
|
33
|
+
json.dump(obj, f, indent=2)
|
|
34
|
+
|
|
35
|
+
vscode_workspace = {
|
|
36
|
+
"folders": [{ 'path': '.' }],
|
|
37
|
+
"settings": {
|
|
38
|
+
"python.defaultInterpreterPath": sys.executable,
|
|
39
|
+
},
|
|
40
|
+
"launch": {
|
|
41
|
+
"version": "0.2.0",
|
|
42
|
+
"configurations": [{
|
|
43
|
+
"name": "replay",
|
|
44
|
+
"type": "debugpy",
|
|
45
|
+
"request": "launch",
|
|
46
|
+
|
|
47
|
+
"python": sys.executable,
|
|
48
|
+
"module": "retracesoftware",
|
|
49
|
+
"args": [
|
|
50
|
+
"--recording", "..",
|
|
51
|
+
"--skip_weakref_callbacks",
|
|
52
|
+
"--read_timeout", "1000"
|
|
53
|
+
],
|
|
54
|
+
|
|
55
|
+
"cwd": "${workspaceFolder}/run",
|
|
56
|
+
"console": "integratedTerminal",
|
|
57
|
+
"justMyCode": False
|
|
58
|
+
}]
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
def scriptname(argv):
|
|
63
|
+
return argv[1] if argv[0] == "-m" else argv[0]
|
|
64
|
+
|
|
65
|
+
def collector(multiplier):
|
|
66
|
+
collect_gen = utils.CollectPred(multiplier = multiplier)
|
|
67
|
+
|
|
68
|
+
return functional.lazy(functional.sequence(collect_gen, functional.when_not_none(gc.collect)))
|
|
69
|
+
|
|
70
|
+
def file_md5(path):
|
|
71
|
+
return hashlib.md5(path.read_bytes()).hexdigest()
|
|
72
|
+
|
|
73
|
+
def checksum(path):
|
|
74
|
+
return file_md5(path) if path.is_file() else {entry.name: checksum(entry) for entry in path.iterdir() if entry.name != '__pycache__'}
|
|
75
|
+
|
|
76
|
+
def retrace_extension_paths():
|
|
77
|
+
names = ['retracesoftware_utils', 'retracesoftware_functional', 'retracesoftware_stream']
|
|
78
|
+
return {name: Path(sys.modules[name].__file__) for name in names}
|
|
79
|
+
|
|
80
|
+
def retrace_module_paths():
|
|
81
|
+
paths = retrace_extension_paths()
|
|
82
|
+
paths['retracesoftware'] = Path(sys.modules['retracesoftware'].__file__).parent
|
|
83
|
+
return paths
|
|
84
|
+
|
|
85
|
+
def checksums():
|
|
86
|
+
return {name: checksum(path) for name, path in retrace_module_paths().items()}
|
|
87
|
+
|
|
88
|
+
def record(options, args):
|
|
89
|
+
|
|
90
|
+
path = Path(expand_recording_path(options.recording))
|
|
91
|
+
# ensure the path exists
|
|
92
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
93
|
+
|
|
94
|
+
# write various recording files to directory
|
|
95
|
+
dump_as_json(path / 'settings.json', {
|
|
96
|
+
'argv': args,
|
|
97
|
+
'magic_markers': options.magic_markers,
|
|
98
|
+
'trace_inputs': options.trace_inputs,
|
|
99
|
+
'trace_shutdown': options.trace_shutdown,
|
|
100
|
+
'env': dict(os.environ),
|
|
101
|
+
'python_version': sys.version,
|
|
102
|
+
'md5_checksums': checksums(),
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
rundir = path / 'run'
|
|
106
|
+
rundir.mkdir(exist_ok=True)
|
|
107
|
+
|
|
108
|
+
script = Path(scriptname(args))
|
|
109
|
+
if script.exists():
|
|
110
|
+
copy2(script, rundir)
|
|
111
|
+
|
|
112
|
+
dump_as_json(path / 'replay.code-workspace', vscode_workspace)
|
|
113
|
+
|
|
114
|
+
with stream.writer(path = path / 'trace.bin',
|
|
115
|
+
thread = thread_id,
|
|
116
|
+
verbose = options.verbose,
|
|
117
|
+
stacktraces = options.stacktraces,
|
|
118
|
+
magic_markers = options.magic_markers) as writer:
|
|
119
|
+
|
|
120
|
+
# flusher = threading.Timer(5.0, periodic_task)
|
|
121
|
+
# flusher.start()
|
|
122
|
+
|
|
123
|
+
writer.exclude_from_stacktrace(record)
|
|
124
|
+
writer.exclude_from_stacktrace(main)
|
|
125
|
+
|
|
126
|
+
thread_state = utils.ThreadState(*thread_states)
|
|
127
|
+
|
|
128
|
+
tracing_config = {}
|
|
129
|
+
|
|
130
|
+
multiplier = 2
|
|
131
|
+
gc.set_threshold(*map(lambda x: x * multiplier, gc.get_threshold()))
|
|
132
|
+
|
|
133
|
+
system = RecordProxySystem(
|
|
134
|
+
writer = writer,
|
|
135
|
+
thread_state = thread_state,
|
|
136
|
+
immutable_types = ImmutableTypes(),
|
|
137
|
+
tracing_config = tracing_config,
|
|
138
|
+
maybe_collect = collector(multiplier = multiplier),
|
|
139
|
+
traceargs = options.trace_inputs)
|
|
140
|
+
|
|
141
|
+
# force a full collection
|
|
142
|
+
install(system)
|
|
143
|
+
|
|
144
|
+
gc.collect()
|
|
145
|
+
gc.callbacks.append(system.on_gc_event)
|
|
146
|
+
|
|
147
|
+
run_with_retrace(system, args, options.trace_shutdown)
|
|
148
|
+
|
|
149
|
+
gc.callbacks.remove(system.on_gc_event)
|
|
150
|
+
|
|
151
|
+
def replay(args):
|
|
152
|
+
path = Path(args.recording)
|
|
153
|
+
|
|
154
|
+
if not path.exists():
|
|
155
|
+
raise Exception(f"Recording path: {path} does not exist")
|
|
156
|
+
|
|
157
|
+
settings = load_json(path / "settings.json")
|
|
158
|
+
|
|
159
|
+
if settings['md5_checksums'] != checksums():
|
|
160
|
+
raise Exception("Checksums for Retrace do not match, cannot run replay with different version of retrace to record")
|
|
161
|
+
|
|
162
|
+
if settings['python_version'] != sys.version:
|
|
163
|
+
raise Exception("Python version does not match, cannot run replay with different version of Python to record")
|
|
164
|
+
|
|
165
|
+
os.environ.update(settings['env'])
|
|
166
|
+
|
|
167
|
+
thread_state = utils.ThreadState(*thread_states)
|
|
168
|
+
|
|
169
|
+
# with stream.reader(path = path / 'trace.bin',
|
|
170
|
+
# thread = thread_id,
|
|
171
|
+
# timeout_seconds = args.timeout,
|
|
172
|
+
# verbose = args.verbose,
|
|
173
|
+
# on_stack_difference = thread_state.wrap('disabled', on_stack_difference),
|
|
174
|
+
# magic_markers = settings['magic_markers']) as reader:
|
|
175
|
+
|
|
176
|
+
with stream.reader1(path = path / 'trace.bin',
|
|
177
|
+
read_timeout = args.read_timeout,
|
|
178
|
+
verbose = args.verbose,
|
|
179
|
+
magic_markers = settings['magic_markers']) as reader:
|
|
180
|
+
|
|
181
|
+
tracing_config = {}
|
|
182
|
+
|
|
183
|
+
system = ReplayProxySystem(
|
|
184
|
+
reader = reader,
|
|
185
|
+
thread_state = thread_state,
|
|
186
|
+
immutable_types = ImmutableTypes(),
|
|
187
|
+
tracing_config = tracing_config,
|
|
188
|
+
traceargs = settings['trace_inputs'],
|
|
189
|
+
verbose = args.verbose,
|
|
190
|
+
skip_weakref_callbacks = args.skip_weakref_callbacks)
|
|
191
|
+
|
|
192
|
+
install(system)
|
|
193
|
+
|
|
194
|
+
gc.collect()
|
|
195
|
+
gc.disable()
|
|
196
|
+
|
|
197
|
+
run_with_retrace(system, settings['argv'], settings['trace_shutdown'])
|
|
198
|
+
|
|
199
|
+
def main():
|
|
200
|
+
parser = argparse.ArgumentParser(
|
|
201
|
+
prog="python -m retracesoftware",
|
|
202
|
+
description="Run a Python module with debugging, logging, etc."
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
parser.add_argument(
|
|
206
|
+
'--verbose',
|
|
207
|
+
action='store_true',
|
|
208
|
+
help='Enable verbose output'
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
parser.add_argument(
|
|
212
|
+
'--recording', # or '-r'
|
|
213
|
+
type = str, # ensures it's a string (optional, but safe)
|
|
214
|
+
default = '.', # default value if not provided
|
|
215
|
+
help = 'the directory to place the recording files'
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
if '--' in sys.argv:
|
|
219
|
+
parser.add_argument(
|
|
220
|
+
'--stacktraces',
|
|
221
|
+
action='store_true',
|
|
222
|
+
help='Capture stacktrace for every event'
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
parser.add_argument(
|
|
226
|
+
'--magic_markers',
|
|
227
|
+
action='store_true',
|
|
228
|
+
help='Write magic markers to tracefile, used for debugging'
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
parser.add_argument(
|
|
232
|
+
'--trace_shutdown',
|
|
233
|
+
action='store_true',
|
|
234
|
+
help='Whether to trace system shutdown and cleanup hooks'
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
parser.add_argument(
|
|
238
|
+
'--trace_inputs',
|
|
239
|
+
action='store_true',
|
|
240
|
+
help='Whether to write call parameters, used for debugging'
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
parser.add_argument('rest', nargs = argparse.REMAINDER, help='target application and arguments')
|
|
244
|
+
|
|
245
|
+
args = parser.parse_args()
|
|
246
|
+
|
|
247
|
+
record(args, args.rest[1:])
|
|
248
|
+
else:
|
|
249
|
+
|
|
250
|
+
parser.add_argument(
|
|
251
|
+
'--skip_weakref_callbacks',
|
|
252
|
+
action='store_true',
|
|
253
|
+
help = 'whether to disable retrace in weakref callbacks on replay'
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
parser.add_argument(
|
|
257
|
+
'--read_timeout', # or '-r'
|
|
258
|
+
type = int, # ensures it's a string (optional, but safe)
|
|
259
|
+
default = 1000, # default value if not provided
|
|
260
|
+
help = 'timeout in millseconds for incomplete read of element to timeout'
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
replay(parser.parse_args())
|
|
264
|
+
|
|
265
|
+
if __name__ == "__main__":
|
|
266
|
+
main()
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
if __name__ == "__main__":
|
|
2
|
+
import sysconfig
|
|
3
|
+
import pathlib
|
|
4
|
+
|
|
5
|
+
# 'purelib' is the name of the directory where non-platform-specific modules are installed.
|
|
6
|
+
file = pathlib.Path(sysconfig.get_paths()["purelib"]) / 'retrace.pth'
|
|
7
|
+
file.write_text('import retracesoftware.autoenable;', encoding='utf-8')
|
|
8
|
+
|
|
9
|
+
print(f'Retrace autoinstall enabled by creating: {file}')
|
|
10
|
+
else:
|
|
11
|
+
import os
|
|
12
|
+
|
|
13
|
+
def is_true(name):
|
|
14
|
+
if name in os.environ:
|
|
15
|
+
return os.environ[name].lower() in {'true', '1', 't', 'y', 'yes'}
|
|
16
|
+
else:
|
|
17
|
+
return False
|
|
18
|
+
|
|
19
|
+
def is_running_retrace():
|
|
20
|
+
return sys.orig_argv[1] == '-m' and sys.orig_argv[2].startswith('retracesoftware')
|
|
21
|
+
|
|
22
|
+
# only do anything is the RETRACE env variable is set
|
|
23
|
+
if is_true('RETRACE'):
|
|
24
|
+
import sys
|
|
25
|
+
|
|
26
|
+
if not is_running_retrace():
|
|
27
|
+
|
|
28
|
+
new_argv = [sys.orig_argv[0], '-m', 'retracesoftware']
|
|
29
|
+
|
|
30
|
+
if is_true('RETRACE_VERBOSE'):
|
|
31
|
+
new_argv.append('--verbose')
|
|
32
|
+
|
|
33
|
+
if 'RETRACE_RECORDING_PATH' in os.environ:
|
|
34
|
+
new_argv.append('--recording')
|
|
35
|
+
new_argv.append(os.environ['RETRACE_RECORDING_PATH'])
|
|
36
|
+
|
|
37
|
+
if is_true('RETRACE_STACKTRACES'):
|
|
38
|
+
new_argv.append('--stacktraces')
|
|
39
|
+
|
|
40
|
+
if is_true('RETRACE_SHUTDOWN'):
|
|
41
|
+
new_argv.append('--trace_shutdown')
|
|
42
|
+
|
|
43
|
+
if is_true('RETRACE_MAGIC_MARKERS'):
|
|
44
|
+
new_argv.append('--magic_markers')
|
|
45
|
+
|
|
46
|
+
if is_true('RETRACE_TRACE_INPUTS'):
|
|
47
|
+
new_argv.append('--trace_inputs')
|
|
48
|
+
|
|
49
|
+
new_argv.append('--')
|
|
50
|
+
new_argv.extend(sys.orig_argv[1:])
|
|
51
|
+
|
|
52
|
+
# print(f'Running: {new_argv}')
|
|
53
|
+
os.execv(sys.executable, new_argv)
|
retracesoftware/config.json
CHANGED
|
@@ -4,15 +4,18 @@
|
|
|
4
4
|
"internal", {"comment": "Default state when retrace is disabled for a thread"},
|
|
5
5
|
"external", {"comment": "When target thread is running outside the python system to be recorded"},
|
|
6
6
|
"retrace", {"comment": "When target thread is running outside the retrace system"},
|
|
7
|
+
"importing", {"comment": "When target thread is running outside the retrace system"},
|
|
7
8
|
"gc", {"comment": "When the target thread is running inside the pyton garbage collector"}
|
|
8
9
|
],
|
|
9
10
|
|
|
11
|
+
"trace_calls": false,
|
|
12
|
+
|
|
10
13
|
"recording_path": "recordings/%Y%m%d_%H%M%S_%f",
|
|
11
14
|
|
|
12
15
|
"tracing_levels": {
|
|
13
16
|
"none": [],
|
|
14
17
|
"all": [
|
|
15
|
-
"tracecalls",
|
|
18
|
+
"tracecalls.call",
|
|
16
19
|
"proxy.ext.disabled.call",
|
|
17
20
|
"proxy.int.disabled.call",
|
|
18
21
|
"proxy.wrapping.new",
|
|
@@ -36,6 +39,7 @@
|
|
|
36
39
|
"install.module.phase.results"
|
|
37
40
|
],
|
|
38
41
|
"debug": [
|
|
42
|
+
"tracecalls.call",
|
|
39
43
|
"proxy.wrapping.new",
|
|
40
44
|
"proxy.wrapping.new.method",
|
|
41
45
|
"proxy.int_to_ext.stack",
|
|
@@ -55,7 +59,8 @@
|
|
|
55
59
|
"install.module",
|
|
56
60
|
"install.module.phase",
|
|
57
61
|
"install.module.phase.results"
|
|
58
|
-
]
|
|
62
|
+
],
|
|
63
|
+
"release": []
|
|
59
64
|
},
|
|
60
65
|
|
|
61
66
|
"default_tracing_level": "debug",
|
|
@@ -68,6 +73,18 @@
|
|
|
68
73
|
],
|
|
69
74
|
|
|
70
75
|
"preload": [
|
|
76
|
+
"logging",
|
|
77
|
+
"http.client",
|
|
78
|
+
"queue",
|
|
79
|
+
"mimetypes",
|
|
80
|
+
"encodings.idna",
|
|
81
|
+
"hmac",
|
|
82
|
+
"ipaddress",
|
|
83
|
+
"tempfile",
|
|
84
|
+
"zipfile",
|
|
85
|
+
"importlib.resources",
|
|
86
|
+
"atexit",
|
|
87
|
+
"weakref"
|
|
71
88
|
],
|
|
72
89
|
|
|
73
90
|
"predicates": {
|
|
@@ -154,298 +171,5 @@
|
|
|
154
171
|
}
|
|
155
172
|
]
|
|
156
173
|
}
|
|
157
|
-
},
|
|
158
|
-
|
|
159
|
-
"modules": {
|
|
160
|
-
"_frozen_importlib_external": {
|
|
161
|
-
"comment": {
|
|
162
|
-
"with_state": {
|
|
163
|
-
"disabled": ["_path_stat"]
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
},
|
|
167
|
-
"_imp": {
|
|
168
|
-
"patch_extension_exec": ["exec_dynamic", "exec_builtin"],
|
|
169
|
-
"comment": {
|
|
170
|
-
"with_state": {
|
|
171
|
-
"internal": ["create_dynamic"]
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
},
|
|
175
|
-
"bdb": {
|
|
176
|
-
"methods_with_state": {
|
|
177
|
-
"disabled": {
|
|
178
|
-
"Bdb": ["trace_dispatch"]
|
|
179
|
-
}
|
|
180
|
-
},
|
|
181
|
-
"with_state": {
|
|
182
|
-
"disabled": ["Bdb.trace_dispatch"]
|
|
183
|
-
}
|
|
184
|
-
},
|
|
185
|
-
|
|
186
|
-
"sys": {
|
|
187
|
-
"with_state": {
|
|
188
|
-
"disabled": ["excepthook"]
|
|
189
|
-
},
|
|
190
|
-
"comment": {
|
|
191
|
-
"with_state_recursive": {
|
|
192
|
-
"disabled": ["settrace", "setprofile"]
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
},
|
|
196
|
-
|
|
197
|
-
"importlib": {
|
|
198
|
-
"with_state": {
|
|
199
|
-
"disabled": ["import_module"]
|
|
200
|
-
}
|
|
201
|
-
},
|
|
202
|
-
|
|
203
|
-
"importlib._bootstrap": {
|
|
204
|
-
"comment": {
|
|
205
|
-
"with_state": {
|
|
206
|
-
"internal": [
|
|
207
|
-
"_load_unlocked",
|
|
208
|
-
"_find_spec",
|
|
209
|
-
"_lock_unlock_module",
|
|
210
|
-
"_get_module_lock"]
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
},
|
|
214
|
-
|
|
215
|
-
"encodings": {
|
|
216
|
-
"with_state": {
|
|
217
|
-
"disabled": ["search_function"]
|
|
218
|
-
}
|
|
219
|
-
},
|
|
220
|
-
"_retrace_utils": {
|
|
221
|
-
"immutable_types": [
|
|
222
|
-
"PyCFunctionProxy"
|
|
223
|
-
]
|
|
224
|
-
},
|
|
225
|
-
"enum": {
|
|
226
|
-
"immutable_types": [
|
|
227
|
-
"Enum"
|
|
228
|
-
]
|
|
229
|
-
},
|
|
230
|
-
"os": {
|
|
231
|
-
"immutable_types": [
|
|
232
|
-
"stat_result",
|
|
233
|
-
"terminal_size",
|
|
234
|
-
"statvfs_result"
|
|
235
|
-
]
|
|
236
|
-
},
|
|
237
|
-
"mmap": {
|
|
238
|
-
"proxy": [
|
|
239
|
-
"mmapXXX"
|
|
240
|
-
]
|
|
241
|
-
},
|
|
242
|
-
"types": {
|
|
243
|
-
"patch_hash": ["FunctionType"],
|
|
244
|
-
"immutable_types": [
|
|
245
|
-
"TracebackType",
|
|
246
|
-
"ModuleType"
|
|
247
|
-
]
|
|
248
|
-
},
|
|
249
|
-
"builtins": {
|
|
250
|
-
"patch_hash": ["object"],
|
|
251
|
-
|
|
252
|
-
"comment": {
|
|
253
|
-
"with_state": {
|
|
254
|
-
"disabled": ["__import__"]
|
|
255
|
-
}
|
|
256
|
-
},
|
|
257
|
-
|
|
258
|
-
"immutable_types": [
|
|
259
|
-
"BaseException",
|
|
260
|
-
"memoryview",
|
|
261
|
-
"int",
|
|
262
|
-
"float",
|
|
263
|
-
"complex",
|
|
264
|
-
"str",
|
|
265
|
-
"bytes",
|
|
266
|
-
"bool",
|
|
267
|
-
"bytearray",
|
|
268
|
-
"type",
|
|
269
|
-
"slice"],
|
|
270
|
-
|
|
271
|
-
"patch_exec": "exec"
|
|
272
|
-
},
|
|
273
|
-
"_thread": {
|
|
274
|
-
"proxy": [
|
|
275
|
-
"allocate",
|
|
276
|
-
"allocate_lock",
|
|
277
|
-
"RLock"
|
|
278
|
-
],
|
|
279
|
-
"patch_start_new_thread": ["start_new_thread", "start_new"]
|
|
280
|
-
},
|
|
281
|
-
"_datetime": {
|
|
282
|
-
"immutable_types": [
|
|
283
|
-
"datetime",
|
|
284
|
-
"tzinfo",
|
|
285
|
-
"timezone"
|
|
286
|
-
],
|
|
287
|
-
"proxy_type_attributes": {
|
|
288
|
-
"datetime": [
|
|
289
|
-
"now",
|
|
290
|
-
"utcnow",
|
|
291
|
-
"today"
|
|
292
|
-
]
|
|
293
|
-
}
|
|
294
|
-
},
|
|
295
|
-
"time": {
|
|
296
|
-
"immutable_types": [
|
|
297
|
-
"struct_time"
|
|
298
|
-
],
|
|
299
|
-
"proxy": [
|
|
300
|
-
"perf_counter",
|
|
301
|
-
"time",
|
|
302
|
-
"gmtime",
|
|
303
|
-
"localtime",
|
|
304
|
-
"monotonic",
|
|
305
|
-
"monotonic_ns",
|
|
306
|
-
"time_ns"
|
|
307
|
-
]
|
|
308
|
-
},
|
|
309
|
-
"io": {
|
|
310
|
-
"comment": {
|
|
311
|
-
"proxy": ["open_code", "open"]
|
|
312
|
-
},
|
|
313
|
-
"path_predicates": {
|
|
314
|
-
"open_code": "path",
|
|
315
|
-
"open": "file"
|
|
316
|
-
},
|
|
317
|
-
"patchtype": {
|
|
318
|
-
"FileIO": {
|
|
319
|
-
"readinfo": "retracesoftware.install.edgecases.readinto"
|
|
320
|
-
},
|
|
321
|
-
"BufferedReader": {
|
|
322
|
-
"readinfo": "retracesoftware.install.edgecases.readinto"
|
|
323
|
-
},
|
|
324
|
-
"BufferedRandom": {
|
|
325
|
-
"readinfo": "retracesoftware.install.edgecases.readinto"
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
},
|
|
329
|
-
"pathlib": {
|
|
330
|
-
"immutable_types": [
|
|
331
|
-
"PosixPath"
|
|
332
|
-
]
|
|
333
|
-
},
|
|
334
|
-
"posix": {
|
|
335
|
-
"immutable_types": [
|
|
336
|
-
"times_result",
|
|
337
|
-
"statvfs_result",
|
|
338
|
-
"uname_result",
|
|
339
|
-
"stat_result",
|
|
340
|
-
"terminal_size"
|
|
341
|
-
],
|
|
342
|
-
|
|
343
|
-
"proxy_all_except": [
|
|
344
|
-
"fork",
|
|
345
|
-
"register_at_fork",
|
|
346
|
-
"basename",
|
|
347
|
-
"readlink",
|
|
348
|
-
"strerror",
|
|
349
|
-
"listdir",
|
|
350
|
-
"_path_normpath",
|
|
351
|
-
"DirEntry"
|
|
352
|
-
],
|
|
353
|
-
|
|
354
|
-
"with_state_recursive0": {
|
|
355
|
-
"disabled": ["register_at_fork"]
|
|
356
|
-
},
|
|
357
|
-
|
|
358
|
-
"wrappers": {
|
|
359
|
-
"fork_exec": "retracesoftware.install.edgecases.fork_exec",
|
|
360
|
-
"posix_spawn": "retracesoftware.install.edgecases.posix_spawn"
|
|
361
|
-
}
|
|
362
|
-
},
|
|
363
|
-
"_posixsubprocess": {
|
|
364
|
-
"proxy_all_except": []
|
|
365
|
-
},
|
|
366
|
-
"fcntl": {
|
|
367
|
-
"proxy_all_except": []
|
|
368
|
-
},
|
|
369
|
-
"_signal": {
|
|
370
|
-
"proxy_all_except": []
|
|
371
|
-
},
|
|
372
|
-
"_socket": {
|
|
373
|
-
"proxy_all_except": [
|
|
374
|
-
"CAPI"
|
|
375
|
-
],
|
|
376
|
-
"immutable_types": [
|
|
377
|
-
"error",
|
|
378
|
-
"herror",
|
|
379
|
-
"gaierror",
|
|
380
|
-
"timeout"
|
|
381
|
-
],
|
|
382
|
-
"patchtype": {
|
|
383
|
-
"socket": {
|
|
384
|
-
"recvfrom_into": "retracesoftware.install.edgecases.recvfrom_into",
|
|
385
|
-
"recv_into": "retracesoftware.install.edgecases.recv_into",
|
|
386
|
-
"recvmsg_into": "retracesoftware.install.edgecases.recvmsg_into"
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
},
|
|
390
|
-
"select": {
|
|
391
|
-
"proxy_all_except": []
|
|
392
|
-
},
|
|
393
|
-
"_sqlite3": {
|
|
394
|
-
"proxy_all_except": []
|
|
395
|
-
},
|
|
396
|
-
"_ssl": {
|
|
397
|
-
"proxy_all_except": [],
|
|
398
|
-
|
|
399
|
-
"proxy_functions": ".*",
|
|
400
|
-
"comment": {
|
|
401
|
-
"proxy_types": [
|
|
402
|
-
"MemoryBIO",
|
|
403
|
-
"_SSLSocket",
|
|
404
|
-
"_SSLContext"
|
|
405
|
-
],
|
|
406
|
-
"wrap": {
|
|
407
|
-
"_SSLSocket.write": "write"
|
|
408
|
-
}
|
|
409
|
-
},
|
|
410
|
-
"patchtype": {
|
|
411
|
-
"_SSLSocket": {
|
|
412
|
-
"read": "retracesoftware.install.edgecases.read"
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
},
|
|
416
|
-
"_random": {
|
|
417
|
-
"proxy_all_except": [],
|
|
418
|
-
"proxy_types": [
|
|
419
|
-
"Random"
|
|
420
|
-
],
|
|
421
|
-
"proxy_functions": ".*"
|
|
422
|
-
},
|
|
423
|
-
"_multiprocessing": {
|
|
424
|
-
"proxy_functions": ".*"
|
|
425
|
-
},
|
|
426
|
-
"multiprocessing.context": {
|
|
427
|
-
"proxy": ["_default_context"]
|
|
428
|
-
},
|
|
429
|
-
|
|
430
|
-
"PIL._imaging": {
|
|
431
|
-
"proxy_all_except": ["map_buffer"]
|
|
432
|
-
},
|
|
433
|
-
"psycopg2._psycopg": {
|
|
434
|
-
"proxy_all_except": ["Error"]
|
|
435
|
-
},
|
|
436
|
-
|
|
437
|
-
"_weakrefset": {
|
|
438
|
-
"sync_types": ["WeakSet", "_IterationGuard"]
|
|
439
|
-
},
|
|
440
|
-
"_collections": {
|
|
441
|
-
"sync_types": [
|
|
442
|
-
"deque"
|
|
443
|
-
]
|
|
444
|
-
},
|
|
445
|
-
"_queue": {
|
|
446
|
-
"sync_types": [
|
|
447
|
-
"SimpleQueue"
|
|
448
|
-
]
|
|
449
|
-
}
|
|
450
174
|
}
|
|
451
175
|
}
|
|
@@ -2,6 +2,7 @@ import pkgutil
|
|
|
2
2
|
import json
|
|
3
3
|
import os
|
|
4
4
|
import datetime
|
|
5
|
+
import tomllib
|
|
5
6
|
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
|
|
@@ -36,6 +37,11 @@ def recording_path(config):
|
|
|
36
37
|
os.environ['RETRACE_RECORDING_PATH'] = str(recording_path)
|
|
37
38
|
return recording_path
|
|
38
39
|
|
|
40
|
+
def load_module_config(filename):
|
|
41
|
+
data = pkgutil.get_data("retracesoftware", filename)
|
|
42
|
+
assert data is not None
|
|
43
|
+
return tomllib.loads(data.decode("utf-8"))
|
|
44
|
+
|
|
39
45
|
def load_config(filename):
|
|
40
46
|
|
|
41
47
|
data = pkgutil.get_data("retracesoftware", filename)
|