Home | History | Annotate | Download | only in trace_event_impl
      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.
      4 import atexit
      5 import json
      6 import os
      7 import sys
      8 import time
      9 import threading
     10 
     11 from py_trace_event import trace_time
     12 
     13 from py_utils import lock
     14 
     15 
     16 _lock = threading.Lock()
     17 
     18 _enabled = False
     19 _log_file = None
     20 
     21 _cur_events = [] # events that have yet to be buffered
     22 
     23 _tls = threading.local() # tls used to detect forking/etc
     24 _atexit_regsitered_for_pid = None
     25 
     26 _control_allowed = True
     27 
     28 
     29 class TraceException(Exception):
     30   pass
     31 
     32 def _note(msg, *args):
     33   pass
     34 #  print "%i: %s" % (os.getpid(), msg)
     35 
     36 
     37 def _locked(fn):
     38   def locked_fn(*args,**kwargs):
     39     _lock.acquire()
     40     try:
     41       ret = fn(*args,**kwargs)
     42     finally:
     43       _lock.release()
     44     return ret
     45   return locked_fn
     46 
     47 def _disallow_tracing_control():
     48   global _control_allowed
     49   _control_allowed = False
     50 
     51 def trace_enable(log_file=None):
     52   _trace_enable(log_file)
     53 
     54 @_locked
     55 def _trace_enable(log_file=None):
     56   global _enabled
     57   if _enabled:
     58     raise TraceException("Already enabled")
     59   if not _control_allowed:
     60     raise TraceException("Tracing control not allowed in child processes.")
     61   _enabled = True
     62   global _log_file
     63   if log_file == None:
     64     if sys.argv[0] == '':
     65       n = 'trace_event'
     66     else:
     67       n = sys.argv[0]
     68     log_file = open("%s.json" % n, "ab", False)
     69     _note("trace_event: tracelog name is %s.json" % n)
     70   elif isinstance(log_file, basestring):
     71     _note("trace_event: tracelog name is %s" % log_file)
     72     log_file = open("%s" % log_file, "ab", False)
     73   elif not hasattr(log_file, 'fileno'):
     74     raise TraceException(
     75         "Log file must be None, a string, or file-like object with a fileno()")
     76 
     77   _log_file = log_file
     78   with lock.FileLock(_log_file, lock.LOCK_EX):
     79     _log_file.seek(0, os.SEEK_END)
     80 
     81     lastpos = _log_file.tell()
     82     creator = lastpos == 0
     83     if creator:
     84       _note("trace_event: Opened new tracelog, lastpos=%i", lastpos)
     85       _log_file.write('[')
     86 
     87       tid = threading.current_thread().ident
     88       if not tid:
     89         tid = os.getpid()
     90       x = {"ph": "M", "category": "process_argv",
     91            "pid": os.getpid(), "tid": threading.current_thread().ident,
     92            "ts": trace_time.Now(),
     93            "name": "process_argv", "args": {"argv": sys.argv}}
     94       _log_file.write("%s\n" % json.dumps(x))
     95     else:
     96       _note("trace_event: Opened existing tracelog")
     97     _log_file.flush()
     98 
     99 @_locked
    100 def trace_flush():
    101   if _enabled:
    102     _flush()
    103 
    104 @_locked
    105 def trace_disable():
    106   global _enabled
    107   if not _control_allowed:
    108     raise TraceException("Tracing control not allowed in child processes.")
    109   if not _enabled:
    110     return
    111   _enabled = False
    112   _flush(close=True)
    113 
    114 def _flush(close=False):
    115   global _log_file
    116   with lock.FileLock(_log_file, lock.LOCK_EX):
    117     _log_file.seek(0, os.SEEK_END)
    118     if len(_cur_events):
    119       _log_file.write(",\n")
    120       _log_file.write(",\n".join([json.dumps(e) for e in _cur_events]))
    121       del _cur_events[:]
    122 
    123     if close:
    124       # We might not be the only process writing to this logfile. So,
    125       # we will simply close the file rather than writign the trailing ] that
    126       # it technically requires. The trace viewer understands that this may
    127       # happen and will insert a trailing ] during loading.
    128       pass
    129     _log_file.flush()
    130 
    131   if close:
    132     _note("trace_event: Closed")
    133     _log_file.close()
    134     _log_file = None
    135   else:
    136     _note("trace_event: Flushed")
    137 
    138 @_locked
    139 def trace_is_enabled():
    140   return _enabled
    141 
    142 @_locked
    143 def add_trace_event(ph, ts, category, name, args=None):
    144   global _enabled
    145   if not _enabled:
    146     return
    147   if not hasattr(_tls, 'pid') or _tls.pid != os.getpid():
    148     _tls.pid = os.getpid()
    149     global _atexit_regsitered_for_pid
    150     if _tls.pid != _atexit_regsitered_for_pid:
    151       _atexit_regsitered_for_pid = _tls.pid
    152       atexit.register(_trace_disable_atexit)
    153       _tls.pid = os.getpid()
    154       del _cur_events[:] # we forked, clear the event buffer!
    155     tid = threading.current_thread().ident
    156     if not tid:
    157       tid = os.getpid()
    158     _tls.tid = tid
    159 
    160   _cur_events.append({"ph": ph,
    161                       "category": category,
    162                       "pid": _tls.pid,
    163                       "tid": _tls.tid,
    164                       "ts": ts,
    165                       "name": name,
    166                       "args": args or {}});
    167 
    168 def trace_begin(name, args=None):
    169   add_trace_event("B", trace_time.Now(), "python", name, args)
    170 
    171 def trace_end(name, args=None):
    172   add_trace_event("E", trace_time.Now(), "python", name, args)
    173 
    174 def trace_set_thread_name(thread_name):
    175   add_trace_event("M", trace_time.Now(), "__metadata", "thread_name",
    176                   {"name": thread_name})
    177 
    178 def _trace_disable_atexit():
    179   trace_disable()
    180 
    181 def is_tracing_controllable():
    182   global _control_allowed
    183   return _control_allowed
    184