File indexing completed on 2024-05-05 05:44:21

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