diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp
index fc6ab6a1a879423487dcea85f8635b09ea6462b3..a18de4de12b3aacf0d391421e8d8762dc656cd26 100644
--- a/src/plugins/debugger/cdb/cdbengine.cpp
+++ b/src/plugins/debugger/cdb/cdbengine.cpp
@@ -343,8 +343,8 @@ void CdbEngine::syncOperateByInstruction(bool operateByInstruction)
         return;
     QTC_ASSERT(m_accessible, return);
     m_operateByInstruction = operateByInstruction;
-    postCommand(m_operateByInstruction ? QByteArray("l-t") : QByteArray("l+t"));
-    postCommand(m_operateByInstruction ? QByteArray("l-s") : QByteArray("l+s"));
+    postCommand(DebuggerCommand(m_operateByInstruction ? QByteArray("l-t") : QByteArray("l+t")));
+    postCommand(DebuggerCommand(m_operateByInstruction ? QByteArray("l-s") : QByteArray("l+s")));
 }
 
 void CdbEngine::syncVerboseLog(bool verboseLog)
@@ -353,7 +353,7 @@ void CdbEngine::syncVerboseLog(bool verboseLog)
         return;
     QTC_ASSERT(m_accessible, return);
     m_verboseLog = verboseLog;
-    postCommand(m_verboseLog ? QByteArray("!sym noisy") : QByteArray("!sym quiet"));
+    postCommand(DebuggerCommand(m_verboseLog ? QByteArray("!sym noisy") : QByteArray("!sym quiet")));
 }
 
 bool CdbEngine::canHandleToolTip(const DebuggerToolTipContext &context) const
@@ -477,7 +477,7 @@ void CdbEngine::consoleStubExited()
 
 void CdbEngine::createFullBacktrace()
 {
-    postBuiltinCommand("~*kp", CB(handleCreateFullBackTrace));
+    postCommand(DebuggerCommand("~*kp", BuiltinCommand, CB(handleCreateFullBackTrace)));
 }
 
 void CdbEngine::handleCreateFullBackTrace(const DebuggerResponse &response)
@@ -650,9 +650,7 @@ bool CdbEngine::launchCDB(const DebuggerRunParameters &sp, QString *errorMessage
     m_hasDebuggee = true;
     if (isRemote) { // We do not get an 'idle' in a remote session, but are accessible
         m_accessible = true;
-        const QByteArray loadCommand = QByteArray(".load ")
-                + extensionFileName.toLocal8Bit();
-        postCommand(loadCommand);
+        postCommand(DebuggerCommand(".load " + extensionFileName.toLocal8Bit()));
         STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyEngineSetupOk")
         notifyEngineSetupOk();
     }
@@ -665,24 +663,26 @@ void CdbEngine::setupInferior()
         qDebug("setupInferior");
     const DebuggerRunParameters &rp = runParameters();
     if (!rp.commandsAfterConnect.isEmpty())
-        postCommand(rp.commandsAfterConnect);
+        postCommand(DebuggerCommand(rp.commandsAfterConnect));
     // QmlCppEngine expects the QML engine to be connected before any breakpoints are hit
     // (attemptBreakpointSynchronization() will be directly called then)
     attemptBreakpointSynchronization();
     if (rp.breakOnMain) {
         const BreakpointParameters bp(BreakpointAtMain);
         BreakpointModelId id(quint16(-1));
-        postBuiltinCommand(cdbAddBreakpointCommand(bp, m_sourcePathMappings, id, true),
-                           [this, id](const DebuggerResponse &r) { handleBreakInsert(r, id); });
-    }
-    postCommand("sxn 0x4000001f"); // Do not break on WowX86 exceptions.
-    postCommand("sxn ibp"); // Do not break on initial breakpoints.
-    postCommand(".asm source_line"); // Source line in assembly
-    postCommand(m_extensionCommandPrefixBA + "setparameter maxStringLength="
+        postCommand(DebuggerCommand(
+                        cdbAddBreakpointCommand(bp, m_sourcePathMappings, id, true),
+                        BuiltinCommand,
+                        [this, id](const DebuggerResponse &r) { handleBreakInsert(r, id); }));
+    }
+    postCommand(DebuggerCommand("sxn 0x4000001f")); // Do not break on WowX86 exceptions.
+    postCommand(DebuggerCommand("sxn ibp")); // Do not break on initial breakpoints.
+    postCommand(DebuggerCommand(".asm source_line")); // Source line in assembly
+    postCommand(DebuggerCommand(m_extensionCommandPrefixBA + "setparameter maxStringLength="
                 + action(MaximalStringLength)->value().toByteArray()
                 + " maxStackDepth="
-                + action(MaximalStackDepth)->value().toByteArray());
-    postExtensionCommand("pid", QByteArray(), CB(handlePid));
+                + action(MaximalStackDepth)->value().toByteArray()));
+    postCommand(DebuggerCommand("pid", ExtensionCommand, CB(handlePid)));
 }
 
 static QByteArray msvcRunTime(const Abi::OSFlavor flavour)
@@ -725,33 +725,37 @@ void CdbEngine::runEngine()
 
     const QStringList breakEvents = stringListSetting(CdbBreakEvents);
     foreach (const QString &breakEvent, breakEvents)
-        postCommand(QByteArray("sxe ") + breakEvent.toLatin1());
+        postCommand(DebuggerCommand(QByteArray("sxe ") + breakEvent.toLatin1()));
     // Break functions: each function must be fully qualified,
     // else the debugger will slow down considerably.
     if (boolSetting(CdbBreakOnCrtDbgReport)) {
         const QByteArray module = msvcRunTime(runParameters().toolChainAbi.osFlavor());
         const QByteArray debugModule = module + 'D';
         const QByteArray wideFunc = QByteArray(CdbOptionsPage::crtDbgReport).append('W');
-        postBuiltinCommand(breakAtFunctionCommand(CdbOptionsPage::crtDbgReport, module),
-                           [this](const DebuggerResponse &r) { handleBreakInsert(r, BreakpointModelId()); });
-        postBuiltinCommand(breakAtFunctionCommand(wideFunc, module),
-                           [this](const DebuggerResponse &r) { handleBreakInsert(r, BreakpointModelId()); });
-        postBuiltinCommand(breakAtFunctionCommand(CdbOptionsPage::crtDbgReport, debugModule),
-                           [this](const DebuggerResponse &r) { handleBreakInsert(r, BreakpointModelId()); });
-        postBuiltinCommand(breakAtFunctionCommand(wideFunc, debugModule),
-                           [this](const DebuggerResponse &r) { handleBreakInsert(r, BreakpointModelId()); });
+        postCommand(DebuggerCommand(
+                                breakAtFunctionCommand(CdbOptionsPage::crtDbgReport, module), BuiltinCommand,
+                                [this](const DebuggerResponse &r) { handleBreakInsert(r, BreakpointModelId()); }));
+        postCommand(DebuggerCommand(
+                                breakAtFunctionCommand(wideFunc, module), BuiltinCommand,
+                                [this](const DebuggerResponse &r) { handleBreakInsert(r, BreakpointModelId()); }));
+        postCommand(DebuggerCommand(
+                                breakAtFunctionCommand(CdbOptionsPage::crtDbgReport, debugModule), BuiltinCommand,
+                                [this](const DebuggerResponse &r) { handleBreakInsert(r, BreakpointModelId()); }));
+        postCommand(DebuggerCommand(
+                                breakAtFunctionCommand(wideFunc, debugModule), BuiltinCommand,
+                                [this](const DebuggerResponse &r) { handleBreakInsert(r, BreakpointModelId()); }));
     }
     if (boolSetting(BreakOnWarning)) {
-        postBuiltinCommand("bm /( QtCored4!qWarning", // 'bm': All overloads.
-                           [this](const DebuggerResponse &r) { handleBreakInsert(r, BreakpointModelId()); });
-        postBuiltinCommand("bm /( Qt5Cored!QMessageLogger::warning",
-                           [this](const DebuggerResponse &r) { handleBreakInsert(r, BreakpointModelId()); });
+        postCommand(DebuggerCommand( "bm /( QtCored4!qWarning", BuiltinCommand, // 'bm': All overloads.
+                                [this](const DebuggerResponse &r) { handleBreakInsert(r, BreakpointModelId()); }));
+        postCommand(DebuggerCommand( "bm /( Qt5Cored!QMessageLogger::warning", BuiltinCommand,
+                                [this](const DebuggerResponse &r) { handleBreakInsert(r, BreakpointModelId()); }));
     }
     if (boolSetting(BreakOnFatal)) {
-        postBuiltinCommand("bm /( QtCored4!qFatal", // 'bm': All overloads.
-                           [this](const DebuggerResponse &r) { handleBreakInsert(r, BreakpointModelId()); });
-        postBuiltinCommand("bm /( Qt5Cored!QMessageLogger::fatal",
-                           [this](const DebuggerResponse &r) { handleBreakInsert(r, BreakpointModelId()); });
+        postCommand(DebuggerCommand("bm /( QtCored4!qFatal", BuiltinCommand, // 'bm': All overloads.
+                           [this](const DebuggerResponse &r) { handleBreakInsert(r, BreakpointModelId()); }));
+        postCommand(DebuggerCommand("bm /( Qt5Cored!QMessageLogger::fatal", BuiltinCommand,
+                           [this](const DebuggerResponse &r) { handleBreakInsert(r, BreakpointModelId()); }));
     }
     if (runParameters().startMode == AttachCore) {
         QTC_ASSERT(!m_coreStopReason.isNull(), return; );
@@ -835,10 +839,10 @@ void CdbEngine::shutdownEngine()
             detachDebugger();
         // Remote requires a bit more force to quit.
         if (m_effectiveStartMode == AttachToRemoteServer) {
-            postCommand(m_extensionCommandPrefixBA + "shutdownex");
-            postCommand("qq");
+            postCommand(DebuggerCommand(m_extensionCommandPrefixBA + "shutdownex"));
+            postCommand(DebuggerCommand("qq"));
         } else {
-            postCommand("q");
+            postCommand(DebuggerCommand("q"));
         }
     } else {
         // Remote process. No can do, currently
@@ -872,7 +876,7 @@ void CdbEngine::processFinished()
 
 void CdbEngine::detachDebugger()
 {
-    postCommand(".detach");
+    postCommand(DebuggerCommand(".detach"));
 }
 
 static inline bool isWatchIName(const QByteArray &iname)
@@ -900,21 +904,21 @@ void CdbEngine::executeStep()
 {
     if (!m_operateByInstruction)
         m_sourceStepInto = true; // See explanation at handleStackTrace().
-    postCommand(QByteArray("t")); // Step into-> t (trace)
+    postCommand(DebuggerCommand(QByteArray("t"))); // Step into-> t (trace)
     STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
     notifyInferiorRunRequested();
 }
 
 void CdbEngine::executeStepOut()
 {
-    postCommand(QByteArray("gu")); // Step out-> gu (go up)
+    postCommand(DebuggerCommand(QByteArray("gu"))); // Step out-> gu (go up)
     STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
     notifyInferiorRunRequested();
 }
 
 void CdbEngine::executeNext()
 {
-    postCommand(QByteArray("p")); // Step over -> p
+    postCommand(DebuggerCommand(QByteArray("p"))); // Step over -> p
     STATE_DEBUG(state(), Q_FUNC_INFO, __LINE__, "notifyInferiorRunRequested")
     notifyInferiorRunRequested();
 }
@@ -938,7 +942,7 @@ void CdbEngine::continueInferior()
 
 void CdbEngine::doContinueInferior()
 {
-    postCommand(QByteArray("g"));
+    postCommand(DebuggerCommand(QByteArray("g")));
 }
 
 bool CdbEngine::canInterruptInferior() const
@@ -1011,8 +1015,10 @@ void CdbEngine::executeRunToLine(const ContextData &data)
         bp.fileName = data.fileName;
         bp.lineNumber = data.lineNumber;
     }
-    postBuiltinCommand(cdbAddBreakpointCommand(bp, m_sourcePathMappings, BreakpointModelId(), true),
-                       [this](const DebuggerResponse &r) { handleBreakInsert(r, BreakpointModelId()); });
+    postCommand(DebuggerCommand(
+                    cdbAddBreakpointCommand(bp, m_sourcePathMappings, BreakpointModelId(), true),
+                    BuiltinCommand,
+                    [this](const DebuggerResponse &r) { handleBreakInsert(r, BreakpointModelId()); }));
     continueInferior();
 }
 
@@ -1022,8 +1028,10 @@ void CdbEngine::executeRunToFunction(const QString &functionName)
     BreakpointParameters bp(BreakpointByFunction);
     bp.functionName = functionName;
 
-    postBuiltinCommand(cdbAddBreakpointCommand(bp, m_sourcePathMappings, BreakpointModelId(), true),
-                       [this](const DebuggerResponse &r) { handleBreakInsert(r, BreakpointModelId()); });
+    postCommand(DebuggerCommand(
+                    cdbAddBreakpointCommand(bp, m_sourcePathMappings, BreakpointModelId(), true),
+                    BuiltinCommand,
+                    [this](const DebuggerResponse &r) { handleBreakInsert(r, BreakpointModelId()); }));
     continueInferior();
 }
 
@@ -1033,7 +1041,7 @@ void CdbEngine::setRegisterValue(const QByteArray &name, const QString &value)
     QByteArray cmd;
     ByteArrayInputStream str(cmd);
     str << "r " << name << '=' << value;
-    postCommand(cmd);
+    postCommand(DebuggerCommand(cmd));
     reloadRegisters();
 }
 
@@ -1048,7 +1056,8 @@ void CdbEngine::executeJumpToLine(const ContextData &data)
         QByteArray cmd;
         ByteArrayInputStream str(cmd);
         str << "? `" << QDir::toNativeSeparators(data.fileName) << ':' << data.lineNumber << '`';
-        postBuiltinCommand(cmd, [this, data](const DebuggerResponse &r) { handleJumpToLineAddressResolution(r, data); });
+        postCommand(DebuggerCommand(cmd, BuiltinCommand,
+                [this, data](const DebuggerResponse &r) { handleJumpToLineAddressResolution(r, data); }));
     }
 }
 
@@ -1062,7 +1071,7 @@ void CdbEngine::jumpToAddress(quint64 address)
     str.setHexPrefix(true);
     str.setIntegerBase(16);
     str << address;
-    postCommand(registerCmd);
+    postCommand(DebuggerCommand(registerCmd));
 }
 
 void CdbEngine::handleJumpToLineAddressResolution(const DebuggerResponse &response, const ContextData &context)
@@ -1127,7 +1136,7 @@ void CdbEngine::assignValueInDebugger(WatchItem *w, const QString &expr, const Q
         break;
     }
 
-    postCommand(cmd);
+    postCommand(DebuggerCommand(cmd));
     // Update all locals in case we change a union or something pointed to
     // that affects other variables, too.
     updateLocals();
@@ -1151,81 +1160,52 @@ void CdbEngine::handleThreads(const DebuggerResponse &response)
 void CdbEngine::executeDebuggerCommand(const QString &command, DebuggerLanguages languages)
 {
     if (languages & CppLanguage)
-        postCommand(command.toLocal8Bit());
+        postCommand(DebuggerCommand(command.toLocal8Bit()));
 }
 
 // Post command to the cdb process
-void CdbEngine::postCommand(const QByteArray &cmd)
-{
-    if (debug)
-        qDebug("CdbEngine::postCommand %dms '%s' %s\n",
-               elapsedLogTime(), cmd.constData(), stateName(state()));
-    showMessage(QString::fromLocal8Bit(cmd), LogInput);
-    m_process.write(cmd + '\n');
-}
-
-// Post a built-in-command producing free-format output with a callback.
-// In order to catch the output, it is enclosed in 'echo' commands
-// printing a specially formatted token to be identifiable in the output.
-void CdbEngine::postBuiltinCommand(const QByteArray &cmd,
-                                   CommandHandler handler)
+void CdbEngine::postCommand(const DebuggerCommand &dbgCmd)
 {
+    QByteArray cmd = dbgCmd.function + dbgCmd.arguments();
     if (!m_accessible) {
-        const QString msg = QString::fromLatin1("Attempt to issue builtin command \"%1\" to non-accessible session (%2)")
+        const QString msg = QString::fromLatin1("Attempt to issue command \"%1\" to non-accessible session (%2)")
                 .arg(QString::fromLocal8Bit(cmd), QString::fromLatin1(stateName(state())));
         showMessage(msg, LogError);
         return;
     }
-    const int token = m_nextCommandToken++;
-    CdbCommandPtr pendingCommand(new CdbCommand(handler));
 
-    m_commandForToken.insert(token, pendingCommand);
-    // Enclose command in echo-commands for token
     QByteArray fullCmd;
     ByteArrayInputStream str(fullCmd);
-    str << ".echo \"" << m_tokenPrefix << token << "<\"\n"
+    if (dbgCmd.flags & BuiltinCommand) {
+        // Post a built-in-command producing free-format output with a callback.
+        // In order to catch the output, it is enclosed in 'echo' commands
+        // printing a specially formatted token to be identifiable in the output.
+        const int token = m_nextCommandToken++;
+        str << ".echo \"" << m_tokenPrefix << token << "<\"\n"
             << cmd << "\n.echo \"" << m_tokenPrefix << token << ">\"";
-    if (debug)
-        qDebug("CdbEngine::postBuiltinCommand %dms '%s' token=%d %s, pending=%d",
-               elapsedLogTime(), cmd.constData(), token, stateName(state()),
+        m_commandForToken.insert(token, dbgCmd);
+    } else if (dbgCmd.flags & ExtensionCommand) {
+        // Post an extension command producing one-line output with a callback,
+        // pass along token for identification in hash.
+        const int token = m_nextCommandToken++;
+        str << m_extensionCommandPrefixBA << dbgCmd.function << " -t " << token;
+        if (!dbgCmd.args.isEmpty())
+            str <<  ' ' << dbgCmd.args;
+        m_commandForToken.insert(token, dbgCmd);
+    } else {
+        str << cmd;
+    }
+    if (debug) {
+        qDebug("CdbEngine::postCommand %dms '%s' %s, pending=%d",
+               elapsedLogTime(), dbgCmd.function.data(), stateName(state()),
                m_commandForToken.size());
-    if (debug > 1)
-        qDebug("CdbEngine::postBuiltinCommand: resulting command '%s'\n",
+    }
+    if (debug > 1) {
+        qDebug("CdbEngine::postCommand: resulting command '%s'\n",
                fullCmd.constData());
-    postCommand(fullCmd);
-}
-
-// Post an extension command producing one-line output with a callback,
-// pass along token for identification in queue.
-void CdbEngine::postExtensionCommand(const QByteArray &cmd,
-                                     const QByteArray &arguments,
-                                     CommandHandler handler)
-{
-    if (!m_accessible) {
-        const QString msg = QString::fromLatin1("Attempt to issue extension command \"%1\" to non-accessible session (%2)")
-                .arg(QString::fromLocal8Bit(cmd), QString::fromLatin1(stateName(state())));
-        showMessage(msg, LogError);
-        return;
     }
-
-    const int token = m_nextCommandToken++;
-
-    // Format full command with token to be recognizeable in the output
-    QByteArray fullCmd;
-    ByteArrayInputStream str(fullCmd);
-    str << m_extensionCommandPrefixBA << cmd << " -t " << token;
-    if (!arguments.isEmpty())
-        str <<  ' ' << arguments;
-
-    CdbCommandPtr pendingCommand(new CdbCommand(handler));
-
-    m_commandForToken.insert(token, pendingCommand);
-    // Enclose command in echo-commands for token
-    if (debug)
-        qDebug("CdbEngine::postExtensionCommand %dms '%s' token=%d %s, pending=%d",
-               elapsedLogTime(), fullCmd.constData(), token, stateName(state()),
-               m_commandForToken.size());
-    postCommand(fullCmd);
+    showMessage(QString::fromLocal8Bit(cmd), LogInput);
+    m_process.write(fullCmd + '\n');
 }
 
 void CdbEngine::activateFrame(int index)
@@ -1343,8 +1323,8 @@ void CdbEngine::doUpdateLocals(const UpdateParameters &updateParameters)
     if (partialUpdate)
         str << blankSeparator << updateParameters.partialVariable;
 
-    postExtensionCommand("locals", arguments,
-                [this, partialUpdate](const DebuggerResponse &r) { handleLocals(r, partialUpdate); });
+    postCommand(DebuggerCommand("locals", arguments.constData(), ExtensionCommand,
+                [this, partialUpdate](const DebuggerResponse &r) { handleLocals(r, partialUpdate); }));
 }
 
 void CdbEngine::updateAll()
@@ -1360,9 +1340,9 @@ void CdbEngine::selectThread(ThreadId threadId)
     threadsHandler()->setCurrentThread(threadId);
 
     const QByteArray cmd = '~' + QByteArray::number(threadId.raw()) + " s";
-    postBuiltinCommand(cmd, [this](const DebuggerResponse &) {
-        postExtensionCommand("stack", "unlimited", CB(handleStackTrace));
-    });
+    postCommand(DebuggerCommand(cmd, BuiltinCommand, [this](const DebuggerResponse &) {
+        reloadFullStack();
+    }));
 }
 
 // Default address range for showing disassembly.
@@ -1408,7 +1388,8 @@ void CdbEngine::postDisassemblerCommand(quint64 address, quint64 endAddress,
     QByteArray cmd;
     ByteArrayInputStream str(cmd);
     str <<  "u " << hex <<hexPrefixOn << address << ' ' << endAddress;
-    postBuiltinCommand(cmd, [this, agent](const DebuggerResponse &r) { handleDisassembler(r, agent); });
+    postCommand(DebuggerCommand(cmd, BuiltinCommand,
+            [this, agent](const DebuggerResponse &r) { handleDisassembler(r, agent); }));
 }
 
 void CdbEngine::postResolveSymbol(const QString &module, const QString &function,
@@ -1420,8 +1401,10 @@ void CdbEngine::postResolveSymbol(const QString &module, const QString &function
     const QList<quint64> addresses = m_symbolAddressCache.values(symbol);
     if (addresses.isEmpty()) {
         showMessage(QLatin1String("Resolving symbol: ") + symbol + QLatin1String("..."), LogMisc);
-        postBuiltinCommand(QByteArray("x ") + symbol.toLatin1(),
-                           [this, symbol, agent](const DebuggerResponse &r) { handleResolveSymbol(r, symbol, agent); });
+        postCommand(DebuggerCommand(QByteArray("x ") + symbol.toLatin1(), BuiltinCommand,
+                                            [this, symbol, agent](const DebuggerResponse &r) {
+            handleResolveSymbol(r, symbol, agent);
+        }));
     } else {
         showMessage(QString::fromLatin1("Using cached addresses for %1.").
                     arg(symbol), LogMisc);
@@ -1569,8 +1552,8 @@ void CdbEngine::postFetchMemory(const MemoryViewCookie &cookie)
     QByteArray args;
     ByteArrayInputStream str(args);
     str << cookie.address << ' ' << cookie.length;
-    postExtensionCommand("memory", args,
-                         [this, cookie](const DebuggerResponse &r) { handleMemory(r, cookie); });
+    postCommand(DebuggerCommand("memory", args, ExtensionCommand,
+                         [this, cookie](const DebuggerResponse &r) { handleMemory(r, cookie); }));
 }
 
 void CdbEngine::changeMemory(Internal::MemoryAgent *, QObject *, quint64 addr, const QByteArray &data)
@@ -1580,7 +1563,7 @@ void CdbEngine::changeMemory(Internal::MemoryAgent *, QObject *, quint64 addr, c
         const MemoryChangeCookie cookie(addr, data);
         doInterruptInferiorCustomSpecialStop(qVariantFromValue(cookie));
     } else {
-        postCommand(cdbWriteMemoryCommand(addr, data));
+        postCommand(DebuggerCommand(cdbWriteMemoryCommand(addr, data)));
     }
 }
 
@@ -1598,7 +1581,7 @@ void CdbEngine::handleMemory(const DebuggerResponse &response, const MemoryViewC
 
 void CdbEngine::reloadModules()
 {
-    postExtensionCommand("modules", QByteArray(), CB(handleModules));
+    postCommand(DebuggerCommand("modules", ExtensionCommand, CB(handleModules)));
 }
 
 void CdbEngine::loadSymbols(const QString & /* moduleName */)
@@ -1617,7 +1600,7 @@ void CdbEngine::requestModuleSymbols(const QString &moduleName)
 void CdbEngine::reloadRegisters()
 {
     QTC_ASSERT(threadsHandler()->currentThreadIndex() >= 0,  return);
-    postExtensionCommand("registers", QByteArray(), CB(handleRegistersExt));
+    postCommand(DebuggerCommand("registers", ExtensionCommand, CB(handleRegistersExt)));
 }
 
 void CdbEngine::reloadSourceFiles()
@@ -1628,12 +1611,12 @@ void CdbEngine::reloadFullStack()
 {
     if (debug)
         qDebug("%s", Q_FUNC_INFO);
-    postExtensionCommand("stack", "unlimited", CB(handleStackTrace));
+    postCommand(DebuggerCommand("stack", "unlimited", ExtensionCommand, CB(handleStackTrace)));
 }
 
 void CdbEngine::listBreakpoints()
 {
-    postExtensionCommand("breakpoints", QByteArray("-v"), CB(handleBreakPoints));
+    postCommand(DebuggerCommand("breakpoints", "-v", ExtensionCommand, CB(handleBreakPoints)));
 }
 
 void CdbEngine::handlePid(const DebuggerResponse &response)
@@ -1861,8 +1844,8 @@ unsigned CdbEngine::examineStopReason(const GdbMi &stopReason,
                         exp.prepend('"');
                         exp.append('"');
                     }
-                    postExtensionCommand("expression", exp,
-                         [this, id, stopReason](const DebuggerResponse &r) { handleExpression(r, id, stopReason); });
+                    postCommand(DebuggerCommand("expression", exp, ExtensionCommand,
+                         [this, id, stopReason](const DebuggerResponse &r) { handleExpression(r, id, stopReason); }));
 
                     return StopReportLog;
                 }
@@ -2010,7 +1993,7 @@ void CdbEngine::processStop(const GdbMi &stopReason, bool conditionalBreakPointT
         // Start sequence to get all relevant data.
         if (stopFlags & StopInArtificialThread) {
             showMessage(tr("Switching to main thread..."), LogMisc);
-            postCommand("~0 s");
+            postCommand(DebuggerCommand("~0 s"));
             forcedThreadId = ThreadId(0);
             // Re-fetch stack again.
             reloadFullStack();
@@ -2025,8 +2008,8 @@ void CdbEngine::processStop(const GdbMi &stopReason, bool conditionalBreakPointT
                     executeStepOut();
                     return;
                 case ParseStackWow64:
-                    postBuiltinCommand("lm m wow64",
-                                       [this, stack](const DebuggerResponse &r) { handleCheckWow64(r, stack); });
+                    postCommand(DebuggerCommand("lm m wow64", BuiltinCommand,
+                            [this, stack](const DebuggerResponse &r) { handleCheckWow64(r, stack); }));
                     break;
                 }
             } else {
@@ -2117,7 +2100,8 @@ void CdbEngine::handleCheckWow64(const DebuggerResponse &response, const GdbMi &
     // start             end                 module name
     // 00000000`77490000 00000000`774d5000   wow64      (deferred)
     if (response.data.data().contains("wow64")) {
-        postBuiltinCommand("k", [this, stack](const DebuggerResponse &r) { ensureUsing32BitStackInWow64(r, stack); });
+        postCommand(DebuggerCommand("k", BuiltinCommand,
+                [this, stack](const DebuggerResponse &r) { ensureUsing32BitStackInWow64(r, stack); }));
         return;
     }
     m_wow64State = noWow64Stack;
@@ -2137,7 +2121,7 @@ void CdbEngine::ensureUsing32BitStackInWow64(const DebuggerResponse &response, c
             return;
         } else if (line.startsWith("Child-SP")) {
             m_wow64State = wow64Stack64Bit;
-            postBuiltinCommand("!wow64exts.sw", CB(handleSwitchWow64Stack));
+            postCommand(DebuggerCommand("!wow64exts.sw", BuiltinCommand, CB(handleSwitchWow64Stack)));
             return;
         }
     }
@@ -2154,7 +2138,7 @@ void CdbEngine::handleSwitchWow64Stack(const DebuggerResponse &response)
     else
         m_wow64State = noWow64Stack;
     // reload threads and the stack after switching the mode
-    postExtensionCommand("threads", QByteArray(), CB(handleThreads));
+    postCommand(DebuggerCommand("threads", ExtensionCommand, CB(handleThreads)));
 }
 
 void CdbEngine::handleSessionAccessible(unsigned long cdbExState)
@@ -2239,35 +2223,33 @@ void CdbEngine::handleExtensionMessage(char t, int token, const QByteArray &what
             return;
         }
         // Did the command finish? Take off queue and complete, invoke CB
-        const CdbCommandPtr command = m_commandForToken.take(token);
-        if (!command.isNull()) {
-            if (debug)
-                qDebug("### Completed extension command for token=%d, pending=%d",
-                       token, m_commandForToken.size());
+        const DebuggerCommand command = m_commandForToken.take(token);
+        if (debug)
+            qDebug("### Completed extension command '%s' for token=%d, pending=%d",
+                   command.function.data(), token, m_commandForToken.size());
 
-            if (!command->handler)
-                return;
-            DebuggerResponse response;
-            response.data.m_name = "data";
-            if (t == 'R') {
-                response.resultClass = ResultDone;
-                response.data.fromString(message);
-                if (!response.data.isValid()) {
-                    response.data.m_data = message;
-                    response.data.m_type = GdbMi::Tuple;
-                }
-            } else {
-                response.resultClass = ResultError;
-                GdbMi msg;
-                msg.m_name = "msg";
-                msg.m_data = message;
-                msg.m_type = GdbMi::Tuple;
+        if (!command.callback)
+            return;
+        DebuggerResponse response;
+        response.data.m_name = "data";
+        if (t == 'R') {
+            response.resultClass = ResultDone;
+            response.data.fromString(message);
+            if (!response.data.isValid()) {
+                response.data.m_data = message;
                 response.data.m_type = GdbMi::Tuple;
-                response.data.m_children.push_back(msg);
             }
-            command->handler(response);
-            return;
+        } else {
+            response.resultClass = ResultError;
+            GdbMi msg;
+            msg.m_name = "msg";
+            msg.m_data = message;
+            msg.m_type = GdbMi::Tuple;
+            response.data.m_type = GdbMi::Tuple;
+            response.data.m_children.push_back(msg);
         }
+        command.callback(response);
+        return;
     }
 
     if (what == "debuggee_output") {
@@ -2421,25 +2403,25 @@ void CdbEngine::parseOutputLine(QByteArray line)
         QTC_ASSERT(!isStartToken, return);
         if (isCommandToken) {
             // Did the command finish? Invoke callback and remove from queue.
+            const DebuggerCommand &command = m_commandForToken.take(token);
             if (debug)
-                qDebug("### Completed builtin command for token=%d, %d lines, pending=%d",
-                       m_currentBuiltinResponseToken, m_currentBuiltinResponse.count('\n'),
-                       m_commandForToken.size() - 1);
+                qDebug("### Completed builtin command '%s' for token=%d, %d lines, pending=%d",
+                       command.function.data(), m_currentBuiltinResponseToken,
+                       m_currentBuiltinResponse.count('\n'), m_commandForToken.size() - 1);
             QTC_ASSERT(token == m_currentBuiltinResponseToken, return);
             if (boolSetting(VerboseLog))
                 showMessage(QLatin1String(m_currentBuiltinResponse), LogMisc);
-            const CdbCommandPtr &currentCommand = m_commandForToken.take(token);
-            QTC_ASSERT(!currentCommand.isNull(), return);
+            m_currentBuiltinResponseToken = -1;
+            m_currentBuiltinResponse.clear();
+            if (!command.callback)
+                return;
             DebuggerResponse response;
             response.token = token;
             response.data.m_name = "data";
             response.data.m_data = m_currentBuiltinResponse;
             response.data.m_type = GdbMi::Tuple;
             response.resultClass = ResultDone;
-            if (currentCommand->handler)
-                currentCommand->handler(response);
-            m_currentBuiltinResponseToken = -1;
-            m_currentBuiltinResponse.clear();
+            command.callback(response);
         } else {
             // Record output of current command
             if (!m_currentBuiltinResponse.isEmpty())
@@ -2688,16 +2670,18 @@ void CdbEngine::attemptBreakpointSynchronization()
                     lineCorrection.reset(new BreakpointCorrectionContext(Internal::cppCodeModelSnapshot(),
                                                                          CppTools::CppModelManager::instance()->workingCopy()));
                 response.lineNumber = lineCorrection->fixLineNumber(parameters.fileName, parameters.lineNumber);
-                postBuiltinCommand(
+                postCommand(DebuggerCommand(
                             cdbAddBreakpointCommand(response, m_sourcePathMappings, id, false),
-                            [this, id](const DebuggerResponse &r) { handleBreakInsert(r, id); });
+                            BuiltinCommand,
+                            [this, id](const DebuggerResponse &r) { handleBreakInsert(r, id); }));
             } else {
-                postBuiltinCommand(
+                postCommand(DebuggerCommand(
                             cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false),
-                            [this, id](const DebuggerResponse &r) { handleBreakInsert(r, id); });
+                            BuiltinCommand,
+                            [this, id](const DebuggerResponse &r) { handleBreakInsert(r, id); }));
             }
             if (!parameters.enabled)
-                postCommand("bd " + QByteArray::number(breakPointIdToCdbId(id)));
+                postCommand(DebuggerCommand("bd " + QByteArray::number(breakPointIdToCdbId(id))));
             bp.notifyBreakpointInsertProceeding();
             bp.notifyBreakpointInsertOk();
             m_pendingBreakpointMap.insert(id, response);
@@ -2716,24 +2700,25 @@ void CdbEngine::attemptBreakpointSynchronization()
                     qPrintable(parameters.toString()));
             if (parameters.enabled != bp.response().enabled) {
                 // Change enabled/disabled breakpoints without triggering update.
-                postCommand((parameters.enabled ? "be " : "bd ")
-                    + QByteArray::number(breakPointIdToCdbId(id)));
+                postCommand(DebuggerCommand((parameters.enabled ? "be " : "bd ")
+                    + QByteArray::number(breakPointIdToCdbId(id))));
                 response.pending = false;
                 response.enabled = parameters.enabled;
                 bp.setResponse(response);
             } else {
                 // Delete and re-add, triggering update
                 addedChanged = true;
-                postCommand(cdbClearBreakpointCommand(id));
-                postBuiltinCommand(
-                            cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false),
-                            [this, id](const DebuggerResponse &r) { handleBreakInsert(r, id); });
+                postCommand(DebuggerCommand(cdbClearBreakpointCommand(id)));
+                postCommand(DebuggerCommand(
+                                cdbAddBreakpointCommand(parameters, m_sourcePathMappings, id, false),
+                                BuiltinCommand,
+                                [this, id](const DebuggerResponse &r) { handleBreakInsert(r, id); }));
                 m_pendingBreakpointMap.insert(id, response);
             }
             bp.notifyBreakpointChangeOk();
             break;
         case BreakpointRemoveRequested:
-            postCommand(cdbClearBreakpointCommand(id));
+            postCommand(DebuggerCommand(cdbClearBreakpointCommand(id)));
             bp.notifyBreakpointRemoveProceeding();
             bp.notifyBreakpointRemoveOk();
             m_pendingBreakpointMap.remove(id);
@@ -2745,7 +2730,7 @@ void CdbEngine::attemptBreakpointSynchronization()
     foreach (BreakpointModelId id, m_insertSubBreakpointMap.keys()) {
         addedChanged = true;
         const BreakpointResponse &response = m_insertSubBreakpointMap.value(id);
-        postCommand(cdbAddBreakpointCommand(response, m_sourcePathMappings, id, false));
+        postCommand(DebuggerCommand(cdbAddBreakpointCommand(response, m_sourcePathMappings, id, false)));
         m_insertSubBreakpointMap.remove(id);
         m_pendingSubBreakpointMap.insert(id, response);
     }
@@ -2872,7 +2857,7 @@ unsigned CdbEngine::parseStackTrace(const GdbMi &data, bool sourceStepInto)
 
 void CdbEngine::loadAdditionalQmlStack()
 {
-    postExtensionCommand("qmlstack", QByteArray(), CB(handleAdditionalQmlStack));
+    postCommand(DebuggerCommand("qmlstack", ExtensionCommand, CB(handleAdditionalQmlStack)));
 }
 
 void CdbEngine::handleAdditionalQmlStack(const DebuggerResponse &response)
@@ -2917,8 +2902,8 @@ void CdbEngine::handleStackTrace(const DebuggerResponse &response)
     GdbMi stack = response.data;
     if (response.resultClass == ResultDone) {
         if (parseStackTrace(stack, false) == ParseStackWow64) {
-            postBuiltinCommand("lm m wow64",
-                               [this, stack](const DebuggerResponse &r) { handleCheckWow64(r, stack); });
+            postCommand(DebuggerCommand("lm m wow64", BuiltinCommand,
+                               [this, stack](const DebuggerResponse &r) { handleCheckWow64(r, stack); }));
         }
     } else {
         showMessage(stack["msg"].toLatin1(), LogError);
@@ -3095,14 +3080,14 @@ void CdbEngine::postWidgetAtCommand()
     QByteArray arguments = QByteArray::number(m_watchPointX);
     arguments.append(' ');
     arguments.append(QByteArray::number(m_watchPointY));
-    postExtensionCommand("widgetat", arguments, CB(handleWidgetAt));
+    postCommand(DebuggerCommand("widgetat", arguments.constData(), ExtensionCommand, CB(handleWidgetAt)));
 }
 
 void CdbEngine::handleCustomSpecialStop(const QVariant &v)
 {
     if (v.canConvert<MemoryChangeCookie>()) {
         const MemoryChangeCookie changeData = qvariant_cast<MemoryChangeCookie>(v);
-        postCommand(cdbWriteMemoryCommand(changeData.address, changeData.data));
+        postCommand(DebuggerCommand(cdbWriteMemoryCommand(changeData.address, changeData.data)));
         return;
     }
     if (v.canConvert<MemoryViewCookie>()) {
diff --git a/src/plugins/debugger/cdb/cdbengine.h b/src/plugins/debugger/cdb/cdbengine.h
index 7b730f7c897132692df193af856bcec9447e224a..abad2366dac976e421188ec0ac6a77f6c52e8e6c 100644
--- a/src/plugins/debugger/cdb/cdbengine.h
+++ b/src/plugins/debugger/cdb/cdbengine.h
@@ -127,14 +127,7 @@ private slots:
     void readyReadStandardError();
     void processError();
     void processFinished();
-    void postCommand(const QByteArray &cmd);
-    void postBuiltinCommand(const QByteArray &cmd,
-                            CommandHandler handler);
-
-    void postExtensionCommand(const QByteArray &cmd,
-                              const QByteArray &arguments,
-                              CommandHandler handler);
-
+    void postCommand(const DebuggerCommand &cmd);
     void operateByInstructionTriggered(bool);
     void verboseLogTriggered(bool);
 
@@ -170,7 +163,11 @@ private:
         ParseStackStepOut = 2, // Need to step out, hit on a frame without debug information
         ParseStackWow64 = 3 // Hit on a frame with 32bit emulation, switch debugger to 32 bit mode
     };
-
+    enum CommandFlags {
+        NoCallBack = 0,
+        BuiltinCommand,
+        ExtensionCommand,
+    };
 
     bool startConsole(const DebuggerRunParameters &sp, QString *errorMessage);
     void init();
@@ -245,7 +242,7 @@ private:
     SpecialStopMode m_specialStopMode;
     ProjectExplorer::DeviceProcessSignalOperation::Ptr m_signalOperation;
     int m_nextCommandToken;
-    QHash<int, CdbCommandPtr> m_commandForToken;
+    QHash<int, DebuggerCommand> m_commandForToken;
     QByteArray m_currentBuiltinResponse;
     int m_currentBuiltinResponseToken;
     QMap<QString, NormalizedSourceFileName> m_normalizedFileCache;
diff --git a/src/plugins/debugger/debuggerprotocol.h b/src/plugins/debugger/debuggerprotocol.h
index 4b2ae830acd78b5953181841a6bf58d90aadfa57..8a57e19f573a5e1bed2c41c9c1709ff8b7609a58 100644
--- a/src/plugins/debugger/debuggerprotocol.h
+++ b/src/plugins/debugger/debuggerprotocol.h
@@ -53,6 +53,9 @@ public:
     DebuggerCommand(const char *f, int flags = 0, Callback cb = Callback())
         : function(f), callback(cb), flags(flags)
     {}
+    DebuggerCommand(const char *f, const char *a, int flags = 0, Callback cb = Callback())
+        : function(f), args(a), callback(cb), flags(flags)
+    {}
     DebuggerCommand(const char *f, Callback cb)
         : function(f), callback(cb), flags(0)
     {}