Add gdb tracepoint support for Linux

Change-Id: Id2e46bae576a730f8c1b64a247aeed12e6d721af
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Mattias Johansson
2021-01-07 13:28:05 +01:00
committed by Gustav Johansson
parent 874029809f
commit 2081038953
10 changed files with 773 additions and 19 deletions

View File

@@ -942,7 +942,7 @@ class DumperBase():
for (k, v) in list(value.items())]) + '}'
if isinstance(value, list):
return '[' + ','.join([self.resultToMi(k)
for k in list(value.items())]) + ']'
for k in value]) + ']'
return '"%s"' % value
def variablesToMi(self, value, prefix):

View File

@@ -38,6 +38,8 @@ import tempfile
from dumper import DumperBase, Children, toInteger, TopLevelItem
from utils import TypeCode
from gdbtracepoint import *
#######################################################################
#
@@ -1466,7 +1468,55 @@ class Dumper(DumperBase):
print(timeit.repeat('theDumper.fetchVariables(%s)' % args,
'from __main__ import theDumper', number=10))
def tracepointModified(self, tp):
self.tpExpressions = {}
self.tpExpressionWarnings = []
s = self.resultToMi(tp.dicts())
def handler():
print("tracepointmodified=%s" % s)
gdb.post_event(handler)
def tracepointHit(self, tp, result):
expressions = '{' + ','.join(["%s=%s" % (k,v) for k,v in self.tpExpressions.items()]) + '}'
warnings = []
if 'warning' in result.keys():
warnings.append(result.pop('warning'))
warnings += self.tpExpressionWarnings
r = self.resultToMi(result)
w = self.resultToMi(warnings)
def handler():
print("tracepointhit={result=%s,expressions=%s,warnings=%s}" % (r, expressions, w))
gdb.post_event(handler)
def tracepointExpression(self, tp, expression, value, args):
key = "x" + str(len(self.tpExpressions))
if (isinstance(value, gdb.Value)):
try:
val = self.fromNativeValue(value)
self.prepare(args)
with TopLevelItem(self, expression):
self.putItem(val)
self.tpExpressions[key] = self.output
except Exception as e:
self.tpExpressions[key] = '"<N/A>"'
self.tpExpressionWarnings.append(str(e))
elif (isinstance(value, Exception)):
self.tpExpressions[key] = '"<N/A>"'
self.tpExpressionWarnings.append(str(value))
else:
self.tpExpressions[key] = '"<N/A>"'
self.tpExpressionWarnings.append('Unknown expression value type')
return key
def createTracepoint(self, args):
"""
Creates a tracepoint
"""
tp = GDBTracepoint.create(args,
onModified=self.tracepointModified,
onHit=self.tracepointHit,
onExpression=lambda tp, expr, val: self.tracepointExpression(tp, expr, val, args))
self.reportResult("tracepoint=%s" % self.resultToMi(tp.dicts()), args)
class CliDumper(Dumper):
def __init__(self):
Dumper.__init__(self)

View File

@@ -0,0 +1,399 @@
############################################################################
#
# Copyright (C) 2021 The Qt Company Ltd.
# Contact: https://www.qt.io/licensing/
#
# This file is part of Qt Creator.
#
# Commercial License Usage
# Licensees holding valid commercial Qt licenses may use this file in
# accordance with the commercial license agreement provided with the
# Software or, alternatively, in accordance with the terms contained in
# a written agreement between you and The Qt Company. For licensing terms
# and conditions see https://www.qt.io/terms-conditions. For further
# information use the contact form at https://www.qt.io/contact-us.
#
# GNU General Public License Usage
# Alternatively, this file may be used under the terms of the GNU
# General Public License version 3 as published by the Free Software
# Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
# included in the packaging of this file. Please review the following
# information to ensure the GNU General Public License requirements will
# be met: https://www.gnu.org/licenses/gpl-3.0.html.
#
############################################################################
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}

View File

@@ -611,17 +611,13 @@ void BreakpointDialog::setPartsEnabled(unsigned partsMask)
m_lineEditModule->setEnabled(partsMask & ModulePart);
m_labelTracepoint->setEnabled(partsMask & TracePointPart);
m_labelTracepoint->hide();
m_checkBoxTracepoint->setEnabled(partsMask & TracePointPart);
m_checkBoxTracepoint->hide();
m_labelCommands->setEnabled(partsMask & CommandPart);
m_textEditCommands->setEnabled(partsMask & CommandPart);
m_labelMessage->setEnabled(partsMask & TracePointPart);
m_labelMessage->hide();
m_lineEditMessage->setEnabled(partsMask & TracePointPart);
m_lineEditMessage->hide();
}
void BreakpointDialog::clearOtherParts(unsigned partsMask)
@@ -2106,6 +2102,8 @@ QString BreakpointItem::msgBreakpointTriggered(const QString &threadId) const
QVariant SubBreakpointItem::data(int column, int role) const
{
if (role == Qt::DecorationRole && column == 0) {
if (params.tracepoint)
return Icons::TRACEPOINT.icon();
return params.enabled ? Icons::BREAKPOINT.icon()
: Icons::BREAKPOINT_DISABLED.icon();
}

View File

@@ -176,6 +176,7 @@ public:
void setIgnoreCount(int count) { m_parameters.ignoreCount = count; }
void setCommand(const QString &command) { m_parameters.command = command; }
void setCondition(const QString &condition) { m_parameters.condition = condition; }
void setMessage(const QString& message) { m_parameters.message = message; }
QString msgWatchpointByAddressTriggered(quint64 address) const;
QString msgWatchpointByAddressTriggered(quint64 address, const QString &threadId) const;

View File

@@ -506,6 +506,12 @@ DebuggerSettings::DebuggerSettings()
item->setDefaultValue(true);
insertItem(UseAnnotationsInMainEditor, item);
item = new SavedAction;
item->setSettingsKey(debugModeGroup, "UsePseudoTracepoints");
item->setCheckable(true);
item->setDefaultValue(true);
insertItem(UsePseudoTracepoints, item);
item = new SavedAction;
item->setSettingsKey(debugModeGroup, "UseToolTips");
item->setText(tr("Use tooltips in main editor when debugging"));

View File

@@ -134,6 +134,7 @@ enum DebuggerActionCode
WarnOnReleaseBuilds,
MultiInferior,
IntelFlavor,
UsePseudoTracepoints,
// Stack
MaximalStackDepth,

View File

@@ -115,6 +115,31 @@ static QMessageBox *showMessageBox(QMessageBox::Icon icon,
return mb;
}
enum class TracepointCaptureType
{
Address,
Caller,
Callstack,
FilePos,
Function,
Pid,
ProcessName,
Tick,
Tid,
ThreadName,
Expression
};
struct TracepointCaptureData
{
TracepointCaptureType type;
QVariant expression;
int start;
int end;
};
const char tracepointCapturePropertyName[] = "GDB.TracepointCapture";
///////////////////////////////////////////////////////////////////////
//
// GdbEngine
@@ -307,6 +332,18 @@ void GdbEngine::handleResponse(const QString &buff)
handleInterpreterBreakpointModified(allData["interpreterasync"]);
break;
}
if (data.startsWith("tracepointhit={")) {
GdbMi allData;
allData.fromStringMultiple(data);
handleTracepointHit(allData["tracepointhit"]);
break;
}
if (data.startsWith("tracepointmodified=")) {
GdbMi allData;
allData.fromStringMultiple(data);
handleTracepointModified(allData["tracepointmodified"]);
break;
}
m_pendingConsoleStreamOutput += data;
// Fragile, but it's all we have.
@@ -2162,6 +2199,7 @@ void GdbEngine::handleCatchInsert(const DebuggerResponse &response, const Breakp
void GdbEngine::handleBkpt(const GdbMi &bkpt, const Breakpoint &bp)
{
QTC_ASSERT(bp, return);
bool usePseudoTracepoints = boolSetting(UsePseudoTracepoints);
const QString nr = bkpt["number"].data();
if (nr.contains('.')) {
// A sub-breakpoint.
@@ -2169,6 +2207,10 @@ void GdbEngine::handleBkpt(const GdbMi &bkpt, const Breakpoint &bp)
QTC_ASSERT(sub, return);
sub->params.updateFromGdbOutput(bkpt);
sub->params.type = bp->type();
if (usePseudoTracepoints && bp->isTracepoint()) {
sub->params.tracepoint = true;
sub->params.message = bp->message();
}
return;
}
@@ -2183,12 +2225,18 @@ void GdbEngine::handleBkpt(const GdbMi &bkpt, const Breakpoint &bp)
QTC_ASSERT(sub, return);
sub->params.updateFromGdbOutput(location);
sub->params.type = bp->type();
if (usePseudoTracepoints && bp->isTracepoint()) {
sub->params.tracepoint = true;
sub->params.message = bp->message();
}
}
}
// A (the?) primary breakpoint.
bp->setResponseId(nr);
bp->updateFromGdbOutput(bkpt);
if (usePseudoTracepoints && bp->isTracepoint())
bp->setMessage(bp->requestedParameters().message);
}
void GdbEngine::handleBreakInsert1(const DebuggerResponse &response, const Breakpoint &bp)
@@ -2334,6 +2382,176 @@ void GdbEngine::handleBreakCondition(const DebuggerResponse &, const Breakpoint
updateBreakpoint(bp); // Maybe there's more to do.
}
void GdbEngine::updateTracepointCaptures(const Breakpoint &bp)
{
static QRegularExpression capsRegExp(
"(^|[^\\\\])(\\$(ADDRESS|CALLER|CALLSTACK|FILEPOS|FUNCTION|PID|PNAME|TICK|TID|TNAME)"
"|{[^}]+})");
QString message = bp->globalBreakpoint()->requestedParameters().message;
if (message.isEmpty()) {
bp->setProperty(tracepointCapturePropertyName, {});
return;
}
QVariantList caps;
QRegularExpressionMatch match = capsRegExp.match(message, 0);
while (match.hasMatch()) {
QString t = match.captured(2);
if (t[0] == '$') {
TracepointCaptureType type;
if (t == "$ADDRESS")
type = TracepointCaptureType::Address;
else if (t == "$CALLER")
type = TracepointCaptureType::Caller;
else if (t == "$CALLSTACK")
type = TracepointCaptureType::Callstack;
else if (t == "$FILEPOS")
type = TracepointCaptureType::FilePos;
else if (t == "$FUNCTION")
type = TracepointCaptureType::Function;
else if (t == "$PID")
type = TracepointCaptureType::Pid;
else if (t == "$PNAME")
type = TracepointCaptureType::ProcessName;
else if (t == "$TICK")
type = TracepointCaptureType::Tick;
else if (t == "$TID")
type = TracepointCaptureType::Tid;
else if (t == "$TNAME")
type = TracepointCaptureType::ThreadName;
else
QTC_ASSERT(false, continue);
caps << QVariant::fromValue<TracepointCaptureData>(
{type, {}, match.capturedStart(2), match.capturedEnd(2)});
} else {
QString expression = t.mid(1, t.length() - 2);
caps << QVariant::fromValue<TracepointCaptureData>(
{TracepointCaptureType::Expression, expression, match.capturedStart(2), match.capturedEnd(2)});
}
match = capsRegExp.match(message, match.capturedEnd());
}
bp->setProperty(tracepointCapturePropertyName, caps);
}
void GdbEngine::handleTracepointInsert(const DebuggerResponse &response, const Breakpoint &bp)
{
QTC_ASSERT(bp, return);
if (bp->state() == BreakpointRemoveRequested) {
if (response.resultClass == ResultDone) {
// This delete was deferred. Act now.
const GdbMi mainbkpt = response.data["tracepoint"][0];
notifyBreakpointRemoveProceeding(bp);
DebuggerCommand cmd("-break-delete " + mainbkpt["number"].data());
cmd.flags = NeedsTemporaryStop;
runCommand(cmd);
notifyBreakpointRemoveOk(bp);
return;
}
}
if (response.resultClass == ResultDone) {
for (const GdbMi &bkpt : response.data["tracepoint"])
handleBkpt(bkpt, bp);
if (bp->needsChange()) {
bp->gotoState(BreakpointUpdateRequested, BreakpointInsertionProceeding);
updateBreakpoint(bp);
} else {
notifyBreakpointInsertOk(bp);
}
}
}
void GdbEngine::handleTracepointHit(const GdbMi &data)
{
const GdbMi &result = data["result"];
const QString rid = result["number"].data();
Breakpoint bp = breakHandler()->findBreakpointByResponseId(rid);
QTC_ASSERT(bp, return);
const GdbMi &warnings = data["warnings"];
if (warnings.childCount() > 0) {
for (const GdbMi &warning: warnings) {
emit appendMessageRequested(warning.toString(), ErrorMessageFormat, true);
}
}
QString message = bp->message();
QVariant caps = bp->property(tracepointCapturePropertyName);
if (caps.isValid()) {
QList<QVariant> capsList = caps.toList();
const GdbMi &miCaps = result["caps"];
if (capsList.length() == miCaps.childCount()) {
// reverse iterate to make start/end correct
for (int i = capsList.length() - 1; i >= 0; --i) {
TracepointCaptureData cap = capsList.at(i).value<TracepointCaptureData>();
const GdbMi &miCap = miCaps.childAt(i);
switch (cap.type) {
case TracepointCaptureType::Callstack: {
QStringList frames;
for (const GdbMi &frame: miCap) {
frames.append(frame.data());
}
message.replace(cap.start, cap.end - cap.start, frames.join(" <- "));
break;
}
case TracepointCaptureType::Expression: {
QString key = miCap.data();
const GdbMi &expression = data["expressions"][key.toLatin1().data()];
if (expression.isValid()) {
QString s = expression.toString();
// remove '<key>='
s = s.right(s.length() - key.length() - 1);
message.replace(cap.start, cap.end - cap.start, s);
} else {
QTC_CHECK(false);
}
break;
}
default:
message.replace(cap.start, cap.end - cap.start, miCap.data());
}
}
} else {
QTC_CHECK(false);
}
}
showMessage(message);
emit appendMessageRequested(message, NormalMessageFormat, true);
}
void GdbEngine::handleTracepointModified(const GdbMi &data)
{
QString ba = data.toString();
// remove original-location
const int pos1 = ba.indexOf("original-location=");
const int pos2 = ba.indexOf(":", pos1 + 17);
int pos3 = ba.indexOf('"', pos2 + 1);
if (ba[pos3 + 1] == ',')
++pos3;
ba.remove(pos1, pos3 - pos1 + 1);
GdbMi res;
res.fromString(ba);
BreakHandler *handler = breakHandler();
Breakpoint bp;
for (const GdbMi &bkpt : res) {
const QString nr = bkpt["number"].data();
if (nr.contains('.')) {
// A sub-breakpoint.
QTC_ASSERT(bp, continue);
SubBreakpoint loc = bp->findOrCreateSubBreakpoint(nr);
loc->params.updateFromGdbOutput(bkpt);
loc->params.type = bp->type();
if (bp->isTracepoint()) {
loc->params.tracepoint = true;
loc->params.message = bp->message();
}
} else {
// A primary breakpoint.
bp = handler->findBreakpointByResponseId(nr);
if (bp)
bp->updateFromGdbOutput(bkpt);
}
}
QTC_ASSERT(bp, return);
bp->adjustMarker();
}
bool GdbEngine::acceptsBreakpoint(const BreakpointParameters &bp) const
{
if (runParameters().startMode == AttachToCore)
@@ -2387,31 +2605,100 @@ void GdbEngine::insertBreakpoint(const Breakpoint &bp)
cmd.function = "catch syscall";
cmd.callback = handleCatch;
} else {
int spec = requested.threadSpec;
if (requested.isTracepoint()) {
cmd.function = "-break-insert -a -f ";
if (boolSetting(UsePseudoTracepoints)) {
cmd.function = "createTracepoint";
if (requested.oneShot)
cmd.arg("temporary", true);
if (int ignoreCount = requested.ignoreCount)
cmd.arg("ignore_count", ignoreCount);
QString condition = requested.condition;
if (!condition.isEmpty())
cmd.arg("condition", condition.replace('"', "\\\""));
if (spec >= 0)
cmd.arg("thread", spec);
updateTracepointCaptures(bp);
QVariant tpCaps = bp->property(tracepointCapturePropertyName);
if (tpCaps.isValid()) {
QJsonArray caps;
foreach (const auto &tpCap, tpCaps.toList()) {
TracepointCaptureData data = tpCap.value<TracepointCaptureData>();
QJsonArray cap;
cap.append(static_cast<int>(data.type));
if (data.expression.isValid())
cap.append(data.expression.toString());
else
cap.append(QJsonValue::Null);
caps.append(cap);
}
cmd.arg("caps", caps);
}
// for dumping of expressions
const static bool alwaysVerbose = qEnvironmentVariableIsSet("QTC_DEBUGGER_PYTHON_VERBOSE");
cmd.arg("passexceptions", alwaysVerbose);
cmd.arg("fancy", boolSetting(UseDebuggingHelpers));
cmd.arg("autoderef", boolSetting(AutoDerefPointers));
cmd.arg("dyntype", boolSetting(UseDynamicType));
cmd.arg("qobjectnames", boolSetting(ShowQObjectNames));
cmd.arg("nativemixed", isNativeMixedActive());
cmd.arg("stringcutoff", action(MaximalStringLength)->value().toString());
cmd.arg("displaystringlimit", action(DisplayStringLimit)->value().toString());
cmd.arg("spec", breakpointLocation2(requested));
cmd.callback = [this, bp](const DebuggerResponse &r) { handleTracepointInsert(r, bp); };
} else {
cmd.function = "-break-insert -a -f ";
if (requested.oneShot)
cmd.function += "-t ";
if (!requested.enabled)
cmd.function += "-d ";
if (int ignoreCount = requested.ignoreCount)
cmd.function += "-i " + QString::number(ignoreCount) + ' ';
QString condition = requested.condition;
if (!condition.isEmpty())
cmd.function += " -c \"" + condition.replace('"', "\\\"") + "\" ";
cmd.function += breakpointLocation(requested);
cmd.callback = [this, bp](const DebuggerResponse &r) { handleBreakInsert1(r, bp); };
}
} else {
int spec = requested.threadSpec;
cmd.function = "-break-insert ";
if (spec >= 0)
cmd.function += "-p " + QString::number(spec);
cmd.function += " -f ";
}
if (requested.oneShot)
cmd.function += "-t ";
if (requested.oneShot)
cmd.function += "-t ";
if (!requested.enabled)
cmd.function += "-d ";
if (!requested.enabled)
cmd.function += "-d ";
if (int ignoreCount = requested.ignoreCount)
cmd.function += "-i " + QString::number(ignoreCount) + ' ';
if (int ignoreCount = requested.ignoreCount)
cmd.function += "-i " + QString::number(ignoreCount) + ' ';
QString condition = requested.condition;
if (!condition.isEmpty())
cmd.function += " -c \"" + condition.replace('"', "\\\"") + "\" ";
QString condition = requested.condition;
if (!condition.isEmpty())
cmd.function += " -c \"" + condition.replace('"', "\\\"") + "\" ";
cmd.function += breakpointLocation(requested);
cmd.callback = [this, bp](const DebuggerResponse &r) { handleBreakInsert1(r, bp); };
cmd.function += breakpointLocation(requested);
cmd.callback = [this, bp](const DebuggerResponse &r) { handleBreakInsert1(r, bp); };
}
}
cmd.flags = NeedsTemporaryStop;
runCommand(cmd);
@@ -4831,3 +5118,4 @@ DebuggerEngine *createGdbEngine()
} // namespace Debugger
Q_DECLARE_METATYPE(Debugger::Internal::GdbMi)
Q_DECLARE_METATYPE(Debugger::Internal::TracepointCaptureData)

View File

@@ -223,6 +223,9 @@ private: ////////// General Interface //////////
void handleBreakCondition(const DebuggerResponse &response, const Breakpoint &bp);
void handleBreakThreadSpec(const DebuggerResponse &response, const Breakpoint &bp);
void handleBreakLineNumber(const DebuggerResponse &response, const Breakpoint &bp);
void handleTracepointInsert(const DebuggerResponse &response, const Breakpoint &bp);
void handleTracepointHit(const GdbMi &data);
void handleTracepointModified(const GdbMi &data);
void handleInsertInterpreterBreakpoint(const DebuggerResponse &response, const Breakpoint &bp);
void handleInterpreterBreakpointModified(const GdbMi &data);
void handleWatchInsert(const DebuggerResponse &response, const Breakpoint &bp);
@@ -231,6 +234,7 @@ private: ////////// General Interface //////////
QString breakpointLocation(const BreakpointParameters &data); // For gdb/MI.
QString breakpointLocation2(const BreakpointParameters &data); // For gdb/CLI fallback.
QString breakLocation(const QString &file) const;
void updateTracepointCaptures(const Breakpoint &bp);
//
// Modules specific stuff

View File

@@ -153,6 +153,11 @@ GdbOptionsPageWidget::GdbOptionsPageWidget()
"<html><head/><body>GDB shows by default AT&&T style disassembly."
"</body></html>"));
auto checkBoxUsePseudoTracepoints = new QCheckBox(groupBoxGeneral);
checkBoxUsePseudoTracepoints->setText(GdbOptionsPage::tr("Use pseudo message tracepoints"));
checkBoxUsePseudoTracepoints->setToolTip(GdbOptionsPage::tr(
"Uses python to extend the ordinary GDB breakpoint class."));
QString howToUsePython = GdbOptionsPage::tr(
"<p>To execute simple Python commands, prefix them with \"python\".</p>"
"<p>To execute sequences of Python commands spanning multiple lines "
@@ -225,6 +230,7 @@ GdbOptionsPageWidget::GdbOptionsPageWidget()
formLayout->addRow(checkBoxLoadGdbInit);
formLayout->addRow(checkBoxLoadGdbDumpers);
formLayout->addRow(checkBoxIntelFlavor);
formLayout->addRow(checkBoxUsePseudoTracepoints);
auto startLayout = new QGridLayout(groupBoxStartupCommands);
startLayout->addWidget(textEditStartupCommands, 0, 0, 1, 1);
@@ -248,6 +254,7 @@ GdbOptionsPageWidget::GdbOptionsPageWidget()
group.insert(action(IntelFlavor), checkBoxIntelFlavor);
group.insert(action(UseMessageBoxForSignals), checkBoxUseMessageBoxForSignals);
group.insert(action(SkipKnownFrames), checkBoxSkipKnownFrames);
group.insert(action(UsePseudoTracepoints), checkBoxUsePseudoTracepoints);
//lineEditSelectedPluginBreakpointsPattern->
// setEnabled(action(SelectedPluginBreakpoints)->value().toBool());