Commit 525c33f9 authored by hjk's avatar hjk Committed by Christian Stenger

Debugger: Infrastructure for reworked native mixed debugging

- Remove old experimental native mixed approach.
- Move some common stack parsing to Stackhandler.
- Mark gdbbridge.py debug output explicitly to remove it
  from actual reponse handling

New native mixed needs QtDeclarative changes and
QTC_DEBUGGER_NATIVE_MIXED=1 for now.

Change-Id: I09eed1da51cea878636d36756015b7bfaed34203
Reviewed-by: default avatarChristian Stenger <christian.stenger@theqtcompany.com>
parent ea39476e
This diff is collapsed.
This diff is collapsed.
......@@ -57,7 +57,7 @@ def showException(msg, exType, exValue, exTraceback):
lines = [line for line in traceback.format_exception(exType, exValue, exTraceback)]
warn('\n'.join(lines))
def fileName(file):
def fileNameAsString(file):
return str(file) if file.IsValid() else ''
......@@ -775,7 +775,7 @@ class Dumper(DumperBase):
def describeLocation(self, frame):
if int(frame.pc) == 0xffffffffffffffff:
return ''
file = fileName(frame.line_entry.file)
file = fileNameAsString(frame.line_entry.file)
line = frame.line_entry.line
return 'location={file="%s",line="%s",addr="%s"}' % (file, line, frame.pc)
......@@ -822,8 +822,8 @@ class Dumper(DumperBase):
result += ',fp="0x%x"' % frame.fp
result += ',func="%s"' % frame.GetFunctionName()
result += ',line="%s"' % frame.line_entry.line
result += ',fullname="%s"' % fileName(frame.line_entry.file)
result += ',file="%s"' % fileName(frame.line_entry.file)
result += ',fullname="%s"' % fileNameAsString(frame.line_entry.file)
result += ',file="%s"' % fileNameAsString(frame.line_entry.file)
result += '}},'
result += '],current-thread-id="%s"' % self.currentThread().id
......@@ -849,7 +849,7 @@ class Dumper(DumperBase):
self.report(self.describeLocation(thread.GetFrameAtIndex(0))) # FIXME
isNativeMixed = int(args.get('nativeMixed', 0))
isNativeMixed = int(args.get('nativemixed', 0))
limit = args.get('stacklimit', -1)
(n, isLimited) = (limit, True) if limit > 0 else (thread.GetNumFrames(), False)
......@@ -869,39 +869,38 @@ class Dumper(DumperBase):
level = frame.idx
addr = frame.GetPCAddress().GetLoadAddress(self.target)
functionName = frame.GetFunctionName()
fullname = fileName(lineEntry.file)
fileName = fileNameAsString(lineEntry.file)
usable = None
language = None
if isNativeMixed:
if self.isReportableQmlFrame(functionName):
if False and isNativeMixed:
if self.isReportableInterpreterFrame(functionName):
engine = frame.FindVariable("engine")
self.context = engine
h = self.extractQmlLocation(engine)
pc = 0
functionName = h['functionName']
fullname = h['fileName']
lineNumber = h['lineNumber']
functionName = h['function']
fileName = h['file']
lineNumber = h['line']
addr = h['context']
language = 'js'
elif not functionName is None:
if functionName.startswith("qt_v4"):
usable = 0
elif functionName.find("QV4::") >= 0:
usable = 0
#elif not functionName is None:
# if functionName.startswith("qt_v4"):
# usable = 0
# elif functionName.find("QV4::") >= 0:
# usable = 0
result += '{pc="0x%x"' % pc
result += ',level="%d"' % level
result += ',addr="0x%x"' % addr
result += ',address="0x%x"' % addr
if not usable is None:
result += ',usable="%s"' % usable
result += ',func="%s"' % functionName
result += ',function="%s"' % functionName
result += ',line="%d"' % lineNumber
if not language is None:
result += ',language="%s"' % language
result += ',fullname="%s"' % fullname
result += ',file="%s"},' % fullname
result += ',file="%s"},' % fileName
result += ']'
result += ',hasmore="%d"' % isLimited
result += ',limit="%d"' % limit
......@@ -1400,7 +1399,7 @@ class Dumper(DumperBase):
addr = loc.GetAddress()
lineEntry = addr.GetLineEntry()
result += '{locid="%s"' % loc.GetID()
result += ',func="%s"' % addr.GetFunction().GetName()
result += ',function="%s"' % addr.GetFunction().GetName()
result += ',enabled="%s"' % (1 if loc.IsEnabled() else 0)
result += ',resolved="%s"' % (1 if loc.IsResolved() else 0)
result += ',valid="%s"' % (1 if loc.IsValid() else 0)
......@@ -1421,16 +1420,16 @@ class Dumper(DumperBase):
def insertBreakpoint(self, args):
bpType = args["type"]
if bpType == BreakpointByFileAndLine:
fileName = args["fileName"]
fileName = args["file"]
if fileName.endswith(".js") or fileName.endswith(".qml"):
self.insertQmlBreakpoint(args)
self.doInsertInterpreterBreakpoint(args, False)
return
extra = ''
more = True
if bpType == BreakpointByFileAndLine:
bp = self.target.BreakpointCreateByLocation(
str(args["fileName"]), int(args["lineNumber"]))
str(args["file"]), int(args["line"]))
elif bpType == BreakpointByFunction:
bp = self.target.BreakpointCreateByName(args["function"])
elif bpType == BreakpointByAddress:
......
......@@ -34,10 +34,12 @@
#include <QString>
namespace QmlDebug {
enum QmlDebugServicesPreset {
NoQmlDebugServices,
QmlDebuggerServices,
QmlProfilerServices
QmlProfilerServices,
QmlNativeDebuggerServices
};
static inline QString qmlDebugServices(QmlDebugServicesPreset preset)
......@@ -49,6 +51,8 @@ static inline QString qmlDebugServices(QmlDebugServicesPreset preset)
return QStringLiteral("DebugMessages,QmlDebugger,V8Debugger,QmlInspector");
case QmlProfilerServices:
return QStringLiteral("CanvasFrameRate,EngineControl");
case QmlNativeDebuggerServices:
return QStringLiteral("NativeQmlDebugger");
default:
Q_ASSERT(false);
return QString();
......@@ -61,6 +65,10 @@ static inline QString qmlDebugCommandLineArguments(QmlDebugServicesPreset servic
if (services == NoQmlDebugServices)
return QString();
if (services == QmlNativeDebuggerServices)
return QString::fromLatin1("-qmljsdebugger=native,services:%1")
.arg(qmlDebugServices(services));
return QString::fromLatin1("-qmljsdebugger=port:%1,block,services:%2")
.arg(port ? QString::number(port) : QStringLiteral("%qml_port%"))
.arg(qmlDebugServices(services));
......
......@@ -757,14 +757,15 @@ const BreakpointParameters &Breakpoint::parameters() const
void Breakpoint::addToCommand(DebuggerCommand *cmd) const
{
cmd->arg("modelid", id().toByteArray());
cmd->arg("id", int(response().id.majorPart()));
cmd->arg("type", type());
cmd->arg("ignorecount", ignoreCount());
cmd->arg("condition", condition().toHex());
cmd->arg("function", functionName().toUtf8());
cmd->arg("oneshot", isOneShot());
cmd->arg("enabled", isEnabled());
cmd->arg("fileName", fileName().toUtf8());
cmd->arg("lineNumber", lineNumber());
cmd->arg("file", fileName().toUtf8());
cmd->arg("line", lineNumber());
cmd->arg("address", address());
cmd->arg("expression", expression());
}
......
......@@ -2539,28 +2539,29 @@ bool CdbEngine::stateAcceptsBreakpointChanges() const
bool CdbEngine::acceptsBreakpoint(Breakpoint bp) const
{
if (!bp.parameters().isCppBreakpoint())
return false;
switch (bp.type()) {
case UnknownBreakpointType:
case LastBreakpointType:
case BreakpointAtFork:
case WatchpointAtExpression:
case BreakpointAtSysCall:
case BreakpointOnQmlSignalEmit:
case BreakpointAtJavaScriptThrow:
return false;
case WatchpointAtAddress:
case BreakpointByFileAndLine:
case BreakpointByFunction:
case BreakpointByAddress:
case BreakpointAtThrow:
case BreakpointAtCatch:
case BreakpointAtMain:
case BreakpointAtExec:
break;
if (bp.parameters().isCppBreakpoint()) {
switch (bp.type()) {
case UnknownBreakpointType:
case LastBreakpointType:
case BreakpointAtFork:
case WatchpointAtExpression:
case BreakpointAtSysCall:
case BreakpointOnQmlSignalEmit:
case BreakpointAtJavaScriptThrow:
return false;
case WatchpointAtAddress:
case BreakpointByFileAndLine:
case BreakpointByFunction:
case BreakpointByAddress:
case BreakpointAtThrow:
case BreakpointAtCatch:
case BreakpointAtMain:
case BreakpointAtExec:
break;
}
return true;
}
return true;
return isNativeMixedEnabled();
}
// Context for fixing file/line-type breakpoints, for delayed creation.
......@@ -2801,7 +2802,7 @@ static StackFrames parseFrames(const GdbMi &gdbmi, bool *incomplete = 0)
break;
}
StackFrame frame;
frame.level = i;
frame.level = QByteArray::number(i);
const GdbMi fullName = frameMi["fullname"];
if (fullName.isValid()) {
frame.file = QFile::decodeName(fullName.data());
......@@ -2811,9 +2812,10 @@ static StackFrames parseFrames(const GdbMi &gdbmi, bool *incomplete = 0)
if (languageMi.isValid() && languageMi.data() == "js")
frame.language = QmlLanguage;
}
frame.function = QLatin1String(frameMi["func"].data());
frame.from = QLatin1String(frameMi["from"].data());
frame.address = frameMi["addr"].data().toULongLong(0, 16);
frame.function = QLatin1String(frameMi["function"].data());
frame.module = QLatin1String(frameMi["from"].data());
frame.context = frameMi["context"].data();
frame.address = frameMi["address"].data().toULongLong(0, 16);
rc.push_back(frame);
}
return rc;
......@@ -2895,7 +2897,7 @@ void CdbEngine::handleAdditionalQmlStack(const DebuggerResponse &response)
break;
}
for (int i = 0; i < qmlFrameCount; ++i)
qmlFrames[i].fixQmlFrame(runParameters());
qmlFrames[i].fixQrcFrame(runParameters());
stackHandler()->prependFrames(qmlFrames);
} while (false);
if (!errorMessage.isEmpty())
......
......@@ -15,6 +15,7 @@ QtcPlugin {
Depends { name: "Core" }
Depends { name: "CppTools" }
Depends { name: "ProjectExplorer" }
Depends { name: "QtSupport" }
Depends { name: "TextEditor" }
cpp.includePaths: base.concat([project.sharedSourcesDir + "/registryaccess"])
......
......@@ -12,6 +12,7 @@ QTC_PLUGIN_DEPENDS += \
coreplugin \
cpptools \
projectexplorer \
qtsupport \
texteditor
QTC_PLUGIN_RECOMMENDS += \
cppeditor
......@@ -184,18 +184,6 @@ DebuggerSettings::DebuggerSettings()
item->setIconVisibleInMenu(false);
insertItem(OperateByInstruction, item);
item = new SavedAction(this);
item->setText(tr("Native Mixed Mode"));
item->setCheckable(true);
item->setDefaultValue(true);
item->setIcon(QIcon(QLatin1String(Core::Constants::ICON_LINK)));
item->setToolTip(tr("<p>This switches the debugger to native-mixed "
"operation mode. In this mode, stepping and data display will "
"be handled by the native debugger backend (GDB, LLDB or CDB) "
"for C++, QML and JS sources."));
item->setIconVisibleInMenu(false);
insertItem(OperateNativeMixed, item);
item = new SavedAction(this);
item->setText(tr("Dereference Pointers Automatically"));
item->setCheckable(true);
......
......@@ -99,7 +99,6 @@ enum DebuggerActionCode
LogTimeStamps,
VerboseLog,
OperateByInstruction,
OperateNativeMixed,
CloseSourceBuffersOnExit,
CloseMemoryBuffersOnExit,
SwitchModeOnExit,
......
......@@ -61,7 +61,6 @@ const char NEXT[] = "Debugger.NextLine";
const char REVERSE[] = "Debugger.ReverseDirection";
const char RESET[] = "Debugger.Reset";
const char OPERATE_BY_INSTRUCTION[] = "Debugger.OperateByInstruction";
const char OPERATE_NATIVE_MIXED[] = "Debugger.OperateNativeMixed";
const char QML_SHOW_APP_ON_TOP[] = "Debugger.QmlShowAppOnTop";
const char QML_SELECTTOOL[] = "Debugger.QmlSelectTool";
const char QML_ZOOMTOOL[] = "Debugger.QmlZoomTool";
......
......@@ -110,8 +110,6 @@ DebuggerEngine *currentEngine();
QMessageBox *showMessageBox(int icon, const QString &title,
const QString &text, int buttons = 0);
bool isNativeMixedActive();
bool isNativeMixedEnabled();
bool isReverseDebuggingEnabled();
} // namespace Internal
......
......@@ -78,6 +78,11 @@
#include <QFileInfo>
#include <QDir>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
using namespace Core;
using namespace Debugger::Internal;
using namespace ProjectExplorer;
......@@ -126,7 +131,7 @@ Location::Location(const StackFrame &frame, bool marker)
m_functionName = frame.function;
m_hasDebugInfo = frame.isUsable();
m_address = frame.address;
m_from = frame.from;
m_from = frame.module;
}
......@@ -192,7 +197,7 @@ public:
m_modulesHandler(engine),
m_registerHandler(engine),
m_sourceFilesHandler(),
m_stackHandler(),
m_stackHandler(engine),
m_threadsHandler(),
m_watchHandler(engine),
m_disassemblerAgent(engine),
......@@ -203,8 +208,6 @@ public:
this, &DebuggerEnginePrivate::resetLocation);
connect(action(IntelFlavor), &Utils::SavedAction::valueChanged,
this, &DebuggerEnginePrivate::reloadDisassembly);
connect(action(OperateNativeMixed), &QAction::triggered,
engine, &DebuggerEngine::reloadFullStack);
Utils::globalMacroExpander()->registerFileVariables(PrefixDebugExecutable,
tr("Debugged executable"),
......@@ -2023,6 +2026,26 @@ void DebuggerEngine::checkState(DebuggerState state, const char *file, int line)
qDebug("%s", qPrintable(msg));
}
bool DebuggerEngine::isNativeMixedEnabled() const
{
return runParameters().nativeMixedEnabled && (runParameters().languages & QmlLanguage);
}
bool DebuggerEngine::isNativeMixedActive() const
{
return isNativeMixedEnabled(); //&& boolSetting(OperateNativeMixed);
}
bool DebuggerEngine::isNativeMixedActiveFrame() const
{
if (!isNativeMixedActive())
return false;
if (stackHandler()->frames().isEmpty())
return false;
StackFrame frame = stackHandler()->frameAt(0);
return frame.language == QmlLanguage;
}
} // namespace Internal
} // namespace Debugger
......
......@@ -117,6 +117,8 @@ public:
// Used by AttachCrashedExternal.
QString crashParameter;
bool nativeMixedEnabled = false;
// For Debugger testing.
int testCase = 0;
};
......@@ -443,6 +445,9 @@ protected:
void updateLocalsView(const GdbMi &all);
void checkState(DebuggerState state, const char *file, int line);
bool isNativeMixedEnabled() const;
bool isNativeMixedActive() const;
bool isNativeMixedActiveFrame() const;
private:
// Wrapper engine needs access to state of its subengines.
......
......@@ -474,7 +474,6 @@ bool DummyEngine::hasCapability(unsigned cap) const
return cap & (WatchpointByAddressCapability
| BreakConditionCapability
| TracePointCapability
| OperateNativeMixed
| OperateByInstructionCapability);
// This is a Qml or unknown engine.
......@@ -2321,17 +2320,6 @@ QMessageBox *showMessageBox(int icon, const QString &title,
return mb;
}
bool isNativeMixedEnabled()
{
static bool enabled = qEnvironmentVariableIsSet("QTC_DEBUGGER_NATIVE_MIXED");
return enabled;
}
bool isNativeMixedActive()
{
return isNativeMixedEnabled() && boolSetting(OperateNativeMixed);
}
bool isReverseDebuggingEnabled()
{
static bool enabled = qEnvironmentVariableIsSet("QTC_DEBUGGER_ENABLE_REVERSE");
......@@ -2769,16 +2757,6 @@ void DebuggerPluginPrivate::extensionsInitialized()
cmd->setAttribute(Command::CA_Hide);
debugMenu->addAction(cmd);
if (isNativeMixedEnabled()) {
SavedAction *act = action(OperateNativeMixed);
act->setValue(true);
cmd = ActionManager::registerAction(act, Constants::OPERATE_NATIVE_MIXED);
cmd->setAttribute(Command::CA_Hide);
debugMenu->addAction(cmd);
connect(cmd->action(), &QAction::triggered,
[this] { currentEngine()->updateAll(); });
}
cmd = ActionManager::registerAction(m_breakAction, "Debugger.ToggleBreak");
cmd->setDefaultKeySequence(QKeySequence(UseMacShortcuts ? tr("F8") : tr("F9")));
debugMenu->addAction(cmd);
......@@ -2912,8 +2890,6 @@ void DebuggerPluginPrivate::extensionsInitialized()
hbox->addWidget(toolButton(Constants::STEPOUT));
hbox->addWidget(toolButton(Constants::RESET));
hbox->addWidget(toolButton(Constants::OPERATE_BY_INSTRUCTION));
if (isNativeMixedEnabled())
hbox->addWidget(toolButton(Constants::OPERATE_NATIVE_MIXED));
if (isReverseDebuggingEnabled()) {
m_reverseToolButton = toolButton(Constants::REVERSE);
......
......@@ -80,7 +80,7 @@ void GdbMi::parseResultOrValue(const char *&from, const char *to)
if (from == to || *from == '(')
return;
const char *ptr = from;
while (ptr < to && *ptr != '=') {
while (ptr < to && *ptr != '=' && *ptr != ':') {
//qDebug() << "adding" << QChar(*ptr) << "to name";
++ptr;
}
......@@ -770,6 +770,12 @@ QString decodeData(const QByteArray &ba, DebuggerEncoding encoding)
case SpecialEmptyStructureValue: { // 39
return QLatin1String("{...}");
}
case SpecialUndefinedValue: { // 40
return QLatin1String("Undefined");
}
case SpecialNullValue: { // 41
return QLatin1String("Null");
}
}
qDebug() << "ENCODING ERROR: " << encoding;
return QCoreApplication::translate("Debugger", "<Encoding error>");
......@@ -857,5 +863,22 @@ QByteArray DebuggerCommand::argsToString() const
return args.toString().toLatin1();
}
DebuggerEncoding debuggerEncoding(const QByteArray &data)
{
if (data == "utf16")
return Hex4EncodedLittleEndianWithQuotes;
if (data == "empty")
return SpecialEmptyValue;
if (data == "minimumitemcount")
return SpecialMinimumItemCountValue;
if (data == "undefined")
return SpecialUndefinedValue;
if (data == "null")
return SpecialNullValue;
if (data == "itemcount")
return SpecialItemCountValue;
return DebuggerEncoding(data.toInt());
}
} // namespace Internal
} // namespace Debugger
......@@ -252,9 +252,13 @@ enum DebuggerEncoding
SpecialNotCallableValue = 36,
SpecialNullReferenceValue = 37,
SpecialOptimizedOutValue = 38,
SpecialEmptyStructureValue = 39
SpecialEmptyStructureValue = 39,
SpecialUndefinedValue = 40,
SpecialNullValue = 41
};
DebuggerEncoding debuggerEncoding(const QByteArray &data);
// Decode string data as returned by the dumper helpers.
QString decodeData(const QByteArray &baIn, DebuggerEncoding encoding);
......
......@@ -59,6 +59,8 @@
#include <coreplugin/icore.h>
#include <qmldebug/qmldebugcommandlinearguments.h>
#include <qtsupport/qtkitinformation.h>
#include <QTcpServer>
using namespace Debugger::Internal;
......@@ -416,6 +418,19 @@ void DebuggerRunControlCreator::enrich(const RunConfiguration *runConfig, const
if (m_project && m_rp.projectSourceFiles.isEmpty())
m_rp.projectSourceFiles = m_project->files(Project::ExcludeGeneratedFiles);
if (m_project && m_rp.projectSourceFiles.isEmpty())
m_rp.projectSourceFiles = m_project->files(Project::ExcludeGeneratedFiles);
if (false && m_project && m_kit) {
const QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(m_kit);
m_rp.nativeMixedEnabled = version && version->qtVersion() >= QtSupport::QtVersionNumber(5, 7, 0);
}
bool ok = false;
int nativeMixedOverride = qgetenv("QTC_DEBUGGER_NATIVE_MIXED").toInt(&ok);
if (ok)
m_rp.nativeMixedEnabled = bool(nativeMixedOverride);
// validate debugger if C++ debugging is enabled
if (m_rp.languages & CppLanguage) {
const QList<Task> tasks = DebuggerKitInformation::validateDebugger(m_kit);
......@@ -482,9 +497,6 @@ void DebuggerRunControlCreator::enrich(const RunConfiguration *runConfig, const
const QString optimizerKey = _("QML_DISABLE_OPTIMIZER");
if (!m_rp.environment.hasKey(optimizerKey))
m_rp.environment.set(optimizerKey, _("1"));
QtcProcess::addArg(&m_rp.processArgs, QmlDebug::qmlDebugCommandLineArguments(
QmlDebug::QmlDebuggerServices, m_rp.qmlServerPort));
}
}
}
......@@ -502,14 +514,23 @@ void DebuggerRunControlCreator::enrich(const RunConfiguration *runConfig, const
}
if (m_rp.masterEngineType == NoEngineType && m_debuggerAspect) {
const bool useCppDebugger = m_debuggerAspect->useCppDebugger() && (m_rp.languages & CppLanguage);
const bool useQmlDebugger = m_debuggerAspect->useQmlDebugger() && (m_rp.languages & QmlLanguage);
if (useQmlDebugger) {
if (useCppDebugger)
m_rp.masterEngineType = QmlCppEngineType;
else
const bool wantCppDebugger = m_debuggerAspect->useCppDebugger() && (m_rp.languages & CppLanguage);
const bool wantQmlDebugger = m_debuggerAspect->useQmlDebugger() && (m_rp.languages & QmlLanguage);
if (wantQmlDebugger) {
QString qmlArgs;
if (wantCppDebugger) {
if (m_rp.nativeMixedEnabled) {
qmlArgs = QmlDebug::qmlDebugCommandLineArguments(QmlDebug::QmlNativeDebuggerServices);
} else {
m_rp.masterEngineType = QmlCppEngineType;
qmlArgs = QmlDebug::qmlDebugCommandLineArguments(QmlDebug::QmlDebuggerServices, m_rp.qmlServerPort);
}
} else {
m_rp.masterEngineType = QmlEngineType;
qmlArgs = QmlDebug::qmlDebugCommandLineArguments(QmlDebug::QmlDebuggerServices, m_rp.qmlServerPort);
}
QtcProcess::addArg(&m_rp.processArgs, qmlArgs);
}
}
......
This diff is collapsed.
......@@ -216,7 +216,6 @@ protected:
void handleStop1(const GdbMi &data);
void handleStop2(const GdbMi &data);
Q_SLOT void handleStop2();
StackFrame parseStackFrame(const GdbMi &mi, int level);
void resetCommandQueue();
bool isSynchronous() const override { return true; }
......@@ -367,7 +366,6 @@ protected:
Q_SLOT void reloadStack();
Q_SLOT virtual void reloadFullStack() override;
virtual void loadAdditionalQmlStack() override;
void handleQmlStackFrameArguments(const DebuggerResponse &response);
void handleQmlStackTrace(const DebuggerResponse &response);
int currentFrame() const;
......@@ -399,7 +397,7 @@ protected:
Q_SLOT void createFullBacktrace();
void doUpdateLocals(const UpdateParameters &parameters) override;
void handleStackFrame(const DebuggerResponse &response);
void handleFetchVariables(const DebuggerResponse &response);
void setLocals(const QList<GdbMi> &locals);
......
......@@ -537,7 +537,7 @@ void LldbEngine::selectThread(ThreadId threadId)
cmd.arg("id", threadId.raw());
cmd.callback = [this](const DebuggerResponse &) {
DebuggerCommand cmd("fetchStack");
cmd.arg("nativeMixed", isNativeMixedActive());
cmd.arg("nativemixed", isNativeMixedActive());