ATLAS Offline Software
Debugging.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
2 
3 
8 
9 __doc__ = """py-module to hold a few tools and utilities to help debugging
10 configurables and/or Athena application.
11 """
12 
13 import io
14 
15 class DbgStage:
16  """Class to hold the stage at which the user asked to hook the debugger
17  """
18  value = None
19  allowed_values = ( "conf", "init", "exec", "fini" )
20  pass # class DbgStage
21 
23  """debugging helper, hooks debugger to running interpreter process
24  gdb can be overridden via $ATLAS_DEBUGGER
25  """
26  import os
27  debugger = os.environ.get('ATLAS_DEBUGGER', 'gdb')
28 
29  pid = os.spawnvp(os.P_NOWAIT,
30  debugger, [debugger, '-q', 'python', str(os.getpid())])
31 
32  # give debugger some time to attach to the python process
33  import time
34  time.sleep( 1 )
35 
36  # verify the process' existence (will raise OSError if failed)
37  os.waitpid( pid, os.WNOHANG )
38  os.kill( pid, 0 )
39  return
40 
41 def hookStrace(out=None):
42  """Same than for hookDebugger: spawn strace and attach to the running
43  interpreter process
44  """
45  import os
46  if out is None: out = 'athena.strace.log.%i' % os.getpid()
47  if isinstance(out, io.IOBase):
48  out = out.name
49  elif not isinstance(out,str):
50  raise TypeError('argument 0 needs to be either a file or a filename')
51 
52  pid = os.spawnvp(os.P_NOWAIT,
53  'strace', ['strace',
54  '-f', # follow children
55  '-p', str(os.getpid()),
56  '-o', out])
57 
58  # give strace some time to attach to the python process
59  import time
60  time.sleep( 1 )
61 
62  # verify the process' existence (will raise OSError if failed)
63  os.waitpid( pid, os.WNOHANG )
64  os.kill( pid, 0 )
65  return
66 
67 
69  """On kernels with Yama enabled, ptrace may not work by default on processes
70 which are not decendants of the tracing process. Among other things, that
71 causes the way we attach the debugger to fail. However, we can disable this
72 on a per-process basis. Do that here.
73 
74 See https://www.kernel.org/doc/Documentation/security/Yama.txt and prctl(2).
75 """
76 
77  # First test to see if ptrace restrictions are enabled.
78  import os
79  # Return if this kernel does not support ptrace restrictions.
80  if not os.path.exists ('/proc/sys/kernel/yama/ptrace_scope'): return
81 
82  # Return if ptrace restrictions are disabled.
83  with open('/proc/sys/kernel/yama/ptrace_scope') as f:
84  if f.readline().strip() == '0':
85  return
86 
87  # Use prctl to try to enable ptrace.
88  from ctypes import CDLL
89  libc = CDLL("libc.so.6")
90  # Args are PTRACE_SET_PTRACER (4HYama) and
91  # PR_SET_PTRACER_ANY ((unsigned long)-1).
92  libc.prctl (0x59616d61, 0xffffffffffffffff)
93 
94  return
95 
96 
97 def dumpPythonProfile(filename):
98  """Save python profile data of the default athena profiler instance
99  into filename (.txt or .pkl format).
100  """
101  from AthenaCommon.Logging import log
102  import cProfile
103  import pstats
104 
105  profiler = cProfile._athena_python_profiler
106  profiler.disable()
107  if filename.endswith(".txt"):
108  stats = pstats.Stats(profiler, stream=open(filename, 'w'))
109  stats.strip_dirs().sort_stats("time").print_stats()
110  log.info("Python profile summary stored in %s", filename)
111  else:
112  profiler.dump_stats(filename)
113  log.info("Python profile stored in %s", filename)
114 
115 
116 def traceExecution(script, level):
117  """Run script with the given trace level"""
118  import sys
119  import trace
120  ignore_dirs = []
121  ignore_mods = []
122 
123  # exclude system and root libraries
124  if level > 0:
125  ignore_dirs = [x for x in sys.path if x.find("ROOT")!=-1]
126  ignore_dirs.append(sys.prefix)
127  if level > 1:
128  ignore_mods += ['_db', '__init__', '_configurables', 'GaudiHandles', 'six']
129  if level > 2:
130  ignore_mods += [
131  # Legacy stuff we pull in b/c PythonAlgorithms and such still derive from old-style configurables
132  'Configurable','Configurables','PropertyProxy','DataHandle','ConfigurableDb',
133  'ConfigurableMeta', 'CfgMgr',
134  # Internals of the ComponentAccumulator
135  'ComponentAccumulator','Logging','AthConfigFlags','AllConfigFlags','Deduplication',
136  'semantics','AccumulatorCache','DebuggingContext','AtlasSemantics','ComponentFactory',
137  'LegacySupport','ItemListSemantics'
138  ]
139 
140  # taken from cpython/Lib/trace.py
141  tracer = trace.Trace(ignoredirs=ignore_dirs, ignoremods=ignore_mods)
142 
143  with io.open_code(script) as f:
144  code = compile(f.read(), script, 'exec')
145  tracer.run(code)
python.Debugging.traceExecution
def traceExecution(script, level)
Definition: Debugging.py:116
python.Debugging.DbgStage
Definition: Debugging.py:15
python.Debugging.dumpPythonProfile
def dumpPythonProfile(filename)
Definition: Debugging.py:97
python.Debugging.allowPtrace
def allowPtrace()
Definition: Debugging.py:68
Trk::open
@ open
Definition: BinningType.h:40
python.Debugging.hookStrace
def hookStrace(out=None)
Definition: Debugging.py:41
str
Definition: BTagTrackIpAccessor.cxx:11
python.Debugging.hookDebugger
def hookDebugger()
Definition: Debugging.py:22