forked from qt-creator/qt-creator
Debugger[New CDB]: Fix disassembly.
Introduce GDBMI-based 'stack' extension command instead of the builtin 'k' as this does not print the correct instruction pointer.
This commit is contained in:
@@ -33,7 +33,6 @@
|
||||
#include "gdbmihelpers.h"
|
||||
|
||||
static const char eventContextC[] = "event";
|
||||
static const char moduleContextC[] = "module";
|
||||
|
||||
// Special exception codes (see dbgwinutils.cpp).
|
||||
enum { winExceptionCppException = 0xe06d7363,
|
||||
@@ -239,9 +238,6 @@ STDMETHODIMP EventCallback::LoadModule(
|
||||
__in ULONG TimeDateStamp
|
||||
)
|
||||
{
|
||||
ExtensionContext::instance().report('E', 0, moduleContextC, "L:%s:%s:0x%llx:0x%llx\n",
|
||||
ModuleName, ImageName, BaseOffset, ModuleSize);
|
||||
|
||||
return m_wrapped ? m_wrapped->LoadModule(ImageFileHandle, BaseOffset,
|
||||
ModuleSize, ModuleName, ImageName,
|
||||
CheckSum, TimeDateStamp) : S_OK;
|
||||
@@ -253,9 +249,6 @@ STDMETHODIMP EventCallback::UnloadModule(
|
||||
__in ULONG64 BaseOffset
|
||||
)
|
||||
{
|
||||
ExtensionContext::instance().report('U', 0, moduleContextC, "U:%s\n",
|
||||
ImageBaseName);
|
||||
|
||||
return m_wrapped ? m_wrapped->UnloadModule(ImageBaseName, BaseOffset) : S_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -48,8 +48,18 @@ void StackFrame::formatGDBMI(std::ostream &str, unsigned level) const
|
||||
{
|
||||
str << "frame={level=\"" << level << "\",addr=\"0x"
|
||||
<< std::hex << address << std::dec << '"';
|
||||
if (!function.empty())
|
||||
if (!function.empty()) {
|
||||
// Split into module/function
|
||||
const std::wstring::size_type exclPos = function.find('!');
|
||||
if (exclPos == std::wstring::npos) {
|
||||
str << ",func=\"" << gdbmiWStringFormat(function) << '"';
|
||||
} else {
|
||||
const std::wstring module = function.substr(0, exclPos);
|
||||
const std::wstring fn = function.substr(exclPos + 1, function.size() - exclPos - 1);
|
||||
str << ",func=\"" << gdbmiWStringFormat(fn)
|
||||
<< "\",from=\"" << gdbmiWStringFormat(module) << '"';
|
||||
}
|
||||
}
|
||||
if (!fullPathName.empty()) { // Creator/gdbmi expects 'clean paths'
|
||||
std::wstring cleanPath = fullPathName;
|
||||
replace(cleanPath, L'\\', L'/');
|
||||
@@ -559,3 +569,50 @@ std::string memoryToBase64(CIDebugDataSpaces *ds, ULONG64 address, ULONG length,
|
||||
delete [] buffer;
|
||||
return str.str();
|
||||
}
|
||||
|
||||
// Format stack as GDBMI
|
||||
static StackFrames getStackTrace(CIDebugControl *debugControl,
|
||||
CIDebugSymbols *debugSymbols,
|
||||
unsigned maxFrames,
|
||||
std::string *errorMessage)
|
||||
{
|
||||
|
||||
if (!maxFrames)
|
||||
return StackFrames();
|
||||
DEBUG_STACK_FRAME *frames = new DEBUG_STACK_FRAME[maxFrames];
|
||||
ULONG frameCount = 0;
|
||||
const HRESULT hr = debugControl->GetStackTrace(0, 0, 0, frames, maxFrames, &frameCount);
|
||||
if (FAILED(hr)) {
|
||||
delete [] frames;
|
||||
*errorMessage = msgDebugEngineComFailed("GetStackTrace", hr);
|
||||
}
|
||||
StackFrames rc(frameCount, StackFrame());
|
||||
for (ULONG f = 0; f < frameCount; f++)
|
||||
getFrame(debugSymbols, frames[f], &(rc[f]));
|
||||
delete [] frames;
|
||||
return rc;
|
||||
}
|
||||
|
||||
std::string gdbmiStack(CIDebugControl *debugControl,
|
||||
CIDebugSymbols *debugSymbols,
|
||||
unsigned maxFrames,
|
||||
bool humanReadable, std::string *errorMessage)
|
||||
{
|
||||
const StackFrames frames = getStackTrace(debugControl, debugSymbols,
|
||||
maxFrames, errorMessage);
|
||||
if (frames.empty() && maxFrames > 0)
|
||||
return std::string();
|
||||
|
||||
std::ostringstream str;
|
||||
str << '[';
|
||||
const StackFrames::size_type size = frames.size();
|
||||
for (StackFrames::size_type i = 0; i < size; i++) {
|
||||
if (i)
|
||||
str << ',';
|
||||
frames.at(i).formatGDBMI(str, (int)i);
|
||||
if (humanReadable)
|
||||
str << '\n';
|
||||
}
|
||||
str << ']';
|
||||
return str.str();
|
||||
}
|
||||
|
||||
@@ -51,6 +51,8 @@ struct StackFrame
|
||||
ULONG line;
|
||||
};
|
||||
|
||||
typedef std::vector<StackFrame> StackFrames;
|
||||
|
||||
bool getFrame(unsigned n, StackFrame *f, std::string *errorMessage);
|
||||
bool getFrame(CIDebugSymbols *debugSymbols, CIDebugControl *debugControl,
|
||||
unsigned n, StackFrame *f, std::string *errorMessage);
|
||||
@@ -145,4 +147,12 @@ std::string gdbmiRegisters(CIDebugRegisters *regs,
|
||||
|
||||
std::string memoryToBase64(CIDebugDataSpaces *ds, ULONG64 address, ULONG length, std::string *errorMessage);
|
||||
|
||||
// Stack helpers
|
||||
StackFrames getStackTrace(CIDebugControl *debugControl, CIDebugSymbols *debugSymbols,
|
||||
unsigned maxFrames, std::string *errorMessage);
|
||||
|
||||
std::string gdbmiStack(CIDebugControl *debugControl, CIDebugSymbols *debugSymbols,
|
||||
unsigned maxFrames, bool humanReadable,
|
||||
std::string *errorMessage);
|
||||
|
||||
#endif // THREADLIST_H
|
||||
|
||||
@@ -13,4 +13,5 @@ idle
|
||||
help
|
||||
memory
|
||||
shutdownex
|
||||
stack
|
||||
KnownStructOutput
|
||||
|
||||
@@ -351,6 +351,37 @@ extern "C" HRESULT CALLBACK memory(CIDebugClient *Client, PCSTR argsIn)
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Extension command 'stack'
|
||||
// Report stack correctly as 'k' does not list instruction pointer
|
||||
// correctly.
|
||||
extern "C" HRESULT CALLBACK stack(CIDebugClient *Client, PCSTR argsIn)
|
||||
{
|
||||
ExtensionCommandContext exc(Client);
|
||||
std::string errorMessage;
|
||||
|
||||
int token;
|
||||
bool humanReadable = false;
|
||||
unsigned maxFrames = 1000;
|
||||
|
||||
StringList tokens = commandTokens<StringList>(argsIn, &token);
|
||||
if (!tokens.empty() && tokens.front() == "-h") {
|
||||
humanReadable = true;
|
||||
tokens.pop_front();
|
||||
}
|
||||
if (!tokens.empty())
|
||||
integerFromString(tokens.front(), &maxFrames);
|
||||
|
||||
const std::string stack = gdbmiStack(exc.control(), exc.symbols(),
|
||||
maxFrames, humanReadable, &errorMessage);
|
||||
|
||||
if (stack.empty()) {
|
||||
ExtensionContext::instance().report('N', token, "stack", errorMessage.c_str());
|
||||
} else {
|
||||
ExtensionContext::instance().report('R', token, "stack", stack.c_str());
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Extension command 'shutdownex' (shutdown is reserved):
|
||||
// Unhook the output callbacks. This is normally done by the session
|
||||
// inaccessible notification, however, this does not work for remote-controlled sessions.
|
||||
|
||||
@@ -308,9 +308,23 @@ CdbEngine::~CdbEngine()
|
||||
|
||||
void CdbEngine::operateByInstructionTriggered(bool operateByInstruction)
|
||||
{
|
||||
if (state() == InferiorStopOk) {
|
||||
syncOperateByInstruction(operateByInstruction);
|
||||
} else {
|
||||
// To be set next time session becomes accessible
|
||||
m_operateByInstructionPending = operateByInstruction;
|
||||
}
|
||||
}
|
||||
|
||||
void CdbEngine::syncOperateByInstruction(bool operateByInstruction)
|
||||
{
|
||||
if (m_operateByInstruction == operateByInstruction)
|
||||
return;
|
||||
QTC_ASSERT(m_accessible, return; )
|
||||
m_operateByInstruction = operateByInstruction;
|
||||
postCommand(m_operateByInstruction ? QByteArray("l-t") : QByteArray("l+t"), 0);
|
||||
postCommand(m_operateByInstruction ? QByteArray("l-s") : QByteArray("l+s"), 0);
|
||||
}
|
||||
|
||||
void CdbEngine::setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos)
|
||||
{
|
||||
@@ -1197,11 +1211,7 @@ void CdbEngine::handleSessionIdle(const QByteArray &message)
|
||||
stateName(state()), m_specialStopMode);
|
||||
|
||||
// Switch source level debugging
|
||||
if (m_operateByInstructionPending != m_operateByInstruction) {
|
||||
m_operateByInstruction = m_operateByInstructionPending;
|
||||
postCommand(m_operateByInstruction ? QByteArray("l-t") : QByteArray("l+t"), 0);
|
||||
postCommand(m_operateByInstruction ? QByteArray("l-s") : QByteArray("l+s"), 0);
|
||||
}
|
||||
syncOperateByInstruction(m_operateByInstructionPending);
|
||||
|
||||
const SpecialStopMode specialStopMode = m_specialStopMode;
|
||||
m_specialStopMode = NoSpecialStop;
|
||||
@@ -1704,21 +1714,58 @@ QString CdbEngine::normalizeFileName(const QString &f)
|
||||
return normalized;
|
||||
}
|
||||
|
||||
void CdbEngine::handleStackTrace(const CdbBuiltinCommandPtr &command)
|
||||
// Parse frame from GDBMI. Duplicate of the gdb code, but that
|
||||
// has more processing.
|
||||
static StackFrames parseFrames(const QByteArray &data)
|
||||
{
|
||||
StackFrames frames;
|
||||
const int current = parseCdbStackTrace(command->reply, &frames);
|
||||
if (debug)
|
||||
qDebug("handleStackTrace %d of %d", current, frames.size());
|
||||
const StackFrames::iterator end = frames.end();
|
||||
for (StackFrames::iterator it = frames.begin(); it != end; ++it) {
|
||||
if (!it->file.isEmpty())
|
||||
it->file = QDir::cleanPath(normalizeFileName(it->file));
|
||||
GdbMi gdbmi;
|
||||
gdbmi.fromString(data);
|
||||
if (!gdbmi.isValid())
|
||||
return StackFrames();
|
||||
|
||||
StackFrames rc;
|
||||
const int count = gdbmi.childCount();
|
||||
rc.reserve(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
const GdbMi &frameMi = gdbmi.childAt(i);
|
||||
StackFrame frame;
|
||||
frame.level = i;
|
||||
const GdbMi fullName = frameMi.findChild("fullname");
|
||||
if (fullName.isValid()) {
|
||||
frame.file = QFile::decodeName(fullName.data());
|
||||
frame.line = frameMi.findChild("line").data().toInt();
|
||||
}
|
||||
frame.function = QLatin1String(frameMi.findChild("func").data());
|
||||
frame.from = QLatin1String(frameMi.findChild("from").data());
|
||||
frame.address = frameMi.findChild("addr").data().toULongLong(0, 16);
|
||||
rc.push_back(frame);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
void CdbEngine::handleStackTrace(const CdbExtensionCommandPtr &command)
|
||||
{
|
||||
// Parse frames, find current.
|
||||
if (command->success) {
|
||||
int current = -1;
|
||||
StackFrames frames = parseFrames(command->reply);
|
||||
const int count = frames.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (!frames.at(i).file.isEmpty()) {
|
||||
frames[i].file = QDir::cleanPath(normalizeFileName(frames.at(i).file));
|
||||
if (current == -1)
|
||||
current = i;
|
||||
}
|
||||
}
|
||||
if (count && current == -1) // No usable frame, use assembly.
|
||||
current = 0;
|
||||
// Set
|
||||
stackHandler()->setFrames(frames);
|
||||
activateFrame(current);
|
||||
postCommandSequence(command->commandSequence);
|
||||
} else {
|
||||
showMessage(command->errorMessage, LogError);
|
||||
}
|
||||
}
|
||||
|
||||
void CdbEngine::dummyHandler(const CdbBuiltinCommandPtr &command)
|
||||
@@ -1739,7 +1786,7 @@ void CdbEngine::postCommandSequence(unsigned mask)
|
||||
return;
|
||||
}
|
||||
if (mask & CommandListStack) {
|
||||
postBuiltinCommand("k", 0, &CdbEngine::handleStackTrace, mask & ~CommandListStack);
|
||||
postExtensionCommand("stack", QByteArray(), 0, &CdbEngine::handleStackTrace, mask & ~CommandListStack);
|
||||
return;
|
||||
}
|
||||
if (mask & CommandListRegisters) {
|
||||
|
||||
@@ -152,10 +152,11 @@ private:
|
||||
inline void parseOutputLine(QByteArray line);
|
||||
inline bool isCdbProcessRunning() const { return m_process.state() != QProcess::NotRunning; }
|
||||
bool canInterruptInferior() const;
|
||||
void syncOperateByInstruction(bool operateByInstruction);
|
||||
|
||||
// Builtin commands
|
||||
void dummyHandler(const CdbBuiltinCommandPtr &);
|
||||
void handleStackTrace(const CdbBuiltinCommandPtr &);
|
||||
void handleStackTrace(const CdbExtensionCommandPtr &);
|
||||
void handleRegisters(const CdbBuiltinCommandPtr &);
|
||||
void handleDisassembler(const CdbBuiltinCommandPtr &);
|
||||
void handleJumpToLineAddressResolution(const CdbBuiltinCommandPtr &);
|
||||
|
||||
@@ -154,75 +154,6 @@ QVariant cdbIntegerValue(const QByteArray &t)
|
||||
return converted;
|
||||
}
|
||||
|
||||
/* Parse: 64bit:
|
||||
\code
|
||||
Child-SP RetAddr Call Site
|
||||
00000000`0012a290 00000000`70deb844 QtCored4!QString::QString+0x18 [c:\qt\src\corelib\tools\qstring.h @ 729]
|
||||
\endcode 32bit:
|
||||
\code
|
||||
ChildEBP RetAddr
|
||||
0012cc68 6714d114 QtCored4!QString::QString+0xf [d:\dev\qt4.7-vs8\qt\src\corelib\tools\qstring.h @ 729]
|
||||
\endcode */
|
||||
|
||||
static inline bool isHexDigit(char c)
|
||||
{
|
||||
return std::isdigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
|
||||
}
|
||||
|
||||
static inline bool parseStackFrame(QByteArray line, Debugger::Internal::StackFrame *frame)
|
||||
{
|
||||
frame->clear();
|
||||
if (line.isEmpty() || line.startsWith("Child") || !isHexDigit(line.at(0)))
|
||||
return false;
|
||||
if (line.endsWith(']')) {
|
||||
const int sourceFilePos = line.lastIndexOf('[');
|
||||
const int sepPos = line.lastIndexOf(" @ ");
|
||||
if (sourceFilePos != -1 && sepPos != -1) {
|
||||
const QString fileName = QString::fromLocal8Bit(line.mid(sourceFilePos + 1, sepPos - sourceFilePos - 1));
|
||||
frame->file = QDir::cleanPath(fileName);
|
||||
frame->line = line.mid(sepPos + 3, line.size() - sepPos - 4).toInt();
|
||||
line.truncate(sourceFilePos - 1);
|
||||
}
|
||||
}
|
||||
// Split address tokens
|
||||
const int retAddrPos = line.indexOf(' ');
|
||||
const int symbolPos = retAddrPos != -1 ? line.indexOf(' ', retAddrPos + 1) : -1;
|
||||
if (symbolPos == -1)
|
||||
return false;
|
||||
|
||||
// Remove offset off symbol
|
||||
const int offsetPos = line.lastIndexOf("+0x");
|
||||
if (offsetPos != -1)
|
||||
line.truncate(offsetPos);
|
||||
|
||||
frame->address = cdbIntegerValue(line.mid(0, retAddrPos)).toULongLong();
|
||||
// Module!foo
|
||||
frame->function = QString::fromAscii(line.mid(symbolPos));
|
||||
const int moduleSep = frame->function.indexOf(QLatin1Char('!'));
|
||||
if (moduleSep != -1) {
|
||||
frame->from = frame->function.left(moduleSep);
|
||||
frame->function.remove(0, moduleSep + 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int parseCdbStackTrace(const QList<QByteArray> &in, QList<Debugger::Internal::StackFrame> *frames)
|
||||
{
|
||||
frames->clear();
|
||||
Debugger::Internal::StackFrame frame;
|
||||
frames->reserve(in.size());
|
||||
int level = 0;
|
||||
int current = -1;
|
||||
foreach(const QByteArray &line, in)
|
||||
if (parseStackFrame(line, &frame)) {
|
||||
frame.level = level++;
|
||||
if (current == -1 && frame.isUsable())
|
||||
current = frames->size();
|
||||
frames->push_back(frame);
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
/* \code
|
||||
0:002> ~ [Debugger-Id] Id: <hex pid> <hex tid> Suspends count thread environment block add state name
|
||||
0 Id: 133c.1374 Suspend: 1 Teb: 000007ff`fffdd000 Unfrozen
|
||||
|
||||
@@ -63,9 +63,6 @@ QByteArray fixCdbIntegerValue(QByteArray t, bool stripLeadingZeros = false, int
|
||||
// Convert a CDB integer value into quint64 or int64
|
||||
QVariant cdbIntegerValue(const QByteArray &t);
|
||||
|
||||
// Parse stack frames and return current
|
||||
int parseCdbStackTrace(const QList<QByteArray> &in, QList<Debugger::Internal::StackFrame> *frames);
|
||||
|
||||
QString debugByteArray(const QByteArray &a);
|
||||
QString StringFromBase64EncodedUtf16(const QByteArray &a);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user