Debugger: Create a copy of the current dumper code

The current code supports Python 2 and Python 3 based debugger backends
(gdb, lldb) at the same time, but we'd like to drop Python 2 support
so we can take advantage of some of Python 3's goodies.

This copy here is not meant to be used in general but could perhaps be
used to replace the main code in situations that cannot use Python 3 yet.

Change-Id: I62273bc41b5a1e3a24720e167e64e4eac2e0c056
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
hjk
2023-12-04 16:18:00 +01:00
parent ee4493ffa4
commit 311c95cfcc
17 changed files with 17653 additions and 0 deletions

View File

@@ -0,0 +1,377 @@
# Copyright (C) 2021 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
import gdb
import sys
import time
# for ProcessName capture
try:
import psutil
except:
psutil = None
# Caps types
Address, \
Caller, \
Callstack, \
FilePos, \
Function, \
Pid, \
ProcessName, \
Tick, \
Tid, \
ThreadName, \
Expression, \
= range(0, 11)
class GDBTracepoint(gdb.Breakpoint):
"""
Python Breakpoint extension for "tracepoints", breakpoints that do not stop the inferior
"""
@staticmethod
def create(args, onModified, onHit, onExpression):
"""
Static creator function
"""
tp_kwargs = {}
if 'temporary' in args.keys():
tp_kwargs['temporary'] = args['temporary']
spec = args['spec']
tp = GDBTracepoint(spec, **tp_kwargs)
tp.onModified = onModified
tp.onHit = onHit
tp.onExpression = onExpression
if 'ignore_count' in args.keys():
tp.ignore_count = args['ignore_count']
if 'enabled' in args.keys():
tp.enabled = args['enabled']
if 'thread' in args.keys():
tp.thread = args['thread']
if 'condition' in args.keys():
tp.condition = args['condition']
if 'caps' in args.keys():
for ce in args['caps']:
tp.addCaps(ce[0], ce[1])
return tp
def __init__(self, spec, **kwargs):
"""
Constructor
"""
kwargs['internal'] = True
super(GDBTracepoint, self).__init__(spec, **kwargs)
self.caps = []
_hexSize = 8 if sys.maxsize > 2**32 else 4
_hasMonotonicTime = False if sys.version_info[0] <= 2 or (sys.version_info[0] == 3 and sys.version_info[1] < 3) else True
def dicts(self):
"""
Returns dictionareis for mi representation
"""
results = []
result = {}
result['number'] = str(self.number)
result['enabled'] = 'y' if self.enabled else 'n'
result['type'] = 'pseudo_tracepoint'
result['disp'] = 'del' if self.temporary else 'keep'
result['times'] = str(self.hit_count)
result['original-location'] = self.location
try:
d = gdb.decode_line(self.location)
if d[1] is None:
result['addr'] = '<PENDING>'
result['pending'] = self.location
results.append(result)
else:
if len(d[1]) > 1:
result['addr'] = '<MULTIPLE>'
results.append(result)
for i, sl in enumerate(d[1]):
result_ = {}
result_['number'] = result['number'] + "." + str(i + 1)
result_['enabled'] = 'y' if self.enabled else 'n'
if sl.pc is None:
result_['addr'] = '<no address>'
else:
result_['addr'] = '{0:#0{1}x}'.format(sl.pc, self._hexSize + 2)
if sl.symtab and sl.symtab.is_valid():
func = self._getFunctionFromAddr(sl.pc)
if func:
result_['func'] = func.print_name
result_['file'] = sl.symtab.filename
result_['fullname'] = sl.symtab.fullname()
result_['line'] = sl.line
results.append(result_)
else:
sl = d[1][0]
if sl.pc is None:
result['addr'] = '<no address>'
else:
result['addr'] = '{0:#0{1}x}'.format(sl.pc, self._hexSize + 2)
if sl.symtab and sl.symtab.is_valid():
func = self._getFunctionFromAddr(sl.pc)
if func:
result['func'] = func.print_name
result['file'] = sl.symtab.filename
result['fullname'] = sl.symtab.fullname()
result['line'] = sl.line
results.append(result)
except Exception as e:
import traceback
traceback.print_exc()
result['addr'] = '<PENDING>'
result['pending'] = self.location
results.append(result)
return results
def addCaps(self, capsType, expression=None):
"""
Adds capture expressions for a tracepoint
:param caps_type: Type of capture
:param expression: Expression for Expression caps type
"""
if capsType != Expression:
expression = None
else:
if expression is None:
expression = ''
self.caps.append((self.capsMap[capsType], expression))
def stop(self):
"""
Overridden stop function, this evaluates conditions and captures data from the inferior
:return: Always False
"""
try:
self.onModified(self)
result = {}
result['number'] = self.number
try:
if self.condition:
try:
result = gdb.parse_and_eval(self.condition)
if result.type.code == gdb.TYPE_CODE_BOOL and str(result) == 'false':
return False
except:
pass
if self.ignore_count > 0:
return False
if self.thread and gdb.selected_thread().global_num != self.thread:
return False
except Exception as e:
result['warning'] = str(e)
self.onHit(self, result)
return False
if len(self.caps) > 0:
caps = []
try:
for func, expr in self.caps:
if expr is None:
caps.append(func(self))
else:
caps.append(func(self, expr))
except Exception as e:
result['warning'] = str(e)
self.onHit(self, result)
return False
result['caps'] = caps
self.onHit(self, result)
return False
except:
# Always return false, regardless...
return False
def _getFunctionFromAddr(self, addr):
try:
block = gdb.block_for_pc(addr)
while block and not block.function:
block = block.superblock
if block is None:
return None
return block.function
except:
return None
def _getAddress(self):
"""
Capture function for Address
"""
try:
frame = gdb.selected_frame()
if not (frame is None) and (frame.is_valid()):
return '{0:#0{1}x}'.format(frame.pc(), self._hexSize + 2)
except Exception as e:
return str(e)
return '<null address>'
def _getCaller(self):
"""
Capture function for Caller
"""
try:
frame = gdb.selected_frame()
if not (frame is None) and (frame.is_valid()):
frame = frame.older()
if not (frame is None) and (frame.is_valid()):
name = frame.name()
if name is None:
return '<unknown caller>'
return name
except Exception as e:
return str(e)
return '<unknown caller>'
def _getCallstack(self):
"""
Capture function for Callstack
"""
try:
frames = []
frame = gdb.selected_frame()
if (frame is None) or (not frame.is_valid()):
frames.append('<unknown frame>')
return str(frames)
while not (frame is None):
func = frame.function()
if func is None:
frames.append('{0:#0{1}x}'.format(frame.pc(), self._hexSize + 2))
else:
sl = frame.find_sal()
if sl is None:
frames.append(func.symtab.filename)
else:
frames.append(func.symtab.filename + ':' + str(sl.line))
frame = frame.older()
return frames
except Exception as e:
return str(e)
def _getFilePos(self):
"""
Capture function for FilePos
"""
try:
frame = gdb.selected_frame()
if (frame is None) or (not frame.is_valid()):
return '<unknown file pos>'
sl = frame.find_sal()
if sl is None:
return '<unknown file pos>'
return sl.symtab.filename + ':' + str(sl.line)
except Exception as e:
return str(e)
def _getFunction(self):
"""
Capture function for Function
"""
try:
frame = gdb.selected_frame()
if not (frame is None):
return str(frame.name())
except Exception as e:
return str(e)
return '<unknown function>'
def _getPid(self):
"""
Capture function for Pid
"""
try:
thread = gdb.selected_thread()
if not (thread is None):
(pid, lwpid, tid) = thread.ptid
return str(pid)
except Exception as e:
return str(e)
return '<unknown pid>'
def _getProcessName(slef):
"""
Capture for ProcessName
"""
# gdb does not expose process name, neither does (standard) python
# You can use for example psutil, but it might not be present.
# Default to name of thread with ID 1
inf = gdb.selected_inferior()
if psutil is None:
try:
if inf is None:
return '<unknown process name>'
threads = filter(lambda t: t.num == 1, list(inf.threads()))
if len(threads) < 1:
return '<unknown process name>'
thread = threads[0]
# use thread name
return thread.name
except Exception as e:
return str(e)
else:
return psutil.Process(inf.pid).name()
def _getTick(self):
"""
Capture function for Tick
"""
if self._hasMonotonicTime:
return str(int(time.monotonic() * 1000))
else:
return '<monotonic time not available>'
def _getTid(self):
"""
Capture function for Tid
"""
try:
thread = gdb.selected_thread()
if not (thread is None):
(pid, lwpid, tid) = thread.ptid
if tid == 0:
return str(lwpid)
else:
return str(tid)
except Exception as e:
return str(e)
return '<unknown tid>'
def _getThreadName(self):
"""
Capture function for ThreadName
"""
try:
thread = gdb.selected_thread()
if not (thread is None):
return str(thread.name)
except Exception as e:
return str(e)
return '<unknown thread name>'
def _getExpression(self, expression):
"""
Capture function for Expression
:param expr: The expression to evaluate
"""
try:
value = gdb.parse_and_eval(expression)
if value:
return self.onExpression(self, expression, value)
except Exception as e:
return self.onExpression(self, expression, e)
capsMap = {Address: _getAddress,
Caller: _getCaller,
Callstack: _getCallstack,
FilePos: _getFilePos,
Function: _getFunction,
Pid: _getPid,
ProcessName: _getProcessName,
Tid: _getTid,
Tick: _getTick,
ThreadName: _getThreadName,
Expression: _getExpression}