1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
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 The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 
26 #include "disassemblerlines.h"
27 #include "sourceutils.h"
28 
29 #include <QDebug>
30 #include <QFile>
31 #include <QTextStream>
32 
33 namespace Debugger {
34 namespace Internal {
35 
fromString(const QString & unparsed)36 void DisassemblerLine::fromString(const QString &unparsed)
37 {
38     int pos = -1;
39     for (int i = 0; i != unparsed.size(); ++i) {
40         const uint c = unparsed.at(i).unicode();
41         if (c == ' ' || c == ':' || c == '\t') {
42             pos = i;
43             break;
44         }
45     }
46 
47     // Mac gdb has an overflow reporting 64bit addresses causing the instruction
48     // to follow the last digit "0x000000013fff4810mov 1,1". Truncate here.
49     if (pos > 19 && unparsed.mid(3, 16).toULongLong())
50         pos = 19;
51 
52     QString addr = unparsed.left(pos);
53     // MSVC 64bit: Remove 64bit separator 00000000`00a45000'.
54     if (addr.size() >= 9 && addr.at(8) == '`')
55         addr.remove(8, 1);
56 
57     if (addr.endsWith(':')) // clang
58         addr.chop(1);
59     if (addr.startsWith("0x"))
60         addr.remove(0, 2);
61     bool ok = false;
62     address = addr.toULongLong(&ok, 16);
63     if (ok)
64         data = unparsed.mid(pos + 1);
65     else
66         data = unparsed;
67 }
68 
startAddress() const69 quint64 DisassemblerLines::startAddress() const
70 {
71     for (int i = 0; i < m_data.size(); ++i)
72         if (m_data.at(i).address)
73             return m_data.at(i).address;
74     return 0;
75 }
76 
endAddress() const77 quint64 DisassemblerLines::endAddress() const
78 {
79     for (int i = m_data.size()- 1; i >= 0; --i)
80         if (m_data.at(i).address)
81             return m_data.at(i).address;
82     return 0;
83 }
84 
lineForAddress(quint64 address) const85 int DisassemblerLines::lineForAddress(quint64 address) const
86 {
87     return m_rowCache.value(address);
88 }
89 
coversAddress(quint64 address) const90 bool DisassemblerLines::coversAddress(quint64 address) const
91 {
92     return m_rowCache.value(address) != 0;
93 }
94 
appendLine(const DisassemblerLine & dl)95 void DisassemblerLines::appendLine(const DisassemblerLine &dl)
96 {
97     m_data.append(dl);
98     m_rowCache[dl.address] = m_data.size();
99 }
100 
101 // Append source line: Cache current file.
102 struct SourceFileCache
103 {
104     QString fileName;
105     QStringList lines;
106 };
107 
Q_GLOBAL_STATIC(SourceFileCache,sourceFileCache)108 Q_GLOBAL_STATIC(SourceFileCache, sourceFileCache)
109 
110 void DisassemblerLines::appendSourceLine(const QString &fileName, int lineNumber)
111 {
112 
113     if (fileName.isEmpty() || lineNumber == 0)
114         return;
115     lineNumber--; // Fix 1..n range.
116     SourceFileCache *cache = sourceFileCache();
117     if (fileName != cache->fileName) {
118         cache->fileName = fileName;
119         cache->lines.clear();
120         QFile file(fileName);
121         if (file.open(QIODevice::ReadOnly)) {
122             QTextStream ts(&file);
123             cache->lines = ts.readAll().split('\n');
124         }
125     }
126     if (lineNumber >= cache->lines.size())
127         return;
128     DisassemblerLine dl;
129     dl.lineNumber = lineNumber;
130     dl.data = cache->lines.at(lineNumber);
131     appendLine(dl);
132 }
133 
appendComment(const QString & line)134 void DisassemblerLines::appendComment(const QString &line)
135 {
136     DisassemblerLine dl;
137     dl.data = line;
138     appendLine(dl);
139 }
140 
appendUnparsed(const QString & unparsed)141 void DisassemblerLines::appendUnparsed(const QString &unparsed)
142 {
143     QString line = unparsed.trimmed();
144     if (line.isEmpty())
145         return;
146     if (line.startsWith("Current language:"))
147         return;
148     if (line.startsWith("Dump of assembler")) {
149         m_lastFunction.clear();
150         return;
151     }
152     if (line.startsWith("The current source"))
153         return;
154     if (line.startsWith("End of assembler")) {
155         m_lastFunction.clear();
156         return;
157     }
158     if (line.startsWith("=> "))
159         line = line.mid(3);
160     if (line.startsWith("0x")) {
161         // Address line. Split at the tab.
162         int tab1 = line.indexOf('\t');
163         if (tab1 == -1) {
164             appendComment(line);
165             return;
166         }
167         int tab2 = line.indexOf('\t', tab1 + 1);
168         if (tab2 == -1)
169             tab2 = tab1;
170         QString address = line.left(tab1);
171         if (address.endsWith(':'))
172             address.chop(1);
173         int pos1 = address.indexOf('<') + 1;
174         DisassemblerLine dl;
175         dl.bytes = line.mid(tab1, tab2 - tab1).trimmed();
176         m_bytesLength = qMax(m_bytesLength, tab2 - tab1);
177         dl.data = line.mid(tab2).trimmed();
178         if (pos1 && address.indexOf("<UNDEFINED> instruction:") == -1) {
179             if (address.endsWith('>'))
180                 address.chop(1);
181             int pos2 = address.indexOf('+', pos1);
182             if (pos1 < pos2) {
183                 QString function = address.mid(pos1, pos2 - pos1);
184                 if (function != m_lastFunction) {
185                     DisassemblerLine dl;
186                     dl.data = "Function: " + function;
187                     m_data.append(dl);
188                     m_lastFunction = function;
189                 }
190             }
191             dl.address = address.left(pos1 - 1).toULongLong(nullptr, 0);
192             dl.function = m_lastFunction;
193             dl.offset = address.mid(pos2).toUInt();
194         } else {
195             // Plain data like "0x0000cd64:\tadd\tlr, pc, lr\n"
196             dl.address = address.toULongLong(nullptr, 0);
197             dl.function = m_lastFunction;
198             dl.offset = 0;
199         }
200         m_rowCache[dl.address] = m_data.size() + 1;
201         m_data.append(dl);
202     } else {
203         // Comment or code line.
204         QTextStream ts(&line);
205         DisassemblerLine dl;
206         ts >> dl.lineNumber;
207         dl.data = line.mid(ts.pos());
208         m_data.append(dl);
209     }
210 }
211 
toString(int maxOp) const212 QString DisassemblerLine::toString(int maxOp) const
213 {
214     const QString someSpace = "        ";
215     QString str;
216     if (isAssembler()) {
217         if (address)
218             str += QString("0x%1  ").arg(address, 0, 16);
219         if (offset)
220             str += QString("<+%1> ").arg(offset, 5);
221         else
222             str += "         ";
223         str += QString("       %1 ").arg(bytes);
224         str += QString(maxOp - bytes.size(), ' ');
225         str += data;
226     } else if (isCode()) {
227         str += someSpace;
228         str += QString::number(lineNumber);
229         if (hunk)
230             str += QString(" [%1]").arg(hunk);
231         else
232             str += "    ";
233         str += data;
234     } else {
235         str += someSpace;
236         str += data;
237     }
238     return str;
239 }
240 
toString() const241 QString DisassemblerLines::toString() const
242 {
243     QString str;
244     for (int i = 0, n = size(); i != n; ++i) {
245         str += m_data.at(i).toString(m_bytesLength);
246         str += '\n';
247     }
248     return str;
249 }
250 
251 } // namespace Internal
252 } // namespace Debugger
253