blob: 062501a1e08c5cab82bcd9120f65e2f7975c4def [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the tools applications of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QDebug>
#include <QCoreApplication>
#include <QObject>
#include <QFile>
#include <QDir>
#include "trksignalhandler.h"
#include "trkutils.h"
class CrashState
{
public:
uint pid;
uint tid;
QString crashReason;
uint crashPC;
};
class TrkSignalHandlerPrivate
{
friend class TrkSignalHandler;
public:
TrkSignalHandlerPrivate();
~TrkSignalHandlerPrivate();
private:
QTextStream out;
QTextStream err;
int loglevel;
int lastpercent;
QList<trk::Library> libraries;
QFile crashlogtextfile;
QFile crashstackfile;
QList<CrashState> queuedCrashes;
QList<int> dyingThreads;
QString crashlogPath;
bool crashlog;
bool terminateNeeded;
};
void TrkSignalHandler::copyingStarted()
{
if (d->loglevel > 0)
d->out << "Copying..." << endl;
}
void TrkSignalHandler::canNotConnect(const QString &errorMessage)
{
d->err << "Cannot connect - " << errorMessage << endl;
}
void TrkSignalHandler::canNotCreateFile(const QString &filename, const QString &errorMessage)
{
d->err << "Cannot create file (" << filename << ") - " << errorMessage << endl;
}
void TrkSignalHandler::canNotWriteFile(const QString &filename, const QString &errorMessage)
{
d->err << "Cannot write file (" << filename << ") - " << errorMessage << endl;
}
void TrkSignalHandler::canNotCloseFile(const QString &filename, const QString &errorMessage)
{
d->err << "Cannot close file (" << filename << ") - " << errorMessage << endl;
}
void TrkSignalHandler::installingStarted()
{
if (d->loglevel > 0)
d->out << "Installing..." << endl;
}
void TrkSignalHandler::canNotInstall(const QString &packageFilename, const QString &errorMessage)
{
d->err << "Cannot install file (" << packageFilename << ") - " << errorMessage << endl;
}
void TrkSignalHandler::installingFinished()
{
if (d->loglevel > 0)
d->out << "Installing finished" << endl;
}
void TrkSignalHandler::startingApplication()
{
if (d->loglevel > 0)
d->out << "Starting app..." << endl;
}
void TrkSignalHandler::applicationRunning(uint pid)
{
Q_UNUSED(pid)
if (d->loglevel > 0)
d->out << "Running..." << endl;
}
void TrkSignalHandler::canNotRun(const QString &errorMessage)
{
d->err << "Cannot run - " << errorMessage << endl;
}
void TrkSignalHandler::finished()
{
if (d->loglevel > 0)
d->out << "Done." << endl;
QCoreApplication::quit();
}
void TrkSignalHandler::applicationOutputReceived(const QString &output)
{
d->out << output << flush;
}
void TrkSignalHandler::copyProgress(int percent)
{
if (d->loglevel > 0) {
if (d->lastpercent == 0)
d->out << "[ ]\r[" << flush;
while (percent > d->lastpercent) {
d->out << QLatin1Char('#');
d->lastpercent+=2; //because typical console is 80 chars wide
}
d->out.flush();
if (percent==100)
d->out << endl;
}
}
void TrkSignalHandler::stateChanged(int state)
{
if (d->loglevel > 1)
d->out << "State" << state << endl;
}
void TrkSignalHandler::setLogLevel(int level)
{
d->loglevel = level;
}
void TrkSignalHandler::setCrashLogging(bool enabled)
{
d->crashlog = enabled;
}
void TrkSignalHandler::setCrashLogPath(QString path)
{
d->crashlogPath = path;
}
bool lessThanCodeBase(const trk::Library& cs1, const trk::Library& cs2)
{
return cs1.codeseg < cs2.codeseg;
}
void TrkSignalHandler::stopped(uint pc, uint pid, uint tid, const QString& reason)
{
d->err << "STOPPED: pc=" << hex << pc << " pid=" << pid
<< " tid=" << tid << dec << " - " << reason << endl;
if (d->crashlog) {
CrashState cs;
cs.pid = pid;
cs.tid = tid;
cs.crashPC = pc;
cs.crashReason = reason;
if (d->dyingThreads.contains(tid)) {
if(d->queuedCrashes.isEmpty())
emit terminate();
else
d->terminateNeeded = true;
} else {
d->queuedCrashes.append(cs);
d->dyingThreads.append(tid);
if (d->queuedCrashes.count() == 1) {
d->err << "Fetching registers and stack..." << endl;
emit getRegistersAndCallStack(pid, tid);
}
}
}
else
emit terminate();
}
void TrkSignalHandler::registersAndCallStackReadComplete(const QList<uint>& registers, const QByteArray& stack)
{
CrashState cs = d->queuedCrashes.first();
QDir dir(d->crashlogPath);
d->crashlogtextfile.setFileName(dir.filePath(QString("d_exc_%1.txt").arg(cs.tid)));
d->crashstackfile.setFileName(dir.filePath(QString("d_exc_%1.stk").arg(cs.tid)));
d->crashlogtextfile.open(QIODevice::WriteOnly);
QTextStream crashlog(&d->crashlogtextfile);
crashlog << "-----------------------------------------------------------------------------" << endl;
crashlog << "EKA2 USER CRASH LOG" << endl;
crashlog << "Thread Name: " << QString("ProcessID-%1::ThreadID-%2").arg(cs.pid).arg(cs.tid) << endl;
crashlog << "Thread ID: " << cs.tid << endl;
//this is wrong, but TRK doesn't make stack limit available so we lie
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;
//this is also wrong, but TRK doesn't give all information for exceptions
crashlog << QString("Panic: PC=%1 ").arg(cs.crashPC, 8, 16, QChar('0')) << cs.crashReason << endl;
crashlog << endl;
crashlog << "USER REGISTERS:" << endl;
crashlog << QString("CPSR=%1").arg(registers.at(16), 8, 16, QChar('0')) << endl;
for (int i=0;i<16;i+=4) {
crashlog << QString("r%1=%2 %3 %4 %5")
.arg(i, 2, 10, QChar('0'))
.arg(registers.at(i), 8, 16, QChar('0'))
.arg(registers.at(i+1), 8, 16, QChar('0'))
.arg(registers.at(i+2), 8, 16, QChar('0'))
.arg(registers.at(i+3), 8, 16, QChar('0')) << endl;
}
crashlog << endl;
//emit info for post mortem debug
qSort(d->libraries.begin(), d->libraries.end(), lessThanCodeBase);
d->err << "Code Segments:" << endl;
crashlog << "CODE SEGMENTS:" << endl;
for(int i=0; i<d->libraries.count(); i++) {
const trk::Library& seg = d->libraries.at(i);
if(seg.pid != cs.pid)
continue;
if (d->loglevel > 1) {
d->err << QString("Code: %1 Data: %2 Name: ")
.arg(seg.codeseg, 8, 16, QChar('0'))
.arg(seg.dataseg, 8, 16, QChar('0'))
<< seg.name << endl;
}
//produce fake code segment end addresses since we don't get the real ones from TRK
uint end;
if (i+1 < d->libraries.count())
end = d->libraries.at(i+1).codeseg - 1;
else
end = 0xFFFFFFFF;
crashlog << QString("%1-%2 ")
.arg(seg.codeseg, 8, 16, QChar('0'))
.arg(end, 8, 16, QChar('0'))
<< seg.name << endl;
}
d->crashlogtextfile.close();
if (d->loglevel > 1) {
d->err << "Registers:" << endl;
for (int i=0;i<16;i++) {
d->err << QString("R%1: %2 ").arg(i, 2, 10, QChar('0')).arg(registers.at(i), 8, 16, QChar('0'));
if (i % 4 == 3)
d->err << endl;
}
d->err << QString("CPSR: %1").arg(registers.at(16), 8, 16, QChar('0')) << endl;
d->err << "Stack:" << endl;
uint sp = registers.at(13);
for(int i=0; i<stack.size(); i+=16, sp+=16) {
d->err << QString("%1: ").arg(sp, 8, 16, QChar('0'));
d->err << trk::stringFromArray(stack.mid(i,16));
d->err << endl;
}
}
d->crashstackfile.open(QIODevice::WriteOnly);
d->crashstackfile.write(stack);
d->crashstackfile.close();
if (d->loglevel > 0)
d->err << "Crash logs saved to " << d->crashlogtextfile.fileName() << " & " << d->crashstackfile.fileName() << endl;
// resume the thread to allow Symbian OS to handle the panic normally.
// terminate when a non main thread is suspended reboots the phone (TRK bug)
emit resume(cs.pid, cs.tid);
//fetch next crashed thread
d->queuedCrashes.removeFirst();
if (d->queuedCrashes.count()) {
cs = d->queuedCrashes.first();
d->err << "Fetching registers and stack..." << endl;
emit getRegistersAndCallStack(cs.pid, cs.tid);
}
else if (d->terminateNeeded)
emit terminate();
}
void TrkSignalHandler::libraryLoaded(const trk::Library &lib)
{
d->libraries << lib;
}
void TrkSignalHandler::libraryUnloaded(const trk::Library &lib)
{
for (QList<trk::Library>::iterator i = d->libraries.begin(); i != d->libraries.end(); i++) {
if((*i).name == lib.name && (*i).pid == lib.pid)
i = d->libraries.erase(i);
}
}
void TrkSignalHandler::timeout()
{
d->err << "FAILED: stopping test due to timeout" << endl;
emit terminate();
}
TrkSignalHandlerPrivate::TrkSignalHandlerPrivate()
: out(stdout),
err(stderr),
loglevel(0),
lastpercent(0),
terminateNeeded(false)
{
}
TrkSignalHandlerPrivate::~TrkSignalHandlerPrivate()
{
out.flush();
err.flush();
}
TrkSignalHandler::TrkSignalHandler()
: d(new TrkSignalHandlerPrivate())
{
}
TrkSignalHandler::~TrkSignalHandler()
{
delete d;
}