Debugger[New CDB]: Split up long extension messages.

To accommodate the limitation of output line width of CDB.
This commit is contained in:
Friedemann Kleint
2011-01-04 12:40:52 +01:00
parent d9ec7dd0a6
commit b7df5467d8
8 changed files with 87 additions and 49 deletions

View File

@@ -173,7 +173,7 @@ STDMETHODIMP EventCallback::Exception(
std::ostringstream str; std::ostringstream str;
formatGdbmiHash(str, parameters); formatGdbmiHash(str, parameters);
ExtensionContext::instance().setStopReason(parameters, "exception"); ExtensionContext::instance().setStopReason(parameters, "exception");
ExtensionContext::instance().report('E', 0, "exception", "%s", str.str().c_str()); ExtensionContext::instance().report('E', 0, 0, "exception", "%s", str.str().c_str());
return m_wrapped ? m_wrapped->Exception(Ex, FirstChance) : S_OK; return m_wrapped ? m_wrapped->Exception(Ex, FirstChance) : S_OK;
} }
@@ -222,7 +222,7 @@ STDMETHODIMP EventCallback::ExitProcess(
__in ULONG ExitCode __in ULONG ExitCode
) )
{ {
ExtensionContext::instance().report('E', 0, eventContextC, "Process exited (%lu)", ExtensionContext::instance().report('E', 0, 0, eventContextC, "Process exited (%lu)",
ExitCode); ExitCode);
const HRESULT hr = m_wrapped ? m_wrapped->ExitProcess(ExitCode) : S_OK; const HRESULT hr = m_wrapped ? m_wrapped->ExitProcess(ExitCode) : S_OK;
// Remotely debugged process exited, there is no session-inactive notification. // Remotely debugged process exited, there is no session-inactive notification.

View File

@@ -37,6 +37,8 @@
#include "outputcallback.h" #include "outputcallback.h"
#include "stringutils.h" #include "stringutils.h"
#include <algorithm>
// wdbgexts.h declares 'extern WINDBG_EXTENSION_APIS ExtensionApis;' // wdbgexts.h declares 'extern WINDBG_EXTENSION_APIS ExtensionApis;'
// and it's inline functions rely on its existence. // and it's inline functions rely on its existence.
WINDBG_EXTENSION_APIS ExtensionApis = {sizeof(WINDBG_EXTENSION_APIS), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; WINDBG_EXTENSION_APIS ExtensionApis = {sizeof(WINDBG_EXTENSION_APIS), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
@@ -167,7 +169,7 @@ void ExtensionContext::notifyIdle()
// Format // Format
std::ostringstream str; std::ostringstream str;
formatGdbmiHash(str, stopReasons); formatGdbmiHash(str, stopReasons);
report('E', 0, "session_idle", "%s", str.str().c_str()); reportLong('E', 0, "session_idle", str.str());
m_stopReason.clear(); m_stopReason.clear();
} }
@@ -176,16 +178,16 @@ void ExtensionContext::notifyState(ULONG Notify)
const ULONG ex = executionStatus(); const ULONG ex = executionStatus();
switch (Notify) { switch (Notify) {
case DEBUG_NOTIFY_SESSION_ACTIVE: case DEBUG_NOTIFY_SESSION_ACTIVE:
report('E', 0, "session_active", "%u", ex); report('E', 0, 0, "session_active", "%u", ex);
break; break;
case DEBUG_NOTIFY_SESSION_ACCESSIBLE: // Meaning, commands accepted case DEBUG_NOTIFY_SESSION_ACCESSIBLE: // Meaning, commands accepted
report('E', 0, "session_accessible", "%u", ex); report('E', 0, 0, "session_accessible", "%u", ex);
break; break;
case DEBUG_NOTIFY_SESSION_INACCESSIBLE: case DEBUG_NOTIFY_SESSION_INACCESSIBLE:
report('E', 0, "session_inaccessible", "%u", ex); report('E', 0, 0, "session_inaccessible", "%u", ex);
break; break;
case DEBUG_NOTIFY_SESSION_INACTIVE: case DEBUG_NOTIFY_SESSION_INACTIVE:
report('E', 0, "session_inactive", "%u", ex); report('E', 0, 0, "session_inactive", "%u", ex);
discardSymbolGroup(); discardSymbolGroup();
// We lost the debuggee, at this point restore output. // We lost the debuggee, at this point restore output.
if (ex & DEBUG_STATUS_NO_DEBUGGEE) if (ex & DEBUG_STATUS_NO_DEBUGGEE)
@@ -218,12 +220,13 @@ void ExtensionContext::discardSymbolGroup()
m_symbolGroup.reset(); m_symbolGroup.reset();
} }
bool ExtensionContext::report(char code, int token, const char *serviceName, PCSTR Format, ...) bool ExtensionContext::report(char code, int token, int remainingChunks, const char *serviceName, PCSTR Format, ...)
{ {
if (!isInitialized()) if (!isInitialized())
return false; return false;
// '<qtcreatorcdbext>|R|<token>|<serviceName>|<one-line-output>'. // '<qtcreatorcdbext>|R|<token>|<serviceName>|<one-line-output>'.
m_control->Output(DEBUG_OUTPUT_NORMAL, "<qtcreatorcdbext>|%c|%d|%s|", code, token, serviceName); m_control->Output(DEBUG_OUTPUT_NORMAL, "<qtcreatorcdbext>|%c|%d|%d|%s|",
code, token, remainingChunks, serviceName);
va_list Args; va_list Args;
va_start(Args, Format); va_start(Args, Format);
m_control->OutputVaList(DEBUG_OUTPUT_NORMAL, Format, Args); m_control->OutputVaList(DEBUG_OUTPUT_NORMAL, Format, Args);
@@ -232,6 +235,26 @@ bool ExtensionContext::report(char code, int token, const char *serviceName, PCS
return true; return true;
} }
bool ExtensionContext::reportLong(char code, int token, const char *serviceName, const std::string &message)
{
const std::string::size_type size = message.size();
if (size < outputChunkSize)
return report(code, token, 0, serviceName, "%s", message.c_str());
// Split up
std::string::size_type chunkCount = size / outputChunkSize;
if (size % outputChunkSize)
chunkCount++;
std::string::size_type pos = 0;
for (int remaining = int(chunkCount) - 1; remaining >= 0 ; remaining--) {
std::string::size_type nextPos = pos + outputChunkSize; // No consistent std::min/std::max in VS8/10
if (nextPos > size)
nextPos = size;
report(code, token, remaining, serviceName, "%s", message.substr(pos, nextPos - pos).c_str());
pos = nextPos;
}
return true;
}
// Exported C-functions // Exported C-functions
extern "C" { extern "C" {

View File

@@ -68,11 +68,17 @@ public:
// Undo hooking. // Undo hooking.
void unhookCallbacks(); void unhookCallbacks();
// Report output in standardized format understood by Qt Creator. // CDB has a limitation on output, so, long messages need to be split (empirically ca 10KB)
// '<qtcreatorcdbext>|R|<token>|<serviceName>|<one-line-output>'. static const size_t outputChunkSize = 10240;
// Char code is 'R' command reply, 'N' command fail, 'E' event notification /* Report output in standardized format understood by Qt Creator.
bool report(char code, int token, const char *serviceName, PCSTR Format, ...); * '<qtcreatorcdbext>|R|<token>|remainingChunks|<serviceName>|<one-line-output>'.
* Char code is 'R' command reply, 'N' command fail, 'E' event notification,
* 'X' exception, error. If the message is larger than outputChunkSize,
* it needs to be split up in chunks, remainingChunks needs to indicate the number
* of the following chunks (0 for just one chunk). */
bool report(char code, int remainingChunks, int token, const char *serviceName, PCSTR Format, ...);
// Convenience for reporting potentially long messages in chunks
bool reportLong(char code, int token, const char *serviceName, const std::string &message);
ULONG executionStatus() const; ULONG executionStatus() const;
// Call from notify handler, tell engine about state. // Call from notify handler, tell engine about state.
void notifyState(ULONG Notify); void notifyState(ULONG Notify);

View File

@@ -97,6 +97,6 @@ STDMETHODIMP OutputCallback::Output(
// Base encode as GDBMI is not really made for wide chars // Base encode as GDBMI is not really made for wide chars
std::ostringstream str; std::ostringstream str;
base64Encode(str, reinterpret_cast<const unsigned char *>(text), sizeof(wchar_t) * std::wcslen(text)); base64Encode(str, reinterpret_cast<const unsigned char *>(text), sizeof(wchar_t) * std::wcslen(text));
ExtensionContext::instance().report('E', 0, "debuggee_output", str.str().c_str()); ExtensionContext::instance().report('E', 0, 0, "debuggee_output", str.str().c_str());
return S_OK; return S_OK;
} }

View File

@@ -235,9 +235,9 @@ extern "C" HRESULT CALLBACK pid(CIDebugClient *client, PCSTR args)
commandTokens<StringList>(args, &token); commandTokens<StringList>(args, &token);
if (const ULONG pid = currentProcessId(client)) { if (const ULONG pid = currentProcessId(client)) {
ExtensionContext::instance().report('R', token, "pid", "%u", pid); ExtensionContext::instance().report('R', token, 0, "pid", "%u", pid);
} else { } else {
ExtensionContext::instance().report('N', token, "pid", "0"); ExtensionContext::instance().report('N', token, 0, "pid", "0");
} }
return S_OK; return S_OK;
} }
@@ -277,7 +277,7 @@ extern "C" HRESULT CALLBACK expandlocals(CIDebugClient *client, PCSTR args)
symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), frame, &errorMessage); symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), frame, &errorMessage);
if (!symGroup) { if (!symGroup) {
ExtensionContext::instance().report('N', token, "expandlocals", errorMessage.c_str()); ExtensionContext::instance().report('N', token, 0, "expandlocals", errorMessage.c_str());
return S_OK; return S_OK;
} }
@@ -285,7 +285,7 @@ extern "C" HRESULT CALLBACK expandlocals(CIDebugClient *client, PCSTR args)
symGroup->expandListRunComplexDumpers(inames, SymbolGroupValueContext(exc.dataSpaces(), exc.symbols()), &errorMessage) : symGroup->expandListRunComplexDumpers(inames, SymbolGroupValueContext(exc.dataSpaces(), exc.symbols()), &errorMessage) :
symGroup->expandList(inames, &errorMessage); symGroup->expandList(inames, &errorMessage);
ExtensionContext::instance().report('R', token, "expandlocals", "%u/%u %s", ExtensionContext::instance().report('R', token, 0, "expandlocals", "%u/%u %s",
succeeded, unsigned(inames.size()), errorMessage.c_str()); succeeded, unsigned(inames.size()), errorMessage.c_str());
return S_OK; return S_OK;
} }
@@ -403,9 +403,9 @@ extern "C" HRESULT CALLBACK locals(CIDebugClient *client, PCSTR args)
int token; int token;
const std::string output = commmandLocals(exc, args, &token, &errorMessage); const std::string output = commmandLocals(exc, args, &token, &errorMessage);
if (output.empty()) { if (output.empty()) {
ExtensionContext::instance().report('N', token, "locals", errorMessage.c_str()); ExtensionContext::instance().report('N', token, 0, "locals", errorMessage.c_str());
} else { } else {
ExtensionContext::instance().report('R', token, "locals", "%s", output.c_str()); ExtensionContext::instance().reportLong('R', token, "locals", output);
} }
return S_OK; return S_OK;
} }
@@ -454,9 +454,9 @@ extern "C" HRESULT CALLBACK dumplocal(CIDebugClient *client, PCSTR argsIn)
int token = 0; int token = 0;
const std::string value = dumplocalHelper(exc,argsIn, &token, &errorMessage); const std::string value = dumplocalHelper(exc,argsIn, &token, &errorMessage);
if (value.empty()) { if (value.empty()) {
ExtensionContext::instance().report('N', token, "dumplocal", errorMessage.c_str()); ExtensionContext::instance().report('N', token, 0, "dumplocal", errorMessage.c_str());
} else { } else {
ExtensionContext::instance().report('R', token, "dumplocal", value.c_str()); ExtensionContext::instance().reportLong('R', token, "dumplocal", value);
} }
return S_OK; return S_OK;
} }
@@ -483,9 +483,9 @@ extern "C" HRESULT CALLBACK typecast(CIDebugClient *client, PCSTR args)
errorMessage = singleLineUsage(commandDescriptions[CmdTypecast]); errorMessage = singleLineUsage(commandDescriptions[CmdTypecast]);
} }
if (symGroup != 0 && symGroup->typeCast(iname, desiredType, &errorMessage)) { if (symGroup != 0 && symGroup->typeCast(iname, desiredType, &errorMessage)) {
ExtensionContext::instance().report('R', token, "typecast", "OK"); ExtensionContext::instance().report('R', token, 0, "typecast", "OK");
} else { } else {
ExtensionContext::instance().report('N', token, "typecast", errorMessage.c_str()); ExtensionContext::instance().report('N', token, 0, "typecast", errorMessage.c_str());
} }
return S_OK; return S_OK;
} }
@@ -513,9 +513,9 @@ extern "C" HRESULT CALLBACK addsymbol(CIDebugClient *client, PCSTR args)
errorMessage = singleLineUsage(commandDescriptions[CmdAddsymbol]); errorMessage = singleLineUsage(commandDescriptions[CmdAddsymbol]);
} }
if (symGroup != 0 && symGroup->addSymbol(name, iname, &errorMessage)) { if (symGroup != 0 && symGroup->addSymbol(name, iname, &errorMessage)) {
ExtensionContext::instance().report('R', token, "addsymbol", "OK"); ExtensionContext::instance().report('R', token, 0, "addsymbol", "OK");
} else { } else {
ExtensionContext::instance().report('N', token, "addsymbol", errorMessage.c_str()); ExtensionContext::instance().report('N', token, 0, "addsymbol", errorMessage.c_str());
} }
return S_OK; return S_OK;
} }
@@ -554,9 +554,9 @@ extern "C" HRESULT CALLBACK assign(CIDebugClient *client, PCSTR argsIn)
} while (false); } while (false);
if (success) { if (success) {
ExtensionContext::instance().report('R', token, "assign", "Ok"); ExtensionContext::instance().report('R', token, 0, "assign", "Ok");
} else { } else {
ExtensionContext::instance().report('N', token, "assign", errorMessage.c_str()); ExtensionContext::instance().report('N', token, 0, "assign", errorMessage.c_str());
} }
return S_OK; return S_OK;
} }
@@ -578,9 +578,9 @@ extern "C" HRESULT CALLBACK threads(CIDebugClient *client, PCSTR argsIn)
exc.advanced(), exc.advanced(),
&errorMessage); &errorMessage);
if (gdbmi.empty()) { if (gdbmi.empty()) {
ExtensionContext::instance().report('N', token, "threads", errorMessage.c_str()); ExtensionContext::instance().report('N', token, 0, "threads", errorMessage.c_str());
} else { } else {
ExtensionContext::instance().report('R', token, "threads", gdbmi.c_str()); ExtensionContext::instance().reportLong('R', token, "threads", gdbmi);
} }
return S_OK; return S_OK;
} }
@@ -598,9 +598,9 @@ extern "C" HRESULT CALLBACK registers(CIDebugClient *Client, PCSTR argsIn)
const bool humanReadable = !tokens.empty() && tokens.front() == "-h"; const bool humanReadable = !tokens.empty() && tokens.front() == "-h";
const std::string regs = gdbmiRegisters(exc.registers(), exc.control(), humanReadable, IncludePseudoRegisters, &errorMessage); const std::string regs = gdbmiRegisters(exc.registers(), exc.control(), humanReadable, IncludePseudoRegisters, &errorMessage);
if (regs.empty()) { if (regs.empty()) {
ExtensionContext::instance().report('N', token, "registers", errorMessage.c_str()); ExtensionContext::instance().report('N', token, 0, "registers", errorMessage.c_str());
} else { } else {
ExtensionContext::instance().report('R', token, "registers", regs.c_str()); ExtensionContext::instance().reportLong('R', token, "registers", regs);
} }
return S_OK; return S_OK;
} }
@@ -618,9 +618,9 @@ extern "C" HRESULT CALLBACK modules(CIDebugClient *Client, PCSTR argsIn)
const bool humanReadable = !tokens.empty() && tokens.front() == "-h"; const bool humanReadable = !tokens.empty() && tokens.front() == "-h";
const std::string modules = gdbmiModules(exc.symbols(), humanReadable, &errorMessage); const std::string modules = gdbmiModules(exc.symbols(), humanReadable, &errorMessage);
if (modules.empty()) { if (modules.empty()) {
ExtensionContext::instance().report('N', token, "modules", errorMessage.c_str()); ExtensionContext::instance().report('N', token, 0, "modules", errorMessage.c_str());
} else { } else {
ExtensionContext::instance().report('R', token, "modules", modules.c_str()); ExtensionContext::instance().reportLong('R', token, "modules", modules);
} }
return S_OK; return S_OK;
} }
@@ -671,11 +671,11 @@ extern "C" HRESULT CALLBACK memory(CIDebugClient *Client, PCSTR argsIn)
} }
if (memory.empty()) { if (memory.empty()) {
ExtensionContext::instance().report('N', token, "memory", errorMessage.c_str()); ExtensionContext::instance().report('N', token, 0, "memory", errorMessage.c_str());
} else { } else {
ExtensionContext::instance().report('R', token, "memory", memory.c_str()); ExtensionContext::instance().reportLong('R', token, "memory", memory);
if (!errorMessage.empty()) if (!errorMessage.empty())
ExtensionContext::instance().report('W', token, "memory", errorMessage.c_str()); ExtensionContext::instance().report('W', token, 0, "memory", errorMessage.c_str());
} }
return S_OK; return S_OK;
} }
@@ -704,9 +704,9 @@ extern "C" HRESULT CALLBACK stack(CIDebugClient *Client, PCSTR argsIn)
maxFrames, humanReadable, &errorMessage); maxFrames, humanReadable, &errorMessage);
if (stack.empty()) { if (stack.empty()) {
ExtensionContext::instance().report('N', token, "stack", errorMessage.c_str()); ExtensionContext::instance().report('N', token, 0, "stack", errorMessage.c_str());
} else { } else {
ExtensionContext::instance().report('R', token, "stack", stack.c_str()); ExtensionContext::instance().reportLong('R', token, "stack", stack);
} }
return S_OK; return S_OK;
} }

View File

@@ -950,7 +950,7 @@ bool SymbolGroupNode::expand(std::string *errorMessage)
if (FAILED(hr)) { if (FAILED(hr)) {
*errorMessage = msgExpandFailed(name(), absoluteFullIName(), m_index, msgDebugEngineComFailed("ExpandSymbol", hr)); *errorMessage = msgExpandFailed(name(), absoluteFullIName(), m_index, msgDebugEngineComFailed("ExpandSymbol", hr));
ExtensionContext::instance().report('X', 0, "Error", "%s", errorMessage->c_str()); ExtensionContext::instance().report('X', 0, 0, "Error", "%s", errorMessage->c_str());
return false; return false;
} }
SymbolGroup::SymbolParameterVector parameters; SymbolGroup::SymbolParameterVector parameters;
@@ -1020,12 +1020,12 @@ SymbolGroupNode *SymbolGroupNode::addSymbolByName(const std::string &name,
HRESULT hr = m_symbolGroup->debugSymbolGroup()->AddSymbol(name.c_str(), &index); HRESULT hr = m_symbolGroup->debugSymbolGroup()->AddSymbol(name.c_str(), &index);
if (FAILED(hr)) { if (FAILED(hr)) {
*errorMessage = msgCannotAddSymbol(name, msgDebugEngineComFailed("AddSymbol", hr)); *errorMessage = msgCannotAddSymbol(name, msgDebugEngineComFailed("AddSymbol", hr));
ExtensionContext::instance().report('X', 0, "Error", "%s", errorMessage->c_str()); ExtensionContext::instance().report('X', 0, 0, "Error", "%s", errorMessage->c_str());
return 0; return 0;
} }
if (index == DEBUG_ANY_ID) { // Occasionally happens for unknown or 'complicated' types if (index == DEBUG_ANY_ID) { // Occasionally happens for unknown or 'complicated' types
*errorMessage = msgCannotAddSymbol(name, "DEBUG_ANY_ID was returned as symbol index by AddSymbol."); *errorMessage = msgCannotAddSymbol(name, "DEBUG_ANY_ID was returned as symbol index by AddSymbol.");
ExtensionContext::instance().report('X', 0, "Error", "%s", errorMessage->c_str()); ExtensionContext::instance().report('X', 0, 0, "Error", "%s", errorMessage->c_str());
return 0; return 0;
} }
SymbolParameterVector parameters(1, DEBUG_SYMBOL_PARAMETERS()); SymbolParameterVector parameters(1, DEBUG_SYMBOL_PARAMETERS());

View File

@@ -1585,23 +1585,31 @@ void CdbEngine::parseOutputLine(QByteArray line)
// can mix things up. // can mix things up.
while (isCdbPrompt(line)) while (isCdbPrompt(line))
line.remove(0, CdbPromptLength); line.remove(0, CdbPromptLength);
// An extension notification // An extension notification (potentially consisting of several chunks)
if (line.startsWith(m_creatorExtPrefix)) { if (line.startsWith(m_creatorExtPrefix)) {
// "<qtcreatorcdbext>|type_char|token|serviceName|message" // "<qtcreatorcdbext>|type_char|token|remainingChunks|serviceName|message"
const char type = line.at(m_creatorExtPrefix.size()); const char type = line.at(m_creatorExtPrefix.size());
// integer token // integer token
const int tokenPos = m_creatorExtPrefix.size() + 2; const int tokenPos = m_creatorExtPrefix.size() + 2;
const int tokenEndPos = line.indexOf('|', tokenPos); const int tokenEndPos = line.indexOf('|', tokenPos);
QTC_ASSERT(tokenEndPos != -1, return) QTC_ASSERT(tokenEndPos != -1, return)
const int token = line.mid(tokenPos, tokenEndPos - tokenPos).toInt(); const int token = line.mid(tokenPos, tokenEndPos - tokenPos).toInt();
// remainingChunks
const int remainingChunksPos = tokenEndPos + 1;
const int remainingChunksEndPos = line.indexOf('|', remainingChunksPos);
QTC_ASSERT(remainingChunksEndPos != -1, return)
const int remainingChunks = line.mid(remainingChunksPos, remainingChunksEndPos - remainingChunksPos).toInt();
// const char 'serviceName' // const char 'serviceName'
const int whatPos = tokenEndPos + 1; const int whatPos = remainingChunksEndPos + 1;
const int whatEndPos = line.indexOf('|', whatPos); const int whatEndPos = line.indexOf('|', whatPos);
QTC_ASSERT(whatEndPos != -1, return) QTC_ASSERT(whatEndPos != -1, return)
const QByteArray what = line.mid(whatPos, whatEndPos - whatPos); const QByteArray what = line.mid(whatPos, whatEndPos - whatPos);
// Message // Build up buffer, call handler once last chunk was encountered
const QByteArray message = line.mid(whatEndPos + 1); m_extensionMessageBuffer += line.mid(whatEndPos + 1);
handleExtensionMessage(type, token, what, message); if (remainingChunks == 0) {
handleExtensionMessage(type, token, what, m_extensionMessageBuffer);
m_extensionMessageBuffer.clear();
}
return; return;
} }
// Check for command start/end tokens within which the builtin command // Check for command start/end tokens within which the builtin command

View File

@@ -205,6 +205,7 @@ private:
bool m_hasDebuggee; bool m_hasDebuggee;
QTime m_logTime; QTime m_logTime;
mutable int m_elapsedLogTime; mutable int m_elapsedLogTime;
QByteArray m_extensionMessageBuffer;
}; };
} // namespace Cdb } // namespace Cdb