forked from qt-creator/qt-creator
Replace the current license disclaimer in files by a SPDX-License-Identifier. Task-number: QTBUG-67283 Change-Id: I708fd1f9f2b73d60f57cc3568646929117825813 Reviewed-by: Eike Ziller <eike.ziller@qt.io>
413 lines
14 KiB
C++
413 lines
14 KiB
C++
// Copyright (C) 2016 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
|
|
|
#pragma once
|
|
|
|
#include <debugger/debuggerengine.h>
|
|
|
|
#include <debugger/breakhandler.h>
|
|
#include <debugger/registerhandler.h>
|
|
#include <debugger/peripheralregisterhandler.h>
|
|
#include <debugger/watchhandler.h>
|
|
#include <debugger/watchutils.h>
|
|
#include <debugger/debuggeritem.h>
|
|
#include <debugger/debuggertooltipmanager.h>
|
|
#include <debugger/outputcollector.h>
|
|
|
|
#include <utils/id.h>
|
|
#include <utils/qtcprocess.h>
|
|
|
|
#include <QProcess>
|
|
#include <QTextCodec>
|
|
#include <QTimer>
|
|
|
|
namespace Debugger::Internal {
|
|
|
|
class BreakpointParameters;
|
|
class DebugInfoTask;
|
|
class DebugInfoTaskHandler;
|
|
class DebuggerResponse;
|
|
class DisassemblerAgentCookie;
|
|
class GdbMi;
|
|
class MemoryAgentCookie;
|
|
|
|
struct CoreInfo
|
|
{
|
|
QString rawStringFromCore;
|
|
Utils::FilePath foundExecutableName; // empty if no corresponding exec could be found
|
|
bool isCore = false;
|
|
|
|
static CoreInfo readExecutableNameFromCore(const ProjectExplorer::Runnable &debugger,
|
|
const Utils::FilePath &coreFile);
|
|
};
|
|
|
|
class GdbEngine final : public CppDebuggerEngine
|
|
{
|
|
public:
|
|
GdbEngine();
|
|
~GdbEngine() final;
|
|
|
|
private: ////////// General Interface //////////
|
|
void handleGdbStartFailed();
|
|
void prepareForRestart() final;
|
|
|
|
bool hasCapability(unsigned) const final;
|
|
void detachDebugger() final;
|
|
void shutdownInferior() final;
|
|
void abortDebuggerProcess() final;
|
|
void resetInferior() final;
|
|
|
|
bool acceptsDebuggerCommands() const final;
|
|
void executeDebuggerCommand(const QString &command) final;
|
|
|
|
////////// General State //////////
|
|
|
|
bool m_registerNamesListed = false;
|
|
|
|
////////// Gdb Process Management //////////
|
|
|
|
void handleInferiorShutdown(const DebuggerResponse &response);
|
|
void handleGdbExit(const DebuggerResponse &response);
|
|
void setLinuxOsAbi();
|
|
|
|
void loadInitScript();
|
|
void setEnvironmentVariables();
|
|
|
|
// Something went wrong with the adapter *before* adapterStarted() was emitted.
|
|
// Make sure to clean up everything before emitting this signal.
|
|
void handleAdapterStartFailed(const QString &msg,
|
|
Utils::Id settingsIdHint = Utils::Id());
|
|
|
|
// Called after target setup.
|
|
void handleInferiorPrepared();
|
|
|
|
void handleDebugInfoLocation(const DebuggerResponse &response);
|
|
|
|
// The engine is still running just fine, but it failed to acquire a debuggee.
|
|
void notifyInferiorSetupFailedHelper(const QString &msg);
|
|
|
|
void handleGdbStarted();
|
|
void handleGdbDone();
|
|
void readGdbStandardOutput();
|
|
void readGdbStandardError();
|
|
void readDebuggeeOutput(const QByteArray &ba);
|
|
|
|
QTextCodec *m_gdbOutputCodec;
|
|
QTextCodec::ConverterState m_gdbOutputCodecState;
|
|
QTextCodec *m_inferiorOutputCodec;
|
|
QTextCodec::ConverterState m_inferiorOutputCodecState;
|
|
|
|
QByteArray m_inbuffer;
|
|
bool m_busy = false;
|
|
|
|
// Name of the convenience variable containing the last
|
|
// known function return value.
|
|
QString m_resultVarName;
|
|
|
|
////////// Gdb Command Management //////////
|
|
|
|
void runCommand(const DebuggerCommand &command) final;
|
|
|
|
void commandTimeout();
|
|
void setTokenBarrier();
|
|
|
|
// Sets up an "unexpected result" for the following commeand.
|
|
void scheduleTestResponse(int testCase, const QString &response);
|
|
|
|
QHash<int, DebuggerCommand> m_commandForToken;
|
|
QHash<int, int> m_flagsForToken;
|
|
int commandTimeoutTime() const;
|
|
QTimer m_commandTimer;
|
|
|
|
QString m_pendingConsoleStreamOutput;
|
|
QString m_pendingLogStreamOutput;
|
|
|
|
// This contains the first token number for the current round
|
|
// of evaluation. Responses with older tokens are considers
|
|
// out of date and discarded.
|
|
int m_oldestAcceptableToken = -1;
|
|
int m_nonDiscardableCount = 0;
|
|
|
|
bool m_rerunPending = false;
|
|
bool m_ignoreNextTrap = false;
|
|
|
|
////////// Gdb Output, State & Capability Handling //////////
|
|
|
|
Q_INVOKABLE void handleResponse(const QString &buff);
|
|
void handleAsyncOutput(const QString &asyncClass, const GdbMi &result);
|
|
void handleStopResponse(const GdbMi &data);
|
|
void handleResultRecord(DebuggerResponse *response);
|
|
void handleStop1(const GdbMi &data);
|
|
void handleStop2(const GdbMi &data);
|
|
void handleStop3();
|
|
void resetCommandQueue();
|
|
|
|
// Gdb initialization sequence
|
|
void handleShowVersion(const DebuggerResponse &response);
|
|
void handlePythonSetup(const DebuggerResponse &response);
|
|
|
|
int m_gdbVersion = 100; // 7.6.1 is 70601
|
|
int m_pythonVersion = 0; // 2.7.2 is 20702
|
|
bool m_isQnxGdb = false;
|
|
|
|
////////// Inferior Management //////////
|
|
|
|
// This should be always the last call in a function.
|
|
bool acceptsBreakpoint(const BreakpointParameters &bp) const final;
|
|
void insertBreakpoint(const Breakpoint &bp) final;
|
|
void removeBreakpoint(const Breakpoint &bp) final;
|
|
void updateBreakpoint(const Breakpoint &bp) final;
|
|
void enableSubBreakpoint(const SubBreakpoint &sbp, bool on) final;
|
|
|
|
void executeStepIn(bool byInstruction) final;
|
|
void executeStepOut() final;
|
|
void executeStepOver(bool byInstruction) final;
|
|
|
|
void continueInferiorInternal();
|
|
void continueInferior() final;
|
|
void interruptInferior() final;
|
|
|
|
void executeRunToLine(const ContextData &data) final;
|
|
void executeRunToFunction(const QString &functionName) final;
|
|
void executeJumpToLine(const ContextData &data) final;
|
|
void executeReturn() final;
|
|
void executeRecordReverse(bool reverse) final;
|
|
|
|
void handleExecuteContinue(const DebuggerResponse &response);
|
|
void handleExecuteStep(const DebuggerResponse &response);
|
|
void handleExecuteNext(const DebuggerResponse &response);
|
|
void handleExecuteReturn(const DebuggerResponse &response);
|
|
void handleExecuteJumpToLine(const DebuggerResponse &response);
|
|
void handleExecuteRunToLine(const DebuggerResponse &response);
|
|
|
|
QString msgPtraceError(DebuggerStartMode sm);
|
|
|
|
////////// View & Data Stuff //////////
|
|
|
|
void selectThread(const Thread &thread) final;
|
|
void activateFrame(int index) final;
|
|
|
|
//
|
|
// Breakpoint specific stuff
|
|
//
|
|
void handleBreakModifications(const GdbMi &bkpts);
|
|
void handleBreakIgnore(const DebuggerResponse &response, const Breakpoint &bp);
|
|
void handleBreakDisable(const DebuggerResponse &response, const Breakpoint &bp);
|
|
void handleBreakEnable(const DebuggerResponse &response, const Breakpoint &bp);
|
|
void handleBreakInsert1(const DebuggerResponse &response, const Breakpoint &bp);
|
|
void handleBreakInsert2(const DebuggerResponse &response, const Breakpoint &bp);
|
|
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);
|
|
void handleCatchInsert(const DebuggerResponse &response, const Breakpoint &bp);
|
|
void handleBkpt(const GdbMi &bkpt, const Breakpoint &bp);
|
|
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
|
|
//
|
|
void loadSymbols(const QString &moduleName) final;
|
|
void loadAllSymbols() final;
|
|
void loadSymbolsForStack() final;
|
|
void requestModuleSymbols(const QString &moduleName) final;
|
|
void requestModuleSections(const QString &moduleName) final;
|
|
void reloadModules() final;
|
|
void examineModules() final;
|
|
|
|
void reloadModulesInternal();
|
|
void handleModulesList(const DebuggerResponse &response);
|
|
void handleShowModuleSections(const DebuggerResponse &response, const QString &moduleName);
|
|
|
|
//
|
|
// Snapshot specific stuff
|
|
//
|
|
void createSnapshot() final;
|
|
void handleMakeSnapshot(const DebuggerResponse &response, const QString &coreFile);
|
|
|
|
//
|
|
// Register specific stuff
|
|
//
|
|
void reloadRegisters() final;
|
|
void reloadPeripheralRegisters() final;
|
|
void setRegisterValue(const QString &name, const QString &value) final;
|
|
void setPeripheralRegisterValue(quint64 address, quint64 value) final;
|
|
void handleRegisterListNames(const DebuggerResponse &response);
|
|
void handleRegisterListing(const DebuggerResponse &response);
|
|
void handleRegisterListValues(const DebuggerResponse &response);
|
|
void handlePeripheralRegisterListValues(const DebuggerResponse &response);
|
|
void handleMaintPrintRegisters(const DebuggerResponse &response);
|
|
QHash<int, Register> m_registers; // Map GDB register numbers to indices
|
|
|
|
//
|
|
// Disassembler specific stuff
|
|
//
|
|
// Chain of fallbacks: PointMixed -> PointPlain -> RangeMixed -> RangePlain.
|
|
void fetchDisassembler(DisassemblerAgent *agent) final;
|
|
void fetchDisassemblerByCliPointMixed(const DisassemblerAgentCookie &ac);
|
|
void fetchDisassemblerByCliRangeMixed(const DisassemblerAgentCookie &ac);
|
|
void fetchDisassemblerByCliRangePlain(const DisassemblerAgentCookie &ac);
|
|
bool handleCliDisassemblerResult(const QString &response, DisassemblerAgent *agent);
|
|
|
|
//
|
|
// Source file specific stuff
|
|
//
|
|
void reloadSourceFiles() final;
|
|
void reloadSourceFilesInternal();
|
|
void handleQuerySources(const DebuggerResponse &response);
|
|
|
|
QString fullName(const QString &fileName);
|
|
QString cleanupFullName(const QString &fileName);
|
|
|
|
// awful hack to keep track of used files
|
|
QMap<QString, QString> m_shortToFullName;
|
|
QMap<QString, QString> m_fullToShortName;
|
|
QMultiMap<QString, QString> m_baseNameToFullName;
|
|
|
|
bool m_sourcesListUpdating = false;
|
|
|
|
//
|
|
// Stack specific stuff
|
|
//
|
|
void updateAll() final;
|
|
void handleStackListFrames(const DebuggerResponse &response, bool isFull);
|
|
void handleStackSelectThread(const DebuggerResponse &response);
|
|
void handleThreadListIds(const DebuggerResponse &response);
|
|
void handleThreadInfo(const DebuggerResponse &response);
|
|
void handleThreadNames(const DebuggerResponse &response);
|
|
DebuggerCommand stackCommand(int depth);
|
|
void reloadStack();
|
|
void reloadFullStack() final;
|
|
void loadAdditionalQmlStack() final;
|
|
int currentFrame() const;
|
|
|
|
//
|
|
// Watch specific stuff
|
|
//
|
|
void reloadLocals();
|
|
void assignValueInDebugger(WatchItem *item,
|
|
const QString &expr, const QVariant &value) final;
|
|
|
|
void fetchMemory(MemoryAgent *agent, quint64 addr, quint64 length) final;
|
|
void fetchMemoryHelper(const MemoryAgentCookie &cookie);
|
|
void handleChangeMemory(const DebuggerResponse &response);
|
|
void changeMemory(MemoryAgent *agent, quint64 addr, const QByteArray &data) final;
|
|
void handleFetchMemory(const DebuggerResponse &response, MemoryAgentCookie ac);
|
|
|
|
void showToolTip();
|
|
|
|
void handleVarAssign(const DebuggerResponse &response);
|
|
void handleThreadGroupCreated(const GdbMi &result);
|
|
void handleThreadGroupExited(const GdbMi &result);
|
|
|
|
void createFullBacktrace();
|
|
|
|
void doUpdateLocals(const UpdateParameters ¶meters) final;
|
|
void handleFetchVariables(const DebuggerResponse &response);
|
|
|
|
void setLocals(const QList<GdbMi> &locals);
|
|
|
|
//
|
|
// Dumper Management
|
|
//
|
|
void reloadDebuggingHelpers() final;
|
|
|
|
//
|
|
// Convenience Functions
|
|
//
|
|
void showExecutionError(const QString &message);
|
|
QString failedToStartMessage();
|
|
|
|
// For short-circuiting stack and thread list evaluation.
|
|
bool m_stackNeeded = false;
|
|
|
|
// For suppressing processing *stopped and *running responses
|
|
// while updating locals.
|
|
bool m_inUpdateLocals = false;
|
|
|
|
// HACK:
|
|
QString m_currentThread;
|
|
QString m_lastWinException;
|
|
bool m_expectTerminalTrap = false;
|
|
bool usesExecInterrupt() const;
|
|
bool usesTargetAsync() const;
|
|
|
|
DebuggerCommandSequence m_onStop;
|
|
|
|
QHash<int, QString> m_scheduledTestResponses;
|
|
QSet<int> m_testCases;
|
|
|
|
bool m_systemDumpersLoaded = false;
|
|
|
|
static QString msgGdbStopFailed(const QString &why);
|
|
static QString msgInferiorStopFailed(const QString &why);
|
|
static QString msgAttachedToStoppedInferior();
|
|
static QString msgInferiorSetupOk();
|
|
static QString msgInferiorRunOk();
|
|
static QString msgConnectRemoteServerFailed(const QString &why);
|
|
|
|
void debugLastCommand() final;
|
|
DebuggerCommand m_lastDebuggableCommand;
|
|
|
|
bool isLocalRunEngine() const;
|
|
bool isPlainEngine() const;
|
|
bool isCoreEngine() const;
|
|
bool isRemoteEngine() const;
|
|
bool isLocalAttachEngine() const;
|
|
bool isTermEngine() const;
|
|
|
|
void setupEngine() final;
|
|
void runEngine();
|
|
void shutdownEngine() final;
|
|
|
|
void interruptInferior2();
|
|
QChar mixedDisasmFlag() const;
|
|
|
|
// Plain
|
|
void handleExecRun(const DebuggerResponse &response);
|
|
void handleFileExecAndSymbols(const DebuggerResponse &response);
|
|
|
|
// Attach
|
|
void handleLocalAttach(const DebuggerResponse &response);
|
|
void handleRemoteAttach(const DebuggerResponse &response);
|
|
|
|
// Remote
|
|
void callTargetRemote();
|
|
void handleSetTargetAsync(const DebuggerResponse &response);
|
|
void handleTargetRemote(const DebuggerResponse &response);
|
|
void handleTargetExtendedRemote(const DebuggerResponse &response);
|
|
void handleTargetExtendedAttach(const DebuggerResponse &response);
|
|
void handleTargetQnx(const DebuggerResponse &response);
|
|
void handleSetNtoExecutable(const DebuggerResponse &response);
|
|
void handleInterruptInferior(const DebuggerResponse &response);
|
|
void interruptLocalInferior(qint64 pid);
|
|
|
|
// Terminal
|
|
void handleStubAttached(const DebuggerResponse &response, qint64 mainThreadId);
|
|
|
|
// Core
|
|
void handleTargetCore(const DebuggerResponse &response);
|
|
void handleCoreRoundTrip(const DebuggerResponse &response);
|
|
QString coreFileName() const;
|
|
|
|
QString mainFunction() const;
|
|
void setupInferior();
|
|
void claimInitialBreakpoints();
|
|
|
|
bool usesOutputCollector() const;
|
|
|
|
Utils::QtcProcess m_gdbProc;
|
|
OutputCollector m_outputCollector;
|
|
QString m_errorString;
|
|
};
|
|
|
|
} // Debugger::Internal
|