11 |
11 |
12 # We need the YIELD_VALUE opcode below, in a comparison-friendly form. |
12 # We need the YIELD_VALUE opcode below, in a comparison-friendly form. |
13 YIELD_VALUE = dis.opmap['YIELD_VALUE'] |
13 YIELD_VALUE = dis.opmap['YIELD_VALUE'] |
14 if env.PY2: |
14 if env.PY2: |
15 YIELD_VALUE = chr(YIELD_VALUE) |
15 YIELD_VALUE = chr(YIELD_VALUE) |
|
16 |
|
17 # When running meta-coverage, this file can try to trace itself, which confuses |
|
18 # everything. Don't trace ourselves. |
|
19 |
|
20 THIS_FILE = __file__.rstrip("co") |
16 |
21 |
17 |
22 |
18 class PyTracer(object): |
23 class PyTracer(object): |
19 """Python implementation of the raw data tracer.""" |
24 """Python implementation of the raw data tracer.""" |
20 |
25 |
70 ) |
75 ) |
71 |
76 |
72 def log(self, marker, *args): |
77 def log(self, marker, *args): |
73 """For hard-core logging of what this tracer is doing.""" |
78 """For hard-core logging of what this tracer is doing.""" |
74 with open("/tmp/debug_trace.txt", "a") as f: |
79 with open("/tmp/debug_trace.txt", "a") as f: |
75 f.write("{} {:x}.{:x}[{}] {:x} {}\n".format( |
80 f.write("{} {}[{}]".format( |
76 marker, |
81 marker, |
77 id(self), |
82 id(self), |
78 self.thread.ident, |
|
79 len(self.data_stack), |
83 len(self.data_stack), |
80 self.threading.currentThread().ident, |
|
81 " ".join(map(str, args)) |
|
82 )) |
84 )) |
|
85 if 0: |
|
86 f.write(".{:x}.{:x}".format( |
|
87 self.thread.ident, |
|
88 self.threading.currentThread().ident, |
|
89 )) |
|
90 f.write(" {}".format(" ".join(map(str, args)))) |
|
91 if 0: |
|
92 f.write(" | ") |
|
93 stack = " / ".join( |
|
94 (fname or "???").rpartition("/")[-1] |
|
95 for _, fname, _, _ in self.data_stack |
|
96 ) |
|
97 f.write(stack) |
|
98 f.write("\n") |
83 |
99 |
84 def _trace(self, frame, event, arg_unused): |
100 def _trace(self, frame, event, arg_unused): |
85 """The trace function passed to sys.settrace.""" |
101 """The trace function passed to sys.settrace.""" |
86 |
102 |
87 #self.log(":", frame.f_code.co_filename, frame.f_lineno, event) |
103 if THIS_FILE in frame.f_code.co_filename: |
|
104 return None |
|
105 |
|
106 #self.log(":", frame.f_code.co_filename, frame.f_lineno, frame.f_code.co_name + "()", event) |
88 |
107 |
89 if (self.stopped and sys.gettrace() == self._trace): # pylint: disable=comparison-with-callable |
108 if (self.stopped and sys.gettrace() == self._trace): # pylint: disable=comparison-with-callable |
90 # The PyTrace.stop() method has been called, possibly by another |
109 # The PyTrace.stop() method has been called, possibly by another |
91 # thread, let's deactivate ourselves now. |
110 # thread, let's deactivate ourselves now. |
92 #self.log("X", frame.f_code.co_filename, frame.f_lineno) |
111 if 0: |
|
112 self.log("---\nX", frame.f_code.co_filename, frame.f_lineno) |
|
113 f = frame |
|
114 while f: |
|
115 self.log(">", f.f_code.co_filename, f.f_lineno, f.f_code.co_name, f.f_trace) |
|
116 f = f.f_back |
93 sys.settrace(None) |
117 sys.settrace(None) |
|
118 self.cur_file_dict, self.cur_file_name, self.last_line, self.started_context = ( |
|
119 self.data_stack.pop() |
|
120 ) |
94 return None |
121 return None |
95 |
122 |
96 if self.last_exc_back: |
123 if self.last_exc_back: |
97 if frame == self.last_exc_back: |
124 if frame == self.last_exc_back: |
98 # Someone forgot a return event. |
125 # Someone forgot a return event. |
102 self.cur_file_dict, self.cur_file_name, self.last_line, self.started_context = ( |
129 self.cur_file_dict, self.cur_file_name, self.last_line, self.started_context = ( |
103 self.data_stack.pop() |
130 self.data_stack.pop() |
104 ) |
131 ) |
105 self.last_exc_back = None |
132 self.last_exc_back = None |
106 |
133 |
|
134 # if event != 'call' and frame.f_code.co_filename != self.cur_file_name: |
|
135 # self.log("---\n*", frame.f_code.co_filename, self.cur_file_name, frame.f_lineno) |
|
136 |
107 if event == 'call': |
137 if event == 'call': |
108 # Should we start a new context? |
138 # Should we start a new context? |
109 if self.should_start_context and self.context is None: |
139 if self.should_start_context and self.context is None: |
110 context_maybe = self.should_start_context(frame) # pylint: disable=not-callable |
140 context_maybe = self.should_start_context(frame) |
111 if context_maybe is not None: |
141 if context_maybe is not None: |
112 self.context = context_maybe |
142 self.context = context_maybe |
113 self.started_context = True |
143 self.started_context = True |
114 self.switch_context(self.context) |
144 self.switch_context(self.context) |
115 else: |
145 else: |
130 ) |
160 ) |
131 filename = frame.f_code.co_filename |
161 filename = frame.f_code.co_filename |
132 self.cur_file_name = filename |
162 self.cur_file_name = filename |
133 disp = self.should_trace_cache.get(filename) |
163 disp = self.should_trace_cache.get(filename) |
134 if disp is None: |
164 if disp is None: |
135 disp = self.should_trace(filename, frame) # pylint: disable=not-callable |
165 disp = self.should_trace(filename, frame) |
136 self.should_trace_cache[filename] = disp # pylint: disable=unsupported-assignment-operation |
166 self.should_trace_cache[filename] = disp |
137 |
167 |
138 self.cur_file_dict = None |
168 self.cur_file_dict = None |
139 if disp.trace: |
169 if disp.trace: |
140 tracename = disp.source_filename |
170 tracename = disp.source_filename |
141 if tracename not in self.data: # pylint: disable=unsupported-membership-test |
171 if tracename not in self.data: |
142 self.data[tracename] = {} # pylint: disable=unsupported-assignment-operation |
172 self.data[tracename] = {} |
143 self.cur_file_dict = self.data[tracename] # pylint: disable=unsubscriptable-object |
173 self.cur_file_dict = self.data[tracename] |
144 # The call event is really a "start frame" event, and happens for |
174 # The call event is really a "start frame" event, and happens for |
145 # function calls and re-entering generators. The f_lasti field is |
175 # function calls and re-entering generators. The f_lasti field is |
146 # -1 for calls, and a real offset for generators. Use <0 as the |
176 # -1 for calls, and a real offset for generators. Use <0 as the |
147 # line number for calls, and the real line number for generators. |
177 # line number for calls, and the real line number for generators. |
148 if getattr(frame, 'f_lasti', -1) < 0: |
178 if getattr(frame, 'f_lasti', -1) < 0: |
151 self.last_line = frame.f_lineno |
181 self.last_line = frame.f_lineno |
152 elif event == 'line': |
182 elif event == 'line': |
153 # Record an executed line. |
183 # Record an executed line. |
154 if self.cur_file_dict is not None: |
184 if self.cur_file_dict is not None: |
155 lineno = frame.f_lineno |
185 lineno = frame.f_lineno |
156 #if frame.f_code.co_filename != self.cur_file_name: |
186 |
157 # self.log("*", frame.f_code.co_filename, self.cur_file_name, lineno) |
|
158 if self.trace_arcs: |
187 if self.trace_arcs: |
159 self.cur_file_dict[(self.last_line, lineno)] = None |
188 self.cur_file_dict[(self.last_line, lineno)] = None |
160 else: |
189 else: |
161 self.cur_file_dict[lineno] = None |
190 self.cur_file_dict[lineno] = None |
162 self.last_line = lineno |
191 self.last_line = lineno |
225 # PyPy clears the trace function before running atexit functions, |
254 # PyPy clears the trace function before running atexit functions, |
226 # so don't warn if we are in atexit on PyPy and the trace function |
255 # so don't warn if we are in atexit on PyPy and the trace function |
227 # has changed to None. |
256 # has changed to None. |
228 dont_warn = (env.PYPY and env.PYPYVERSION >= (5, 4) and self.in_atexit and tf is None) |
257 dont_warn = (env.PYPY and env.PYPYVERSION >= (5, 4) and self.in_atexit and tf is None) |
229 if (not dont_warn) and tf != self._trace: # pylint: disable=comparison-with-callable |
258 if (not dont_warn) and tf != self._trace: # pylint: disable=comparison-with-callable |
230 self.warn( # pylint: disable=not-callable |
259 self.warn( |
231 "Trace function changed, measurement is likely wrong: %r" % (tf,), |
260 "Trace function changed, measurement is likely wrong: %r" % (tf,), |
232 slug="trace-changed", |
261 slug="trace-changed", |
233 ) |
262 ) |
234 |
263 |
235 def activity(self): |
264 def activity(self): |