forked from qt-creator/qt-creator
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>
378 lines
12 KiB
Python
378 lines
12 KiB
Python
# 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}
|