OLD | NEW |
1 """Raw data collector for Coverage.""" | 1 """Raw data collector for Coverage.""" |
2 | 2 |
3 import os, sys, threading | 3 import os, sys, threading |
4 | 4 |
5 try: | 5 try: |
6 # Use the C extension code when we can, for speed. | 6 # Use the C extension code when we can, for speed. |
7 from coverage.tracer import CTracer # pylint: disable=F0401,E0611 | 7 from coverage.tracer import CTracer # pylint: disable=F0401,E0611 |
8 except ImportError: | 8 except ImportError: |
9 # Couldn't import the C extension, maybe it isn't built. | 9 # Couldn't import the C extension, maybe it isn't built. |
10 if os.getenv('COVERAGE_TEST_TRACER') == 'c': | 10 if os.getenv('COVERAGE_TEST_TRACER') == 'c': |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
44 self.data = None | 44 self.data = None |
45 self.should_trace = None | 45 self.should_trace = None |
46 self.should_trace_cache = None | 46 self.should_trace_cache = None |
47 self.warn = None | 47 self.warn = None |
48 self.cur_file_data = None | 48 self.cur_file_data = None |
49 self.last_line = 0 | 49 self.last_line = 0 |
50 self.data_stack = [] | 50 self.data_stack = [] |
51 self.last_exc_back = None | 51 self.last_exc_back = None |
52 self.last_exc_firstlineno = 0 | 52 self.last_exc_firstlineno = 0 |
53 self.arcs = False | 53 self.arcs = False |
| 54 self.thread = None |
| 55 self.stopped = False |
54 | 56 |
55 def _trace(self, frame, event, arg_unused): | 57 def _trace(self, frame, event, arg_unused): |
56 """The trace function passed to sys.settrace.""" | 58 """The trace function passed to sys.settrace.""" |
57 | 59 |
58 #print("trace event: %s %r @%d" % ( | 60 if self.stopped: |
59 # event, frame.f_code.co_filename, frame.f_lineno), | 61 return |
60 # file=sys.stderr) | 62 |
| 63 if 0: |
| 64 sys.stderr.write("trace event: %s %r @%d\n" % ( |
| 65 event, frame.f_code.co_filename, frame.f_lineno |
| 66 )) |
61 | 67 |
62 if self.last_exc_back: | 68 if self.last_exc_back: |
63 if frame == self.last_exc_back: | 69 if frame == self.last_exc_back: |
64 # Someone forgot a return event. | 70 # Someone forgot a return event. |
65 if self.arcs and self.cur_file_data: | 71 if self.arcs and self.cur_file_data: |
66 pair = (self.last_line, -self.last_exc_firstlineno) | 72 pair = (self.last_line, -self.last_exc_firstlineno) |
67 self.cur_file_data[pair] = None | 73 self.cur_file_data[pair] = None |
68 self.cur_file_data, self.last_line = self.data_stack.pop() | 74 self.cur_file_data, self.last_line = self.data_stack.pop() |
69 self.last_exc_back = None | 75 self.last_exc_back = None |
70 | 76 |
71 if event == 'call': | 77 if event == 'call': |
72 # Entering a new function context. Decide if we should trace | 78 # Entering a new function context. Decide if we should trace |
73 # in this file. | 79 # in this file. |
74 self.data_stack.append((self.cur_file_data, self.last_line)) | 80 self.data_stack.append((self.cur_file_data, self.last_line)) |
75 filename = frame.f_code.co_filename | 81 filename = frame.f_code.co_filename |
76 tracename = self.should_trace_cache.get(filename) | 82 if filename not in self.should_trace_cache: |
77 if tracename is None: | |
78 tracename = self.should_trace(filename, frame) | 83 tracename = self.should_trace(filename, frame) |
79 self.should_trace_cache[filename] = tracename | 84 self.should_trace_cache[filename] = tracename |
| 85 else: |
| 86 tracename = self.should_trace_cache[filename] |
80 #print("called, stack is %d deep, tracename is %r" % ( | 87 #print("called, stack is %d deep, tracename is %r" % ( |
81 # len(self.data_stack), tracename)) | 88 # len(self.data_stack), tracename)) |
82 if tracename: | 89 if tracename: |
83 if tracename not in self.data: | 90 if tracename not in self.data: |
84 self.data[tracename] = {} | 91 self.data[tracename] = {} |
85 self.cur_file_data = self.data[tracename] | 92 self.cur_file_data = self.data[tracename] |
86 else: | 93 else: |
87 self.cur_file_data = None | 94 self.cur_file_data = None |
88 # Set the last_line to -1 because the next arc will be entering a | 95 # Set the last_line to -1 because the next arc will be entering a |
89 # code block, indicated by (-1, n). | 96 # code block, indicated by (-1, n). |
(...skipping 20 matching lines...) Expand all Loading... |
110 self.last_exc_back = frame.f_back | 117 self.last_exc_back = frame.f_back |
111 self.last_exc_firstlineno = frame.f_code.co_firstlineno | 118 self.last_exc_firstlineno = frame.f_code.co_firstlineno |
112 return self._trace | 119 return self._trace |
113 | 120 |
114 def start(self): | 121 def start(self): |
115 """Start this Tracer. | 122 """Start this Tracer. |
116 | 123 |
117 Return a Python function suitable for use with sys.settrace(). | 124 Return a Python function suitable for use with sys.settrace(). |
118 | 125 |
119 """ | 126 """ |
| 127 self.thread = threading.currentThread() |
120 sys.settrace(self._trace) | 128 sys.settrace(self._trace) |
121 return self._trace | 129 return self._trace |
122 | 130 |
123 def stop(self): | 131 def stop(self): |
124 """Stop this Tracer.""" | 132 """Stop this Tracer.""" |
| 133 self.stopped = True |
| 134 if self.thread != threading.currentThread(): |
| 135 # Called on a different thread than started us: we can't unhook |
| 136 # ourseves, but we've set the flag that we should stop, so we won't |
| 137 # do any more tracing. |
| 138 return |
| 139 |
125 if hasattr(sys, "gettrace") and self.warn: | 140 if hasattr(sys, "gettrace") and self.warn: |
126 if sys.gettrace() != self._trace: | 141 if sys.gettrace() != self._trace: |
127 msg = "Trace function changed, measurement is likely wrong: %r" | 142 msg = "Trace function changed, measurement is likely wrong: %r" |
128 self.warn(msg % (sys.gettrace(),)) | 143 self.warn(msg % (sys.gettrace(),)) |
129 #--debug | 144 #print("Stopping tracer on %s" % threading.current_thread().ident) |
130 #from coverage.misc import short_stack | |
131 #self.warn(msg % (sys.gettrace()))#, short_stack())) | |
132 sys.settrace(None) | 145 sys.settrace(None) |
133 | 146 |
134 def get_stats(self): | 147 def get_stats(self): |
135 """Return a dictionary of statistics, or None.""" | 148 """Return a dictionary of statistics, or None.""" |
136 return None | 149 return None |
137 | 150 |
138 | 151 |
139 class Collector(object): | 152 class Collector(object): |
140 """Collects trace data. | 153 """Collects trace data. |
141 | 154 |
(...skipping 12 matching lines...) Expand all Loading... |
154 | 167 |
155 # The stack of active Collectors. Collectors are added here when started, | 168 # The stack of active Collectors. Collectors are added here when started, |
156 # and popped when stopped. Collectors on the stack are paused when not | 169 # and popped when stopped. Collectors on the stack are paused when not |
157 # the top, and resumed when they become the top again. | 170 # the top, and resumed when they become the top again. |
158 _collectors = [] | 171 _collectors = [] |
159 | 172 |
160 def __init__(self, should_trace, timid, branch, warn): | 173 def __init__(self, should_trace, timid, branch, warn): |
161 """Create a collector. | 174 """Create a collector. |
162 | 175 |
163 `should_trace` is a function, taking a filename, and returning a | 176 `should_trace` is a function, taking a filename, and returning a |
164 canonicalized filename, or False depending on whether the file should | 177 canonicalized filename, or None depending on whether the file should |
165 be traced or not. | 178 be traced or not. |
166 | 179 |
167 If `timid` is true, then a slower simpler trace function will be | 180 If `timid` is true, then a slower simpler trace function will be |
168 used. This is important for some environments where manipulation of | 181 used. This is important for some environments where manipulation of |
169 tracing functions make the faster more sophisticated trace function not | 182 tracing functions make the faster more sophisticated trace function not |
170 operate properly. | 183 operate properly. |
171 | 184 |
172 If `branch` is true, then branches will be measured. This involves | 185 If `branch` is true, then branches will be measured. This involves |
173 collecting data on which statements followed each other (arcs). Use | 186 collecting data on which statements followed each other (arcs). Use |
174 `get_arc_data` to get the arc data. | 187 `get_arc_data` to get the arc data. |
(...skipping 23 matching lines...) Expand all Loading... |
198 return self._trace_class.__name__ | 211 return self._trace_class.__name__ |
199 | 212 |
200 def reset(self): | 213 def reset(self): |
201 """Clear collected data, and prepare to collect more.""" | 214 """Clear collected data, and prepare to collect more.""" |
202 # A dictionary mapping filenames to dicts with linenumber keys, | 215 # A dictionary mapping filenames to dicts with linenumber keys, |
203 # or mapping filenames to dicts with linenumber pairs as keys. | 216 # or mapping filenames to dicts with linenumber pairs as keys. |
204 self.data = {} | 217 self.data = {} |
205 | 218 |
206 # A cache of the results from should_trace, the decision about whether | 219 # A cache of the results from should_trace, the decision about whether |
207 # to trace execution in a file. A dict of filename to (filename or | 220 # to trace execution in a file. A dict of filename to (filename or |
208 # False). | 221 # None). |
209 self.should_trace_cache = {} | 222 self.should_trace_cache = {} |
210 | 223 |
211 # Our active Tracers. | 224 # Our active Tracers. |
212 self.tracers = [] | 225 self.tracers = [] |
213 | 226 |
214 def _start_tracer(self): | 227 def _start_tracer(self): |
215 """Start a new Tracer object, and store it in self.tracers.""" | 228 """Start a new Tracer object, and store it in self.tracers.""" |
216 tracer = self._trace_class() | 229 tracer = self._trace_class() |
217 tracer.data = self.data | 230 tracer.data = self.data |
218 tracer.arcs = self.branch | 231 tracer.arcs = self.branch |
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
331 Data is { filename: { (l1, l2): None, ...}, ...} | 344 Data is { filename: { (l1, l2): None, ...}, ...} |
332 | 345 |
333 Note that no data is collected or returned if the Collector wasn't | 346 Note that no data is collected or returned if the Collector wasn't |
334 created with `branch` true. | 347 created with `branch` true. |
335 | 348 |
336 """ | 349 """ |
337 if self.branch: | 350 if self.branch: |
338 return self.data | 351 return self.data |
339 else: | 352 else: |
340 return {} | 353 return {} |
OLD | NEW |