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
retracesoftware/proxy/record.py
CHANGED
|
@@ -7,6 +7,7 @@ from retracesoftware.proxy.proxytype import *
|
|
|
7
7
|
from retracesoftware.proxy.proxysystem import ProxySystem
|
|
8
8
|
from retracesoftware.proxy.thread import write_thread_switch, ThreadSwitch, thread_id
|
|
9
9
|
from retracesoftware.install.tracer import Tracer
|
|
10
|
+
from retracesoftware.install.patchfindspec import patch_find_spec
|
|
10
11
|
from retracesoftware.proxy.stubfactory import StubRef, ExtendedRef
|
|
11
12
|
from retracesoftware.proxy.globalref import GlobalRef
|
|
12
13
|
|
|
@@ -15,11 +16,11 @@ import os
|
|
|
15
16
|
import types
|
|
16
17
|
import gc
|
|
17
18
|
|
|
18
|
-
class Placeholder:
|
|
19
|
-
|
|
19
|
+
# class Placeholder:
|
|
20
|
+
# __slots__ = ['id', '__weakref__']
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
# def __init__(self, id):
|
|
23
|
+
# self.id = id
|
|
23
24
|
|
|
24
25
|
def keys_where_value(pred, dict):
|
|
25
26
|
for key,value in dict.items():
|
|
@@ -50,13 +51,14 @@ def resolveable_name(obj):
|
|
|
50
51
|
# when
|
|
51
52
|
class RecordProxySystem(ProxySystem):
|
|
52
53
|
|
|
53
|
-
def bind(self, obj):
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
# def bind(self, obj):
|
|
55
|
+
# self.bindings[obj] = self.writer.handle(Placeholder(self.next_placeholder_id))
|
|
56
|
+
# self.writer(self.bindings[obj])
|
|
57
|
+
# self.next_placeholder_id += 1
|
|
57
58
|
|
|
58
59
|
def before_fork(self):
|
|
59
|
-
self.writer.
|
|
60
|
+
self.writer.keep_open = False
|
|
61
|
+
# self.writer.close()
|
|
60
62
|
super().before_fork()
|
|
61
63
|
# self.writer.path = self.dynamic_path
|
|
62
64
|
|
|
@@ -64,107 +66,136 @@ class RecordProxySystem(ProxySystem):
|
|
|
64
66
|
new_path = self.new_child_path(self.writer.path)
|
|
65
67
|
new_path.parent.mkdir()
|
|
66
68
|
self.writer.path = new_path
|
|
69
|
+
self.writer.keep_open = True
|
|
67
70
|
super().after_fork_in_child()
|
|
68
71
|
|
|
69
72
|
def after_fork_in_parent(self):
|
|
70
73
|
super().after_fork_in_parent()
|
|
71
74
|
self.thread_state.value = self.saved_thread_state
|
|
72
|
-
self.writer.
|
|
75
|
+
self.writer.keep_open = True
|
|
76
|
+
# self.writer.reopen()
|
|
73
77
|
|
|
74
78
|
def set_thread_id(self, id):
|
|
75
79
|
utils.set_thread_id(self.writer.handle(ThreadSwitch(id)))
|
|
76
80
|
# utils.set_thread_id(id)
|
|
77
81
|
|
|
78
|
-
def is_entry_frame(self, frame):
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
82
|
+
# def is_entry_frame(self, frame):
|
|
83
|
+
# if super().is_entry_frame(frame):
|
|
84
|
+
# self.write_main_path(frame.function.__code__.co_filename)
|
|
85
|
+
# return True
|
|
86
|
+
# return False
|
|
87
|
+
|
|
88
|
+
def create_from_external(self, obj):
|
|
89
|
+
# class of obj is bound
|
|
90
|
+
# can now write type(obj)
|
|
91
|
+
|
|
92
|
+
self.bind(obj)
|
|
93
|
+
|
|
94
|
+
breakpoint()
|
|
95
|
+
|
|
96
|
+
def patch_type(self, cls):
|
|
97
|
+
|
|
98
|
+
patched = super().patch_type(cls)
|
|
99
|
+
|
|
100
|
+
self.writer.type_serializer[patched] = functional.side_effect(self.writer.ext_bind)
|
|
101
|
+
|
|
102
|
+
return patched
|
|
103
|
+
|
|
104
|
+
def exclude_from_stacktrace(self, func):
|
|
105
|
+
self.writer.exclude_from_stacktrace(func)
|
|
83
106
|
|
|
84
|
-
def
|
|
107
|
+
def on_gc_event(self, phase, info):
|
|
108
|
+
if phase == 'start':
|
|
109
|
+
self.writer.stacktraces = False
|
|
110
|
+
self.on_start_collect(info['generation'])
|
|
111
|
+
|
|
112
|
+
elif phase == 'stop':
|
|
113
|
+
self.on_end_collect()
|
|
114
|
+
self.writer.stacktraces = self.stacktraces
|
|
115
|
+
|
|
116
|
+
def __init__(self, thread_state,
|
|
117
|
+
writer,
|
|
85
118
|
immutable_types,
|
|
86
119
|
tracing_config,
|
|
87
|
-
|
|
88
|
-
|
|
120
|
+
maybe_collect,
|
|
121
|
+
traceargs):
|
|
89
122
|
|
|
90
123
|
self.fork_counter = 0
|
|
91
|
-
self.write_main_path = write_main_path
|
|
92
|
-
|
|
124
|
+
# self.write_main_path = write_main_path
|
|
125
|
+
|
|
93
126
|
self.getpid = thread_state.wrap(
|
|
94
127
|
desired_state = 'disabled', function = os.getpid)
|
|
95
128
|
|
|
96
129
|
self.pid = self.getpid()
|
|
97
130
|
|
|
98
|
-
self.writer =
|
|
99
|
-
|
|
100
|
-
# def on_switch():
|
|
101
|
-
# print(f"On thread switch!!!")
|
|
102
|
-
# # print(f"On thread switch!!!: {utils.thread_id().id}")
|
|
103
|
-
# # utils.sigtrap(utils.thread_id())
|
|
104
|
-
# self.writer(utils.thread_id())
|
|
105
|
-
# # utils.sigtrap(utils.thread_id())
|
|
106
|
-
|
|
107
|
-
# write = utils.observer(on_call = utils.thread_switch_monitor(on_switch), function = self.writer)
|
|
108
|
-
|
|
109
|
-
# w = self.writer.handle('TRACE')
|
|
110
|
-
# def trace_writer(*args):
|
|
111
|
-
# print(f'Trace: {args}')
|
|
112
|
-
# w(*args)
|
|
113
|
-
|
|
114
|
-
self.extended_types = {}
|
|
115
|
-
self.bindings = utils.id_dict()
|
|
116
|
-
self.next_placeholder_id = 0
|
|
131
|
+
self.writer = writer
|
|
117
132
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
# def on_switch():
|
|
121
|
-
# print("On thread switch!!!")
|
|
122
|
-
# # utils.sigtrap(utils.thread_id())
|
|
123
|
-
# utils.thread_id()()
|
|
124
|
-
# # utils.sigtrap(utils.thread_id())
|
|
133
|
+
self.stacktraces = self.writer.stacktraces
|
|
125
134
|
|
|
126
|
-
|
|
135
|
+
if self.stacktraces:
|
|
136
|
+
def set(status):
|
|
137
|
+
self.writer.stacktraces = status
|
|
127
138
|
|
|
128
|
-
|
|
139
|
+
self.wrap_weakref_callback = functional.sequence(
|
|
140
|
+
self.wrap_weakref_callback,
|
|
141
|
+
lambda callback:
|
|
142
|
+
utils.observer(
|
|
143
|
+
on_call = functional.lazy(set, False),
|
|
144
|
+
on_result = functional.lazy(set, True),
|
|
145
|
+
on_error = functional.lazy(set, True),
|
|
146
|
+
function = callback))
|
|
147
|
+
|
|
148
|
+
self.exclude_from_stacktrace(RecordProxySystem.patch_type)
|
|
149
|
+
self.exclude_from_stacktrace(patch_find_spec.__call__)
|
|
129
150
|
|
|
130
|
-
|
|
151
|
+
self.extended_types = {}
|
|
131
152
|
|
|
132
153
|
sync_handle = self.writer.handle('SYNC')
|
|
133
154
|
|
|
155
|
+
self.on_ext_call = functional.lazy(utils.runall(maybe_collect, sync_handle) if maybe_collect else sync_handle)
|
|
156
|
+
|
|
134
157
|
write_sync = thread_state.dispatch(utils.noop, internal = functional.lazy(sync_handle))
|
|
135
158
|
|
|
136
159
|
self.sync = lambda function: \
|
|
137
160
|
utils.observer(on_call = write_sync, function = function)
|
|
138
|
-
|
|
139
161
|
error = self.writer.handle('ERROR')
|
|
140
162
|
|
|
141
163
|
def write_error(cls, val, traceback):
|
|
164
|
+
assert isinstance(val, BaseException)
|
|
142
165
|
error(cls, val)
|
|
143
166
|
|
|
144
|
-
# self.set_thread_id = functional.partial(utils.set_thread_id, self.writer)
|
|
145
|
-
|
|
146
|
-
# w = self.writer.handle('TRACE')
|
|
147
|
-
# def foo(name, *args):
|
|
148
|
-
# print(f'writing: {self.writer.messages_written} {name} {args}')
|
|
149
|
-
# w(self.writer.messages_written, name, *args)
|
|
150
|
-
|
|
151
|
-
# tracer = Tracer(tracing_config, writer = foo)
|
|
152
167
|
tracer = Tracer(tracing_config, writer = self.writer.handle('TRACE'))
|
|
153
168
|
|
|
154
|
-
|
|
169
|
+
self.writer.exclude_from_stacktrace(write_error)
|
|
170
|
+
# self.writer.exclude_from_stacktrace(Tracer.write_call)
|
|
155
171
|
|
|
156
|
-
self.on_int_call =
|
|
172
|
+
self.on_int_call = self.writer.handle('CALL')
|
|
157
173
|
|
|
158
|
-
|
|
174
|
+
# write_new_ref = self.writer.handle('RESULT')
|
|
175
|
+
|
|
176
|
+
self.on_ext_result = self.writer.handle('RESULT')
|
|
159
177
|
|
|
160
178
|
self.on_ext_error = write_error
|
|
161
179
|
|
|
162
|
-
self.ext_apply = self.int_apply = functional.apply
|
|
163
|
-
|
|
164
180
|
self.writer.type_serializer[types.ModuleType] = GlobalRef
|
|
165
181
|
|
|
182
|
+
self.bind = self.writer.bind
|
|
183
|
+
|
|
184
|
+
self.create_from_external = self.writer.ext_bind
|
|
185
|
+
|
|
186
|
+
self.write_trace = self.writer.handle('TRACER')
|
|
187
|
+
|
|
188
|
+
self.checkpoint = self.writer.handle('CHECKPOINT')
|
|
189
|
+
|
|
190
|
+
self.on_weakref_callback_start = functional.lazy(self.writer.handle('ON_WEAKREF_CALLBACK_START'))
|
|
191
|
+
self.on_weakref_callback_end = functional.lazy(self.writer.handle('ON_WEAKREF_CALLBACK_END'))
|
|
192
|
+
|
|
193
|
+
self.on_start_collect = self.writer.handle('ON_START_COLLECT')
|
|
194
|
+
self.on_end_collect = self.writer.handle('ON_END_COLLECT')
|
|
195
|
+
|
|
166
196
|
super().__init__(thread_state = thread_state,
|
|
167
197
|
tracer = tracer,
|
|
198
|
+
traceargs = traceargs,
|
|
168
199
|
immutable_types = immutable_types)
|
|
169
200
|
|
|
170
201
|
def ext_proxytype(self, cls):
|
|
@@ -172,7 +203,9 @@ class RecordProxySystem(ProxySystem):
|
|
|
172
203
|
proxytype = super().ext_proxytype(cls)
|
|
173
204
|
|
|
174
205
|
ref = self.writer.handle(StubRef(proxytype))
|
|
175
|
-
|
|
206
|
+
|
|
176
207
|
self.writer.type_serializer[proxytype] = functional.constantly(ref)
|
|
177
208
|
|
|
178
209
|
return proxytype
|
|
210
|
+
|
|
211
|
+
|
retracesoftware/proxy/replay.py
CHANGED
|
@@ -1,146 +1,20 @@
|
|
|
1
1
|
import retracesoftware.functional as functional
|
|
2
2
|
import retracesoftware_utils as utils
|
|
3
|
-
import retracesoftware.stream as stream
|
|
4
3
|
|
|
5
4
|
from retracesoftware.install.tracer import Tracer
|
|
6
5
|
from retracesoftware.proxy.thread import per_thread_messages, thread_id
|
|
6
|
+
from retracesoftware.proxy.messagestream import *
|
|
7
7
|
from retracesoftware.proxy.proxytype import *
|
|
8
8
|
# from retracesoftware.proxy.gateway import gateway_pair
|
|
9
|
-
from retracesoftware.proxy.record import StubRef
|
|
10
|
-
from retracesoftware.proxy.proxysystem import ProxySystem
|
|
11
|
-
from retracesoftware.proxy.stubfactory import StubFactory
|
|
9
|
+
from retracesoftware.proxy.record import StubRef
|
|
10
|
+
from retracesoftware.proxy.proxysystem import ProxySystem
|
|
11
|
+
from retracesoftware.proxy.stubfactory import StubFactory
|
|
12
12
|
from retracesoftware.proxy.globalref import GlobalRef
|
|
13
13
|
|
|
14
14
|
import os
|
|
15
|
-
import weakref
|
|
16
|
-
import traceback
|
|
17
|
-
import pprint
|
|
18
|
-
|
|
19
|
-
from itertools import count, islice
|
|
20
|
-
|
|
21
|
-
# we can have a dummy method descriptor, its has a __name__ and when called, returns the next element
|
|
22
|
-
|
|
23
|
-
# for types, we can patch the __new__ method
|
|
24
|
-
# do it from C and immutable types can be patched too
|
|
25
|
-
# patch the tp_new pointer?
|
|
26
|
-
|
|
27
|
-
class ReplayError(RetraceError):
|
|
28
|
-
pass
|
|
29
|
-
|
|
30
|
-
def count_matching(*lists):
|
|
31
|
-
count = 0
|
|
32
|
-
for slice in zip(*lists):
|
|
33
|
-
if len(set(slice)) == 1:
|
|
34
|
-
count += 1
|
|
35
|
-
else:
|
|
36
|
-
break
|
|
37
|
-
|
|
38
|
-
return count
|
|
39
|
-
|
|
40
|
-
def on_stack_mismatch(last_matching, record, replay):
|
|
41
|
-
# print('Common:')
|
|
42
|
-
# for index, common, replay, record in zip(count(), last_matching_stack, args[0], record):
|
|
43
|
-
# if common == replay == record:
|
|
44
|
-
# print(common)
|
|
45
|
-
if last_matching:
|
|
46
|
-
matching = count_matching(reversed(last_matching),
|
|
47
|
-
reversed(record),
|
|
48
|
-
reversed(replay))
|
|
49
|
-
|
|
50
|
-
print('Common stacktrace:')
|
|
51
|
-
for line in reversed(list(islice(reversed(last_matching), matching))):
|
|
52
|
-
print(line)
|
|
53
|
-
|
|
54
|
-
print('last matching stacktrace:')
|
|
55
|
-
for line in islice(last_matching, 0, len(last_matching) - matching):
|
|
56
|
-
print(line)
|
|
57
|
-
|
|
58
|
-
print('Replay stacktrace:')
|
|
59
|
-
for line in islice(replay, 0, len(replay) - matching):
|
|
60
|
-
print(line)
|
|
61
|
-
|
|
62
|
-
print('Record stacktrace:')
|
|
63
|
-
for line in islice(record, 0, len(record) - matching):
|
|
64
|
-
print(line)
|
|
65
|
-
|
|
66
|
-
print(f'-----------')
|
|
67
|
-
else:
|
|
68
|
-
matching = count_matching(reversed(record), reversed(replay))
|
|
69
|
-
|
|
70
|
-
print('Common stacktrace:')
|
|
71
|
-
for line in reversed(list(islice(reversed(record), matching))):
|
|
72
|
-
print(line)
|
|
73
|
-
|
|
74
|
-
print('Replay stacktrace:')
|
|
75
|
-
for line in islice(replay, 0, len(replay) - matching):
|
|
76
|
-
print(line)
|
|
77
|
-
|
|
78
|
-
print('Record stacktrace:')
|
|
79
|
-
for line in islice(record, 0, len(record) - matching):
|
|
80
|
-
print(line)
|
|
81
|
-
|
|
82
|
-
|
|
83
15
|
|
|
84
16
|
class ReplayProxySystem(ProxySystem):
|
|
85
17
|
|
|
86
|
-
@utils.striptraceback
|
|
87
|
-
def next_result(self):
|
|
88
|
-
try:
|
|
89
|
-
while True:
|
|
90
|
-
next = self.messages()
|
|
91
|
-
|
|
92
|
-
if next == 'CALL':
|
|
93
|
-
func = self.messages()
|
|
94
|
-
args = self.messages()
|
|
95
|
-
kwargs = self.messages()
|
|
96
|
-
|
|
97
|
-
try:
|
|
98
|
-
func(*args, **kwargs)
|
|
99
|
-
except:
|
|
100
|
-
pass
|
|
101
|
-
|
|
102
|
-
elif next == 'RESULT':
|
|
103
|
-
return self.messages()
|
|
104
|
-
|
|
105
|
-
elif next == 'ERROR':
|
|
106
|
-
# breakpoint()
|
|
107
|
-
err_type = self.messages()
|
|
108
|
-
err_value = self.messages()
|
|
109
|
-
utils.raise_exception(err_type, err_value)
|
|
110
|
-
else:
|
|
111
|
-
assert type(next) is not str
|
|
112
|
-
return next
|
|
113
|
-
except TimeoutError:
|
|
114
|
-
print(f'timeout for reader, active thread: {self.reader.active_thread}, next_control: {self.reader.next_control}')
|
|
115
|
-
|
|
116
|
-
for thread,stack in self.reader.stacktraces.items():
|
|
117
|
-
print(f'thread: {thread}')
|
|
118
|
-
|
|
119
|
-
formatted_frames = [
|
|
120
|
-
(frame.filename, frame.lineno, frame.function, frame.code_context[0].strip() if frame.code_context else None)
|
|
121
|
-
for frame in stack
|
|
122
|
-
]
|
|
123
|
-
formatted_trace = traceback.format_list(formatted_frames)
|
|
124
|
-
print("Traceback (most recent call last):\n" + "".join(formatted_trace))
|
|
125
|
-
|
|
126
|
-
utils.sigtrap(None)
|
|
127
|
-
|
|
128
|
-
def bind(self, obj):
|
|
129
|
-
read = self.messages()
|
|
130
|
-
|
|
131
|
-
assert isinstance(read, Placeholder)
|
|
132
|
-
|
|
133
|
-
self.bindings[read] = obj
|
|
134
|
-
|
|
135
|
-
# def dynamic_path(self):
|
|
136
|
-
# if self.getpid() != self.pid:
|
|
137
|
-
# self.pid = self.getpid()
|
|
138
|
-
# # ok we are in child, calculate new path
|
|
139
|
-
# self.path = self.path / f'fork-{self.fork_counter}'
|
|
140
|
-
# self.fork_counter = 0
|
|
141
|
-
|
|
142
|
-
# return self.path
|
|
143
|
-
|
|
144
18
|
def after_fork_in_child(self):
|
|
145
19
|
self.reader.path = self.new_child_path(self.reader.path)
|
|
146
20
|
super().after_fork_in_child()
|
|
@@ -148,11 +22,9 @@ class ReplayProxySystem(ProxySystem):
|
|
|
148
22
|
# def dynamic_ext_proxytype(self, cls):
|
|
149
23
|
# raise Exception('dynamic_ext_proxytype should not be called in replay')
|
|
150
24
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
return super().proxy_function(func)
|
|
25
|
+
@property
|
|
26
|
+
def ext_apply(self):
|
|
27
|
+
return functional.repeatedly(self.next_result)
|
|
156
28
|
|
|
157
29
|
def proxy__new__(self, __new__, *args, **kwargs):
|
|
158
30
|
func = functional.repeatedly(self.next_result)
|
|
@@ -162,45 +34,6 @@ class ReplayProxySystem(ProxySystem):
|
|
|
162
34
|
def basetype(self, cls):
|
|
163
35
|
return self.stub_factory.create_stubtype(StubRef(cls))
|
|
164
36
|
|
|
165
|
-
def readnext(self):
|
|
166
|
-
with self.thread_state.select('disabled'):
|
|
167
|
-
try:
|
|
168
|
-
# obj = self.messages()
|
|
169
|
-
# print(f'read: {obj}')
|
|
170
|
-
# return obj
|
|
171
|
-
return self.messages()
|
|
172
|
-
except Exception as error:
|
|
173
|
-
# print(f'Error reading stream: {error}')
|
|
174
|
-
traceback.print_exc()
|
|
175
|
-
os._exit(1)
|
|
176
|
-
|
|
177
|
-
def read_required(self, required):
|
|
178
|
-
obj = self.readnext()
|
|
179
|
-
if obj != required:
|
|
180
|
-
print('---------------------------------')
|
|
181
|
-
print('last matching stack')
|
|
182
|
-
print('---------------------------------')
|
|
183
|
-
if self.last_matching_stack:
|
|
184
|
-
for line in self.last_matching_stack:
|
|
185
|
-
print(line)
|
|
186
|
-
|
|
187
|
-
print('---------------------------------')
|
|
188
|
-
print(f'Replay: {required}')
|
|
189
|
-
print('---------------------------------')
|
|
190
|
-
for line in utils.stacktrace():
|
|
191
|
-
print(line)
|
|
192
|
-
print('---------------------------------')
|
|
193
|
-
print(f'Record: {obj}')
|
|
194
|
-
print('---------------------------------')
|
|
195
|
-
for i in range(15):
|
|
196
|
-
print(self.readnext())
|
|
197
|
-
|
|
198
|
-
breakpoint()
|
|
199
|
-
os._exit(1)
|
|
200
|
-
raise Exception(f'Expected: {required} but got: {obj}')
|
|
201
|
-
|
|
202
|
-
# self.last_matching_stack = utils.stacktrace()
|
|
203
|
-
|
|
204
37
|
def trace_writer(self, name, *args):
|
|
205
38
|
with self.thread_state.select('disabled'):
|
|
206
39
|
# read = self.messages_read
|
|
@@ -219,7 +52,7 @@ class ReplayProxySystem(ProxySystem):
|
|
|
219
52
|
on_stack_mismatch(
|
|
220
53
|
last_matching = self.last_matching_stack,
|
|
221
54
|
record = record,
|
|
222
|
-
replay = args[0])
|
|
55
|
+
replay = args[0])
|
|
223
56
|
os._exit(1)
|
|
224
57
|
else:
|
|
225
58
|
# print(f'Trace: {self.reader.messages_read} {name} {args}')
|
|
@@ -227,69 +60,79 @@ class ReplayProxySystem(ProxySystem):
|
|
|
227
60
|
self.read_required(arg)
|
|
228
61
|
|
|
229
62
|
def on_thread_exit(self, thread_id):
|
|
230
|
-
print(f'on_thread_exit!!!!')
|
|
63
|
+
# print(f'on_thread_exit!!!!')
|
|
231
64
|
self.reader.wake_pending()
|
|
232
65
|
|
|
233
|
-
def is_entry_frame(self, frame):
|
|
234
|
-
if super().is_entry_frame(frame) and frame.function.__code__.co_filename == self.mainscript:
|
|
235
|
-
return True
|
|
236
|
-
return False
|
|
237
|
-
|
|
238
66
|
def __init__(self,
|
|
67
|
+
reader,
|
|
239
68
|
thread_state,
|
|
240
69
|
immutable_types,
|
|
241
70
|
tracing_config,
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
fork_path = []
|
|
245
|
-
|
|
246
|
-
# self.messages_read = 0
|
|
71
|
+
traceargs,
|
|
72
|
+
verbose = False,
|
|
73
|
+
fork_path = [],
|
|
74
|
+
skip_weakref_callbacks = False):
|
|
247
75
|
|
|
248
|
-
self.
|
|
76
|
+
self.reader = reader
|
|
77
|
+
# self.skip_weakref_callbacks = skip_weakref_callbacks
|
|
249
78
|
|
|
250
|
-
self.reader = stream.reader(path,
|
|
251
|
-
thread = thread_id,
|
|
252
|
-
timeout_seconds = 60)
|
|
253
|
-
|
|
254
|
-
self.bindings = utils.id_dict()
|
|
255
|
-
# self.set_thread_id = utils.set_thread_id
|
|
256
79
|
self.fork_path = fork_path
|
|
257
|
-
deserialize = functional.walker(self.bindings.get_else_key)
|
|
258
|
-
|
|
259
|
-
# def count(res):
|
|
260
|
-
# self.messages_read += 1
|
|
261
|
-
# return res
|
|
262
80
|
|
|
263
|
-
|
|
81
|
+
self.messages = MessageStream(
|
|
82
|
+
thread_state = thread_state,
|
|
83
|
+
source = reader,
|
|
84
|
+
skip_weakref_callbacks = skip_weakref_callbacks,
|
|
85
|
+
verbose = verbose)
|
|
264
86
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
print(f'res: {utils.thread_id()} {messages_read} stub: {type(res)}')
|
|
270
|
-
else:
|
|
271
|
-
print(f'res: {utils.thread_id()} {messages_read} {res}')
|
|
272
|
-
|
|
273
|
-
return res
|
|
274
|
-
except Exception as error:
|
|
275
|
-
print(f'Error reading next result: {error}')
|
|
276
|
-
raise(error)
|
|
277
|
-
|
|
278
|
-
# self.messages = functional.sequence(per_thread_messages(foo), deserialize)
|
|
279
|
-
|
|
280
|
-
self.messages = functional.sequence(self.reader, deserialize)
|
|
87
|
+
self.checkpoint = self.messages.checkpoint
|
|
88
|
+
self.bind = self.messages.bind
|
|
89
|
+
self.next_result = self.messages.result
|
|
90
|
+
self.exclude_from_stacktrace = self.messages.excludes.add
|
|
281
91
|
|
|
282
92
|
self.stub_factory = StubFactory(thread_state = thread_state, next_result = self.next_result)
|
|
283
93
|
|
|
284
94
|
self.last_matching_stack = None
|
|
285
95
|
|
|
96
|
+
def run_ref(ref):
|
|
97
|
+
print(f'run_ref!!!! {ref}')
|
|
98
|
+
return ref()
|
|
99
|
+
|
|
286
100
|
self.reader.type_deserializer[StubRef] = self.stub_factory
|
|
287
|
-
self.reader.type_deserializer[GlobalRef] = lambda ref: ref()
|
|
288
|
-
|
|
289
|
-
|
|
101
|
+
# self.reader.type_deserializer[GlobalRef] = lambda ref: ref()
|
|
102
|
+
self.reader.type_deserializer[GlobalRef] = run_ref
|
|
103
|
+
|
|
104
|
+
excludes = [ReplayProxySystem.trace_writer]
|
|
105
|
+
|
|
106
|
+
for exclude in excludes:
|
|
107
|
+
self.messages.excludes.add(exclude)
|
|
108
|
+
|
|
109
|
+
sync = functional.lazy(self.messages.read_required, 'SYNC')
|
|
110
|
+
|
|
111
|
+
read_sync = thread_state.dispatch(utils.noop, internal = sync)
|
|
112
|
+
|
|
113
|
+
self.on_ext_call = sync
|
|
290
114
|
|
|
291
115
|
self.sync = lambda function: utils.observer(on_call = read_sync, function = function)
|
|
116
|
+
|
|
117
|
+
self.create_from_external = utils.noop
|
|
118
|
+
|
|
119
|
+
if skip_weakref_callbacks:
|
|
120
|
+
self.wrap_weakref_callback = \
|
|
121
|
+
lambda callback: \
|
|
122
|
+
thread_state.dispatch(
|
|
123
|
+
callback, internal = self.disable_for(callback))
|
|
124
|
+
else:
|
|
125
|
+
self.on_weakref_callback_start = functional.lazy(self.messages.read_required, 'ON_WEAKREF_CALLBACK_START')
|
|
126
|
+
self.on_weakref_callback_end = functional.lazy(self.messages.read_required, 'ON_WEAKREF_CALLBACK_END')
|
|
292
127
|
|
|
293
128
|
super().__init__(thread_state = thread_state,
|
|
294
129
|
tracer = Tracer(tracing_config, writer = self.trace_writer),
|
|
295
|
-
immutable_types = immutable_types
|
|
130
|
+
immutable_types = immutable_types,
|
|
131
|
+
traceargs = traceargs)
|
|
132
|
+
|
|
133
|
+
def write_trace(self, obj):
|
|
134
|
+
if 'TRACER' != self.messages():
|
|
135
|
+
utils.sigtrap(obj)
|
|
136
|
+
|
|
137
|
+
# self.read_required ('TRACER')
|
|
138
|
+
self.read_required(obj)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
def serializer(obj):
|
|
2
|
+
cls = type(obj)
|
|
3
|
+
|
|
4
|
+
try:
|
|
5
|
+
if issubclass(cls, Enum):
|
|
6
|
+
return str(obj)
|
|
7
|
+
|
|
8
|
+
if issubclass(cls, (tuple, list)):
|
|
9
|
+
return list(map(serializer, obj))
|
|
10
|
+
elif issubclass(cls, dict):
|
|
11
|
+
return {str(serializer(k)):serializer(v) for k,v in obj.items()}
|
|
12
|
+
elif issubclass(cls, int):
|
|
13
|
+
if obj > 1000000 or obj < -1000000:
|
|
14
|
+
return "XXXX"
|
|
15
|
+
else:
|
|
16
|
+
return int(obj)
|
|
17
|
+
elif issubclass(cls, (bool, str, types.NoneType)):
|
|
18
|
+
return obj
|
|
19
|
+
elif issubclass(cls, types.FunctionType):
|
|
20
|
+
return obj.__qualname__
|
|
21
|
+
elif issubclass(cls, types.CellType):
|
|
22
|
+
return getattr(obj, '__qualname__', 'CellType')
|
|
23
|
+
elif issubclass(cls, types.GeneratorType):
|
|
24
|
+
return obj.__qualname__
|
|
25
|
+
else:
|
|
26
|
+
return cleanse(str(obj))
|
|
27
|
+
except:
|
|
28
|
+
return f'Unserializable object of type: {cls}'
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from retracesoftware import functional
|
|
2
|
+
from retracesoftware import utils
|
|
3
|
+
|
|
4
|
+
import _thread
|
|
5
|
+
|
|
6
|
+
# _thread.start_new_thread(function, args[, kwargs])
|
|
7
|
+
|
|
8
|
+
# push a per thread executor function, as a context manager
|
|
9
|
+
|
|
10
|
+
counters = _thread._local()
|
|
11
|
+
counters.id = ()
|
|
12
|
+
counters.counter = 0
|
|
13
|
+
|
|
14
|
+
def with_thread_id(thread_id, function, *args, **kwargs):
|
|
15
|
+
counters.id = thread_id
|
|
16
|
+
counters.counter = 0
|
|
17
|
+
return function(*args, **kwargs)
|
|
18
|
+
|
|
19
|
+
thread_id = functional.lazy(getattr, counters, 'id')
|
|
20
|
+
|
|
21
|
+
def start_new_thread(original, wrapper, function, *args):
|
|
22
|
+
return original(wrapper(function), *args)
|
|
23
|
+
|
|
24
|
+
def wrap_thread_function(thread_state, function):
|
|
25
|
+
if thread_state.value == 'internal':
|
|
26
|
+
next_id = counters.id + (counters.counter,)
|
|
27
|
+
counters.counter += 1
|
|
28
|
+
return functional.partial(with_thread_id, next_id, thread_state.wrap('internal', function))
|
|
29
|
+
else:
|
|
30
|
+
return function
|
|
31
|
+
|
|
32
|
+
def patch_thread_start(thread_state):
|
|
33
|
+
|
|
34
|
+
wrapper = functional.partial(wrap_thread_function, thread_state)
|
|
35
|
+
|
|
36
|
+
_thread.start_new = functional.partial(start_new_thread, _thread.start_new, wrapper)
|
|
37
|
+
_thread.start_new_thread = functional.partial(start_new_thread, _thread.start_new_thread, wrapper)
|
|
38
|
+
|
|
39
|
+
import threading
|
|
40
|
+
threading._start_new_thread = _thread.start_new_thread
|