#include "gdbmihelpers.h"
static const char eventContextC[] = "event";
-static const char moduleContextC[] = "module";
// Special exception codes (see dbgwinutils.cpp).
enum { winExceptionCppException = 0xe06d7363,
__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;
__in ULONG64 BaseOffset
)
{
- ExtensionContext::instance().report('U', 0, moduleContextC, "U:%s\n",
- ImageBaseName);
-
return m_wrapped ? m_wrapped->UnloadModule(ImageBaseName, BaseOffset) : S_OK;
}
{
str << "frame={level=\"" << level << "\",addr=\"0x"
<< std::hex << address << std::dec << '"';
- if (!function.empty())
- str << ",func=\"" << gdbmiWStringFormat(function) << '"';
+ 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'/');
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();
+}
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);
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
help
memory
shutdownex
+stack
KnownStructOutput
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.
void CdbEngine::operateByInstructionTriggered(bool operateByInstruction)
{
- // To be set next time session becomes accessible
- m_operateByInstructionPending = 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)
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;
return normalized;
}
-void CdbEngine::handleStackTrace(const CdbBuiltinCommandPtr &command)
-{
- 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));
+// Parse frame from GDBMI. Duplicate of the gdb code, but that
+// has more processing.
+static StackFrames parseFrames(const QByteArray &data)
+{
+ 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;
+}
- stackHandler()->setFrames(frames);
- activateFrame(current);
- postCommandSequence(command->commandSequence);
+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)
return;
}
if (mask & CommandListStack) {
- postBuiltinCommand("k", 0, &CdbEngine::handleStackTrace, mask & ~CommandListStack);
+ postExtensionCommand("stack", QByteArray(), 0, &CdbEngine::handleStackTrace, mask & ~CommandListStack);
return;
}
if (mask & CommandListRegisters) {
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 &);
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
// 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);