1 | /****************************************************************************
|
---|
2 | **
|
---|
3 | ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
---|
4 | ** All rights reserved.
|
---|
5 | ** Contact: Nokia Corporation (qt-info@nokia.com)
|
---|
6 | **
|
---|
7 | ** This file is part of the tools applications of the Qt Toolkit.
|
---|
8 | **
|
---|
9 | ** $QT_BEGIN_LICENSE:LGPL$
|
---|
10 | ** Commercial Usage
|
---|
11 | ** Licensees holding valid Qt Commercial licenses may use this file in
|
---|
12 | ** accordance with the Qt Commercial License Agreement provided with the
|
---|
13 | ** Software or, alternatively, in accordance with the terms contained in
|
---|
14 | ** a written agreement between you and Nokia.
|
---|
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, Nokia gives you certain additional
|
---|
25 | ** rights. These rights are described in the Nokia Qt LGPL Exception
|
---|
26 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
---|
27 | **
|
---|
28 | ** GNU General Public License Usage
|
---|
29 | ** Alternatively, this file may be used under the terms of the GNU
|
---|
30 | ** General Public License version 3.0 as published by the Free Software
|
---|
31 | ** Foundation and appearing in the file LICENSE.GPL included in the
|
---|
32 | ** packaging of this file. Please review the following information to
|
---|
33 | ** ensure the GNU General Public License version 3.0 requirements will be
|
---|
34 | ** met: http://www.gnu.org/copyleft/gpl.html.
|
---|
35 | **
|
---|
36 | ** If you have questions regarding the use of this file, please contact
|
---|
37 | ** Nokia at qt-info@nokia.com.
|
---|
38 | ** $QT_END_LICENSE$
|
---|
39 | **
|
---|
40 | ****************************************************************************/
|
---|
41 |
|
---|
42 | #include <QDebug>
|
---|
43 | #include <QCoreApplication>
|
---|
44 | #include <QObject>
|
---|
45 | #include <QFile>
|
---|
46 | #include <QDir>
|
---|
47 | #include "trksignalhandler.h"
|
---|
48 | #include "trkutils.h"
|
---|
49 |
|
---|
50 | class CrashState
|
---|
51 | {
|
---|
52 | public:
|
---|
53 | uint pid;
|
---|
54 | uint tid;
|
---|
55 | QString crashReason;
|
---|
56 | uint crashPC;
|
---|
57 | };
|
---|
58 |
|
---|
59 | class TrkSignalHandlerPrivate
|
---|
60 | {
|
---|
61 | friend class TrkSignalHandler;
|
---|
62 | public:
|
---|
63 | TrkSignalHandlerPrivate();
|
---|
64 | ~TrkSignalHandlerPrivate();
|
---|
65 | private:
|
---|
66 | QTextStream out;
|
---|
67 | QTextStream err;
|
---|
68 | int loglevel;
|
---|
69 | int lastpercent;
|
---|
70 | QList<trk::Library> libraries;
|
---|
71 | QFile crashlogtextfile;
|
---|
72 | QFile crashstackfile;
|
---|
73 | QList<CrashState> queuedCrashes;
|
---|
74 | QList<int> dyingThreads;
|
---|
75 | QString crashlogPath;
|
---|
76 | bool crashlog;
|
---|
77 | bool terminateNeeded;
|
---|
78 | };
|
---|
79 |
|
---|
80 | void TrkSignalHandler::copyingStarted()
|
---|
81 | {
|
---|
82 | if (d->loglevel > 0)
|
---|
83 | d->out << "Copying..." << endl;
|
---|
84 | }
|
---|
85 |
|
---|
86 | void TrkSignalHandler::canNotConnect(const QString &errorMessage)
|
---|
87 | {
|
---|
88 | d->err << "Cannot connect - " << errorMessage << endl;
|
---|
89 | }
|
---|
90 |
|
---|
91 | void TrkSignalHandler::canNotCreateFile(const QString &filename, const QString &errorMessage)
|
---|
92 | {
|
---|
93 | d->err << "Cannot create file (" << filename << ") - " << errorMessage << endl;
|
---|
94 | }
|
---|
95 |
|
---|
96 | void TrkSignalHandler::canNotWriteFile(const QString &filename, const QString &errorMessage)
|
---|
97 | {
|
---|
98 | d->err << "Cannot write file (" << filename << ") - " << errorMessage << endl;
|
---|
99 | }
|
---|
100 |
|
---|
101 | void TrkSignalHandler::canNotCloseFile(const QString &filename, const QString &errorMessage)
|
---|
102 | {
|
---|
103 | d->err << "Cannot close file (" << filename << ") - " << errorMessage << endl;
|
---|
104 | }
|
---|
105 |
|
---|
106 | void TrkSignalHandler::installingStarted()
|
---|
107 | {
|
---|
108 | if (d->loglevel > 0)
|
---|
109 | d->out << "Installing..." << endl;
|
---|
110 | }
|
---|
111 |
|
---|
112 | void TrkSignalHandler::canNotInstall(const QString &packageFilename, const QString &errorMessage)
|
---|
113 | {
|
---|
114 | d->err << "Cannot install file (" << packageFilename << ") - " << errorMessage << endl;
|
---|
115 | }
|
---|
116 |
|
---|
117 | void TrkSignalHandler::installingFinished()
|
---|
118 | {
|
---|
119 | if (d->loglevel > 0)
|
---|
120 | d->out << "Installing finished" << endl;
|
---|
121 | }
|
---|
122 |
|
---|
123 | void TrkSignalHandler::startingApplication()
|
---|
124 | {
|
---|
125 | if (d->loglevel > 0)
|
---|
126 | d->out << "Starting app..." << endl;
|
---|
127 | }
|
---|
128 |
|
---|
129 | void TrkSignalHandler::applicationRunning(uint pid)
|
---|
130 | {
|
---|
131 | Q_UNUSED(pid)
|
---|
132 | if (d->loglevel > 0)
|
---|
133 | d->out << "Running..." << endl;
|
---|
134 | }
|
---|
135 |
|
---|
136 | void TrkSignalHandler::canNotRun(const QString &errorMessage)
|
---|
137 | {
|
---|
138 | d->err << "Cannot run - " << errorMessage << endl;
|
---|
139 | }
|
---|
140 |
|
---|
141 | void TrkSignalHandler::finished()
|
---|
142 | {
|
---|
143 | if (d->loglevel > 0)
|
---|
144 | d->out << "Done." << endl;
|
---|
145 | QCoreApplication::quit();
|
---|
146 | }
|
---|
147 |
|
---|
148 | void TrkSignalHandler::applicationOutputReceived(const QString &output)
|
---|
149 | {
|
---|
150 | d->out << output << flush;
|
---|
151 | }
|
---|
152 |
|
---|
153 | void TrkSignalHandler::copyProgress(int percent)
|
---|
154 | {
|
---|
155 | if (d->loglevel > 0) {
|
---|
156 | if (d->lastpercent == 0)
|
---|
157 | d->out << "[ ]\r[" << flush;
|
---|
158 | while (percent > d->lastpercent) {
|
---|
159 | d->out << QLatin1Char('#');
|
---|
160 | d->lastpercent+=2; //because typical console is 80 chars wide
|
---|
161 | }
|
---|
162 | d->out.flush();
|
---|
163 | if (percent==100)
|
---|
164 | d->out << endl;
|
---|
165 | }
|
---|
166 | }
|
---|
167 |
|
---|
168 | void TrkSignalHandler::stateChanged(int state)
|
---|
169 | {
|
---|
170 | if (d->loglevel > 1)
|
---|
171 | d->out << "State" << state << endl;
|
---|
172 | }
|
---|
173 |
|
---|
174 | void TrkSignalHandler::setLogLevel(int level)
|
---|
175 | {
|
---|
176 | d->loglevel = level;
|
---|
177 | }
|
---|
178 |
|
---|
179 | void TrkSignalHandler::setCrashLogging(bool enabled)
|
---|
180 | {
|
---|
181 | d->crashlog = enabled;
|
---|
182 | }
|
---|
183 |
|
---|
184 | void TrkSignalHandler::setCrashLogPath(QString path)
|
---|
185 | {
|
---|
186 | d->crashlogPath = path;
|
---|
187 | }
|
---|
188 |
|
---|
189 | bool lessThanCodeBase(const trk::Library& cs1, const trk::Library& cs2)
|
---|
190 | {
|
---|
191 | return cs1.codeseg < cs2.codeseg;
|
---|
192 | }
|
---|
193 |
|
---|
194 | void TrkSignalHandler::stopped(uint pc, uint pid, uint tid, const QString& reason)
|
---|
195 | {
|
---|
196 | d->err << "STOPPED: pc=" << hex << pc << " pid=" << pid
|
---|
197 | << " tid=" << tid << dec << " - " << reason << endl;
|
---|
198 |
|
---|
199 | if (d->crashlog) {
|
---|
200 | CrashState cs;
|
---|
201 | cs.pid = pid;
|
---|
202 | cs.tid = tid;
|
---|
203 | cs.crashPC = pc;
|
---|
204 | cs.crashReason = reason;
|
---|
205 |
|
---|
206 | if (d->dyingThreads.contains(tid)) {
|
---|
207 | if(d->queuedCrashes.isEmpty())
|
---|
208 | emit terminate();
|
---|
209 | else
|
---|
210 | d->terminateNeeded = true;
|
---|
211 | } else {
|
---|
212 | d->queuedCrashes.append(cs);
|
---|
213 | d->dyingThreads.append(tid);
|
---|
214 |
|
---|
215 | if (d->queuedCrashes.count() == 1) {
|
---|
216 | d->err << "Fetching registers and stack..." << endl;
|
---|
217 | emit getRegistersAndCallStack(pid, tid);
|
---|
218 | }
|
---|
219 | }
|
---|
220 | }
|
---|
221 | else
|
---|
222 | emit terminate();
|
---|
223 | }
|
---|
224 |
|
---|
225 | void TrkSignalHandler::registersAndCallStackReadComplete(const QList<uint>& registers, const QByteArray& stack)
|
---|
226 | {
|
---|
227 | CrashState cs = d->queuedCrashes.first();
|
---|
228 | QDir dir(d->crashlogPath);
|
---|
229 | d->crashlogtextfile.setFileName(dir.filePath(QString("d_exc_%1.txt").arg(cs.tid)));
|
---|
230 | d->crashstackfile.setFileName(dir.filePath(QString("d_exc_%1.stk").arg(cs.tid)));
|
---|
231 | d->crashlogtextfile.open(QIODevice::WriteOnly);
|
---|
232 | QTextStream crashlog(&d->crashlogtextfile);
|
---|
233 |
|
---|
234 | crashlog << "-----------------------------------------------------------------------------" << endl;
|
---|
235 | crashlog << "EKA2 USER CRASH LOG" << endl;
|
---|
236 | crashlog << "Thread Name: " << QString("ProcessID-%1::ThreadID-%2").arg(cs.pid).arg(cs.tid) << endl;
|
---|
237 | crashlog << "Thread ID: " << cs.tid << endl;
|
---|
238 | //this is wrong, but TRK doesn't make stack limit available so we lie
|
---|
239 | crashlog << QString("User Stack %1-%2").arg(registers.at(13), 8, 16, QChar('0')).arg(registers.at(13) + stack.size(), 8, 16, QChar('0')) << endl;
|
---|
240 | //this is also wrong, but TRK doesn't give all information for exceptions
|
---|
241 | crashlog << QString("Panic: PC=%1 ").arg(cs.crashPC, 8, 16, QChar('0')) << cs.crashReason << endl;
|
---|
242 | crashlog << endl;
|
---|
243 | crashlog << "USER REGISTERS:" << endl;
|
---|
244 | crashlog << QString("CPSR=%1").arg(registers.at(16), 8, 16, QChar('0')) << endl;
|
---|
245 | for (int i=0;i<16;i+=4) {
|
---|
246 | crashlog << QString("r%1=%2 %3 %4 %5")
|
---|
247 | .arg(i, 2, 10, QChar('0'))
|
---|
248 | .arg(registers.at(i), 8, 16, QChar('0'))
|
---|
249 | .arg(registers.at(i+1), 8, 16, QChar('0'))
|
---|
250 | .arg(registers.at(i+2), 8, 16, QChar('0'))
|
---|
251 | .arg(registers.at(i+3), 8, 16, QChar('0')) << endl;
|
---|
252 | }
|
---|
253 | crashlog << endl;
|
---|
254 |
|
---|
255 | //emit info for post mortem debug
|
---|
256 | qSort(d->libraries.begin(), d->libraries.end(), lessThanCodeBase);
|
---|
257 | d->err << "Code Segments:" << endl;
|
---|
258 | crashlog << "CODE SEGMENTS:" << endl;
|
---|
259 | for(int i=0; i<d->libraries.count(); i++) {
|
---|
260 | const trk::Library& seg = d->libraries.at(i);
|
---|
261 | if(seg.pid != cs.pid)
|
---|
262 | continue;
|
---|
263 | if (d->loglevel > 1) {
|
---|
264 | d->err << QString("Code: %1 Data: %2 Name: ")
|
---|
265 | .arg(seg.codeseg, 8, 16, QChar('0'))
|
---|
266 | .arg(seg.dataseg, 8, 16, QChar('0'))
|
---|
267 | << seg.name << endl;
|
---|
268 | }
|
---|
269 |
|
---|
270 | //produce fake code segment end addresses since we don't get the real ones from TRK
|
---|
271 | uint end;
|
---|
272 | if (i+1 < d->libraries.count())
|
---|
273 | end = d->libraries.at(i+1).codeseg - 1;
|
---|
274 | else
|
---|
275 | end = 0xFFFFFFFF;
|
---|
276 |
|
---|
277 | crashlog << QString("%1-%2 ")
|
---|
278 | .arg(seg.codeseg, 8, 16, QChar('0'))
|
---|
279 | .arg(end, 8, 16, QChar('0'))
|
---|
280 | << seg.name << endl;
|
---|
281 | }
|
---|
282 |
|
---|
283 | d->crashlogtextfile.close();
|
---|
284 |
|
---|
285 | if (d->loglevel > 1) {
|
---|
286 | d->err << "Registers:" << endl;
|
---|
287 | for (int i=0;i<16;i++) {
|
---|
288 | d->err << QString("R%1: %2 ").arg(i, 2, 10, QChar('0')).arg(registers.at(i), 8, 16, QChar('0'));
|
---|
289 | if (i % 4 == 3)
|
---|
290 | d->err << endl;
|
---|
291 | }
|
---|
292 | d->err << QString("CPSR: %1").arg(registers.at(16), 8, 16, QChar('0')) << endl;
|
---|
293 |
|
---|
294 | d->err << "Stack:" << endl;
|
---|
295 | uint sp = registers.at(13);
|
---|
296 | for(int i=0; i<stack.size(); i+=16, sp+=16) {
|
---|
297 | d->err << QString("%1: ").arg(sp, 8, 16, QChar('0'));
|
---|
298 | d->err << trk::stringFromArray(stack.mid(i,16));
|
---|
299 | d->err << endl;
|
---|
300 | }
|
---|
301 | }
|
---|
302 | d->crashstackfile.open(QIODevice::WriteOnly);
|
---|
303 | d->crashstackfile.write(stack);
|
---|
304 | d->crashstackfile.close();
|
---|
305 |
|
---|
306 | if (d->loglevel > 0)
|
---|
307 | d->err << "Crash logs saved to " << d->crashlogtextfile.fileName() << " & " << d->crashstackfile.fileName() << endl;
|
---|
308 |
|
---|
309 | // resume the thread to allow Symbian OS to handle the panic normally.
|
---|
310 | // terminate when a non main thread is suspended reboots the phone (TRK bug)
|
---|
311 | emit resume(cs.pid, cs.tid);
|
---|
312 |
|
---|
313 | //fetch next crashed thread
|
---|
314 | d->queuedCrashes.removeFirst();
|
---|
315 | if (d->queuedCrashes.count()) {
|
---|
316 | cs = d->queuedCrashes.first();
|
---|
317 | d->err << "Fetching registers and stack..." << endl;
|
---|
318 | emit getRegistersAndCallStack(cs.pid, cs.tid);
|
---|
319 | }
|
---|
320 | else if (d->terminateNeeded)
|
---|
321 | emit terminate();
|
---|
322 |
|
---|
323 | }
|
---|
324 |
|
---|
325 | void TrkSignalHandler::libraryLoaded(const trk::Library &lib)
|
---|
326 | {
|
---|
327 | d->libraries << lib;
|
---|
328 | }
|
---|
329 |
|
---|
330 | void TrkSignalHandler::libraryUnloaded(const trk::Library &lib)
|
---|
331 | {
|
---|
332 | for (QList<trk::Library>::iterator i = d->libraries.begin(); i != d->libraries.end(); i++) {
|
---|
333 | if((*i).name == lib.name && (*i).pid == lib.pid)
|
---|
334 | i = d->libraries.erase(i);
|
---|
335 | }
|
---|
336 | }
|
---|
337 |
|
---|
338 | void TrkSignalHandler::timeout()
|
---|
339 | {
|
---|
340 | d->err << "FAILED: stopping test due to timeout" << endl;
|
---|
341 | emit terminate();
|
---|
342 | }
|
---|
343 |
|
---|
344 | TrkSignalHandlerPrivate::TrkSignalHandlerPrivate()
|
---|
345 | : out(stdout),
|
---|
346 | err(stderr),
|
---|
347 | loglevel(0),
|
---|
348 | lastpercent(0),
|
---|
349 | terminateNeeded(false)
|
---|
350 | {
|
---|
351 |
|
---|
352 | }
|
---|
353 |
|
---|
354 | TrkSignalHandlerPrivate::~TrkSignalHandlerPrivate()
|
---|
355 | {
|
---|
356 | out.flush();
|
---|
357 | err.flush();
|
---|
358 | }
|
---|
359 |
|
---|
360 | TrkSignalHandler::TrkSignalHandler()
|
---|
361 | : d(new TrkSignalHandlerPrivate())
|
---|
362 | {
|
---|
363 | }
|
---|
364 |
|
---|
365 | TrkSignalHandler::~TrkSignalHandler()
|
---|
366 | {
|
---|
367 | delete d;
|
---|
368 | }
|
---|