Debugger: Rework GDB disassembly display
[qt-creator:qt-creator.git] / src / plugins / debugger / disassemblerlines.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and Digia.  For licensing terms and
13 ** conditions see http://qt.digia.com/licensing.  For further information
14 ** use the contact form at http://qt.digia.com/contact-us.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Digia gives you certain additional
25 ** rights.  These rights are described in the Digia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ****************************************************************************/
29
30 #include "disassemblerlines.h"
31 #include "debuggerstringutils.h"
32
33 #include <QDebug>
34 #include <QRegExp>
35 #include <QFile>
36
37 namespace Debugger {
38 namespace Internal {
39
40 void DisassemblerLine::fromString(const QString &unparsed)
41 {
42     int pos = -1;
43     for (int i = 0; i != unparsed.size(); ++i) {
44         const uint c = unparsed.at(i).unicode();
45         if (c == ' ' || c == ':' || c == '\t') {
46             pos = i;
47             break;
48         }
49     }
50
51     // Mac gdb has an overflow reporting 64bit addresses causing the instruction
52     // to follow the last digit "0x000000013fff4810mov 1,1". Truncate here.
53     if (pos > 19 && unparsed.mid(3, 16).toULongLong())
54         pos = 19;
55
56     QString addr = unparsed.left(pos);
57     // MSVC 64bit: Remove 64bit separator 00000000`00a45000'.
58     if (addr.size() >= 9 && addr.at(8) == QLatin1Char('`'))
59         addr.remove(8, 1);
60
61     if (addr.endsWith(QLatin1Char(':'))) // clang
62         addr.chop(1);
63     if (addr.startsWith(QLatin1String("0x")))
64         addr.remove(0, 2);
65     bool ok = false;
66     address = addr.toULongLong(&ok, 16);
67     if (ok)
68         data = unparsed.mid(pos + 1);
69     else
70         data = unparsed;
71 }
72
73 quint64 DisassemblerLine::addressFromDisassemblyLine(const QString &line)
74 {
75     DisassemblerLine l;
76     l.fromString(line);
77     return l.address;
78 }
79
80 quint64 DisassemblerLines::startAddress() const
81 {
82     for (int i = 0; i < m_data.size(); ++i)
83         if (m_data.at(i).address)
84             return m_data.at(i).address;
85     return 0;
86 }
87
88 quint64 DisassemblerLines::endAddress() const
89 {
90     for (int i = m_data.size()- 1; i >= 0; --i)
91         if (m_data.at(i).address)
92             return m_data.at(i).address;
93     return 0;
94 }
95
96 int DisassemblerLines::lineForAddress(quint64 address) const
97 {
98     return m_rowCache.value(address);
99 }
100
101 bool DisassemblerLines::coversAddress(quint64 address) const
102 {
103     return m_rowCache.value(address) != 0;
104 }
105
106 void DisassemblerLines::appendLine(const DisassemblerLine &dl)
107 {
108     m_data.append(dl);
109     m_rowCache[dl.address] = m_data.size();
110 }
111
112 // Append source line: Cache current file.
113 struct SourceFileCache
114 {
115     QString fileName;
116     QStringList lines;
117 };
118
119 Q_GLOBAL_STATIC(SourceFileCache, sourceFileCache)
120
121 void DisassemblerLines::appendSourceLine(const QString &fileName, uint lineNumber)
122 {
123
124     if (fileName.isEmpty() || lineNumber == 0)
125         return;
126     lineNumber--; // Fix 1..n range.
127     SourceFileCache *cache = sourceFileCache();
128     if (fileName != cache->fileName) {
129         cache->fileName = fileName;
130         cache->lines.clear();
131         QFile file(fileName);
132         if (file.open(QIODevice::ReadOnly)) {
133             QTextStream ts(&file);
134             cache->lines = ts.readAll().split(QLatin1Char('\n'));
135         }
136     }
137     if (lineNumber >= uint(cache->lines.size()))
138         return;
139     DisassemblerLine dl;
140     dl.lineNumber = lineNumber;
141     dl.data = cache->lines.at(lineNumber);
142     appendLine(dl);
143 }
144
145 void DisassemblerLines::appendComment(const QString &line)
146 {
147     DisassemblerLine dl;
148     dl.data = line;
149     appendLine(dl);
150 }
151
152 void DisassemblerLines::appendUnparsed(const QString &unparsed)
153 {
154     QString line = unparsed.trimmed();
155     if (line.isEmpty())
156         return;
157     if (line.startsWith(QLatin1String("Current language:")))
158         return;
159     if (line.startsWith(QLatin1String("Dump of assembler"))) {
160         m_lastFunction.clear();
161         return;
162     }
163     if (line.startsWith(QLatin1String("The current source")))
164         return;
165     if (line.startsWith(QLatin1String("End of assembler"))) {
166         m_lastFunction.clear();
167         return;
168     }
169     if (line.startsWith(QLatin1String("=> ")))
170         line = line.mid(3);
171     if (line.startsWith(QLatin1String("0x"))) {
172         // Address line. Split at the tab.
173         int tab = line.indexOf(QLatin1Char('\t'));
174         if (tab == -1) {
175             appendComment(line);
176             return;
177         }
178         QString address = line.left(tab);
179         if (address.endsWith(QLatin1Char(':')))
180             address.chop(1);
181         int pos1 = address.indexOf(QLatin1Char('<')) + 1;
182         DisassemblerLine dl;
183         dl.data = line.mid(tab).trimmed();
184         if (pos1 && address.indexOf(QLatin1String("<UNDEFINED> instruction:")) == -1) {
185             if (address.endsWith(QLatin1Char('>')))
186                 address.chop(1);
187             int pos2 = address.indexOf(QLatin1Char('+'), pos1);
188             if (pos1 < pos2) {
189                 QString function = address.mid(pos1, pos2 - pos1);
190                 if (function != m_lastFunction) {
191                     DisassemblerLine dl;
192                     dl.data = _("Function: ") + function;
193                     m_data.append(dl);
194                     m_lastFunction = function;
195                 }
196             }
197             dl.address = address.left(pos1 - 1).toULongLong(0, 0);
198             dl.function = m_lastFunction;
199             dl.offset = address.mid(pos2).toUInt();
200         } else {
201             // Plain data like "0x0000cd64:\tadd\tlr, pc, lr\n"
202             dl.address = address.toULongLong(0, 0);
203             dl.function = m_lastFunction;
204             dl.offset = 0;
205         }
206         m_rowCache[dl.address] = m_data.size() + 1;
207         m_data.append(dl);
208     } else {
209         // Comment line.
210         DisassemblerLine dl;
211         dl.data = line;
212         m_data.append(dl);
213     }
214 }
215
216 QString DisassemblerLine::toString() const
217 {
218     const QString someSpace = _("        ");
219     QString str;
220     if (isAssembler()) {
221         if (address)
222             str += _("0x%1  ").arg(address, 0, 16);
223         if (offset)
224             str += _("<+0x%2> ").arg(offset, 4, 16, QLatin1Char('0'));
225         else
226             str += _("          ");
227         str += _("        ");
228         str += data;
229     } else if (isCode()) {
230         if (hunk)
231             str += _("[%1]").arg(hunk);
232         str += someSpace;
233         str += data;
234     } else {
235         str += someSpace;
236         str += data;
237     }
238     return str;
239 }
240
241 } // namespace Internal
242 } // namespace Debugger