forked from qt-creator/qt-creator
Debugger: Fix Window grabbing on GDB
Needs to make namespace detection work without valid frame Task-number: QTCREATORBUG-17326 Change-Id: Ia7c7017db4ef384d4f246e11a5601d01f4f366f1 Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
@@ -33,6 +33,7 @@ import os
|
|||||||
import os.path
|
import os.path
|
||||||
import sys
|
import sys
|
||||||
import struct
|
import struct
|
||||||
|
import tempfile
|
||||||
import types
|
import types
|
||||||
|
|
||||||
from dumper import *
|
from dumper import *
|
||||||
@@ -1023,27 +1024,21 @@ class Dumper(DumperBase):
|
|||||||
for objfile in gdb.objfiles():
|
for objfile in gdb.objfiles():
|
||||||
name = objfile.filename
|
name = objfile.filename
|
||||||
if name.find('/libQt5Core') >= 0:
|
if name.find('/libQt5Core') >= 0:
|
||||||
|
fd, tmppath = tempfile.mkstemp()
|
||||||
|
os.close(fd)
|
||||||
|
cmd = 'maint print msymbols %s "%s"' % (tmppath, name)
|
||||||
|
symbols = gdb.execute(cmd, to_string = True)
|
||||||
ns = ''
|
ns = ''
|
||||||
|
with open(tmppath) as f:
|
||||||
# This only works when called from a valid frame.
|
for line in f:
|
||||||
try:
|
if line.find('msgHandlerGrabbed ') >= 0:
|
||||||
cand = 'QArrayData::shared_null'
|
# [11] b 0x7ffff683c000 _ZN4MynsL17msgHandlerGrabbedE
|
||||||
symbol = gdb.lookup_symbol(cand)[0]
|
# section .tbss Myns::msgHandlerGrabbed qlogging.cpp
|
||||||
if symbol:
|
ns = re.split('_ZN(\d*)(\w*)L17msgHandlerGrabbedE ', line)[2]
|
||||||
ns = symbol.name[:-len(cand)]
|
if len(ns):
|
||||||
except:
|
ns += '::'
|
||||||
symbol = None
|
break
|
||||||
|
os.remove(tmppath)
|
||||||
if symbol is None:
|
|
||||||
try:
|
|
||||||
# Some GDB 7.11.1 on Arch Linux.
|
|
||||||
cand = 'QArrayData::shared_null[0]'
|
|
||||||
val = gdb.parse_and_eval(cand)
|
|
||||||
if val.type is not None:
|
|
||||||
typeobj = val.type.unqualified()
|
|
||||||
ns = typeobj.name[:-len('QArrayData')]
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
lenns = len(ns)
|
lenns = len(ns)
|
||||||
strns = ('%d%s' % (lenns - 2, ns[:lenns - 2])) if lenns else ''
|
strns = ('%d%s' % (lenns - 2, ns[:lenns - 2])) if lenns else ''
|
||||||
@@ -1054,10 +1049,7 @@ class Dumper(DumperBase):
|
|||||||
sym = '_ZN7QObject11customEventEP6QEvent'
|
sym = '_ZN7QObject11customEventEP6QEvent'
|
||||||
self.qtCustomEventFunc = self.findSymbol(sym)
|
self.qtCustomEventFunc = self.findSymbol(sym)
|
||||||
|
|
||||||
if lenns:
|
sym += '@plt'
|
||||||
sym = '_ZN%s7QObject11customEventEPNS_6QEventE@plt' % strns
|
|
||||||
else:
|
|
||||||
sym = '_ZN7QObject11customEventEP6QEvent@plt'
|
|
||||||
self.qtCustomEventPltFunc = self.findSymbol(sym)
|
self.qtCustomEventPltFunc = self.findSymbol(sym)
|
||||||
|
|
||||||
sym = '_ZNK7%sQObject8propertyEPKc' % strns
|
sym = '_ZNK7%sQObject8propertyEPKc' % strns
|
||||||
@@ -1093,6 +1085,17 @@ class Dumper(DumperBase):
|
|||||||
cmd = 'set variable (%s)=%s' % (expr, value)
|
cmd = 'set variable (%s)=%s' % (expr, value)
|
||||||
gdb.execute(cmd)
|
gdb.execute(cmd)
|
||||||
|
|
||||||
|
def watchPoint(self, args):
|
||||||
|
ns = self.qtNamespace()
|
||||||
|
lenns = len(ns)
|
||||||
|
strns = ('%d%s' % (lenns - 2, ns[:lenns - 2])) if lenns else ''
|
||||||
|
sym = '_ZN%s12QApplication8widgetAtEii' % strns
|
||||||
|
expr = '%s(%s,%s)' % (sym, args['x'], args['y'])
|
||||||
|
res = self.parseAndEvaluate(expr)
|
||||||
|
p = 0 if res is None else res.pointer()
|
||||||
|
n = ("'%sQWidget'" % ns) if lenns else 'QWidget'
|
||||||
|
safePrint('{selected="0x%x",expr="(%s*)0x%x"}' % (p, n, p))
|
||||||
|
|
||||||
def nativeDynamicTypeName(self, address, baseType):
|
def nativeDynamicTypeName(self, address, baseType):
|
||||||
# Needed for Gdb13393 test.
|
# Needed for Gdb13393 test.
|
||||||
nativeType = self.lookupNativeType(baseType.name)
|
nativeType = self.lookupNativeType(baseType.name)
|
||||||
@@ -1361,7 +1364,6 @@ class Dumper(DumperBase):
|
|||||||
|
|
||||||
def profile1(self, args):
|
def profile1(self, args):
|
||||||
'''Internal profiling'''
|
'''Internal profiling'''
|
||||||
import tempfile
|
|
||||||
import cProfile
|
import cProfile
|
||||||
tempDir = tempfile.gettempdir() + '/bbprof'
|
tempDir = tempfile.gettempdir() + '/bbprof'
|
||||||
cProfile.run('theDumper.fetchVariables(%s)' % args, tempDir)
|
cProfile.run('theDumper.fetchVariables(%s)' % args, tempDir)
|
||||||
|
@@ -1646,7 +1646,7 @@ class Dumper(DumperBase):
|
|||||||
result += ',data="%s %s"' % (insn.GetMnemonic(self.target),
|
result += ',data="%s %s"' % (insn.GetMnemonic(self.target),
|
||||||
insn.GetOperands(self.target))
|
insn.GetOperands(self.target))
|
||||||
result += ',function="%s"' % functionName
|
result += ',function="%s"' % functionName
|
||||||
rawData = insn.GetData(lldb.target).uint8s
|
rawData = insn.GetData(self.target).uint8s
|
||||||
result += ',rawdata="%s"' % ' '.join(["%02x" % x for x in rawData])
|
result += ',rawdata="%s"' % ' '.join(["%02x" % x for x in rawData])
|
||||||
if comment:
|
if comment:
|
||||||
result += ',comment="%s"' % self.hexencode(comment)
|
result += ',comment="%s"' % self.hexencode(comment)
|
||||||
@@ -1678,6 +1678,20 @@ class Dumper(DumperBase):
|
|||||||
lhs.SetValueFromCString(value, error)
|
lhs.SetValueFromCString(value, error)
|
||||||
self.reportResult(self.describeError(error), args)
|
self.reportResult(self.describeError(error), args)
|
||||||
|
|
||||||
|
def watchPoint(self, args):
|
||||||
|
self.reportToken(args)
|
||||||
|
ns = self.qtNamespace()
|
||||||
|
lenns = len(ns)
|
||||||
|
funcs = self.target.FindGlobalFunctions('.*QApplication::widgetAt', 2, 1)
|
||||||
|
func = funcs[1]
|
||||||
|
addr = func.GetFunction().GetStartAddress().GetLoadAddress(self.target)
|
||||||
|
expr = '((void*(*)(int,int))0x%x)' % addr
|
||||||
|
#expr = '%sQApplication::widgetAt(%s,%s)' % (ns, args['x'], args['y'])
|
||||||
|
res = self.parseAndEvaluate(expr)
|
||||||
|
p = 0 if res is None else res.pointer()
|
||||||
|
n = ns + 'QWidget'
|
||||||
|
self.reportResult('selected="0x%x",expr="(%s*)0x%x"' % (p, n, p), args)
|
||||||
|
|
||||||
def createResolvePendingBreakpointsHookBreakpoint(self, args):
|
def createResolvePendingBreakpointsHookBreakpoint(self, args):
|
||||||
bp = self.target.BreakpointCreateByName('qt_qmlDebugConnectorOpen')
|
bp = self.target.BreakpointCreateByName('qt_qmlDebugConnectorOpen')
|
||||||
bp.SetOneShot(True)
|
bp.SetOneShot(True)
|
||||||
|
@@ -75,6 +75,23 @@ private:
|
|||||||
void argHelper(const char *name, const QByteArray &value);
|
void argHelper(const char *name, const QByteArray &value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class DebuggerCommandSequence
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DebuggerCommandSequence() {}
|
||||||
|
bool isEmpty() const { return m_commands.isEmpty(); }
|
||||||
|
bool wantContinue() const { return m_continue; }
|
||||||
|
const QList<DebuggerCommand> &commands() const { return m_commands; }
|
||||||
|
void append(const DebuggerCommand &cmd, bool wantContinue) {
|
||||||
|
m_commands.append(cmd);
|
||||||
|
m_continue = wantContinue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
QList<DebuggerCommand> m_commands;
|
||||||
|
bool m_continue = false;
|
||||||
|
};
|
||||||
|
|
||||||
// FIXME: rename into GdbMiValue
|
// FIXME: rename into GdbMiValue
|
||||||
class GdbMi
|
class GdbMi
|
||||||
{
|
{
|
||||||
|
@@ -138,18 +138,6 @@ static int ¤tToken()
|
|||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
static QString parsePlainConsoleStream(const DebuggerResponse &response)
|
|
||||||
{
|
|
||||||
QString out = response.consoleStreamOutput;
|
|
||||||
// FIXME: proper decoding needed
|
|
||||||
if (out.endsWith("\\n"))
|
|
||||||
out.chop(2);
|
|
||||||
while (out.endsWith('\n') || out.endsWith(' '))
|
|
||||||
out.chop(1);
|
|
||||||
int pos = out.indexOf(" = ");
|
|
||||||
return out.mid(pos + 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isMostlyHarmlessMessage(const QStringRef &msg)
|
static bool isMostlyHarmlessMessage(const QStringRef &msg)
|
||||||
{
|
{
|
||||||
return msg == "warning: GDB: Failed to set controlling terminal: "
|
return msg == "warning: GDB: Failed to set controlling terminal: "
|
||||||
@@ -229,7 +217,6 @@ GdbEngine::GdbEngine(const DebuggerRunParameters &startParameters)
|
|||||||
m_commandsDoneCallback = 0;
|
m_commandsDoneCallback = 0;
|
||||||
m_stackNeeded = false;
|
m_stackNeeded = false;
|
||||||
m_terminalTrap = startParameters.useTerminal;
|
m_terminalTrap = startParameters.useTerminal;
|
||||||
m_temporaryStopPending = false;
|
|
||||||
m_fullStartDone = false;
|
m_fullStartDone = false;
|
||||||
m_systemDumpersLoaded = false;
|
m_systemDumpersLoaded = false;
|
||||||
m_rerunPending = false;
|
m_rerunPending = false;
|
||||||
@@ -905,10 +892,6 @@ void GdbEngine::runCommand(const DebuggerCommand &command)
|
|||||||
const int token = ++currentToken();
|
const int token = ++currentToken();
|
||||||
|
|
||||||
DebuggerCommand cmd = command;
|
DebuggerCommand cmd = command;
|
||||||
if (command.flags & PythonCommand) {
|
|
||||||
cmd.arg("token", token);
|
|
||||||
cmd.function = "python theDumper." + cmd.function + "(" + cmd.argsToPython() + ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!stateAcceptsGdbCommands(state())) {
|
if (!stateAcceptsGdbCommands(state())) {
|
||||||
showMessage(QString("NO GDB PROCESS RUNNING, CMD IGNORED: %1 %2")
|
showMessage(QString("NO GDB PROCESS RUNNING, CMD IGNORED: %1 %2")
|
||||||
@@ -916,7 +899,7 @@ void GdbEngine::runCommand(const DebuggerCommand &command)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.flags & RebuildBreakpointModel) {
|
if (cmd.flags & RebuildBreakpointModel) {
|
||||||
++m_pendingBreakpointRequests;
|
++m_pendingBreakpointRequests;
|
||||||
PENDING_DEBUG(" BREAKPOINT MODEL:" << cmd.function
|
PENDING_DEBUG(" BREAKPOINT MODEL:" << cmd.function
|
||||||
<< "INCREMENTS PENDING TO" << m_pendingBreakpointRequests);
|
<< "INCREMENTS PENDING TO" << m_pendingBreakpointRequests);
|
||||||
@@ -925,32 +908,44 @@ void GdbEngine::runCommand(const DebuggerCommand &command)
|
|||||||
<< "LEAVES PENDING BREAKPOINT AT" << m_pendingBreakpointRequests);
|
<< "LEAVES PENDING BREAKPOINT AT" << m_pendingBreakpointRequests);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(command.flags & Discardable))
|
if (cmd.flags & (NeedsTemporaryStop|NeedsFullStop)) {
|
||||||
++m_nonDiscardableCount;
|
|
||||||
|
|
||||||
if (command.flags & NeedsStop) {
|
|
||||||
showMessage("RUNNING NEEDS-STOP COMMAND " + cmd.function);
|
showMessage("RUNNING NEEDS-STOP COMMAND " + cmd.function);
|
||||||
|
const bool wantContinue = bool(cmd.flags & NeedsTemporaryStop);
|
||||||
|
cmd.flags &= ~(NeedsTemporaryStop|NeedsFullStop);
|
||||||
if (state() == InferiorStopRequested) {
|
if (state() == InferiorStopRequested) {
|
||||||
if (command.flags & LosesChild) {
|
if (cmd.flags & LosesChild) {
|
||||||
notifyInferiorIll();
|
notifyInferiorIll();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
showMessage("CHILD ALREADY BEING INTERRUPTED. STILL HOPING.");
|
showMessage("CHILD ALREADY BEING INTERRUPTED. STILL HOPING.");
|
||||||
// Calling shutdown() here breaks all situations where two
|
// Calling shutdown() here breaks all situations where two
|
||||||
// NeedsStop commands are issued in quick succession.
|
// NeedsStop commands are issued in quick succession.
|
||||||
} else if (!m_temporaryStopPending && state() == InferiorRunOk) {
|
m_onStop.append(cmd, wantContinue);
|
||||||
showStatusMessage(tr("Stopping temporarily"), 1000);
|
return;
|
||||||
m_temporaryStopPending = true;
|
|
||||||
requestInterruptInferior();
|
|
||||||
}
|
}
|
||||||
|
if (state() == InferiorRunOk) {
|
||||||
|
showStatusMessage(tr("Stopping temporarily"), 1000);
|
||||||
|
m_onStop.append(cmd, wantContinue);
|
||||||
|
requestInterruptInferior();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
showMessage("UNSAFE STATE FOR QUEUED COMMAND. EXECUTING IMMEDIATELY");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(cmd.flags & Discardable))
|
||||||
|
++m_nonDiscardableCount;
|
||||||
|
|
||||||
|
if (cmd.flags & PythonCommand) {
|
||||||
|
cmd.arg("token", token);
|
||||||
|
cmd.function = "python theDumper." + cmd.function + "(" + cmd.argsToPython() + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
QTC_ASSERT(m_gdbProc.state() == QProcess::Running, return);
|
QTC_ASSERT(m_gdbProc.state() == QProcess::Running, return);
|
||||||
|
|
||||||
cmd.postTime = QTime::currentTime().msecsSinceStartOfDay();
|
cmd.postTime = QTime::currentTime().msecsSinceStartOfDay();
|
||||||
m_commandForToken[token] = cmd;
|
m_commandForToken[token] = cmd;
|
||||||
m_flagsForToken[token] = command.flags;
|
m_flagsForToken[token] = cmd.flags;
|
||||||
if (command.flags & ConsoleCommand)
|
if (cmd.flags & ConsoleCommand)
|
||||||
cmd.function = "-interpreter-exec console \"" + cmd.function + '"';
|
cmd.function = "-interpreter-exec console \"" + cmd.function + '"';
|
||||||
cmd.function = QString::number(token) + cmd.function;
|
cmd.function = QString::number(token) + cmd.function;
|
||||||
showMessage(cmd.function, LogInput);
|
showMessage(cmd.function, LogInput);
|
||||||
@@ -1363,11 +1358,15 @@ void GdbEngine::handleStopResponse(const GdbMi &data)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_temporaryStopPending) {
|
if (!m_onStop.isEmpty()) {
|
||||||
showMessage("INTERNAL CONTINUE AFTER TEMPORARY STOP", LogMisc);
|
|
||||||
m_temporaryStopPending = false;
|
|
||||||
notifyInferiorStopOk();
|
notifyInferiorStopOk();
|
||||||
continueInferiorInternal();
|
showMessage("HANDLING QUEUED COMMANDS AFTER TEMPORARY STOP", LogMisc);
|
||||||
|
DebuggerCommandSequence seq = m_onStop;
|
||||||
|
m_onStop = DebuggerCommandSequence();
|
||||||
|
for (const DebuggerCommand &cmd : seq.commands())
|
||||||
|
runCommand(cmd);
|
||||||
|
if (seq.wantContinue())
|
||||||
|
continueInferiorInternal();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1843,7 +1842,7 @@ void GdbEngine::shutdownInferior()
|
|||||||
DebuggerCommand cmd;
|
DebuggerCommand cmd;
|
||||||
cmd.function = QLatin1String(runParameters().closeMode == DetachAtClose ? "detach" : "kill");
|
cmd.function = QLatin1String(runParameters().closeMode == DetachAtClose ? "detach" : "kill");
|
||||||
cmd.callback = CB(handleInferiorShutdown);
|
cmd.callback = CB(handleInferiorShutdown);
|
||||||
cmd.flags = NeedsStop|LosesChild;
|
cmd.flags = NeedsTemporaryStop|LosesChild;
|
||||||
runCommand(cmd);
|
runCommand(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1978,6 +1977,7 @@ bool GdbEngine::hasCapability(unsigned cap) const
|
|||||||
| WatchpointByAddressCapability
|
| WatchpointByAddressCapability
|
||||||
| WatchpointByExpressionCapability
|
| WatchpointByExpressionCapability
|
||||||
| AddWatcherCapability
|
| AddWatcherCapability
|
||||||
|
| AddWatcherWhileRunningCapability
|
||||||
| WatchWidgetsCapability
|
| WatchWidgetsCapability
|
||||||
| ShowModuleSymbolsCapability
|
| ShowModuleSymbolsCapability
|
||||||
| ShowModuleSectionsCapability
|
| ShowModuleSectionsCapability
|
||||||
@@ -2559,7 +2559,7 @@ void GdbEngine::handleBreakInsert1(const DebuggerResponse &response, Breakpoint
|
|||||||
const GdbMi mainbkpt = response.data["bkpt"];
|
const GdbMi mainbkpt = response.data["bkpt"];
|
||||||
bp.notifyBreakpointRemoveProceeding();
|
bp.notifyBreakpointRemoveProceeding();
|
||||||
DebuggerCommand cmd("-break-delete " + mainbkpt["number"].data(),
|
DebuggerCommand cmd("-break-delete " + mainbkpt["number"].data(),
|
||||||
NeedsStop | RebuildBreakpointModel);
|
NeedsTemporaryStop | RebuildBreakpointModel);
|
||||||
runCommand(cmd);
|
runCommand(cmd);
|
||||||
bp.notifyBreakpointRemoveOk();
|
bp.notifyBreakpointRemoveOk();
|
||||||
return;
|
return;
|
||||||
@@ -2586,14 +2586,14 @@ void GdbEngine::handleBreakInsert1(const DebuggerResponse &response, Breakpoint
|
|||||||
const int lineNumber = bp.lineNumber();
|
const int lineNumber = bp.lineNumber();
|
||||||
DebuggerCommand cmd("trace \"" + GdbMi::escapeCString(fileName) + "\":"
|
DebuggerCommand cmd("trace \"" + GdbMi::escapeCString(fileName) + "\":"
|
||||||
+ QString::number(lineNumber),
|
+ QString::number(lineNumber),
|
||||||
NeedsStop | RebuildBreakpointModel);
|
NeedsTemporaryStop | RebuildBreakpointModel);
|
||||||
runCommand(cmd);
|
runCommand(cmd);
|
||||||
} else {
|
} else {
|
||||||
// Some versions of gdb like "GNU gdb (GDB) SUSE (6.8.91.20090930-2.4)"
|
// Some versions of gdb like "GNU gdb (GDB) SUSE (6.8.91.20090930-2.4)"
|
||||||
// know how to do pending breakpoints using CLI but not MI. So try
|
// know how to do pending breakpoints using CLI but not MI. So try
|
||||||
// again with MI.
|
// again with MI.
|
||||||
DebuggerCommand cmd("break " + breakpointLocation2(bp.parameters()),
|
DebuggerCommand cmd("break " + breakpointLocation2(bp.parameters()),
|
||||||
NeedsStop | RebuildBreakpointModel);
|
NeedsTemporaryStop | RebuildBreakpointModel);
|
||||||
cmd.callback = [this, bp](const DebuggerResponse &r) { handleBreakInsert2(r, bp); };
|
cmd.callback = [this, bp](const DebuggerResponse &r) { handleBreakInsert2(r, bp); };
|
||||||
runCommand(cmd);
|
runCommand(cmd);
|
||||||
}
|
}
|
||||||
@@ -2730,7 +2730,7 @@ void GdbEngine::insertBreakpoint(Breakpoint bp)
|
|||||||
const BreakpointParameters &data = bp.parameters();
|
const BreakpointParameters &data = bp.parameters();
|
||||||
|
|
||||||
if (!data.isCppBreakpoint()) {
|
if (!data.isCppBreakpoint()) {
|
||||||
DebuggerCommand cmd("insertInterpreterBreakpoint", PythonCommand | NeedsStop);
|
DebuggerCommand cmd("insertInterpreterBreakpoint", PythonCommand | NeedsTemporaryStop);
|
||||||
bp.addToCommand(&cmd);
|
bp.addToCommand(&cmd);
|
||||||
cmd.callback = [this, bp](const DebuggerResponse &r) { handleInsertInterpreterBreakpoint(r, bp); };
|
cmd.callback = [this, bp](const DebuggerResponse &r) { handleInsertInterpreterBreakpoint(r, bp); };
|
||||||
runCommand(cmd);
|
runCommand(cmd);
|
||||||
@@ -2752,7 +2752,7 @@ void GdbEngine::insertBreakpoint(Breakpoint bp)
|
|||||||
} else if (type == BreakpointAtFork) {
|
} else if (type == BreakpointAtFork) {
|
||||||
cmd.function = "catch fork";
|
cmd.function = "catch fork";
|
||||||
cmd.callback = handleCatch;
|
cmd.callback = handleCatch;
|
||||||
cmd.flags = NeedsStop | RebuildBreakpointModel;
|
cmd.flags = NeedsTemporaryStop | RebuildBreakpointModel;
|
||||||
runCommand(cmd);
|
runCommand(cmd);
|
||||||
// Another one...
|
// Another one...
|
||||||
cmd.function = "catch vfork";
|
cmd.function = "catch vfork";
|
||||||
@@ -2789,7 +2789,7 @@ void GdbEngine::insertBreakpoint(Breakpoint bp)
|
|||||||
cmd.function += breakpointLocation(bp.parameters());
|
cmd.function += breakpointLocation(bp.parameters());
|
||||||
cmd.callback = [this, bp](const DebuggerResponse &r) { handleBreakInsert1(r, bp); };
|
cmd.callback = [this, bp](const DebuggerResponse &r) { handleBreakInsert1(r, bp); };
|
||||||
}
|
}
|
||||||
cmd.flags = NeedsStop | RebuildBreakpointModel;
|
cmd.flags = NeedsTemporaryStop | RebuildBreakpointModel;
|
||||||
runCommand(cmd);
|
runCommand(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2838,7 +2838,7 @@ void GdbEngine::changeBreakpoint(Breakpoint bp)
|
|||||||
bp.notifyBreakpointChangeOk();
|
bp.notifyBreakpointChangeOk();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cmd.flags = NeedsStop | RebuildBreakpointModel;
|
cmd.flags = NeedsTemporaryStop | RebuildBreakpointModel;
|
||||||
runCommand(cmd);
|
runCommand(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2860,7 +2860,7 @@ void GdbEngine::removeBreakpoint(Breakpoint bp)
|
|||||||
// We already have a fully inserted breakpoint.
|
// We already have a fully inserted breakpoint.
|
||||||
bp.notifyBreakpointRemoveProceeding();
|
bp.notifyBreakpointRemoveProceeding();
|
||||||
showMessage(QString("DELETING BP %1 IN %2").arg(br.id.toString()).arg(bp.fileName()));
|
showMessage(QString("DELETING BP %1 IN %2").arg(br.id.toString()).arg(bp.fileName()));
|
||||||
DebuggerCommand cmd("-break-delete " + br.id.toString(), NeedsStop | RebuildBreakpointModel);
|
DebuggerCommand cmd("-break-delete " + br.id.toString(), NeedsTemporaryStop | RebuildBreakpointModel);
|
||||||
runCommand(cmd);
|
runCommand(cmd);
|
||||||
|
|
||||||
// Pretend it succeeds without waiting for response. Feels better.
|
// Pretend it succeeds without waiting for response. Feels better.
|
||||||
@@ -2996,7 +2996,7 @@ void GdbEngine::requestModuleSymbols(const QString &modulePath)
|
|||||||
return;
|
return;
|
||||||
QString fileName = tf.fileName();
|
QString fileName = tf.fileName();
|
||||||
tf.close();
|
tf.close();
|
||||||
DebuggerCommand cmd("maint print msymbols \"" + fileName + "\" " + modulePath, NeedsStop);
|
DebuggerCommand cmd("maint print msymbols \"" + fileName + "\" " + modulePath, NeedsTemporaryStop);
|
||||||
cmd.callback = [modulePath, fileName](const DebuggerResponse &r) {
|
cmd.callback = [modulePath, fileName](const DebuggerResponse &r) {
|
||||||
handleShowModuleSymbols(r, modulePath, fileName);
|
handleShowModuleSymbols(r, modulePath, fileName);
|
||||||
};
|
};
|
||||||
@@ -3006,7 +3006,7 @@ void GdbEngine::requestModuleSymbols(const QString &modulePath)
|
|||||||
void GdbEngine::requestModuleSections(const QString &moduleName)
|
void GdbEngine::requestModuleSections(const QString &moduleName)
|
||||||
{
|
{
|
||||||
// There seems to be no way to get the symbols from a single .so.
|
// There seems to be no way to get the symbols from a single .so.
|
||||||
DebuggerCommand cmd("maint info section ALLOBJ", NeedsStop);
|
DebuggerCommand cmd("maint info section ALLOBJ", NeedsTemporaryStop);
|
||||||
cmd.callback = [this, moduleName](const DebuggerResponse &r) {
|
cmd.callback = [this, moduleName](const DebuggerResponse &r) {
|
||||||
handleShowModuleSections(r, moduleName);
|
handleShowModuleSections(r, moduleName);
|
||||||
};
|
};
|
||||||
@@ -3059,7 +3059,7 @@ void GdbEngine::reloadModules()
|
|||||||
|
|
||||||
void GdbEngine::reloadModulesInternal()
|
void GdbEngine::reloadModulesInternal()
|
||||||
{
|
{
|
||||||
runCommand({"info shared", NeedsStop, CB(handleModulesList)});
|
runCommand({"info shared", NeedsTemporaryStop, CB(handleModulesList)});
|
||||||
}
|
}
|
||||||
|
|
||||||
static QString nameFromPath(const QString &path)
|
static QString nameFromPath(const QString &path)
|
||||||
@@ -3140,7 +3140,7 @@ void GdbEngine::reloadSourceFiles()
|
|||||||
{
|
{
|
||||||
if ((state() == InferiorRunOk || state() == InferiorStopOk) && !m_sourcesListUpdating) {
|
if ((state() == InferiorRunOk || state() == InferiorStopOk) && !m_sourcesListUpdating) {
|
||||||
m_sourcesListUpdating = true;
|
m_sourcesListUpdating = true;
|
||||||
DebuggerCommand cmd("-file-list-exec-source-files", NeedsStop);
|
DebuggerCommand cmd("-file-list-exec-source-files", NeedsTemporaryStop);
|
||||||
cmd.callback = [this](const DebuggerResponse &response) {
|
cmd.callback = [this](const DebuggerResponse &response) {
|
||||||
m_sourcesListUpdating = false;
|
m_sourcesListUpdating = false;
|
||||||
if (response.resultClass == ResultDone) {
|
if (response.resultClass == ResultDone) {
|
||||||
@@ -3349,7 +3349,7 @@ void GdbEngine::createSnapshot()
|
|||||||
fileName = tf.fileName();
|
fileName = tf.fileName();
|
||||||
tf.close();
|
tf.close();
|
||||||
// This must not be quoted, it doesn't work otherwise.
|
// This must not be quoted, it doesn't work otherwise.
|
||||||
DebuggerCommand cmd("gcore " + fileName, NeedsStop | ConsoleCommand);
|
DebuggerCommand cmd("gcore " + fileName, NeedsTemporaryStop | ConsoleCommand);
|
||||||
cmd.callback = [this, fileName](const DebuggerResponse &r) { handleMakeSnapshot(r, fileName); };
|
cmd.callback = [this, fileName](const DebuggerResponse &r) { handleMakeSnapshot(r, fileName); };
|
||||||
runCommand(cmd);
|
runCommand(cmd);
|
||||||
} else {
|
} else {
|
||||||
@@ -3613,30 +3613,24 @@ void GdbEngine::assignValueInDebugger(WatchItem *item,
|
|||||||
|
|
||||||
void GdbEngine::watchPoint(const QPoint &pnt)
|
void GdbEngine::watchPoint(const QPoint &pnt)
|
||||||
{
|
{
|
||||||
QString x = QString::number(pnt.x());
|
DebuggerCommand cmd("watchPoint", PythonCommand|NeedsFullStop);
|
||||||
QString y = QString::number(pnt.y());
|
cmd.arg("x", pnt.x());
|
||||||
runCommand({"print " + qtNamespace() + "QApplication::widgetAt(" + x + ',' + y + ')',
|
cmd.arg("y", pnt.y());
|
||||||
NeedsStop, CB(handleWatchPoint)});
|
cmd.callback = CB(handleWatchPoint);
|
||||||
|
runCommand(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GdbEngine::handleWatchPoint(const DebuggerResponse &response)
|
void GdbEngine::handleWatchPoint(const DebuggerResponse &response)
|
||||||
{
|
{
|
||||||
if (response.resultClass == ResultDone) {
|
if (response.resultClass == ResultDone) {
|
||||||
// "$5 = (void *) 0xbfa7ebfc\n"
|
GdbMi res;
|
||||||
const QString ba = parsePlainConsoleStream(response);
|
res.fromString(response.consoleStreamOutput);
|
||||||
const int pos0x = ba.indexOf("0x");
|
qulonglong addr = res["selected"].toAddress();
|
||||||
if (pos0x == -1) {
|
if (addr == 0)
|
||||||
showStatusMessage(tr("Cannot read widget data: %1").arg(ba));
|
showStatusMessage(tr("Could not find a widget."));
|
||||||
} else {
|
// Add the watcher entry nevertheless, as that's the place where
|
||||||
const QString addr = ba.mid(pos0x);
|
// the user expects visual feedback.
|
||||||
if (addr.toULongLong(0, 0)) { // Non-null pointer
|
watchHandler()->watchExpression(res["expr"].data(), QString(), true);
|
||||||
const QString type = "::" + qtNamespace() + "QWidget";
|
|
||||||
const QString exp = QString("{%1}%2").arg(type).arg(addr);
|
|
||||||
watchHandler()->watchExpression(exp);
|
|
||||||
} else {
|
|
||||||
showStatusMessage(tr("Could not find a widget."));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3658,7 +3652,7 @@ public:
|
|||||||
void GdbEngine::changeMemory(MemoryAgent *agent, quint64 addr, const QByteArray &data)
|
void GdbEngine::changeMemory(MemoryAgent *agent, quint64 addr, const QByteArray &data)
|
||||||
{
|
{
|
||||||
Q_UNUSED(agent)
|
Q_UNUSED(agent)
|
||||||
DebuggerCommand cmd("-data-write-memory 0x" + QString::number(addr, 16) + " d 1", NeedsStop);
|
DebuggerCommand cmd("-data-write-memory 0x" + QString::number(addr, 16) + " d 1", NeedsTemporaryStop);
|
||||||
foreach (unsigned char c, data)
|
foreach (unsigned char c, data)
|
||||||
cmd.function += ' ' + QString::number(uint(c));
|
cmd.function += ' ' + QString::number(uint(c));
|
||||||
cmd.callback = CB(handleVarAssign);
|
cmd.callback = CB(handleVarAssign);
|
||||||
@@ -3681,7 +3675,7 @@ void GdbEngine::fetchMemoryHelper(const MemoryAgentCookie &ac)
|
|||||||
DebuggerCommand cmd("-data-read-memory 0x"
|
DebuggerCommand cmd("-data-read-memory 0x"
|
||||||
+ QString::number(ac.base + ac.offset, 16) + " x 1 1 "
|
+ QString::number(ac.base + ac.offset, 16) + " x 1 1 "
|
||||||
+ QString::number(ac.length),
|
+ QString::number(ac.length),
|
||||||
NeedsStop);
|
NeedsTemporaryStop);
|
||||||
cmd.callback = [this, ac](const DebuggerResponse &r) { handleFetchMemory(r, ac); };
|
cmd.callback = [this, ac](const DebuggerResponse &r) { handleFetchMemory(r, ac); };
|
||||||
runCommand(cmd);
|
runCommand(cmd);
|
||||||
}
|
}
|
||||||
@@ -4179,7 +4173,7 @@ void GdbEngine::resetInferior()
|
|||||||
foreach (QString command, commands.split('\n')) {
|
foreach (QString command, commands.split('\n')) {
|
||||||
command = command.trimmed();
|
command = command.trimmed();
|
||||||
if (!command.isEmpty())
|
if (!command.isEmpty())
|
||||||
runCommand({command, ConsoleCommand | NeedsStop});
|
runCommand({command, ConsoleCommand | NeedsTemporaryStop});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_rerunPending = true;
|
m_rerunPending = true;
|
||||||
@@ -4325,7 +4319,7 @@ void GdbEngine::handleAdapterCrashed(const QString &msg)
|
|||||||
|
|
||||||
void GdbEngine::createFullBacktrace()
|
void GdbEngine::createFullBacktrace()
|
||||||
{
|
{
|
||||||
DebuggerCommand cmd("thread apply all bt full", NeedsStop | ConsoleCommand);
|
DebuggerCommand cmd("thread apply all bt full", NeedsTemporaryStop | ConsoleCommand);
|
||||||
cmd.callback = [this](const DebuggerResponse &response) {
|
cmd.callback = [this](const DebuggerResponse &response) {
|
||||||
if (response.resultClass == ResultDone) {
|
if (response.resultClass == ResultDone) {
|
||||||
Internal::openTextEditor("Backtrace $",
|
Internal::openTextEditor("Backtrace $",
|
||||||
|
@@ -44,6 +44,8 @@
|
|||||||
#include <QTime>
|
#include <QTime>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
namespace Debugger {
|
namespace Debugger {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
@@ -151,11 +153,13 @@ private: ////////// Gdb Command Management //////////
|
|||||||
enum GdbCommandFlag {
|
enum GdbCommandFlag {
|
||||||
NoFlags = 0,
|
NoFlags = 0,
|
||||||
// The command needs a stopped inferior.
|
// The command needs a stopped inferior.
|
||||||
NeedsStop = 1,
|
NeedsTemporaryStop = 1,
|
||||||
// No need to wait for the reply before continuing inferior.
|
// No need to wait for the reply before continuing inferior.
|
||||||
Discardable = 2,
|
Discardable = 2,
|
||||||
// Needs a dummy extra command to force GDB output flushing.
|
// Needs a dummy extra command to force GDB output flushing.
|
||||||
NeedsFlush = 4,
|
NeedsFlush = 4,
|
||||||
|
// The command needs a stopped inferior and will stay stopped afterward.
|
||||||
|
NeedsFullStop = 8,
|
||||||
// Callback expects ResultRunning instead of ResultDone.
|
// Callback expects ResultRunning instead of ResultDone.
|
||||||
RunRequest = 16,
|
RunRequest = 16,
|
||||||
// Callback expects ResultExit instead of ResultDone.
|
// Callback expects ResultExit instead of ResultDone.
|
||||||
@@ -267,6 +271,7 @@ private: ////////// View & Data Stuff //////////
|
|||||||
|
|
||||||
void selectThread(ThreadId threadId) override;
|
void selectThread(ThreadId threadId) override;
|
||||||
void activateFrame(int index) override;
|
void activateFrame(int index) override;
|
||||||
|
void handleAutoContinueInferior();
|
||||||
|
|
||||||
//
|
//
|
||||||
// Breakpoint specific stuff
|
// Breakpoint specific stuff
|
||||||
@@ -378,7 +383,7 @@ protected:
|
|||||||
void changeMemory(MemoryAgent *agent, quint64 addr, const QByteArray &data) override;
|
void changeMemory(MemoryAgent *agent, quint64 addr, const QByteArray &data) override;
|
||||||
void handleFetchMemory(const DebuggerResponse &response, MemoryAgentCookie ac);
|
void handleFetchMemory(const DebuggerResponse &response, MemoryAgentCookie ac);
|
||||||
|
|
||||||
virtual void watchPoint(const QPoint &) override;
|
void watchPoint(const QPoint &) override;
|
||||||
void handleWatchPoint(const DebuggerResponse &response);
|
void handleWatchPoint(const DebuggerResponse &response);
|
||||||
|
|
||||||
void showToolTip();
|
void showToolTip();
|
||||||
@@ -420,10 +425,11 @@ protected:
|
|||||||
QString m_lastWinException;
|
QString m_lastWinException;
|
||||||
QString m_lastMissingDebugInfo;
|
QString m_lastMissingDebugInfo;
|
||||||
bool m_terminalTrap;
|
bool m_terminalTrap;
|
||||||
bool m_temporaryStopPending;
|
|
||||||
bool usesExecInterrupt() const;
|
bool usesExecInterrupt() const;
|
||||||
bool usesTargetAsync() const;
|
bool usesTargetAsync() const;
|
||||||
|
|
||||||
|
DebuggerCommandSequence m_onStop;
|
||||||
|
|
||||||
QHash<int, QString> m_scheduledTestResponses;
|
QHash<int, QString> m_scheduledTestResponses;
|
||||||
QSet<int> m_testCases;
|
QSet<int> m_testCases;
|
||||||
|
|
||||||
|
@@ -112,25 +112,34 @@ void LldbEngine::executeDebuggerCommand(const QString &command, DebuggerLanguage
|
|||||||
runCommand(cmd);
|
runCommand(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LldbEngine::runCommand(const DebuggerCommand &cmd)
|
void LldbEngine::runCommand(const DebuggerCommand &command)
|
||||||
{
|
{
|
||||||
if (m_lldbProc.state() != QProcess::Running) {
|
if (m_lldbProc.state() != QProcess::Running) {
|
||||||
// This can legally happen e.g. through a reloadModule()
|
// This can legally happen e.g. through a reloadModule()
|
||||||
// triggered by changes in view visibility.
|
// triggered by changes in view visibility.
|
||||||
showMessage(QString("NO LLDB PROCESS RUNNING, CMD IGNORED: %1 %2")
|
showMessage(QString("NO LLDB PROCESS RUNNING, CMD IGNORED: %1 %2")
|
||||||
.arg(cmd.function).arg(state()));
|
.arg(command.function).arg(state()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const int tok = ++currentToken();
|
const int tok = ++currentToken();
|
||||||
DebuggerCommand command = cmd;
|
DebuggerCommand cmd = command;
|
||||||
command.arg("token", tok);
|
cmd.arg("token", tok);
|
||||||
QString token = QString::number(tok);
|
QString token = QString::number(tok);
|
||||||
QString function = command.function + "(" + command.argsToPython() + ")";
|
QString function = cmd.function + "(" + cmd.argsToPython() + ")";
|
||||||
QString msg = token + function + '\n';
|
QString msg = token + function + '\n';
|
||||||
if (cmd.flags == LldbEngine::Silent)
|
if (cmd.flags == Silent)
|
||||||
msg.replace(QRegularExpression("\"environment\":.[^]]*."), "<environment suppressed>");
|
msg.replace(QRegularExpression("\"environment\":.[^]]*."), "<environment suppressed>");
|
||||||
|
if (cmd.flags == NeedsFullStop) {
|
||||||
|
cmd.flags &= ~NeedsFullStop;
|
||||||
|
if (state() == InferiorRunOk) {
|
||||||
|
showStatusMessage(tr("Stopping temporarily"), 1000);
|
||||||
|
m_onStop.append(cmd, false);
|
||||||
|
requestInterruptInferior();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
showMessage(msg, LogInput);
|
showMessage(msg, LogInput);
|
||||||
m_commandForToken[currentToken()] = command;
|
m_commandForToken[currentToken()] = cmd;
|
||||||
m_lldbProc.write("script theDumper." + function.toUtf8() + "\n");
|
m_lldbProc.write("script theDumper." + function.toUtf8() + "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,6 +148,22 @@ void LldbEngine::debugLastCommand()
|
|||||||
runCommand(m_lastDebuggableCommand);
|
runCommand(m_lastDebuggableCommand);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LldbEngine::watchPoint(const QPoint &pnt)
|
||||||
|
{
|
||||||
|
DebuggerCommand cmd("watchPoint", NeedsFullStop);
|
||||||
|
cmd.arg("x", pnt.x());
|
||||||
|
cmd.arg("y", pnt.y());
|
||||||
|
cmd.callback = [this](const DebuggerResponse &response) {
|
||||||
|
qulonglong addr = response.data["selected"].toAddress();
|
||||||
|
if (addr == 0)
|
||||||
|
showStatusMessage(tr("Could not find a widget."));
|
||||||
|
// Add the watcher entry nevertheless, as that's the place where
|
||||||
|
// the user expects visual feedback.
|
||||||
|
watchHandler()->watchExpression(response.data["expr"].data(), QString(), true);
|
||||||
|
};
|
||||||
|
runCommand(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
void LldbEngine::shutdownInferior()
|
void LldbEngine::shutdownInferior()
|
||||||
{
|
{
|
||||||
QTC_ASSERT(state() == InferiorShutdownRequested, qDebug() << state());
|
QTC_ASSERT(state() == InferiorShutdownRequested, qDebug() << state());
|
||||||
@@ -907,11 +932,21 @@ void LldbEngine::handleStateNotification(const GdbMi &reportedState)
|
|||||||
m_continueAtNextSpontaneousStop = true;
|
m_continueAtNextSpontaneousStop = true;
|
||||||
else if (newState == "stopped") {
|
else if (newState == "stopped") {
|
||||||
notifyInferiorSpontaneousStop();
|
notifyInferiorSpontaneousStop();
|
||||||
if (m_continueAtNextSpontaneousStop) {
|
if (m_onStop.isEmpty()) {
|
||||||
m_continueAtNextSpontaneousStop = false;
|
if (m_continueAtNextSpontaneousStop) {
|
||||||
continueInferior();
|
m_continueAtNextSpontaneousStop = false;
|
||||||
|
continueInferior();
|
||||||
|
} else {
|
||||||
|
updateAll();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
updateAll();
|
showMessage("HANDLING QUEUED COMMANDS AFTER TEMPORARY STOP", LogMisc);
|
||||||
|
DebuggerCommandSequence seq = m_onStop;
|
||||||
|
m_onStop = DebuggerCommandSequence();
|
||||||
|
for (const DebuggerCommand &cmd : seq.commands())
|
||||||
|
runCommand(cmd);
|
||||||
|
if (seq.wantContinue())
|
||||||
|
continueInferior();
|
||||||
}
|
}
|
||||||
} else if (newState == "inferiorstopok") {
|
} else if (newState == "inferiorstopok") {
|
||||||
notifyInferiorStopOk();
|
notifyInferiorStopOk();
|
||||||
@@ -1098,8 +1133,8 @@ bool LldbEngine::hasCapability(unsigned cap) const
|
|||||||
| CreateFullBacktraceCapability
|
| CreateFullBacktraceCapability
|
||||||
| WatchpointByAddressCapability
|
| WatchpointByAddressCapability
|
||||||
| WatchpointByExpressionCapability
|
| WatchpointByExpressionCapability
|
||||||
| AddWatcherCapability
|
|
||||||
| WatchWidgetsCapability
|
| WatchWidgetsCapability
|
||||||
|
| AddWatcherCapability
|
||||||
| ShowModuleSymbolsCapability
|
| ShowModuleSymbolsCapability
|
||||||
| ShowModuleSectionsCapability
|
| ShowModuleSectionsCapability
|
||||||
| CatchCapability
|
| CatchCapability
|
||||||
|
@@ -64,7 +64,9 @@ public:
|
|||||||
enum LldbCommandFlag {
|
enum LldbCommandFlag {
|
||||||
NoFlags = 0,
|
NoFlags = 0,
|
||||||
// Do not echo to log.
|
// Do not echo to log.
|
||||||
Silent = 1
|
Silent = 1,
|
||||||
|
// The command needs a stopped inferior and will stay stopped afterward.
|
||||||
|
NeedsFullStop = 8,
|
||||||
};
|
};
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
@@ -150,6 +152,9 @@ private:
|
|||||||
void runCommand(const DebuggerCommand &cmd) override;
|
void runCommand(const DebuggerCommand &cmd) override;
|
||||||
void debugLastCommand() override;
|
void debugLastCommand() override;
|
||||||
|
|
||||||
|
void watchPoint(const QPoint &) override;
|
||||||
|
void handleWatchPoint(const DebuggerResponse &response);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DebuggerCommand m_lastDebuggableCommand;
|
DebuggerCommand m_lastDebuggableCommand;
|
||||||
|
|
||||||
@@ -163,6 +168,7 @@ private:
|
|||||||
QMap<QPointer<DisassemblerAgent>, int> m_disassemblerAgents;
|
QMap<QPointer<DisassemblerAgent>, int> m_disassemblerAgents;
|
||||||
|
|
||||||
QHash<int, DebuggerCommand> m_commandForToken;
|
QHash<int, DebuggerCommand> m_commandForToken;
|
||||||
|
DebuggerCommandSequence m_onStop;
|
||||||
|
|
||||||
// Console handling.
|
// Console handling.
|
||||||
void stubError(const QString &msg);
|
void stubError(const QString &msg);
|
||||||
|
@@ -66,6 +66,7 @@
|
|||||||
#include <QMimeData>
|
#include <QMimeData>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
|
#include <QSet>
|
||||||
#include <QTabWidget>
|
#include <QTabWidget>
|
||||||
#include <QTextEdit>
|
#include <QTextEdit>
|
||||||
#include <QTableWidget>
|
#include <QTableWidget>
|
||||||
@@ -89,6 +90,7 @@ enum { debugModel = 0 };
|
|||||||
#define MODEL_DEBUG(s) do { if (debugModel) qDebug() << s; } while (0)
|
#define MODEL_DEBUG(s) do { if (debugModel) qDebug() << s; } while (0)
|
||||||
|
|
||||||
static QMap<QString, int> theWatcherNames; // Keep order, QTCREATORBUG-12308.
|
static QMap<QString, int> theWatcherNames; // Keep order, QTCREATORBUG-12308.
|
||||||
|
static QSet<QString> theTemporaryWatchers; // Used for 'watched widgets'.
|
||||||
static int theWatcherCount = 0;
|
static int theWatcherCount = 0;
|
||||||
static QHash<QString, int> theTypeFormats;
|
static QHash<QString, int> theTypeFormats;
|
||||||
static QHash<QString, int> theIndividualFormats;
|
static QHash<QString, int> theIndividualFormats;
|
||||||
@@ -430,6 +432,11 @@ public:
|
|||||||
void removeWatchItem(WatchItem *item);
|
void removeWatchItem(WatchItem *item);
|
||||||
void inputNewExpression();
|
void inputNewExpression();
|
||||||
|
|
||||||
|
void grabWidget();
|
||||||
|
void ungrabWidget();
|
||||||
|
void timerEvent(QTimerEvent *event) override;
|
||||||
|
int m_grabWidgetTimerId = -1;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
WatchHandler *m_handler; // Not owned.
|
WatchHandler *m_handler; // Not owned.
|
||||||
DebuggerEngine *m_engine; // Not owned.
|
DebuggerEngine *m_engine; // Not owned.
|
||||||
@@ -446,7 +453,6 @@ public:
|
|||||||
|
|
||||||
QSet<QString> m_expandedINames;
|
QSet<QString> m_expandedINames;
|
||||||
QTimer m_requestUpdateTimer;
|
QTimer m_requestUpdateTimer;
|
||||||
bool m_grabbing = false;
|
|
||||||
|
|
||||||
QHash<QString, TypeInfo> m_reportedTypeInfo;
|
QHash<QString, TypeInfo> m_reportedTypeInfo;
|
||||||
QHash<QString, DisplayFormats> m_reportedTypeFormats; // Type name -> Dumper Formats
|
QHash<QString, DisplayFormats> m_reportedTypeFormats; // Type name -> Dumper Formats
|
||||||
@@ -1067,13 +1073,6 @@ bool WatchModel::setData(const QModelIndex &idx, const QVariant &value, int role
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ev.as<QMouseEvent>(QEvent::MouseButtonPress)) {
|
|
||||||
m_grabbing = false;
|
|
||||||
ev.view()->releaseMouse();
|
|
||||||
m_engine->watchPoint(ev.globalPos());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ev.as<QMouseEvent>(QEvent::MouseButtonDblClick)) {
|
if (ev.as<QMouseEvent>(QEvent::MouseButtonDblClick)) {
|
||||||
if (item && !item->parent()) { // if item is the invisible root item
|
if (item && !item->parent()) { // if item is the invisible root item
|
||||||
inputNewExpression();
|
inputNewExpression();
|
||||||
@@ -1268,6 +1267,46 @@ static QString variableToolTip(const QString &name, const QString &type, quint64
|
|||||||
WatchModel::tr("<i>%1</i> %2").arg(type, name);
|
WatchModel::tr("<i>%1</i> %2").arg(type, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WatchModel::grabWidget()
|
||||||
|
{
|
||||||
|
qApp->setOverrideCursor(Qt::CrossCursor);
|
||||||
|
m_grabWidgetTimerId = startTimer(30);
|
||||||
|
ICore::mainWindow()->grabMouse();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WatchModel::timerEvent(QTimerEvent *event)
|
||||||
|
{
|
||||||
|
if (event->timerId() == m_grabWidgetTimerId) {
|
||||||
|
QPoint pnt = QCursor::pos();
|
||||||
|
Qt::KeyboardModifiers mods = QApplication::queryKeyboardModifiers();
|
||||||
|
QString msg;
|
||||||
|
if (mods == Qt::NoModifier) {
|
||||||
|
msg = tr("Press Ctrl to select widget at (%1, %2). "
|
||||||
|
"Press any other keyboard modifier to stop selection.")
|
||||||
|
.arg(pnt.x()).arg(pnt.y());
|
||||||
|
} else {
|
||||||
|
if (mods == Qt::CTRL) {
|
||||||
|
msg = tr("Selecting widget at (%1, %2).").arg(pnt.x()).arg(pnt.y());
|
||||||
|
m_engine->watchPoint(pnt);
|
||||||
|
} else {
|
||||||
|
msg = tr("Selection aborted.");
|
||||||
|
}
|
||||||
|
ungrabWidget();
|
||||||
|
}
|
||||||
|
showMessage(msg, StatusBar);
|
||||||
|
} else {
|
||||||
|
WatchModelBase::timerEvent(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WatchModel::ungrabWidget()
|
||||||
|
{
|
||||||
|
ICore::mainWindow()->releaseMouse();
|
||||||
|
qApp->restoreOverrideCursor();
|
||||||
|
killTimer(m_grabWidgetTimerId);
|
||||||
|
m_grabWidgetTimerId = -1;
|
||||||
|
}
|
||||||
|
|
||||||
int WatchModel::memberVariableRecursion(WatchItem *item,
|
int WatchModel::memberVariableRecursion(WatchItem *item,
|
||||||
const QString &name,
|
const QString &name,
|
||||||
quint64 start, quint64 end,
|
quint64 start, quint64 end,
|
||||||
@@ -1608,10 +1647,9 @@ bool WatchModel::contextMenuEvent(const ItemViewEvent &ev)
|
|||||||
canRemoveWatches && !m_handler->watchedExpressions().isEmpty(),
|
canRemoveWatches && !m_handler->watchedExpressions().isEmpty(),
|
||||||
[this] { clearWatches(); });
|
[this] { clearWatches(); });
|
||||||
|
|
||||||
// FIXME:
|
addAction(menu, tr("Select Widget to Add into Expression Evaluator"),
|
||||||
// addAction(menu, tr("Select Widget to Add into Expression Evaluator"),
|
state == InferiorRunOk && m_engine->hasCapability(WatchWidgetsCapability),
|
||||||
// canHandleWatches && canInsertWatches && m_engine->hasCapability(WatchWidgetsCapability),
|
[this] { grabWidget(); });
|
||||||
// [this, ev] { ev.view()->grabMouse(Qt::CrossCursor); m_grabbing = true; });
|
|
||||||
|
|
||||||
menu->addSeparator();
|
menu->addSeparator();
|
||||||
menu->addMenu(createFormatMenu(item));
|
menu->addMenu(createFormatMenu(item));
|
||||||
@@ -1901,6 +1939,9 @@ void WatchHandler::cleanup()
|
|||||||
{
|
{
|
||||||
m_model->m_expandedINames.clear();
|
m_model->m_expandedINames.clear();
|
||||||
theWatcherNames.remove(QString());
|
theWatcherNames.remove(QString());
|
||||||
|
for (const QString &exp : theTemporaryWatchers)
|
||||||
|
theWatcherNames.remove(exp);
|
||||||
|
theTemporaryWatchers.clear();
|
||||||
saveWatchers();
|
saveWatchers();
|
||||||
m_model->reinitialize();
|
m_model->reinitialize();
|
||||||
emit m_model->updateFinished();
|
emit m_model->updateFinished();
|
||||||
@@ -2080,13 +2121,15 @@ QString WatchHandler::watcherName(const QString &exp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If \a name is empty, \a exp will be used as name.
|
// If \a name is empty, \a exp will be used as name.
|
||||||
void WatchHandler::watchExpression(const QString &exp, const QString &name)
|
void WatchHandler::watchExpression(const QString &exp, const QString &name, bool temporary)
|
||||||
{
|
{
|
||||||
// Do not insert the same entry more then once.
|
// Do not insert the same entry more then once.
|
||||||
if (exp.isEmpty() || theWatcherNames.contains(exp))
|
if (exp.isEmpty() || theWatcherNames.contains(exp))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
theWatcherNames[exp] = theWatcherCount++;
|
theWatcherNames[exp] = theWatcherCount++;
|
||||||
|
if (temporary)
|
||||||
|
theTemporaryWatchers.insert(exp);
|
||||||
|
|
||||||
auto item = new WatchItem;
|
auto item = new WatchItem;
|
||||||
item->exp = exp;
|
item->exp = exp;
|
||||||
|
@@ -66,7 +66,9 @@ public:
|
|||||||
WatchModelBase *model() const;
|
WatchModelBase *model() const;
|
||||||
|
|
||||||
void cleanup();
|
void cleanup();
|
||||||
void watchExpression(const QString &exp, const QString &name = QString());
|
void grabWidget(QWidget *viewParent);
|
||||||
|
void watchExpression(const QString &exp, const QString &name = QString(),
|
||||||
|
bool temporary = false);
|
||||||
void updateWatchExpression(WatchItem *item, const QString &newExp);
|
void updateWatchExpression(WatchItem *item, const QString &newExp);
|
||||||
void watchVariable(const QString &exp);
|
void watchVariable(const QString &exp);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user