• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2016 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4from py_trace_event import trace_time
5
6
7r"""Instrumentation-based profiling for Python.
8
9trace_event allows you to hand-instrument your code with areas of interest.
10When enabled, trace_event logs the start and stop times of these events to a
11logfile. These resulting logfiles can be viewed with either Chrome's
12about:tracing UI or with the standalone trace_event_viewer available at
13  http://www.github.com/natduca/trace_event_viewer/
14
15To use trace event, call trace_event_enable and start instrumenting your code:
16   from trace_event import *
17
18   if "--trace" in sys.argv:
19     trace_enable("myfile.trace")
20
21   @traced
22   def foo():
23     ...
24
25   class MyFoo(object):
26     @traced
27     def bar(self):
28       ...
29
30trace_event records trace events to an in-memory buffer. If your application is
31long running and you want to see the results of a trace before it exits, you can
32call trace_flush to write any in-memory events to disk.
33
34To help intregrating trace_event into existing codebases that dont want to add
35trace_event as a dependancy, trace_event is split into an import shim
36(trace_event.py) and an implementaiton (trace_event_impl/*). You can copy the
37shim, trace_event.py, directly into your including codebase. If the
38trace_event_impl is not found, the shim will simply noop.
39
40trace_event is safe with regard to Python threads. Simply trace as you normally
41would and each thread's timing will show up in the trace file.
42
43Multiple processes can safely output into a single trace_event logfile. If you
44fork after enabling tracing, the child process will continue outputting to the
45logfile. Use of the multiprocessing module will work as well. In both cases,
46however, note that disabling tracing in the parent process will not stop tracing
47in the child processes.
48"""
49
50try:
51  import trace_event_impl
52except ImportError:
53  trace_event_impl = None
54
55
56def trace_can_enable():
57  """
58  Returns True if a trace_event_impl was found. If false,
59  trace_enable will fail. Regular tracing methods, including
60  trace_begin and trace_end, will simply be no-ops.
61  """
62  return trace_event_impl != None
63
64# Default TracedMetaClass to type incase trace_event_impl is not defined.
65# This is to avoid exception during import time since TracedMetaClass typically
66# used in class definition scope.
67TracedMetaClass = type
68
69if trace_event_impl:
70  import time
71
72
73  def trace_is_enabled():
74    return trace_event_impl.trace_is_enabled()
75
76  def trace_enable(logfile):
77    return trace_event_impl.trace_enable(logfile)
78
79  def trace_disable():
80    return trace_event_impl.trace_disable()
81
82  def trace_flush():
83    trace_event_impl.trace_flush()
84
85  def trace_begin(name, **kwargs):
86    args_to_log = {key: repr(value) for key, value in kwargs.iteritems()}
87    trace_event_impl.add_trace_event("B", trace_time.Now(), "python", name,
88                                     args_to_log)
89
90  def trace_end(name):
91    trace_event_impl.add_trace_event("E", trace_time.Now(), "python", name)
92
93  def trace_set_thread_name(thread_name):
94    trace_event_impl.add_trace_event("M", trace_time.Now(), "__metadata",
95                                     "thread_name", {"name": thread_name})
96
97  def trace(name, **kwargs):
98    return trace_event_impl.trace(name, **kwargs)
99
100  TracedMetaClass = trace_event_impl.TracedMetaClass
101
102  def traced(fn):
103    return trace_event_impl.traced(fn)
104
105  def clock_sync(sync_id, issue_ts=None):
106    '''
107    Add a clock sync event to the trace log.
108
109    Args:
110      sync_id: ID of clock sync event.
111      issue_ts: Time at which clock sync was issued, in microseconds.
112    '''
113    time_stamp = trace_time.Now()
114    args_to_log = {'sync_id': sync_id}
115    if issue_ts: # Issuer if issue_ts is set, else reciever.
116      assert issue_ts <= time_stamp
117      args_to_log['issue_ts'] = issue_ts
118    trace_event_impl.add_trace_event(
119        "c", time_stamp, "python", "clock_sync", args_to_log)
120
121  def is_tracing_controllable():
122    return trace_event_impl.is_tracing_controllable()
123
124else:
125  import contextlib
126
127  def trace_enable():
128    raise TraceException(
129        "Cannot enable trace_event. No trace_event_impl module found.")
130
131  def trace_disable():
132    pass
133
134  def trace_is_enabled():
135    return False
136
137  def trace_flush():
138    pass
139
140  def trace_begin(name, **kwargs):
141    del name # unused.
142    del kwargs # unused.
143    pass
144
145  def trace_end(name):
146    del name # unused.
147    pass
148
149  def trace_set_thread_name(thread_name):
150    del thread_name # unused.
151    pass
152
153  @contextlib.contextmanager
154  def trace(name, **kwargs):
155    del name # unused
156    del kwargs # unused
157    yield
158
159  def traced(fn):
160    return fn
161
162  def clock_sync(sync_id, issue_ts=None):
163    del sync_id # unused.
164    pass
165
166  def is_tracing_controllable():
167    return False
168
169trace_enable.__doc__ = """Enables tracing.
170
171  Once enabled, the enabled bit propagates to forked processes and
172  multiprocessing subprocesses. Regular child processes, e.g. those created via
173  os.system/popen, or subprocess.Popen instances, will not get traced. You can,
174  however, enable tracing on those subprocess manually.
175
176  Trace files are multiprocess safe, so you can have multiple processes
177  outputting to the same tracelog at once.
178
179  log_file can be one of three things:
180
181    None: a logfile is opened based on sys[argv], namely
182          "./" + sys.argv[0] + ".json"
183
184    string: a logfile of the given name is opened.
185
186    file-like object: the fileno() is is used. The underlying file descriptor
187                      must support fcntl.lockf() operations.
188  """
189
190trace_disable.__doc__ = """Disables tracing, if enabled.
191
192  Will not disable tracing on any existing child proceses that were forked
193  from this process. You must disable them yourself.
194  """
195
196trace_flush.__doc__ = """Flushes any currently-recorded trace data to disk.
197
198  trace_event records traces into an in-memory buffer for efficiency. Flushing
199  is only done at process exit or when this method is called.
200  """
201
202trace_is_enabled.__doc__ = """Returns whether tracing is enabled.
203  """
204
205trace_begin.__doc__ = """Records the beginning of an event of the given name.
206
207  The building block for performance tracing. A typical example is:
208     from trace_event import *
209     def something_heavy():
210        trace_begin("something_heavy")
211
212        trace_begin("read")
213        try:
214          lines = open().readlines()
215        finally:
216          trace_end("read")
217
218        trace_begin("parse")
219        try:
220          parse(lines)
221        finally:
222          trace_end("parse")
223
224        trace_end("something_heavy")
225
226  Note that a trace_end call must be issued for every trace_begin call. When
227  tracing around blocks that might throw exceptions, you should use the trace
228  function, or a try-finally pattern to ensure that the trace_end method is
229  called.
230
231  See the documentation for the @traced decorator for a simpler way to
232  instrument functions and methods.
233  """
234
235trace_end.__doc__ = """Records the end of an event of the given name.
236
237  See the documentation for trace_begin for more information.
238
239  Make sure to issue a trace_end for every trace_begin issued. Failure to pair
240  these calls will lead to bizarrely tall looking traces in the
241  trace_event_viewer UI.
242  """
243
244trace_set_thread_name.__doc__ = """Sets the trace's name for the current thread.
245  """
246
247trace.__doc__ = """Traces a block of code using a with statement.
248
249  Example usage:
250    from trace_event import *
251    def something_heavy(lines):
252      with trace("parse_lines", lines=lines):
253        parse(lines)
254
255  If tracing an entire function call, prefer the @traced decorator.
256  """
257
258traced.__doc__ = """
259  Traces the provided function, using the function name for the actual generated
260  event.
261
262  Prefer this decorator over the explicit trace_begin and trace_end functions
263  whenever you are tracing the start and stop of a function. It automatically
264  issues trace_begin/end events, even when the wrapped function throws.
265
266  You can also pass the function's argument names to traced, and the argument
267  values will be added to the trace. Example usage:
268    from trace_event import *
269    @traced("url")
270    def send_request(url):
271      urllib2.urlopen(url).read()
272  """
273
274clock_sync.__doc__ = """
275  Issues a clock sync marker event.
276
277  Clock sync markers are used to synchronize the clock domains of different
278  traces so that they can be used together. It takes a sync_id, and if it is
279  the issuer of a clock sync event it will also require an issue_ts. The
280  issue_ts is a timestamp from when the clocksync was first issued. This is used
281  to calculate the time difference between clock domains.
282  """
283