Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(561)

Unified Diff: tools/telemetry/third_party/coverage/coverage/pytracer.py

Issue 1366913004: Add coverage Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: tools/telemetry/third_party/coverage/coverage/pytracer.py
diff --git a/tools/telemetry/third_party/coverage/coverage/pytracer.py b/tools/telemetry/third_party/coverage/coverage/pytracer.py
new file mode 100644
index 0000000000000000000000000000000000000000..cdb3ae7077faa28768d653582fcf3e4a4048e6c6
--- /dev/null
+++ b/tools/telemetry/third_party/coverage/coverage/pytracer.py
@@ -0,0 +1,152 @@
+# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
+# For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
+
+"""Raw data collector for coverage.py."""
+
+import dis
+import sys
+
+from coverage import env
+
+# We need the YIELD_VALUE opcode below, in a comparison-friendly form.
+YIELD_VALUE = dis.opmap['YIELD_VALUE']
+if env.PY2:
+ YIELD_VALUE = chr(YIELD_VALUE)
+
+
+class PyTracer(object):
+ """Python implementation of the raw data tracer."""
+
+ # Because of poor implementations of trace-function-manipulating tools,
+ # the Python trace function must be kept very simple. In particular, there
+ # must be only one function ever set as the trace function, both through
+ # sys.settrace, and as the return value from the trace function. Put
+ # another way, the trace function must always return itself. It cannot
+ # swap in other functions, or return None to avoid tracing a particular
+ # frame.
+ #
+ # The trace manipulator that introduced this restriction is DecoratorTools,
+ # which sets a trace function, and then later restores the pre-existing one
+ # by calling sys.settrace with a function it found in the current frame.
+ #
+ # Systems that use DecoratorTools (or similar trace manipulations) must use
+ # PyTracer to get accurate results. The command-line --timid argument is
+ # used to force the use of this tracer.
+
+ def __init__(self):
+ # Attributes set from the collector:
+ self.data = None
+ self.trace_arcs = False
+ self.should_trace = None
+ self.should_trace_cache = None
+ self.warn = None
+ # The threading module to use, if any.
+ self.threading = None
+
+ self.cur_file_dict = []
+ self.last_line = [0]
+
+ self.data_stack = []
+ self.last_exc_back = None
+ self.last_exc_firstlineno = 0
+ self.thread = None
+ self.stopped = False
+
+ def __repr__(self):
+ return "<PyTracer at 0x{0:0x}: {1} lines in {2} files>".format(
+ id(self),
+ sum(len(v) for v in self.data.values()),
+ len(self.data),
+ )
+
+ def _trace(self, frame, event, arg_unused):
+ """The trace function passed to sys.settrace."""
+
+ if self.stopped:
+ return
+
+ if self.last_exc_back:
+ if frame == self.last_exc_back:
+ # Someone forgot a return event.
+ if self.trace_arcs and self.cur_file_dict:
+ pair = (self.last_line, -self.last_exc_firstlineno)
+ self.cur_file_dict[pair] = None
+ self.cur_file_dict, self.last_line = self.data_stack.pop()
+ self.last_exc_back = None
+
+ if event == 'call':
+ # Entering a new function context. Decide if we should trace
+ # in this file.
+ self.data_stack.append((self.cur_file_dict, self.last_line))
+ filename = frame.f_code.co_filename
+ disp = self.should_trace_cache.get(filename)
+ if disp is None:
+ disp = self.should_trace(filename, frame)
+ self.should_trace_cache[filename] = disp
+
+ self.cur_file_dict = None
+ if disp.trace:
+ tracename = disp.source_filename
+ if tracename not in self.data:
+ self.data[tracename] = {}
+ self.cur_file_dict = self.data[tracename]
+ # The call event is really a "start frame" event, and happens for
+ # function calls and re-entering generators. The f_lasti field is
+ # -1 for calls, and a real offset for generators. Use -1 as the
+ # line number for calls, and the real line number for generators.
+ self.last_line = -1 if (frame.f_lasti < 0) else frame.f_lineno
+ elif event == 'line':
+ # Record an executed line.
+ if self.cur_file_dict is not None:
+ lineno = frame.f_lineno
+ if self.trace_arcs:
+ self.cur_file_dict[(self.last_line, lineno)] = None
+ else:
+ self.cur_file_dict[lineno] = None
+ self.last_line = lineno
+ elif event == 'return':
+ if self.trace_arcs and self.cur_file_dict:
+ # Record an arc leaving the function, but beware that a
+ # "return" event might just mean yielding from a generator.
+ bytecode = frame.f_code.co_code[frame.f_lasti]
+ if bytecode != YIELD_VALUE:
+ first = frame.f_code.co_firstlineno
+ self.cur_file_dict[(self.last_line, -first)] = None
+ # Leaving this function, pop the filename stack.
+ self.cur_file_dict, self.last_line = self.data_stack.pop()
+ elif event == 'exception':
+ self.last_exc_back = frame.f_back
+ self.last_exc_firstlineno = frame.f_code.co_firstlineno
+ return self._trace
+
+ def start(self):
+ """Start this Tracer.
+
+ Return a Python function suitable for use with sys.settrace().
+
+ """
+ if self.threading:
+ self.thread = self.threading.currentThread()
+ sys.settrace(self._trace)
+ self.stopped = False
+ return self._trace
+
+ def stop(self):
+ """Stop this Tracer."""
+ self.stopped = True
+ if self.threading and self.thread != self.threading.currentThread():
+ # Called on a different thread than started us: we can't unhook
+ # ourselves, but we've set the flag that we should stop, so we
+ # won't do any more tracing.
+ return
+
+ if self.warn:
+ if sys.gettrace() != self._trace:
+ msg = "Trace function changed, measurement is likely wrong: %r"
+ self.warn(msg % (sys.gettrace(),))
+
+ sys.settrace(None)
+
+ def get_stats(self):
+ """Return a dictionary of statistics, or None."""
+ return None
« no previous file with comments | « tools/telemetry/third_party/coverage/coverage/python.py ('k') | tools/telemetry/third_party/coverage/coverage/report.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698