| 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 |
} |