diff options
author | Friedemann Kleint <Friedemann.Kleint@nokia.com> | 2011-01-04 12:40:52 +0100 |
---|---|---|
committer | Friedemann Kleint <Friedemann.Kleint@nokia.com> | 2011-01-04 12:40:52 +0100 |
commit | b7df5467d8b0d1540014c13b49f60e8827b7aa2a (patch) | |
tree | 419d2851014f5a76349e18b1eea8766c11a18cd2 | |
parent | d9ec7dd0a6da5384f2ddd7738a5d7fe40d30aad0 (diff) | |
download | qt-creator-b7df5467d8b0d1540014c13b49f60e8827b7aa2a.tar.gz |
Debugger[New CDB]: Split up long extension messages.
To accommodate the limitation of output line width of CDB.
-rw-r--r-- | src/libs/qtcreatorcdbext/eventcallback.cpp | 4 | ||||
-rw-r--r-- | src/libs/qtcreatorcdbext/extensioncontext.cpp | 37 | ||||
-rw-r--r-- | src/libs/qtcreatorcdbext/extensioncontext.h | 16 | ||||
-rw-r--r-- | src/libs/qtcreatorcdbext/outputcallback.cpp | 2 | ||||
-rw-r--r-- | src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp | 50 | ||||
-rw-r--r-- | src/libs/qtcreatorcdbext/symbolgroupnode.cpp | 6 | ||||
-rw-r--r-- | src/plugins/debugger/cdb2/cdbengine2.cpp | 20 | ||||
-rw-r--r-- | src/plugins/debugger/cdb2/cdbengine2.h | 1 |
8 files changed, 87 insertions, 49 deletions
diff --git a/src/libs/qtcreatorcdbext/eventcallback.cpp b/src/libs/qtcreatorcdbext/eventcallback.cpp index 6fb65d7f67..68b129487f 100644 --- a/src/libs/qtcreatorcdbext/eventcallback.cpp +++ b/src/libs/qtcreatorcdbext/eventcallback.cpp @@ -173,7 +173,7 @@ STDMETHODIMP EventCallback::Exception( std::ostringstream str; formatGdbmiHash(str, parameters); 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; } @@ -222,7 +222,7 @@ STDMETHODIMP EventCallback::ExitProcess( __in ULONG ExitCode ) { - ExtensionContext::instance().report('E', 0, eventContextC, "Process exited (%lu)", + ExtensionContext::instance().report('E', 0, 0, eventContextC, "Process exited (%lu)", ExitCode); const HRESULT hr = m_wrapped ? m_wrapped->ExitProcess(ExitCode) : S_OK; // Remotely debugged process exited, there is no session-inactive notification. diff --git a/src/libs/qtcreatorcdbext/extensioncontext.cpp b/src/libs/qtcreatorcdbext/extensioncontext.cpp index a6f633987e..2c32a1775e 100644 --- a/src/libs/qtcreatorcdbext/extensioncontext.cpp +++ b/src/libs/qtcreatorcdbext/extensioncontext.cpp @@ -37,6 +37,8 @@ #include "outputcallback.h" #include "stringutils.h" +#include <algorithm> + // wdbgexts.h declares 'extern WINDBG_EXTENSION_APIS ExtensionApis;' // 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}; @@ -167,7 +169,7 @@ void ExtensionContext::notifyIdle() // Format std::ostringstream str; formatGdbmiHash(str, stopReasons); - report('E', 0, "session_idle", "%s", str.str().c_str()); + reportLong('E', 0, "session_idle", str.str()); m_stopReason.clear(); } @@ -176,16 +178,16 @@ void ExtensionContext::notifyState(ULONG Notify) const ULONG ex = executionStatus(); switch (Notify) { case DEBUG_NOTIFY_SESSION_ACTIVE: - report('E', 0, "session_active", "%u", ex); + report('E', 0, 0, "session_active", "%u", ex); break; case DEBUG_NOTIFY_SESSION_ACCESSIBLE: // Meaning, commands accepted - report('E', 0, "session_accessible", "%u", ex); + report('E', 0, 0, "session_accessible", "%u", ex); break; case DEBUG_NOTIFY_SESSION_INACCESSIBLE: - report('E', 0, "session_inaccessible", "%u", ex); + report('E', 0, 0, "session_inaccessible", "%u", ex); break; case DEBUG_NOTIFY_SESSION_INACTIVE: - report('E', 0, "session_inactive", "%u", ex); + report('E', 0, 0, "session_inactive", "%u", ex); discardSymbolGroup(); // We lost the debuggee, at this point restore output. if (ex & DEBUG_STATUS_NO_DEBUGGEE) @@ -218,12 +220,13 @@ void ExtensionContext::discardSymbolGroup() 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()) return false; // '<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_start(Args, Format); 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; } +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 extern "C" { diff --git a/src/libs/qtcreatorcdbext/extensioncontext.h b/src/libs/qtcreatorcdbext/extensioncontext.h index 1f7d6c56f4..4a7ee97682 100644 --- a/src/libs/qtcreatorcdbext/extensioncontext.h +++ b/src/libs/qtcreatorcdbext/extensioncontext.h @@ -68,11 +68,17 @@ public: // Undo hooking. void unhookCallbacks(); - // Report output in standardized format understood by Qt Creator. - // '<qtcreatorcdbext>|R|<token>|<serviceName>|<one-line-output>'. - // Char code is 'R' command reply, 'N' command fail, 'E' event notification - bool report(char code, int token, const char *serviceName, PCSTR Format, ...); - + // CDB has a limitation on output, so, long messages need to be split (empirically ca 10KB) + static const size_t outputChunkSize = 10240; + /* Report output in standardized format understood by Qt Creator. + * '<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; // Call from notify handler, tell engine about state. void notifyState(ULONG Notify); diff --git a/src/libs/qtcreatorcdbext/outputcallback.cpp b/src/libs/qtcreatorcdbext/outputcallback.cpp index 0ebe99f52b..5b01ffa46c 100644 --- a/src/libs/qtcreatorcdbext/outputcallback.cpp +++ b/src/libs/qtcreatorcdbext/outputcallback.cpp @@ -97,6 +97,6 @@ STDMETHODIMP OutputCallback::Output( // Base encode as GDBMI is not really made for wide chars std::ostringstream str; 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; } diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp index f2d87dd4cd..96a96d8802 100644 --- a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp +++ b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp @@ -235,9 +235,9 @@ extern "C" HRESULT CALLBACK pid(CIDebugClient *client, PCSTR args) commandTokens<StringList>(args, &token); if (const ULONG pid = currentProcessId(client)) { - ExtensionContext::instance().report('R', token, "pid", "%u", pid); + ExtensionContext::instance().report('R', token, 0, "pid", "%u", pid); } else { - ExtensionContext::instance().report('N', token, "pid", "0"); + ExtensionContext::instance().report('N', token, 0, "pid", "0"); } 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); if (!symGroup) { - ExtensionContext::instance().report('N', token, "expandlocals", errorMessage.c_str()); + ExtensionContext::instance().report('N', token, 0, "expandlocals", errorMessage.c_str()); 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->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()); return S_OK; } @@ -403,9 +403,9 @@ extern "C" HRESULT CALLBACK locals(CIDebugClient *client, PCSTR args) int token; const std::string output = commmandLocals(exc, args, &token, &errorMessage); if (output.empty()) { - ExtensionContext::instance().report('N', token, "locals", errorMessage.c_str()); + ExtensionContext::instance().report('N', token, 0, "locals", errorMessage.c_str()); } else { - ExtensionContext::instance().report('R', token, "locals", "%s", output.c_str()); + ExtensionContext::instance().reportLong('R', token, "locals", output); } return S_OK; } @@ -454,9 +454,9 @@ extern "C" HRESULT CALLBACK dumplocal(CIDebugClient *client, PCSTR argsIn) int token = 0; const std::string value = dumplocalHelper(exc,argsIn, &token, &errorMessage); if (value.empty()) { - ExtensionContext::instance().report('N', token, "dumplocal", errorMessage.c_str()); + ExtensionContext::instance().report('N', token, 0, "dumplocal", errorMessage.c_str()); } else { - ExtensionContext::instance().report('R', token, "dumplocal", value.c_str()); + ExtensionContext::instance().reportLong('R', token, "dumplocal", value); } return S_OK; } @@ -483,9 +483,9 @@ extern "C" HRESULT CALLBACK typecast(CIDebugClient *client, PCSTR args) errorMessage = singleLineUsage(commandDescriptions[CmdTypecast]); } if (symGroup != 0 && symGroup->typeCast(iname, desiredType, &errorMessage)) { - ExtensionContext::instance().report('R', token, "typecast", "OK"); + ExtensionContext::instance().report('R', token, 0, "typecast", "OK"); } else { - ExtensionContext::instance().report('N', token, "typecast", errorMessage.c_str()); + ExtensionContext::instance().report('N', token, 0, "typecast", errorMessage.c_str()); } return S_OK; } @@ -513,9 +513,9 @@ extern "C" HRESULT CALLBACK addsymbol(CIDebugClient *client, PCSTR args) errorMessage = singleLineUsage(commandDescriptions[CmdAddsymbol]); } if (symGroup != 0 && symGroup->addSymbol(name, iname, &errorMessage)) { - ExtensionContext::instance().report('R', token, "addsymbol", "OK"); + ExtensionContext::instance().report('R', token, 0, "addsymbol", "OK"); } else { - ExtensionContext::instance().report('N', token, "addsymbol", errorMessage.c_str()); + ExtensionContext::instance().report('N', token, 0, "addsymbol", errorMessage.c_str()); } return S_OK; } @@ -554,9 +554,9 @@ extern "C" HRESULT CALLBACK assign(CIDebugClient *client, PCSTR argsIn) } while (false); if (success) { - ExtensionContext::instance().report('R', token, "assign", "Ok"); + ExtensionContext::instance().report('R', token, 0, "assign", "Ok"); } else { - ExtensionContext::instance().report('N', token, "assign", errorMessage.c_str()); + ExtensionContext::instance().report('N', token, 0, "assign", errorMessage.c_str()); } return S_OK; } @@ -578,9 +578,9 @@ extern "C" HRESULT CALLBACK threads(CIDebugClient *client, PCSTR argsIn) exc.advanced(), &errorMessage); if (gdbmi.empty()) { - ExtensionContext::instance().report('N', token, "threads", errorMessage.c_str()); + ExtensionContext::instance().report('N', token, 0, "threads", errorMessage.c_str()); } else { - ExtensionContext::instance().report('R', token, "threads", gdbmi.c_str()); + ExtensionContext::instance().reportLong('R', token, "threads", gdbmi); } 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 std::string regs = gdbmiRegisters(exc.registers(), exc.control(), humanReadable, IncludePseudoRegisters, &errorMessage); if (regs.empty()) { - ExtensionContext::instance().report('N', token, "registers", errorMessage.c_str()); + ExtensionContext::instance().report('N', token, 0, "registers", errorMessage.c_str()); } else { - ExtensionContext::instance().report('R', token, "registers", regs.c_str()); + ExtensionContext::instance().reportLong('R', token, "registers", regs); } 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 std::string modules = gdbmiModules(exc.symbols(), humanReadable, &errorMessage); if (modules.empty()) { - ExtensionContext::instance().report('N', token, "modules", errorMessage.c_str()); + ExtensionContext::instance().report('N', token, 0, "modules", errorMessage.c_str()); } else { - ExtensionContext::instance().report('R', token, "modules", modules.c_str()); + ExtensionContext::instance().reportLong('R', token, "modules", modules); } return S_OK; } @@ -671,11 +671,11 @@ extern "C" HRESULT CALLBACK memory(CIDebugClient *Client, PCSTR argsIn) } if (memory.empty()) { - ExtensionContext::instance().report('N', token, "memory", errorMessage.c_str()); + ExtensionContext::instance().report('N', token, 0, "memory", errorMessage.c_str()); } else { - ExtensionContext::instance().report('R', token, "memory", memory.c_str()); + ExtensionContext::instance().reportLong('R', token, "memory", memory); 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; } @@ -704,9 +704,9 @@ extern "C" HRESULT CALLBACK stack(CIDebugClient *Client, PCSTR argsIn) maxFrames, humanReadable, &errorMessage); if (stack.empty()) { - ExtensionContext::instance().report('N', token, "stack", errorMessage.c_str()); + ExtensionContext::instance().report('N', token, 0, "stack", errorMessage.c_str()); } else { - ExtensionContext::instance().report('R', token, "stack", stack.c_str()); + ExtensionContext::instance().reportLong('R', token, "stack", stack); } return S_OK; } diff --git a/src/libs/qtcreatorcdbext/symbolgroupnode.cpp b/src/libs/qtcreatorcdbext/symbolgroupnode.cpp index e7470eb6d6..f17d2f7682 100644 --- a/src/libs/qtcreatorcdbext/symbolgroupnode.cpp +++ b/src/libs/qtcreatorcdbext/symbolgroupnode.cpp @@ -950,7 +950,7 @@ bool SymbolGroupNode::expand(std::string *errorMessage) if (FAILED(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; } SymbolGroup::SymbolParameterVector parameters; @@ -1020,12 +1020,12 @@ SymbolGroupNode *SymbolGroupNode::addSymbolByName(const std::string &name, HRESULT hr = m_symbolGroup->debugSymbolGroup()->AddSymbol(name.c_str(), &index); if (FAILED(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; } 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."); - ExtensionContext::instance().report('X', 0, "Error", "%s", errorMessage->c_str()); + ExtensionContext::instance().report('X', 0, 0, "Error", "%s", errorMessage->c_str()); return 0; } SymbolParameterVector parameters(1, DEBUG_SYMBOL_PARAMETERS()); diff --git a/src/plugins/debugger/cdb2/cdbengine2.cpp b/src/plugins/debugger/cdb2/cdbengine2.cpp index 7b3e64baab..7418a2864d 100644 --- a/src/plugins/debugger/cdb2/cdbengine2.cpp +++ b/src/plugins/debugger/cdb2/cdbengine2.cpp @@ -1585,23 +1585,31 @@ void CdbEngine::parseOutputLine(QByteArray line) // can mix things up. while (isCdbPrompt(line)) line.remove(0, CdbPromptLength); - // An extension notification + // An extension notification (potentially consisting of several chunks) 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()); // integer token const int tokenPos = m_creatorExtPrefix.size() + 2; const int tokenEndPos = line.indexOf('|', tokenPos); QTC_ASSERT(tokenEndPos != -1, return) 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 int whatPos = tokenEndPos + 1; + const int whatPos = remainingChunksEndPos + 1; const int whatEndPos = line.indexOf('|', whatPos); QTC_ASSERT(whatEndPos != -1, return) const QByteArray what = line.mid(whatPos, whatEndPos - whatPos); - // Message - const QByteArray message = line.mid(whatEndPos + 1); - handleExtensionMessage(type, token, what, message); + // Build up buffer, call handler once last chunk was encountered + m_extensionMessageBuffer += line.mid(whatEndPos + 1); + if (remainingChunks == 0) { + handleExtensionMessage(type, token, what, m_extensionMessageBuffer); + m_extensionMessageBuffer.clear(); + } return; } // Check for command start/end tokens within which the builtin command diff --git a/src/plugins/debugger/cdb2/cdbengine2.h b/src/plugins/debugger/cdb2/cdbengine2.h index 3f8d3e6157..fa2b7c9f41 100644 --- a/src/plugins/debugger/cdb2/cdbengine2.h +++ b/src/plugins/debugger/cdb2/cdbengine2.h @@ -205,6 +205,7 @@ private: bool m_hasDebuggee; QTime m_logTime; mutable int m_elapsedLogTime; + QByteArray m_extensionMessageBuffer; }; } // namespace Cdb |