File indexing completed on 2024-05-05 10:03:07
0001 /* 0002 This file is part of KCachegrind. 0003 0004 SPDX-FileCopyrightText: 2002-2016 Josef Weidendorfer <Josef.Weidendorfer@gmx.de> 0005 0006 SPDX-License-Identifier: GPL-2.0-only 0007 */ 0008 0009 #include "loader.h" 0010 0011 #include <QIODevice> 0012 #include <QVector> 0013 #include <QDebug> 0014 0015 #include "addr.h" 0016 #include "tracedata.h" 0017 #include "utils.h" 0018 #include "fixcost.h" 0019 0020 0021 #define TRACE_LOADER 0 0022 0023 /* 0024 * Loader for Callgrind Profile data (format based on Cachegrind format). 0025 * See Callgrind documentation for the file format. 0026 */ 0027 0028 class CachegrindLoader: public Loader 0029 { 0030 public: 0031 CachegrindLoader(); 0032 0033 bool canLoad(QIODevice* file) override; 0034 int load(TraceData*, QIODevice* file, const QString& filename) override; 0035 0036 private: 0037 void error(QString); 0038 void warning(QString); 0039 0040 int loadInternal(TraceData*, QIODevice* file, const QString& filename); 0041 0042 enum lineType { SelfCost, CallCost, BoringJump, CondJump }; 0043 0044 bool parsePosition(FixString& s, PositionSpec& newPos); 0045 0046 // position setters 0047 void clearPosition(); 0048 void ensureObject(); 0049 void ensureFile(); 0050 void ensureFunction(); 0051 void setObject(const QString&); 0052 void setCalledObject(const QString&); 0053 void setFile(const QString&); 0054 void setCalledFile(const QString&); 0055 void setFunction(const QString&); 0056 void setCalledFunction(const QString&); 0057 0058 void prepareNewPart(); 0059 0060 QString _emptyString; 0061 0062 // current line in file to read in 0063 QString _filename; 0064 int _lineNo; 0065 0066 EventTypeMapping* mapping; 0067 TraceData* _data; 0068 TracePart* _part; 0069 int partsAdded; 0070 0071 // current position 0072 lineType nextLineType; 0073 bool hasLineInfo, hasAddrInfo; 0074 PositionSpec currentPos; 0075 0076 // current function/line 0077 TraceObject* currentObject; 0078 TracePartObject* currentPartObject; 0079 TraceFile* currentFile; 0080 TraceFile* currentFunctionFile; 0081 TracePartFile* currentPartFile; 0082 TraceFunction* currentFunction; 0083 TracePartFunction* currentPartFunction; 0084 TraceFunctionSource* currentFunctionSource; 0085 TraceInstr* currentInstr; 0086 TracePartInstr* currentPartInstr; 0087 TraceLine* currentLine; 0088 TracePartLine* currentPartLine; 0089 0090 // current call 0091 TraceObject* currentCalledObject; 0092 TracePartObject* currentCalledPartObject; 0093 TraceFile* currentCalledFile; 0094 TracePartFile* currentCalledPartFile; 0095 TraceFunction* currentCalledFunction; 0096 TracePartFunction* currentCalledPartFunction; 0097 SubCost currentCallCount; 0098 0099 // current jump 0100 TraceFile* currentJumpToFile; 0101 TraceFunction* currentJumpToFunction; 0102 PositionSpec targetPos; 0103 SubCost jumpsFollowed, jumpsExecuted; 0104 0105 /** Support for compressed string format 0106 * This uses the following string compression model 0107 * for objects, files, functions: 0108 * If the name matches 0109 * "(<Integer>) Name": this is a compression specification, 0110 * mapping the integer number to Name and using Name. 0111 * "(<Integer>)" : this is a compression reference. 0112 * Assumes previous compression specification of the 0113 * integer number to a name, uses this name. 0114 * "Name" : Regular name 0115 */ 0116 void clearCompression(); 0117 const QString& checkUnknown(const QString& n); 0118 TraceObject* compressedObject(const QString& name); 0119 TraceFile* compressedFile(const QString& name); 0120 TraceFunction* compressedFunction(const QString& name, 0121 TraceFile*, TraceObject*); 0122 0123 QVector<TraceCostItem*> _objectVector, _fileVector, _functionVector; 0124 }; 0125 0126 0127 0128 /********************************************************** 0129 * Loader 0130 */ 0131 0132 0133 CachegrindLoader::CachegrindLoader() 0134 : Loader(QStringLiteral("Callgrind"), 0135 QObject::tr( "Import filter for Cachegrind/Callgrind generated profile data files") ) 0136 { 0137 } 0138 0139 bool CachegrindLoader::canLoad(QIODevice* file) 0140 { 0141 if (!file) return false; 0142 0143 Q_ASSERT(file->isOpen()); 0144 0145 /* 0146 * We recognize this as cachegrind/callgrind format if 0147 * - it starts with a line "# callgrind format", or 0148 * - if the first 2047 bytes contain either "\nevents:" or "\ncreator:" 0149 */ 0150 char buf[2048]; 0151 int read = file->read(buf,2047); 0152 if (read < 0) 0153 return false; 0154 buf[read] = 0; 0155 0156 QByteArray s = QByteArray::fromRawData(buf, read+1); 0157 0158 if (s.indexOf("# callgrind format\n") == 0) 0159 return true; 0160 0161 int pos = s.indexOf("events:"); 0162 if (pos>0 && buf[pos-1] != '\n') pos = -1; 0163 if (pos>=0) return true; 0164 0165 // callgrind puts a "cmd:" line before "events:", and with big command 0166 // lines, we need another way to detect such callgrind files... 0167 pos = s.indexOf("creator:"); 0168 if (pos>0 && buf[pos-1] != '\n') pos = -1; 0169 0170 return (pos>=0); 0171 } 0172 0173 int CachegrindLoader::load(TraceData* d, 0174 QIODevice* file, const QString& filename) 0175 { 0176 /* do the loading in a new object so parallel load 0177 * operations do not interfere each other. 0178 */ 0179 CachegrindLoader l; 0180 0181 l.setLogger(_logger); 0182 0183 return l.loadInternal(d, file, filename); 0184 } 0185 0186 Loader* createCachegrindLoader() 0187 { 0188 return new CachegrindLoader(); 0189 } 0190 0191 void CachegrindLoader::error(QString msg) 0192 { 0193 loadError(_lineNo, msg); 0194 } 0195 0196 void CachegrindLoader::warning(QString msg) 0197 { 0198 loadWarning(_lineNo, msg); 0199 } 0200 0201 /** 0202 * Return false if this is no position specification 0203 */ 0204 bool CachegrindLoader::parsePosition(FixString& line, 0205 PositionSpec& newPos) 0206 { 0207 char c; 0208 uint diff; 0209 0210 if (hasAddrInfo) { 0211 0212 if (!line.first(c)) return false; 0213 0214 if (c == '*') { 0215 // nothing changed 0216 line.stripFirst(c); 0217 newPos.fromAddr = currentPos.fromAddr; 0218 newPos.toAddr = currentPos.toAddr; 0219 } 0220 else if (c == '+') { 0221 line.stripFirst(c); 0222 line.stripUInt(diff, false); 0223 newPos.fromAddr = currentPos.fromAddr + diff; 0224 newPos.toAddr = newPos.fromAddr; 0225 } 0226 else if (c == '-') { 0227 line.stripFirst(c); 0228 line.stripUInt(diff, false); 0229 newPos.fromAddr = currentPos.fromAddr - diff; 0230 newPos.toAddr = newPos.fromAddr; 0231 } 0232 else if (c >= '0') { 0233 uint64 v; 0234 line.stripUInt64(v, false); 0235 newPos.fromAddr = Addr(v); 0236 newPos.toAddr = newPos.fromAddr; 0237 } 0238 else return false; 0239 0240 // Range specification 0241 if (line.first(c)) { 0242 if (c == '+') { 0243 line.stripFirst(c); 0244 line.stripUInt(diff); 0245 newPos.toAddr = newPos.fromAddr + diff; 0246 } 0247 else if ((c == '-') || (c == ':')) { 0248 line.stripFirst(c); 0249 uint64 v; 0250 line.stripUInt64(v); 0251 newPos.toAddr = Addr(v); 0252 } 0253 } 0254 line.stripSpaces(); 0255 0256 #if TRACE_LOADER 0257 if (newPos.fromAddr == newPos.toAddr) 0258 qDebug() << " Got Addr " << newPos.fromAddr.toString(); 0259 else 0260 qDebug() << " Got AddrRange " << newPos.fromAddr.toString() 0261 << ":" << newPos.toAddr.toString(); 0262 #endif 0263 0264 } 0265 0266 if (hasLineInfo) { 0267 0268 if (!line.first(c)) return false; 0269 0270 if (c > '9') return false; 0271 else if (c == '*') { 0272 // nothing changed 0273 line.stripFirst(c); 0274 newPos.fromLine = currentPos.fromLine; 0275 newPos.toLine = currentPos.toLine; 0276 } 0277 else if (c == '+') { 0278 line.stripFirst(c); 0279 line.stripUInt(diff, false); 0280 newPos.fromLine = currentPos.fromLine + diff; 0281 newPos.toLine = newPos.fromLine; 0282 } 0283 else if (c == '-') { 0284 line.stripFirst(c); 0285 line.stripUInt(diff, false); 0286 if (currentPos.fromLine < diff) { 0287 error(QStringLiteral("Negative line number %1") 0288 .arg((int)currentPos.fromLine - (int)diff)); 0289 diff = currentPos.fromLine; 0290 } 0291 newPos.fromLine = currentPos.fromLine - diff; 0292 newPos.toLine = newPos.fromLine; 0293 } 0294 else if (c >= '0') { 0295 line.stripUInt(newPos.fromLine, false); 0296 newPos.toLine = newPos.fromLine; 0297 } 0298 else return false; 0299 0300 // Range specification 0301 if (line.first(c)) { 0302 if (c == '+') { 0303 line.stripFirst(c); 0304 line.stripUInt(diff); 0305 newPos.toLine = newPos.fromLine + diff; 0306 } 0307 else if ((c == '-') || (c == ':')) { 0308 line.stripFirst(c); 0309 line.stripUInt(newPos.toLine); 0310 } 0311 } 0312 line.stripSpaces(); 0313 0314 #if TRACE_LOADER 0315 if (newPos.fromLine == newPos.toLine) 0316 qDebug() << " Got Line " << newPos.fromLine; 0317 else 0318 qDebug() << " Got LineRange " << newPos.fromLine 0319 << ":" << newPos.toLine; 0320 #endif 0321 0322 } 0323 0324 return true; 0325 } 0326 0327 // Support for compressed strings 0328 void CachegrindLoader::clearCompression() 0329 { 0330 // this does not delete previous contained objects 0331 _objectVector.clear(); 0332 _fileVector.clear(); 0333 _functionVector.clear(); 0334 0335 // reset to reasonable init size. We double lengths if needed. 0336 _objectVector.resize(100); 0337 _fileVector.resize(1000); 0338 _functionVector.resize(10000); 0339 } 0340 0341 const QString& CachegrindLoader::checkUnknown(const QString& n) 0342 { 0343 if (n == QLatin1String("???")) return _emptyString; 0344 return n; 0345 } 0346 0347 TraceObject* CachegrindLoader::compressedObject(const QString& name) 0348 { 0349 if ((name[0] != '(') || !name[1].isDigit()) return _data->object(checkUnknown(name)); 0350 0351 // compressed format using _objectVector 0352 int p = name.indexOf(')'); 0353 if (p<2) { 0354 error(QStringLiteral("Invalid compressed ELF object ('%1')").arg(name)); 0355 return nullptr; 0356 } 0357 int index = name.mid(1, p-1).toInt(); 0358 TraceObject* o = nullptr; 0359 p++; 0360 while((name.length()>p) && name.at(p).isSpace()) p++; 0361 if (name.length()>p) { 0362 if (_objectVector.size() <= index) { 0363 int newSize = index * 2; 0364 #if TRACE_LOADER 0365 qDebug() << " CachegrindLoader: objectVector enlarged to " 0366 << newSize; 0367 #endif 0368 _objectVector.resize(newSize); 0369 } 0370 0371 QString realName = checkUnknown(name.mid(p)); 0372 o = (TraceObject*) _objectVector.at(index); 0373 if (o && (o->name() != realName)) { 0374 error(QStringLiteral("Redefinition of compressed ELF object index %1 (was '%2') to %3") 0375 .arg(index).arg(o->name()).arg(realName)); 0376 } 0377 0378 o = _data->object(realName); 0379 _objectVector.replace(index, o); 0380 } 0381 else { 0382 if ((_objectVector.size() <= index) || 0383 ( (o=(TraceObject*)_objectVector.at(index)) == nullptr)) { 0384 error(QStringLiteral("Undefined compressed ELF object index %1").arg(index)); 0385 return nullptr; 0386 } 0387 } 0388 0389 return o; 0390 } 0391 0392 0393 // Note: Callgrind sometimes gives different IDs for same file 0394 // (when references to same source file come from different ELF objects) 0395 TraceFile* CachegrindLoader::compressedFile(const QString& name) 0396 { 0397 if ((name[0] != '(') || !name[1].isDigit()) return _data->file(checkUnknown(name)); 0398 0399 // compressed format using _fileVector 0400 int p = name.indexOf(')'); 0401 if (p<2) { 0402 error(QStringLiteral("Invalid compressed file ('%1')").arg(name)); 0403 return nullptr; 0404 } 0405 int index = name.mid(1, p-1).toUInt(); 0406 TraceFile* f = nullptr; 0407 p++; 0408 while((name.length()>p) && name.at(p).isSpace()) p++; 0409 if (name.length()>p) { 0410 if (_fileVector.size() <= index) { 0411 int newSize = index * 2; 0412 #if TRACE_LOADER 0413 qDebug() << " CachegrindLoader::fileVector enlarged to " 0414 << newSize; 0415 #endif 0416 _fileVector.resize(newSize); 0417 } 0418 0419 QString realName = checkUnknown(name.mid(p)); 0420 f = (TraceFile*) _fileVector.at(index); 0421 if (f && (f->name() != realName)) { 0422 error(QStringLiteral("Redefinition of compressed file index %1 (was '%2') to %3") 0423 .arg(index).arg(f->name()).arg(realName)); 0424 } 0425 0426 f = _data->file(realName); 0427 _fileVector.replace(index, f); 0428 } 0429 else { 0430 if ((_fileVector.size() <= index) || 0431 ( (f=(TraceFile*)_fileVector.at(index)) == nullptr)) { 0432 error(QStringLiteral("Undefined compressed file index %1").arg(index)); 0433 return nullptr; 0434 } 0435 } 0436 0437 return f; 0438 } 0439 0440 // Note: Callgrind gives different IDs even for same function 0441 // when parts of the function are from different source files. 0442 // Thus, it is no error when multiple indexes map to same function. 0443 TraceFunction* CachegrindLoader::compressedFunction(const QString& name, 0444 TraceFile* file, 0445 TraceObject* object) 0446 { 0447 if ((name[0] != '(') || !name[1].isDigit()) 0448 return _data->function(checkUnknown(name), file, object); 0449 0450 // compressed format using _functionVector 0451 int p = name.indexOf(')'); 0452 if (p<2) { 0453 error(QStringLiteral("Invalid compressed function ('%1')").arg(name)); 0454 return nullptr; 0455 } 0456 0457 0458 int index = name.mid(1, p-1).toUInt(); 0459 TraceFunction* f = nullptr; 0460 p++; 0461 while((name.length()>p) && name.at(p).isSpace()) p++; 0462 if (name.length()>p) { 0463 if (_functionVector.size() <= index) { 0464 int newSize = index * 2; 0465 #if TRACE_LOADER 0466 qDebug() << " CachegrindLoader::functionVector enlarged to " 0467 << newSize; 0468 #endif 0469 _functionVector.resize(newSize); 0470 } 0471 0472 QString realName = checkUnknown(name.mid(p)); 0473 f = (TraceFunction*) _functionVector.at(index); 0474 if (f && (f->name() != realName)) { 0475 error(QStringLiteral("Redefinition of compressed function index %1 (was '%2') to %3") 0476 .arg(index).arg(f->name()).arg(realName)); 0477 } 0478 0479 f = _data->function(realName, file, object); 0480 _functionVector.replace(index, f); 0481 0482 #if TRACE_LOADER 0483 qDebug() << "compressedFunction: Inserted at Index " << index 0484 << "\n " << f->fullName() 0485 << "\n in " << f->cls()->fullName() 0486 << "\n in " << f->file()->fullName() 0487 << "\n in " << f->object()->fullName(); 0488 #endif 0489 } 0490 else { 0491 if ((_functionVector.size() <= index) || 0492 ( (f=(TraceFunction*)_functionVector.at(index)) == nullptr)) { 0493 error(QStringLiteral("Undefined compressed function index %1").arg(index)); 0494 return nullptr; 0495 } 0496 0497 // there was a check if the used function (returned from KCachegrinds 0498 // model) has the same object and file as here given to us, but that was wrong: 0499 // that holds only if we make this assumption on the model... 0500 } 0501 0502 0503 return f; 0504 } 0505 0506 0507 // make sure that a valid object is set, at least dummy with empty name 0508 void CachegrindLoader::ensureObject() 0509 { 0510 if (currentObject) return; 0511 0512 currentObject = _data->object(_emptyString); 0513 currentPartObject = currentObject->partObject(_part); 0514 } 0515 0516 void CachegrindLoader::setObject(const QString& name) 0517 { 0518 currentObject = compressedObject(name); 0519 if (!currentObject) { 0520 error(QStringLiteral("Invalid ELF object specification, setting to unknown")); 0521 0522 currentObject = _data->object(_emptyString); 0523 } 0524 0525 currentPartObject = currentObject->partObject(_part); 0526 currentFunction = nullptr; 0527 currentPartFunction = nullptr; 0528 } 0529 0530 void CachegrindLoader::setCalledObject(const QString& name) 0531 { 0532 currentCalledObject = compressedObject(name); 0533 0534 if (!currentCalledObject) { 0535 error(QStringLiteral("Invalid specification of called ELF object, setting to unknown")); 0536 0537 currentCalledObject = _data->object(_emptyString); 0538 } 0539 0540 currentCalledPartObject = currentCalledObject->partObject(_part); 0541 } 0542 0543 0544 // make sure that a valid file is set, at least dummy with empty name 0545 void CachegrindLoader::ensureFile() 0546 { 0547 if (currentFile) return; 0548 0549 currentFile = _data->file(_emptyString); 0550 currentPartFile = currentFile->partFile(_part); 0551 } 0552 0553 void CachegrindLoader::setFile(const QString& name) 0554 { 0555 currentFile = compressedFile(name); 0556 0557 if (!currentFile) { 0558 error(QStringLiteral("Invalid file specification, setting to unknown")); 0559 0560 currentFile = _data->file(_emptyString); 0561 } 0562 0563 currentPartFile = currentFile->partFile(_part); 0564 currentLine = nullptr; 0565 currentPartLine = nullptr; 0566 } 0567 0568 void CachegrindLoader::setCalledFile(const QString& name) 0569 { 0570 currentCalledFile = compressedFile(name); 0571 0572 if (!currentCalledFile) { 0573 error(QStringLiteral("Invalid specification of called file, setting to unknown")); 0574 0575 currentCalledFile = _data->file(_emptyString); 0576 } 0577 0578 currentCalledPartFile = currentCalledFile->partFile(_part); 0579 } 0580 0581 // make sure that a valid function is set, at least dummy with empty name 0582 void CachegrindLoader::ensureFunction() 0583 { 0584 if (currentFunction) return; 0585 0586 error(QStringLiteral("Function not specified, setting to unknown")); 0587 0588 ensureFile(); 0589 ensureObject(); 0590 0591 currentFunction = _data->function(_emptyString, 0592 currentFile, 0593 currentObject); 0594 currentPartFunction = currentFunction->partFunction(_part, 0595 currentPartFile, 0596 currentPartObject); 0597 } 0598 0599 void CachegrindLoader::setFunction(const QString& name) 0600 { 0601 ensureFile(); 0602 ensureObject(); 0603 0604 currentFunction = compressedFunction( name, 0605 currentFile, 0606 currentObject); 0607 0608 if (!currentFunction) { 0609 error(QStringLiteral("Invalid function specification, setting to unknown")); 0610 0611 currentFunction = _data->function(_emptyString, 0612 currentFile, 0613 currentObject); 0614 } 0615 0616 currentPartFunction = currentFunction->partFunction(_part, 0617 currentPartFile, 0618 currentPartObject); 0619 0620 currentFunctionSource = nullptr; 0621 currentLine = nullptr; 0622 currentPartLine = nullptr; 0623 } 0624 0625 void CachegrindLoader::setCalledFunction(const QString& name) 0626 { 0627 // if called object/file not set, use current object/file 0628 if (!currentCalledObject) { 0629 currentCalledObject = currentObject; 0630 currentCalledPartObject = currentPartObject; 0631 } 0632 0633 if (!currentCalledFile) { 0634 // !=0 as functions needs file 0635 currentCalledFile = currentFile; 0636 currentCalledPartFile = currentPartFile; 0637 } 0638 0639 currentCalledFunction = compressedFunction(name, 0640 currentCalledFile, 0641 currentCalledObject); 0642 if (!currentCalledFunction) { 0643 error(QStringLiteral("Invalid called function, setting to unknown")); 0644 0645 currentCalledFunction = _data->function(_emptyString, 0646 currentCalledFile, 0647 currentCalledObject); 0648 } 0649 0650 currentCalledPartFunction = 0651 currentCalledFunction->partFunction(_part, 0652 currentCalledPartFile, 0653 currentCalledPartObject); 0654 } 0655 0656 0657 void CachegrindLoader::clearPosition() 0658 { 0659 currentPos = PositionSpec(); 0660 0661 // current function/line 0662 currentFunction = nullptr; 0663 currentPartFunction = nullptr; 0664 currentFunctionSource = nullptr; 0665 currentFile = nullptr; 0666 currentFunctionFile = nullptr; 0667 currentPartFile = nullptr; 0668 currentObject = nullptr; 0669 currentPartObject = nullptr; 0670 currentLine = nullptr; 0671 currentPartLine = nullptr; 0672 currentInstr = nullptr; 0673 currentPartInstr = nullptr; 0674 0675 // current call 0676 currentCalledObject = nullptr; 0677 currentCalledPartObject = nullptr; 0678 currentCalledFile = nullptr; 0679 currentCalledPartFile = nullptr; 0680 currentCalledFunction = nullptr; 0681 currentCalledPartFunction = nullptr; 0682 currentCallCount = 0; 0683 0684 // current jump 0685 currentJumpToFile = nullptr; 0686 currentJumpToFunction = nullptr; 0687 targetPos = PositionSpec(); 0688 jumpsFollowed = 0; 0689 jumpsExecuted = 0; 0690 0691 mapping = nullptr; 0692 } 0693 0694 void CachegrindLoader::prepareNewPart() 0695 { 0696 if (_part) { 0697 // really new part needed? 0698 if (mapping == nullptr) return; 0699 0700 // yes 0701 _part->invalidate(); 0702 _part->totals()->clear(); 0703 _part->totals()->addCost(_part); 0704 _data->addPart(_part); 0705 partsAdded++; 0706 } 0707 0708 clearCompression(); 0709 clearPosition(); 0710 0711 _part = new TracePart(_data); 0712 _part->setName(_filename); 0713 } 0714 0715 /** 0716 * The main import function... 0717 */ 0718 int CachegrindLoader::loadInternal(TraceData* data, 0719 QIODevice* device, const QString& filename) 0720 { 0721 if (!data || !device) return 0; 0722 0723 _data = data; 0724 _filename = filename; 0725 _lineNo = 0; 0726 0727 loadStart(_filename); 0728 0729 FixFile file(device, _filename); 0730 if (!file.exists()) { 0731 loadFinished(QStringLiteral("File does not exist")); 0732 return 0; 0733 } 0734 0735 int statusProgress = 0; 0736 0737 #if USE_FIXCOST 0738 // FixCost Memory Pool 0739 FixPool* pool = _data->fixPool(); 0740 #endif 0741 0742 _part = nullptr; 0743 partsAdded = 0; 0744 prepareNewPart(); 0745 0746 FixString line; 0747 char c; 0748 0749 // current position 0750 nextLineType = SelfCost; 0751 // default if there is no "positions:" line 0752 hasLineInfo = true; 0753 hasAddrInfo = false; 0754 0755 while (file.nextLine(line)) { 0756 0757 _lineNo++; 0758 0759 #if TRACE_LOADER 0760 qDebug() << "[CachegrindLoader] " << _filename << ":" << _lineNo 0761 << " - '" << QString(line) << "'"; 0762 #endif 0763 0764 // if we cannot strip a character, this was an empty line 0765 if (!line.first(c)) continue; 0766 0767 if (c <= '9') { 0768 0769 if (c == '#') continue; 0770 0771 // parse position(s) 0772 if (!parsePosition(line, currentPos)) { 0773 error(QStringLiteral("Invalid position specification '%1'").arg(line)); 0774 continue; 0775 } 0776 0777 // go through after big switch 0778 } 0779 else { // if (c > '9') 0780 0781 line.stripFirst(c); 0782 0783 /* in order of probability */ 0784 switch(c) { 0785 0786 case 'f': 0787 0788 // fl= 0789 if (line.stripPrefix("l=")) { 0790 0791 setFile(line); 0792 // this is the default for new functions 0793 currentFunctionFile = currentFile; 0794 continue; 0795 } 0796 0797 // fi=, fe= 0798 if (line.stripPrefix("i=") || 0799 line.stripPrefix("e=")) { 0800 0801 setFile(line); 0802 continue; 0803 } 0804 0805 // fn= 0806 if (line.stripPrefix("n=")) { 0807 0808 if (currentFile != currentFunctionFile) 0809 currentFile = currentFunctionFile; 0810 setFunction(line); 0811 0812 // on a new function, update status 0813 int progress = (int)(100.0 * file.current() / file.len() +.5); 0814 if (progress != statusProgress) { 0815 statusProgress = progress; 0816 0817 /* When this signal is connected, it most probably 0818 * should lead to GUI update. Thus, when multiple 0819 * "long operations" (like file loading) are in progress, 0820 * this can temporarily switch to another operation. 0821 */ 0822 loadProgress(statusProgress); 0823 } 0824 0825 continue; 0826 } 0827 0828 break; 0829 0830 case 'c': 0831 // cob= 0832 if (line.stripPrefix("ob=")) { 0833 setCalledObject(line); 0834 continue; 0835 } 0836 0837 // cfi= / cfl= 0838 if (line.stripPrefix("fl=") || 0839 line.stripPrefix("fi=")) { 0840 setCalledFile(line); 0841 continue; 0842 } 0843 0844 // cfn= 0845 if (line.stripPrefix("fn=")) { 0846 0847 setCalledFunction(line); 0848 continue; 0849 } 0850 0851 // calls= 0852 if (line.stripPrefix("alls=")) { 0853 // ignore long lines... 0854 line.stripUInt64(currentCallCount); 0855 nextLineType = CallCost; 0856 continue; 0857 } 0858 0859 // cmd: 0860 if (line.stripPrefix("md:")) { 0861 QString command = QString(line).trimmed(); 0862 if (!_data->command().isEmpty() && 0863 _data->command() != command) { 0864 0865 error(QStringLiteral("Redefined command, was '%1'").arg(_data->command())); 0866 } 0867 _data->setCommand(command); 0868 continue; 0869 } 0870 0871 // creator: 0872 if (line.stripPrefix("reator:")) { 0873 // ignore ... 0874 continue; 0875 } 0876 0877 break; 0878 0879 case 'j': 0880 0881 // jcnd= 0882 if (line.stripPrefix("cnd=")) { 0883 bool valid; 0884 0885 valid = line.stripUInt64(jumpsFollowed) && 0886 line.stripPrefix("/") && 0887 line.stripUInt64(jumpsExecuted) && 0888 parsePosition(line, targetPos); 0889 0890 if (!valid) { 0891 error(QStringLiteral("Invalid line after 'jcnd'")); 0892 } 0893 else 0894 nextLineType = CondJump; 0895 continue; 0896 } 0897 0898 if (line.stripPrefix("ump=")) { 0899 bool valid; 0900 0901 valid = line.stripUInt64(jumpsExecuted) && 0902 parsePosition(line, targetPos); 0903 0904 if (!valid) { 0905 error(QStringLiteral("Invalid line after 'jump'")); 0906 } 0907 else 0908 nextLineType = BoringJump; 0909 continue; 0910 } 0911 0912 // jfi= 0913 if (line.stripPrefix("fi=")) { 0914 currentJumpToFile = compressedFile(line); 0915 continue; 0916 } 0917 0918 // jfn= 0919 if (line.stripPrefix("fn=")) { 0920 0921 if (!currentJumpToFile) { 0922 // !=0 as functions needs file 0923 currentJumpToFile = currentFile; 0924 } 0925 0926 currentJumpToFunction = 0927 compressedFunction(line, 0928 currentJumpToFile, 0929 currentObject); 0930 continue; 0931 } 0932 0933 break; 0934 0935 case 'o': 0936 0937 // ob= 0938 if (line.stripPrefix("b=")) { 0939 setObject(line); 0940 continue; 0941 } 0942 0943 break; 0944 0945 case '#': 0946 continue; 0947 0948 case 'a': 0949 // "arch: arm" 0950 if (line.stripPrefix("rch: arm")) { 0951 TraceData::Arch a = _data->architecture(); 0952 if ((a != TraceData::ArchUnknown) && 0953 (a != TraceData::ArchARM)) { 0954 error(QStringLiteral("Redefined architecture!")); 0955 } 0956 _data->setArchitecture(TraceData::ArchARM); 0957 continue; 0958 } 0959 break; 0960 0961 case 't': 0962 0963 // totals: 0964 if (line.stripPrefix("otals:")) continue; 0965 0966 // thread: 0967 if (line.stripPrefix("hread:")) { 0968 prepareNewPart(); 0969 _part->setThreadID(QString(line).toInt()); 0970 continue; 0971 } 0972 0973 // timeframe (BB): 0974 if (line.stripPrefix("imeframe (BB):")) { 0975 _part->setTimeframe(line); 0976 continue; 0977 } 0978 0979 break; 0980 0981 case 'd': 0982 0983 // desc: 0984 if (line.stripPrefix("esc:")) { 0985 0986 line.stripSurroundingSpaces(); 0987 0988 // desc: Trigger: 0989 if (line.stripPrefix("Trigger:")) { 0990 _part->setTrigger(line); 0991 } 0992 0993 continue; 0994 } 0995 break; 0996 0997 case 'e': 0998 0999 // events: 1000 if (line.stripPrefix("vents:")) { 1001 prepareNewPart(); 1002 mapping = _data->eventTypes()->createMapping(line); 1003 _part->setEventMapping(mapping); 1004 continue; 1005 } 1006 1007 // event:<name>[=<formula>][:<long name>] 1008 if (line.stripPrefix("vent:")) { 1009 line.stripSurroundingSpaces(); 1010 1011 FixString e, f, l; 1012 if (!line.stripName(e)) { 1013 error(QStringLiteral("Invalid event")); 1014 continue; 1015 } 1016 line.stripSpaces(); 1017 if (!line.stripFirst(c)) continue; 1018 1019 if (c=='=') f = line.stripUntil(':'); 1020 line.stripSpaces(); 1021 1022 // add to known cost types 1023 if (line.isEmpty()) line = e; 1024 EventType::add(new EventType(e,line,f)); 1025 continue; 1026 } 1027 break; 1028 1029 case 'p': 1030 1031 // part: 1032 if (line.stripPrefix("art:")) { 1033 prepareNewPart(); 1034 _part->setPartNumber(QString(line).toInt()); 1035 continue; 1036 } 1037 1038 // pid: 1039 if (line.stripPrefix("id:")) { 1040 prepareNewPart(); 1041 _part->setProcessID(QString(line).toInt()); 1042 continue; 1043 } 1044 1045 // positions: 1046 if (line.stripPrefix("ositions:")) { 1047 prepareNewPart(); 1048 QString positions(line); 1049 hasLineInfo = positions.contains(QLatin1String("line")); 1050 hasAddrInfo = positions.contains(QLatin1String("instr")); 1051 continue; 1052 } 1053 break; 1054 1055 case 'v': 1056 1057 // version: 1058 if (line.stripPrefix("ersion:")) { 1059 // ignore for now 1060 continue; 1061 } 1062 break; 1063 1064 case 's': 1065 1066 // summary: 1067 if (line.stripPrefix("ummary:")) { 1068 if (!mapping) { 1069 error(QStringLiteral("Invalid format: summary before data. Skipping file")); 1070 delete _part; 1071 return false; 1072 } 1073 1074 _part->totals()->set(mapping, line); 1075 continue; 1076 } 1077 break; 1078 1079 case 'r': 1080 1081 // rcalls= (deprecated) 1082 if (line.stripPrefix("calls=")) { 1083 // handle like normal calls: we need the sum of call count 1084 // recursive cost is discarded in cycle detection 1085 line.stripUInt64(currentCallCount); 1086 nextLineType = CallCost; 1087 1088 warning(QStringLiteral("Old file format using deprecated 'rcalls'")); 1089 continue; 1090 } 1091 break; 1092 1093 default: 1094 break; 1095 } 1096 1097 error(QStringLiteral("Invalid line '%1%2'").arg(c).arg(line)); 1098 continue; 1099 } 1100 1101 if (!mapping) { 1102 error(QStringLiteral("Invalid format: data found before 'events' line. Skipping file")); 1103 delete _part; 1104 return false; 1105 } 1106 1107 // for a cost line, we always need a current function 1108 ensureFunction(); 1109 1110 1111 1112 if (!currentFunctionSource || 1113 (currentFunctionSource->file() != currentFile)) { 1114 currentFunctionSource = currentFunction->sourceFile(currentFile, 1115 true); 1116 } 1117 1118 #if !USE_FIXCOST 1119 if (hasAddrInfo) { 1120 if (!currentInstr || 1121 (currentInstr->addr() != currentPos.fromAddr)) { 1122 currentInstr = currentFunction->instr(currentPos.fromAddr, 1123 true); 1124 1125 if (!currentInstr) { 1126 error(QString("Invalid address '%1'").arg(currentPos.fromAddr.toString())); 1127 1128 continue; 1129 } 1130 1131 currentPartInstr = currentInstr->partInstr(_part, 1132 currentPartFunction); 1133 } 1134 } 1135 1136 if (hasLineInfo) { 1137 if (!currentLine || 1138 (currentLine->lineno() != currentPos.fromLine)) { 1139 1140 currentLine = currentFunctionSource->line(currentPos.fromLine, 1141 true); 1142 currentPartLine = currentLine->partLine(_part, 1143 currentPartFunction); 1144 } 1145 if (hasAddrInfo && currentInstr) 1146 currentInstr->setLine(currentLine); 1147 } 1148 #endif 1149 1150 #if TRACE_LOADER 1151 qDebug() << _filename << ":" << _lineNo; 1152 qDebug() << " currentInstr " 1153 << (currentInstr ? qPrintable(currentInstr->toString()) : "."); 1154 qDebug() << " currentLine " 1155 << (currentLine ? qPrintable(currentLine->toString()) : ".") 1156 << "( file " << currentFile->name() << ")"; 1157 qDebug() << " currentFunction " 1158 << qPrintable(currentFunction->prettyName()); 1159 qDebug() << " currentCalled " 1160 << (currentCalledFunction ? qPrintable(currentCalledFunction->prettyName()) : "."); 1161 #endif 1162 1163 // create cost item 1164 1165 if (nextLineType == SelfCost) { 1166 1167 #if USE_FIXCOST 1168 new (pool) FixCost(_part, pool, 1169 currentFunctionSource, 1170 currentPos, 1171 currentPartFunction, 1172 line); 1173 #else 1174 if (hasAddrInfo) { 1175 TracePartInstr* partInstr; 1176 partInstr = currentInstr->partInstr(_part, currentPartFunction); 1177 1178 if (hasLineInfo) { 1179 // we need to set <line> back after reading for the line 1180 int l = line.len(); 1181 const char* s = line.ascii(); 1182 1183 partInstr->addCost(mapping, line); 1184 line.set(s,l); 1185 } 1186 else 1187 partInstr->addCost(mapping, line); 1188 } 1189 1190 if (hasLineInfo) { 1191 TracePartLine* partLine; 1192 partLine = currentLine->partLine(_part, currentPartFunction); 1193 partLine->addCost(mapping, line); 1194 } 1195 #endif 1196 1197 if (!line.isEmpty()) { 1198 error(QStringLiteral("Garbage at end of cost line ('%1')").arg(line)); 1199 } 1200 } 1201 else if (nextLineType == CallCost) { 1202 nextLineType = SelfCost; 1203 1204 TraceCall* calling = currentFunction->calling(currentCalledFunction); 1205 TracePartCall* partCalling = 1206 calling->partCall(_part, currentPartFunction, 1207 currentCalledPartFunction); 1208 1209 #if USE_FIXCOST 1210 FixCallCost* fcc; 1211 fcc = new (pool) FixCallCost(_part, pool, 1212 currentFunctionSource, 1213 hasLineInfo ? currentPos.fromLine : 0, 1214 hasAddrInfo ? currentPos.fromAddr : Addr(0), 1215 partCalling, 1216 currentCallCount, line); 1217 fcc->setMax(_data->callMax()); 1218 _data->updateMaxCallCount(fcc->callCount()); 1219 #else 1220 if (hasAddrInfo) { 1221 TraceInstrCall* instrCall; 1222 TracePartInstrCall* partInstrCall; 1223 1224 instrCall = calling->instrCall(currentInstr); 1225 partInstrCall = instrCall->partInstrCall(_part, partCalling); 1226 partInstrCall->addCallCount(currentCallCount); 1227 1228 if (hasLineInfo) { 1229 // we need to set <line> back after reading for the line 1230 int l = line.len(); 1231 const char* s = line.ascii(); 1232 1233 partInstrCall->addCost(mapping, line); 1234 line.set(s,l); 1235 } 1236 else 1237 partInstrCall->addCost(mapping, line); 1238 1239 // update maximum of call cost 1240 _data->callMax()->maxCost(partInstrCall); 1241 _data->updateMaxCallCount(partInstrCall->callCount()); 1242 } 1243 1244 if (hasLineInfo) { 1245 TraceLineCall* lineCall; 1246 TracePartLineCall* partLineCall; 1247 1248 lineCall = calling->lineCall(currentLine); 1249 partLineCall = lineCall->partLineCall(_part, partCalling); 1250 1251 partLineCall->addCallCount(currentCallCount); 1252 partLineCall->addCost(mapping, line); 1253 1254 // update maximum of call cost 1255 _data->callMax()->maxCost(partLineCall); 1256 _data->updateMaxCallCount(partLineCall->callCount()); 1257 } 1258 #endif 1259 currentCalledFile = nullptr; 1260 currentCalledPartFile = nullptr; 1261 currentCalledObject = nullptr; 1262 currentCalledPartObject = nullptr; 1263 currentCallCount = 0; 1264 1265 if (!line.isEmpty()) { 1266 error(QStringLiteral("Garbage at end of call cost line ('%1')").arg(line)); 1267 } 1268 } 1269 else { // (nextLineType == BoringJump || nextLineType == CondJump) 1270 1271 TraceFunctionSource* targetSource; 1272 1273 if (!currentJumpToFunction) 1274 currentJumpToFunction = currentFunction; 1275 1276 targetSource = (currentJumpToFile) ? 1277 currentJumpToFunction->sourceFile(currentJumpToFile, true) : 1278 currentFunctionSource; 1279 1280 #if USE_FIXCOST 1281 new (pool) FixJump(_part, pool, 1282 /* source */ 1283 hasLineInfo ? currentPos.fromLine : 0, 1284 hasAddrInfo ? currentPos.fromAddr : 0, 1285 currentPartFunction, 1286 currentFunctionSource, 1287 /* target */ 1288 hasLineInfo ? targetPos.fromLine : 0, 1289 hasAddrInfo ? targetPos.fromAddr : Addr(0), 1290 currentJumpToFunction, 1291 targetSource, 1292 (nextLineType == CondJump), 1293 jumpsExecuted, jumpsFollowed); 1294 #else 1295 if (hasAddrInfo) { 1296 TraceInstr* jumpToInstr; 1297 TraceInstrJump* instrJump; 1298 TracePartInstrJump* partInstrJump; 1299 1300 jumpToInstr = currentJumpToFunction->instr(targetPos.fromAddr, 1301 true); 1302 instrJump = currentInstr->instrJump(jumpToInstr, 1303 (nextLineType == CondJump)); 1304 partInstrJump = instrJump->partInstrJump(_part); 1305 partInstrJump->addExecutedCount(jumpsExecuted); 1306 if (nextLineType == CondJump) 1307 partInstrJump->addFollowedCount(jumpsFollowed); 1308 } 1309 1310 if (hasLineInfo) { 1311 TraceLine* jumpToLine; 1312 TraceLineJump* lineJump; 1313 TracePartLineJump* partLineJump; 1314 1315 jumpToLine = targetSource->line(targetPos.fromLine, true); 1316 lineJump = currentLine->lineJump(jumpToLine, 1317 (nextLineType == CondJump)); 1318 partLineJump = lineJump->partLineJump(_part); 1319 1320 partLineJump->addExecutedCount(jumpsExecuted); 1321 if (nextLineType == CondJump) 1322 partLineJump->addFollowedCount(jumpsFollowed); 1323 } 1324 #endif 1325 1326 if (0) { 1327 qDebug() << _filename << ":" << _lineNo 1328 << " - jump from 0x" << currentPos.fromAddr.toString() 1329 << " (line " << currentPos.fromLine 1330 << ") to 0x" << targetPos.fromAddr.toString() 1331 << " (line " << targetPos.fromLine << ")"; 1332 1333 if (nextLineType == BoringJump) 1334 qDebug() << " Boring Jump, count " << jumpsExecuted.pretty(); 1335 else 1336 qDebug() << " Cond. Jump, followed " << jumpsFollowed.pretty() 1337 << ", executed " << jumpsExecuted.pretty(); 1338 } 1339 1340 nextLineType = SelfCost; 1341 currentJumpToFunction = nullptr; 1342 currentJumpToFile = nullptr; 1343 1344 if (!line.isEmpty()) { 1345 error(QStringLiteral("Garbage at end of jump cost line ('%1')").arg(line)); 1346 } 1347 1348 } 1349 } 1350 1351 loadFinished(); 1352 1353 if (mapping) { 1354 _part->invalidate(); 1355 _part->totals()->clear(); 1356 _part->totals()->addCost(_part); 1357 data->addPart(_part); 1358 partsAdded++; 1359 } 1360 else { 1361 error(QStringLiteral("No data found. Skipping file")); 1362 delete _part; 1363 } 1364 1365 device->close(); 1366 1367 return partsAdded; 1368 } 1369