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
** GNU Lesser General Public License Usage
11
** This file may be used under the terms of the GNU Lesser General Public
12
** License version 2.1 as published by the Free Software Foundation and
13
** appearing in the file LICENSE.LGPL included in the packaging of this
14
** file. Please review the following information to ensure the GNU Lesser
15
** General Public License version 2.1 requirements will be met:
16
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17
**
18
** In addition, as a special exception, Nokia gives you certain additional
19
** rights. These rights are described in the Nokia Qt LGPL Exception
20
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21
**
22
** GNU General Public License Usage
23
** Alternatively, this file may be used under the terms of the GNU General
24
** Public License version 3.0 as published by the Free Software Foundation
25
** and appearing in the file LICENSE.GPL included in the packaging of this
26
** file. Please review the following information to ensure the GNU General
27
** Public License version 3.0 requirements will be met:
28
** http://www.gnu.org/copyleft/gpl.html.
29
**
30
** Other Usage
31
** Alternatively, this file may be used in accordance with the terms and
32
** conditions contained in a signed written agreement between you and Nokia.
33
**
34
**
35
**
36
**
37
**
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
}