summaryrefslogtreecommitdiff
path: root/deps/v8/build/android/pylib/utils/instrumentation_tracing.py
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/build/android/pylib/utils/instrumentation_tracing.py')
-rw-r--r--deps/v8/build/android/pylib/utils/instrumentation_tracing.py204
1 files changed, 204 insertions, 0 deletions
diff --git a/deps/v8/build/android/pylib/utils/instrumentation_tracing.py b/deps/v8/build/android/pylib/utils/instrumentation_tracing.py
new file mode 100644
index 0000000000..f1d03a0dcf
--- /dev/null
+++ b/deps/v8/build/android/pylib/utils/instrumentation_tracing.py
@@ -0,0 +1,204 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Functions to instrument all Python function calls.
+
+This generates a JSON file readable by Chrome's about:tracing. To use it,
+either call start_instrumenting and stop_instrumenting at the appropriate times,
+or use the Instrument context manager.
+
+A function is only traced if it is from a Python module that matches at least
+one regular expression object in to_include, and does not match any in
+to_exclude. In between the start and stop events, every function call of a
+function from such a module will be added to the trace.
+"""
+
+import contextlib
+import functools
+import inspect
+import os
+import re
+import sys
+import threading
+
+from py_trace_event import trace_event
+
+
+# Modules to exclude by default (to avoid problems like infinite loops)
+DEFAULT_EXCLUDE = [r'py_trace_event\..*']
+
+class _TraceArguments(object):
+ def __init__(self):
+ """Wraps a dictionary to ensure safe evaluation of repr()."""
+ self._arguments = {}
+
+ @staticmethod
+ def _safeStringify(item):
+ try:
+ item_str = repr(item)
+ except Exception: # pylint: disable=broad-except
+ try:
+ item_str = str(item)
+ except Exception: # pylint: disable=broad-except
+ item_str = "<ERROR>"
+ return item_str
+
+ def add(self, key, val):
+ key_str = _TraceArguments._safeStringify(key)
+ val_str = _TraceArguments._safeStringify(val)
+
+ self._arguments[key_str] = val_str
+
+ def __repr__(self):
+ return repr(self._arguments)
+
+
+saved_thread_ids = set()
+
+def _shouldTrace(frame, to_include, to_exclude, included, excluded):
+ """
+ Decides whether or not the function called in frame should be traced.
+
+ Args:
+ frame: The Python frame object of this function call.
+ to_include: Set of regex objects for modules which should be traced.
+ to_exclude: Set of regex objects for modules which should not be traced.
+ included: Set of module names we've determined should be traced.
+ excluded: Set of module names we've determined should not be traced.
+ """
+ if not inspect.getmodule(frame):
+ return False
+
+ module_name = inspect.getmodule(frame).__name__
+
+ if module_name in included:
+ includes = True
+ elif to_include:
+ includes = any([pattern.match(module_name) for pattern in to_include])
+ else:
+ includes = True
+
+ if includes:
+ included.add(module_name)
+ else:
+ return False
+
+ # Find the modules of every function in the stack trace.
+ frames = inspect.getouterframes(frame)
+ calling_module_names = [inspect.getmodule(fr[0]).__name__ for fr in frames]
+
+ # Return False for anything with an excluded module's function anywhere in the
+ # stack trace (even if the function itself is in an included module).
+ if to_exclude:
+ for calling_module in calling_module_names:
+ if calling_module in excluded:
+ return False
+ for pattern in to_exclude:
+ if pattern.match(calling_module):
+ excluded.add(calling_module)
+ return False
+
+ return True
+
+def _generate_trace_function(to_include, to_exclude):
+ to_include = {re.compile(item) for item in to_include}
+ to_exclude = {re.compile(item) for item in to_exclude}
+ to_exclude.update({re.compile(item) for item in DEFAULT_EXCLUDE})
+
+ included = set()
+ excluded = set()
+
+ tracing_pid = os.getpid()
+
+ def traceFunction(frame, event, arg):
+ del arg
+
+ # Don't try to trace in subprocesses.
+ if os.getpid() != tracing_pid:
+ sys.settrace(None)
+ return None
+
+ # pylint: disable=unused-argument
+ if event not in ("call", "return"):
+ return None
+
+ function_name = frame.f_code.co_name
+ filename = frame.f_code.co_filename
+ line_number = frame.f_lineno
+
+ if _shouldTrace(frame, to_include, to_exclude, included, excluded):
+ if event == "call":
+ # This function is beginning; we save the thread name (if that hasn't
+ # been done), record the Begin event, and return this function to be
+ # used as the local trace function.
+
+ thread_id = threading.current_thread().ident
+
+ if thread_id not in saved_thread_ids:
+ thread_name = threading.current_thread().name
+
+ trace_event.trace_set_thread_name(thread_name)
+
+ saved_thread_ids.add(thread_id)
+
+ arguments = _TraceArguments()
+ # The function's argument values are stored in the frame's
+ # |co_varnames| as the first |co_argcount| elements. (Following that
+ # are local variables.)
+ for idx in range(frame.f_code.co_argcount):
+ arg_name = frame.f_code.co_varnames[idx]
+ arguments.add(arg_name, frame.f_locals[arg_name])
+ trace_event.trace_begin(function_name, arguments=arguments,
+ module=inspect.getmodule(frame).__name__,
+ filename=filename, line_number=line_number)
+
+ # Return this function, so it gets used as the "local trace function"
+ # within this function's frame (and in particular, gets called for this
+ # function's "return" event).
+ return traceFunction
+
+ if event == "return":
+ trace_event.trace_end(function_name)
+ return None
+
+ return traceFunction
+
+
+def no_tracing(f):
+ @functools.wraps(f)
+ def wrapper(*args, **kwargs):
+ trace_func = sys.gettrace()
+ try:
+ sys.settrace(None)
+ threading.settrace(None)
+ return f(*args, **kwargs)
+ finally:
+ sys.settrace(trace_func)
+ threading.settrace(trace_func)
+ return wrapper
+
+
+def start_instrumenting(output_file, to_include=(), to_exclude=()):
+ """Enable tracing of all function calls (from specified modules)."""
+ trace_event.trace_enable(output_file)
+
+ traceFunc = _generate_trace_function(to_include, to_exclude)
+ sys.settrace(traceFunc)
+ threading.settrace(traceFunc)
+
+
+def stop_instrumenting():
+ trace_event.trace_disable()
+
+ sys.settrace(None)
+ threading.settrace(None)
+
+
+@contextlib.contextmanager
+def Instrument(output_file, to_include=(), to_exclude=()):
+ try:
+ start_instrumenting(output_file, to_include, to_exclude)
+ yield None
+ finally:
+ stop_instrumenting()