diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp
index 7b93d25872a98d003a0a721f97acb3e19b7f052e..587c786b0d07fedff292b308e2913ebc710366fb 100644
--- a/src/plugins/debugger/cdb/cdbengine.cpp
+++ b/src/plugins/debugger/cdb/cdbengine.cpp
@@ -823,6 +823,7 @@ void CdbEngine::setupInferior()
         qDebug("setupInferior");
     attemptBreakpointSynchronization();
     postCommand("sxn 0x4000001f", 0); // Do not break on WowX86 exceptions.
+    postCommand(".asm source_line", 0); // Source line in assembly
     postExtensionCommand("pid", QByteArray(), 0, &CdbEngine::handlePid);
 }
 
@@ -1507,10 +1508,7 @@ void CdbEngine::handleDisassembler(const CdbBuiltinCommandPtr &command)
 {
     QTC_ASSERT(qVariantCanConvert<DisassemblerAgent*>(command->cookie), return;)
     DisassemblerAgent *agent = qvariant_cast<DisassemblerAgent*>(command->cookie);
-    DisassemblerLines disassemblerLines;
-    foreach(const QByteArray &line, command->reply)
-        disassemblerLines.appendUnparsed(QString::fromLatin1(line));
-    agent->setContents(disassemblerLines);
+    agent->setContents(parseCdbDisassembler(command->reply));
 }
 
 void CdbEngine::fetchMemory(MemoryAgent *agent, QObject *editor, quint64 addr, quint64 length)
diff --git a/src/plugins/debugger/cdb/cdbparsehelpers.cpp b/src/plugins/debugger/cdb/cdbparsehelpers.cpp
index 446e50abf076d7d5a70ea59a191cfafed15a0e75..1695727178121de77751c215f60b8cff768bcb53 100644
--- a/src/plugins/debugger/cdb/cdbparsehelpers.cpp
+++ b/src/plugins/debugger/cdb/cdbparsehelpers.cpp
@@ -37,19 +37,22 @@
 #include "registerhandler.h"
 #include "bytearrayinputstream.h"
 #include "gdb/gdbmi.h"
+#include "disassemblerlines.h"
 #ifdef Q_OS_WIN
 #    include "shared/dbgwinutils.h"
 #endif
+#include <utils/qtcassert.h>
+
 #include <QtCore/QByteArray>
 #include <QtCore/QVariant>
 #include <QtCore/QString>
 #include <QtCore/QDir>
 #include <QtCore/QDebug>
 
-#include <utils/qtcassert.h>
-
 #include <cctype>
 
+enum { debugDisAsm = 0 };
+
 namespace Debugger {
 namespace Internal {
 
@@ -435,5 +438,152 @@ QDebug operator<<(QDebug s, const WinException &e)
     return s;
 }
 
+/*!
+    \fn DisassemblerLines parseCdbDisassembler(const QList<QByteArray> &a)
+
+    \brief Parse CDB disassembler output into DisassemblerLines (with helpers)
+
+    Expected options (prepend source file line):
+    \code
+    .asm source_line
+    .lines
+    \endcode
+
+    should cause the 'u' command to produce:
+
+    \code
+gitgui!Foo::MainWindow::on_actionPtrs_triggered+0x1f9 [c:\qt\projects\gitgui\app\mainwindow.cpp @ 758]:
+  225 00000001`3fcebfe9 488b842410050000 mov     rax,qword ptr [rsp+510h]
+  225 00000001`3fcebff1 8b4030          mov     eax,dword ptr [rax+30h]
+  226 00000001`3fcebff4 ffc0            inc     eax
+      00000001`3fcebff6 488b8c2410050000 mov     rcx,qword ptr [rsp+510h]
+...
+QtCored4!QTextStreamPrivate::putString+0x34:
+   10 00000000`6e5e7f64 90              nop
+...
+\endcode
+
+    The algorithm checks for a function line and grabs the function name, offset and (optional)
+    source file from it.
+    Instruction lines are checked for address and source line number.
+    When the source line changes, the source instruction is inserted.
+*/
+
+// Parse a function header line: Match: 'nsp::foo::+0x<offset> [<file> @ <line>]:'
+// or 'nsp::foo::+0x<offset>:'
+// Do not use regexp here as it is hard for functions like operator+, operator[].
+bool parseCdbDisassemblerFunctionLine(const QString &l,
+                                      QString *currentFunction, quint64 *functionOffset,
+                                      QString *sourceFile)
+{
+    if (l.isEmpty() || !l.endsWith(QLatin1Char(':')) || l.at(0).isDigit() || l.at(0).isSpace())
+        return false;
+    const int offsetPos = l.indexOf(QLatin1String("+0x"));
+    if (offsetPos == -1)
+        return false;
+    sourceFile->clear();
+    *currentFunction = l.left(offsetPos);
+    if (!l.endsWith(QLatin1String("]:"))) { // No source file.
+        *functionOffset = l.mid(offsetPos + 3, l.size() - offsetPos - 4).trimmed().toULongLong(0, 16);
+        if (debugDisAsm)
+            qDebug() << "Function:" << l << currentFunction << functionOffset;
+        return true;
+    }
+    // Parse file and line.
+    const int filePos   = l.indexOf(QLatin1Char('['), offsetPos + 1);
+    const int linePos   = filePos   != -1 ? l.indexOf(QLatin1String(" @ "), filePos + 1)   : -1;
+    if (linePos == -1)
+        return false;
+    *functionOffset = l.mid(offsetPos + 3, filePos - offsetPos - 4).trimmed().toULongLong(0, 16);
+    *sourceFile = l.mid(filePos + 1, linePos - filePos - 1).trimmed();
+    if (debugDisAsm)
+        qDebug() << "Function with source: " << l << currentFunction
+                 << functionOffset << sourceFile;
+    return true;
+}
+
+// Parse an instruction line:
+// '   21 00000001`3fcebff1 8b4030          mov     eax,dword ptr [rax+30h]'
+// '<source_line> <address> <raw data> <instruction>
+bool parseCdbDisassemblerLine(const QString &lineIn, DisassemblerLine *dLine, uint *sourceLine)
+{
+    *sourceLine = 0;
+    if (lineIn.size() < 6)
+        return false;
+    // Check source line: right-padded 5 digits
+    const bool hasSourceLine = lineIn.at(4).isDigit();
+    const QString line = lineIn.trimmed();
+    int addressPos = 0;
+    const QChar blank = QLatin1Char(' ');
+    // Optional source line.
+    if (hasSourceLine) {
+        const int sourceLineEnd = line.indexOf(blank);
+        if (sourceLineEnd == -1)
+            return false;
+        *sourceLine = line.left(sourceLineEnd).toUInt();
+        addressPos = sourceLineEnd + 1;
+    }
+    // Find positions of address/raw data/instruction
+    const int addressEnd = line.indexOf(blank, addressPos + 1);
+    if (addressEnd < 0)
+        return false;
+    const int rawDataPos = addressEnd + 1;
+    const int rawDataEnd = line.indexOf(blank, rawDataPos + 1);
+    if (rawDataEnd < 0)
+        return false;
+    const int instructionPos = rawDataEnd + 1;
+    bool ok;
+    QString addressS = line.mid(addressPos, addressEnd - addressPos);
+    if (addressS.size() > 9 && addressS.at(8) == QLatin1Char('`'))
+        addressS.remove(8, 1);
+    dLine->address = addressS.toULongLong(&ok, 16);
+    if (!ok)
+        return false;
+    dLine->rawData = QByteArray::fromHex(line.mid(rawDataPos, rawDataEnd - rawDataPos).toAscii());
+    dLine->data = line.right(line.size() - instructionPos).trimmed();
+    return true;
+}
+
+DisassemblerLines parseCdbDisassembler(const QList<QByteArray> &a)
+{
+    DisassemblerLines result;
+    quint64 functionAddress = 0;
+    uint lastSourceLine = 0;
+    QString currentFunction;
+    quint64 functionOffset = 0;
+    QString sourceFile;
+
+    foreach (const QByteArray &lineBA, a) {
+        const QString line = QString::fromLatin1(lineBA);
+        // New function?
+        if (parseCdbDisassemblerFunctionLine(line, &currentFunction, &functionOffset, &sourceFile)) {
+            functionAddress = 0;
+        } else {
+            DisassemblerLine disassemblyLine;
+            uint sourceLine;
+            if (parseCdbDisassemblerLine(line, &disassemblyLine, &sourceLine)) {
+                // New source line: Add source code if available.
+                if (sourceLine && sourceLine != lastSourceLine) {
+                    lastSourceLine = sourceLine;
+                    result.appendSourceLine(sourceFile, sourceLine);
+                }
+            } else {
+                qWarning("Unable to parse assembly line '%s'", lineBA.constData());
+                disassemblyLine.fromString(line);
+            }
+            // Determine address of function from the first assembler line after a
+            // function header line.
+            if (!functionAddress && disassemblyLine.address && functionOffset) {
+                functionAddress = disassemblyLine.address - functionOffset;
+            }
+            if (functionAddress && disassemblyLine.address)
+                disassemblyLine.offset = disassemblyLine.address - functionAddress;
+            disassemblyLine.function = currentFunction;
+            result.appendLine(disassemblyLine);
+        }
+    }
+    return result;
+}
+
 } // namespace Internal
 } // namespace Debugger
diff --git a/src/plugins/debugger/cdb/cdbparsehelpers.h b/src/plugins/debugger/cdb/cdbparsehelpers.h
index 65f9362706748272ffce88bfa8b895dca3e8c1ad..6f761c5e2ab52904625205cee6b88c968fa012ab 100644
--- a/src/plugins/debugger/cdb/cdbparsehelpers.h
+++ b/src/plugins/debugger/cdb/cdbparsehelpers.h
@@ -54,6 +54,7 @@ class BreakpointParameters;
 struct ThreadData;
 class Register;
 class GdbMi;
+class DisassemblerLines;
 
 // Perform mapping on parts of the source tree as reported by/passed to debugger
 // in case the user has specified such mappings in the global settings.
@@ -81,6 +82,8 @@ QByteArray cdbWriteMemoryCommand(quint64 addr, const QByteArray &data);
 QString debugByteArray(const QByteArray &a);
 QString StringFromBase64EncodedUtf16(const QByteArray &a);
 
+DisassemblerLines parseCdbDisassembler(const QList<QByteArray> &a);
+
 // Model EXCEPTION_RECORD + firstchance
 struct WinException
 {
diff --git a/src/plugins/debugger/disassemblerlines.cpp b/src/plugins/debugger/disassemblerlines.cpp
index c9cba4f896bc801299f4539ab1e26b3555441063..0f8523a89267899cbb4f18d2080acdb3e6bc60a1 100644
--- a/src/plugins/debugger/disassemblerlines.cpp
+++ b/src/plugins/debugger/disassemblerlines.cpp
@@ -36,6 +36,8 @@
 
 #include <QtCore/QDebug>
 #include <QtCore/QRegExp>
+#include <QtCore/QFile>
+#include <QtCore/QTextStream>
 
 namespace Debugger {
 namespace Internal {
@@ -95,6 +97,39 @@ void DisassemblerLines::appendLine(const DisassemblerLine &dl)
     m_rowCache[dl.address] = m_data.size();
 }
 
+// Append source line: Cache current file
+struct SourceFileCache
+{
+    QString fileName;
+    QStringList lines;
+};
+
+Q_GLOBAL_STATIC(SourceFileCache, sourceFileCache)
+
+void DisassemblerLines::appendSourceLine(const QString &fileName, uint lineNumber)
+{
+
+    if (fileName.isEmpty() || lineNumber == 0)
+        return;
+    lineNumber--; // fix 1..n range.
+    SourceFileCache *cache = sourceFileCache();
+    if (fileName != cache->fileName) {
+        cache->fileName = fileName;
+        cache->lines.clear();
+        QFile file(fileName);
+        if (file.open(QIODevice::ReadOnly)) {
+            QTextStream ts(&file);
+            cache->lines = ts.readAll().split(QLatin1Char('\n'));
+        } // open
+    }     // different file
+    if (lineNumber >= uint(cache->lines.size()))
+        return;
+    DisassemblerLine dl;
+    dl.lineNumber = lineNumber;
+    dl.data = cache->lines.at(lineNumber);
+    appendLine(dl);
+}
+
 void DisassemblerLines::appendUnparsed(const QString &unparsed)
 {
     QString line = unparsed.trimmed();
diff --git a/src/plugins/debugger/disassemblerlines.h b/src/plugins/debugger/disassemblerlines.h
index 9d17f4b943c16fb7b48dfedf318c8199d72d9c55..e175b9c5ef5a75e1c633cda1df0d8319912fdbbd 100644
--- a/src/plugins/debugger/disassemblerlines.h
+++ b/src/plugins/debugger/disassemblerlines.h
@@ -34,7 +34,7 @@
 #ifndef DEBUGGER_DISASSEMBLERLINES_H
 #define DEBUGGER_DISASSEMBLERLINES_H
 
-#include <QtCore/QString>
+#include <QtCore/QStringList>
 #include <QtCore/QHash>
 #include <QtCore/QVector>
 
@@ -65,6 +65,7 @@ public:
     QString function; // (ass) Function to which current instruction belongs.
     uint offset;      // (ass) Offset of instruction in relation to current function.
     uint lineNumber;  // (src) Line number in source.
+    QByteArray rawData;  // (ass) Raw bytes of the instruction
     QString data;     // (ass) Instruction text, (src) source text, (cmt) arbitrary.
 };
 
@@ -76,6 +77,9 @@ public:
     bool coversAddress(quint64 address) const;
     void appendUnparsed(const QString &line);
     void appendLine(const DisassemblerLine &dl);
+    // Mixed source/assembly: Retrieve contents of source (cached)
+    void appendSourceLine(const QString &fileName, uint line);
+
     int size() const { return m_data.size(); }
     const DisassemblerLine &at(int i) const { return m_data.at(i); }
     int lineForAddress(quint64 address) const;
diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp
index 3e52db0fbced16439e99b253a17bc03e99cde7ea..52098edfe9b300fb0542afb4c2a387f152bc23b8 100644
--- a/src/plugins/debugger/gdb/gdbengine.cpp
+++ b/src/plugins/debugger/gdb/gdbengine.cpp
@@ -4137,30 +4137,14 @@ DisassemblerLines GdbEngine::parseMiDisassembler(const GdbMi &lines)
     // {address="0x0805acf8",func-name="...",offset="25",inst="and $0xe8,%al"},
     // {address="0x0805acfa",func-name="...",offset="27",inst="pop %esp"},
 
-    QStringList fileContents;
-    bool fileLoaded = false;
     DisassemblerLines result;
 
     // FIXME: Performance?
     foreach (const GdbMi &child, lines.children()) {
         if (child.hasName("src_and_asm_line")) {
-            // Mixed mode.
-            if (!fileLoaded) {
-                QString fileName = QFile::decodeName(child.findChild("file").data());
-                fileName = cleanupFullName(fileName);
-                QFile file(fileName);
-                file.open(QIODevice::ReadOnly);
-                QTextStream ts(&file);
-                fileContents = ts.readAll().split(QLatin1Char('\n'));
-                fileLoaded = true;
-            }
-            int line = child.findChild("line").data().toInt();
-            if (line >= 1 && line <= fileContents.size()) {
-                DisassemblerLine dl;
-                dl.lineNumber = line;
-                dl.data = fileContents.at(line - 1);
-                result.appendLine(dl);
-            }
+            const QString fileName = QFile::decodeName(child.findChild("file").data());
+            const uint line = child.findChild("line").data().toUInt();
+            result.appendSourceLine(fileName, line);
             GdbMi insn = child.findChild("line_asm_insn");
             foreach (const GdbMi &item, insn.children())
                 result.appendLine(parseLine(item));