summaryrefslogtreecommitdiffstats
path: root/tdecachegrind/tdecachegrind/cachegrindloader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tdecachegrind/tdecachegrind/cachegrindloader.cpp')
-rw-r--r--tdecachegrind/tdecachegrind/cachegrindloader.cpp1323
1 files changed, 1323 insertions, 0 deletions
diff --git a/tdecachegrind/tdecachegrind/cachegrindloader.cpp b/tdecachegrind/tdecachegrind/cachegrindloader.cpp
new file mode 100644
index 00000000..4fe57d34
--- /dev/null
+++ b/tdecachegrind/tdecachegrind/cachegrindloader.cpp
@@ -0,0 +1,1323 @@
+/* This file is part of KCachegrind.
+ Copyright (C) 2002, 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
+
+ KCachegrind is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation, version 2.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <errno.h>
+
+#include <tqfile.h>
+#include <tqcstring.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+
+#include "loader.h"
+#include "tracedata.h"
+#include "utils.h"
+#include "fixcost.h"
+
+
+#define TRACE_LOADER 0
+
+/*
+ * Loader for Callgrind Profile data (format based on Cachegrind format).
+ * See Callgrind documentation for the file format.
+ */
+
+class CachegrindLoader: public Loader
+{
+public:
+ CachegrindLoader();
+
+ bool canLoadTrace(TQFile* file);
+ bool loadTrace(TracePart*);
+ bool isPartOfTrace(TQString file, TraceData*);
+
+private:
+ bool loadTraceInternal(TracePart*);
+
+ enum lineType { SelfCost, CallCost, BoringJump, CondJump };
+
+ bool parsePosition(FixString& s, PositionSpec& newPos);
+
+ // position setters
+ void clearPosition();
+ void ensureObject();
+ void ensureFile();
+ void ensureFunction();
+ void setObject(const TQString&);
+ void setCalledObject(const TQString&);
+ void setFile(const TQString&);
+ void setCalledFile(const TQString&);
+ void setFunction(const TQString&);
+ void setCalledFunction(const TQString&);
+
+ TQString _emptyString;
+
+ // current line in file to read in
+ TQString _filename;
+ int _lineNo;
+
+ TraceSubMapping* subMapping;
+ TraceData* _data;
+ TracePart* _part;
+
+ // current position
+ lineType nextLineType;
+ bool hasLineInfo, hasAddrInfo;
+ PositionSpec currentPos;
+
+ // current function/line
+ TraceObject* currentObject;
+ TracePartObject* currentPartObject;
+ TraceFile* currentFile;
+ TracePartFile* currentPartFile;
+ TraceFunction* currentFunction;
+ TracePartFunction* currentPartFunction;
+ TraceFunctionSource* currentFunctionSource;
+ TraceInstr* currentInstr;
+ TracePartInstr* currentPartInstr;
+ TraceLine* currentLine;
+ TracePartLine* currentPartLine;
+
+ // current call
+ TraceObject* currentCalledObject;
+ TracePartObject* currentCalledPartObject;
+ TraceFile* currentCalledFile;
+ TracePartFile* currentCalledPartFile;
+ TraceFunction* currentCalledFunction;
+ TracePartFunction* currentCalledPartFunction;
+ SubCost currentCallCount;
+
+ // current jump
+ TraceFile* currentJumpToFile;
+ TraceFunction* currentJumpToFunction;
+ PositionSpec targetPos;
+ SubCost jumpsFollowed, jumpsExecuted;
+
+ /** Support for compressed string format
+ * This uses the following string compression model
+ * for objects, files, functions:
+ * If the name matches
+ * "(<Integer>) Name": this is a compression specification,
+ * mapping the integer number to Name and using Name.
+ * "(<Integer>)" : this is a compression reference.
+ * Assumes previous compression specification of the
+ * integer number to a name, uses this name.
+ * "Name" : Regular name
+ */
+ void clearCompression();
+ const TQString& checkUnknown(const TQString& n);
+ TraceObject* compressedObject(const TQString& name);
+ TraceFile* compressedFile(const TQString& name);
+ TraceFunction* compressedFunction(const TQString& name,
+ TraceFile*, TraceObject*);
+
+ TQPtrVector<TraceCostItem> _objectVector, _fileVector, _functionVector;
+};
+
+
+
+/**********************************************************
+ * Loader
+ */
+
+
+CachegrindLoader::CachegrindLoader()
+ : Loader("Callgrind",
+ i18n( "Import filter for Cachegrind/Callgrind generated profile data files") )
+{
+ _emptyString = TQString("");
+}
+
+bool CachegrindLoader::canLoadTrace(TQFile* file)
+{
+ if (!file) return false;
+
+ if (!file->isOpen()) {
+ if (!file->open( IO_ReadOnly ) ) {
+ kdDebug() << TQFile::encodeName(_filename).data() << ": "
+ << strerror( errno ) << endl;
+ return false;
+ }
+ }
+
+ /*
+ * We recognize this as cachegrind/callgrind format if in the first
+ * 2047 bytes we see the string "\nevents:"
+ */
+ char buf[2048];
+ int read = file->readBlock(buf,2047);
+ if (read < 0)
+ return false;
+ buf[read] = 0;
+
+ TQCString s;
+ s.setRawData(buf, read+1);
+ int pos = s.find("events:");
+ if (pos>0 && buf[pos-1] != '\n') pos = -1;
+ s.resetRawData(buf, read+1);
+ return (pos>=0);
+}
+
+bool CachegrindLoader::loadTrace(TracePart* p)
+{
+ /* do the loading in a new object so parallel load
+ * operations do not interfere each other.
+ */
+ CachegrindLoader l;
+
+ /* emit progress signals via the singleton loader */
+ connect(&l, TQT_SIGNAL(updateStatus(TQString, int)),
+ this, TQT_SIGNAL(updateStatus(TQString, int)));
+
+ return l.loadTraceInternal(p);
+}
+
+Loader* createCachegrindLoader()
+{
+ return new CachegrindLoader();
+}
+
+
+
+/**
+ * Return false if this is no position specification
+ */
+bool CachegrindLoader::parsePosition(FixString& line,
+ PositionSpec& newPos)
+{
+ char c;
+ uint diff;
+
+ if (hasAddrInfo) {
+
+ if (!line.first(c)) return false;
+
+ if (c == '*') {
+ // nothing changed
+ line.stripFirst(c);
+ newPos.fromAddr = currentPos.fromAddr;
+ newPos.toAddr = currentPos.toAddr;
+ }
+ else if (c == '+') {
+ line.stripFirst(c);
+ line.stripUInt(diff, false);
+ newPos.fromAddr = currentPos.fromAddr + diff;
+ newPos.toAddr = newPos.fromAddr;
+ }
+ else if (c == '-') {
+ line.stripFirst(c);
+ line.stripUInt(diff, false);
+ newPos.fromAddr = currentPos.fromAddr - diff;
+ newPos.toAddr = newPos.fromAddr;
+ }
+ else if (c >= '0') {
+ uint64 v;
+ line.stripUInt64(v, false);
+ newPos.fromAddr = Addr(v);
+ newPos.toAddr = newPos.fromAddr;
+ }
+ else return false;
+
+ // Range specification
+ if (line.first(c)) {
+ if (c == '+') {
+ line.stripFirst(c);
+ line.stripUInt(diff);
+ newPos.toAddr = newPos.fromAddr + diff;
+ }
+ else if ((c == '-') || (c == ':')) {
+ line.stripFirst(c);
+ uint64 v;
+ line.stripUInt64(v);
+ newPos.toAddr = Addr(v);
+ }
+ }
+ line.stripSpaces();
+
+#if TRACE_LOADER
+ if (newPos.fromAddr == newPos.toAddr)
+ kdDebug() << " Got Addr " << newPos.fromAddr.toString() << endl;
+ else
+ kdDebug() << " Got AddrRange " << newPos.fromAddr.toString()
+ << ":" << newPos.toAddr.toString() << endl;
+#endif
+
+ }
+
+ if (hasLineInfo) {
+
+ if (!line.first(c)) return false;
+
+ if (c > '9') return false;
+ else if (c == '*') {
+ // nothing changed
+ line.stripFirst(c);
+ newPos.fromLine = currentPos.fromLine;
+ newPos.toLine = currentPos.toLine;
+ }
+ else if (c == '+') {
+ line.stripFirst(c);
+ line.stripUInt(diff, false);
+ newPos.fromLine = currentPos.fromLine + diff;
+ newPos.toLine = newPos.fromLine;
+ }
+ else if (c == '-') {
+ line.stripFirst(c);
+ line.stripUInt(diff, false);
+ if (currentPos.fromLine < diff) {
+ kdError() << _filename << ":" << _lineNo
+ << " - Negative line number "
+ << (int)currentPos.fromLine - (int)diff << endl;
+ diff = currentPos.fromLine;
+ }
+ newPos.fromLine = currentPos.fromLine - diff;
+ newPos.toLine = newPos.fromLine;
+ }
+ else if (c >= '0') {
+ line.stripUInt(newPos.fromLine, false);
+ newPos.toLine = newPos.fromLine;
+ }
+ else return false;
+
+ // Range specification
+ if (line.first(c)) {
+ if (c == '+') {
+ line.stripFirst(c);
+ line.stripUInt(diff);
+ newPos.toLine = newPos.fromLine + diff;
+ }
+ else if ((c == '-') || (c == ':')) {
+ line.stripFirst(c);
+ line.stripUInt(newPos.toLine);
+ }
+ }
+ line.stripSpaces();
+
+#if TRACE_LOADER
+ if (newPos.fromLine == newPos.toLine)
+ kdDebug() << " Got Line " << newPos.fromLine << endl;
+ else
+ kdDebug() << " Got LineRange " << newPos.fromLine
+ << ":" << newPos.toLine << endl;
+#endif
+
+ }
+
+ return true;
+}
+
+// Support for compressed strings
+void CachegrindLoader::clearCompression()
+{
+ // this doesn't delete previous contained objects
+ _objectVector.clear();
+ _fileVector.clear();
+ _functionVector.clear();
+
+ // reset to reasonable init size. We double lengths if needed.
+ _objectVector.resize(100);
+ _fileVector.resize(1000);
+ _functionVector.resize(10000);
+}
+
+const TQString& CachegrindLoader::checkUnknown(const TQString& n)
+{
+ if (n == "???") return _emptyString;
+ return n;
+}
+
+TraceObject* CachegrindLoader::compressedObject(const TQString& name)
+{
+ if ((name[0] != '(') || !name[1].isDigit()) return _data->object(checkUnknown(name));
+
+ // compressed format using _objectVector
+ int p = name.find(')');
+ if (p<2) {
+ kdError() << _filename << ":" << _lineNo
+ << " - Invalid compressed ELF object ('"
+ << name << "')" << endl;
+ return 0;
+ }
+ unsigned index = name.mid(1, p-1).toInt();
+ TraceObject* o = 0;
+ p++;
+ if ((int)name.length()>p) {
+ while(name.at(p).isSpace()) p++;
+
+ if (_objectVector.size() <= index) {
+ int newSize = index * 2;
+#if TRACE_LOADER
+ kdDebug() << " CachegrindLoader: objectVector enlarged to "
+ << newSize << endl;
+#endif
+ _objectVector.resize(newSize);
+ }
+
+ TQString realName = checkUnknown(name.mid(p));
+ o = (TraceObject*) _objectVector.at(index);
+ if (o && (o->name() != realName)) {
+ kdError() << _filename << ":" << _lineNo
+ << " - Redefinition of compressed ELF object index " << index
+ << " (was '" << o->name()
+ << "') to '" << realName << "'" << endl;
+ }
+
+ o = _data->object(realName);
+ _objectVector.insert(index, o);
+ }
+ else {
+ if ((_objectVector.size() <= index) ||
+ ( (o=(TraceObject*)_objectVector.at(index)) == 0)) {
+ kdError() << _filename << ":" << _lineNo
+ << " - Undefined compressed ELF object index " << index << endl;
+ return 0;
+ }
+ }
+
+ return o;
+}
+
+
+// Note: Callgrind sometimes gives different IDs for same file
+// (when references to same source file come from different ELF objects)
+TraceFile* CachegrindLoader::compressedFile(const TQString& name)
+{
+ if ((name[0] != '(') || !name[1].isDigit()) return _data->file(checkUnknown(name));
+
+ // compressed format using _fileVector
+ int p = name.find(')');
+ if (p<2) {
+ kdError() << _filename << ":" << _lineNo
+ << " - Invalid compressed file ('"
+ << name << "')" << endl;
+ return 0;
+ }
+ unsigned int index = name.mid(1, p-1).toUInt();
+ TraceFile* f = 0;
+ p++;
+ if ((int)name.length()>p) {
+ while(name.at(p).isSpace()) p++;
+
+ if (_fileVector.size() <= index) {
+ int newSize = index * 2;
+#if TRACE_LOADER
+ kdDebug() << " CachegrindLoader::fileVector enlarged to "
+ << newSize << endl;
+#endif
+ _fileVector.resize(newSize);
+ }
+
+ TQString realName = checkUnknown(name.mid(p));
+ f = (TraceFile*) _fileVector.at(index);
+ if (f && (f->name() != realName)) {
+ kdError() << _filename << ":" << _lineNo
+ << " - Redefinition of compressed file index " << index
+ << " (was '" << f->name()
+ << "') to '" << realName << "'" << endl;
+ }
+
+ f = _data->file(realName);
+ _fileVector.insert(index, f);
+ }
+ else {
+ if ((_fileVector.size() <= index) ||
+ ( (f=(TraceFile*)_fileVector.at(index)) == 0)) {
+ kdError() << _filename << ":" << _lineNo
+ << " - Undefined compressed file index " << index << endl;
+ return 0;
+ }
+ }
+
+ return f;
+}
+
+// Note: Callgrind gives different IDs even for same function
+// when parts of the function are from different source files.
+// Thus, it is no error when multiple indexes map to same function.
+TraceFunction* CachegrindLoader::compressedFunction(const TQString& name,
+ TraceFile* file,
+ TraceObject* object)
+{
+ if ((name[0] != '(') || !name[1].isDigit())
+ return _data->function(checkUnknown(name), file, object);
+
+ // compressed format using _functionVector
+ int p = name.find(')');
+ if (p<2) {
+ kdError() << _filename << ":" << _lineNo
+ << " - Invalid compressed function ('"
+ << name << "')" << endl;
+ return 0;
+ }
+
+
+ unsigned int index = name.mid(1, p-1).toUInt();
+ TraceFunction* f = 0;
+ p++;
+ if ((int)name.length()>p) {
+ while(name.at(p).isSpace()) p++;
+
+ if (_functionVector.size() <= index) {
+ int newSize = index * 2;
+#if TRACE_LOADER
+ kdDebug() << " CachegrindLoader::functionVector enlarged to "
+ << newSize << endl;
+#endif
+ _functionVector.resize(newSize);
+ }
+
+ TQString realName = checkUnknown(name.mid(p));
+ f = (TraceFunction*) _functionVector.at(index);
+ if (f && (f->name() != realName)) {
+ kdError() << _filename << ":" << _lineNo
+ << " - Redefinition of compressed function index " << index
+ << " (was '" << f->name()
+ << "') to '" << realName << "'" << endl;
+ }
+
+ f = _data->function(realName, file, object);
+ _functionVector.insert(index, f);
+
+#if TRACE_LOADER
+ kdDebug() << "compressedFunction: Inserted at Index " << index
+ << "\n " << f->fullName()
+ << "\n in " << f->cls()->fullName()
+ << "\n in " << f->file()->fullName()
+ << "\n in " << f->object()->fullName() << endl;
+#endif
+ }
+ else {
+ if ((_functionVector.size() <= index) ||
+ ( (f=(TraceFunction*)_functionVector.at(index)) == 0)) {
+ kdError() << _filename << ":" << _lineNo
+ << " - Undefined compressed function index "
+ << index << endl;
+ return 0;
+ }
+
+ // there was a check if the used function (returned from KCachegrinds
+ // model) has the same object and file as here given to us, but that was wrong:
+ // that holds only if we make this assumption on the model...
+ }
+
+ return f;
+}
+
+
+// make sure that a valid object is set, at least dummy with empty name
+void CachegrindLoader::ensureObject()
+{
+ if (currentObject) return;
+
+ currentObject = _data->object(_emptyString);
+ currentPartObject = currentObject->partObject(_part);
+}
+
+void CachegrindLoader::setObject(const TQString& name)
+{
+ currentObject = compressedObject(name);
+ if (!currentObject) {
+ kdError() << _filename << ":" << _lineNo
+ << " - Invalid object specification, setting to unknown" << endl;
+
+ currentObject = _data->object(_emptyString);
+ }
+
+ currentPartObject = currentObject->partObject(_part);
+ currentFunction = 0;
+ currentPartFunction = 0;
+}
+
+void CachegrindLoader::setCalledObject(const TQString& name)
+{
+ currentCalledObject = compressedObject(name);
+
+ if (!currentCalledObject) {
+ kdError() << _filename << ":" << _lineNo
+ << " - Invalid called specification, setting to unknown" << endl;
+
+ currentCalledObject = _data->object(_emptyString);
+ }
+
+ currentCalledPartObject = currentCalledObject->partObject(_part);
+}
+
+
+// make sure that a valid file is set, at least dummy with empty name
+void CachegrindLoader::ensureFile()
+{
+ if (currentFile) return;
+
+ currentFile = _data->file(_emptyString);
+ currentPartFile = currentFile->partFile(_part);
+}
+
+void CachegrindLoader::setFile(const TQString& name)
+{
+ currentFile = compressedFile(name);
+
+ if (!currentFile) {
+ kdWarning() << _filename << ":" << _lineNo
+ << " - Invalid file specification, setting to unknown" << endl;
+
+ currentFile = _data->file(_emptyString);
+ }
+
+ currentPartFile = currentFile->partFile(_part);
+ currentLine = 0;
+ currentPartLine = 0;
+}
+
+void CachegrindLoader::setCalledFile(const TQString& name)
+{
+ currentCalledFile = compressedFile(name);
+
+ if (!currentCalledFile) {
+ kdError() << _filename << ":" << _lineNo
+ << " - Invalid called file specification, setting to unknown" << endl;
+
+ currentCalledFile = _data->file(_emptyString);
+ }
+
+ currentCalledPartFile = currentCalledFile->partFile(_part);
+}
+
+// make sure that a valid function is set, at least dummy with empty name
+void CachegrindLoader::ensureFunction()
+{
+ if (currentFunction) return;
+
+ kdWarning() << _filename << ":" << _lineNo
+ << " - Function name not set" << endl;
+
+ ensureFile();
+ ensureObject();
+
+ currentFunction = _data->function(_emptyString,
+ currentFile,
+ currentObject);
+ currentPartFunction = currentFunction->partFunction(_part,
+ currentPartFile,
+ currentPartObject);
+}
+
+void CachegrindLoader::setFunction(const TQString& name)
+{
+ ensureFile();
+ ensureObject();
+
+ currentFunction = compressedFunction( name,
+ currentFile,
+ currentObject);
+
+ if (!currentFunction) {
+ kdWarning() << _filename << ":" << _lineNo
+ << " - Invalid function, setting to unknown" << endl;
+
+ currentFunction = _data->function(_emptyString,
+ currentFile,
+ currentObject);
+ }
+
+ currentPartFunction = currentFunction->partFunction(_part,
+ currentPartFile,
+ currentPartObject);
+
+ currentFunctionSource = 0;
+ currentLine = 0;
+ currentPartLine = 0;
+}
+
+void CachegrindLoader::setCalledFunction(const TQString& name)
+{
+ // if called object/file not set, use current object/file
+ if (!currentCalledObject) {
+ currentCalledObject = currentObject;
+ currentCalledPartObject = currentPartObject;
+ }
+
+ if (!currentCalledFile) {
+ // !=0 as functions needs file
+ currentCalledFile = currentFile;
+ currentCalledPartFile = currentPartFile;
+ }
+
+ currentCalledFunction = compressedFunction(name,
+ currentCalledFile,
+ currentCalledObject);
+ if (!currentCalledFunction) {
+ kdWarning() << _filename << ":" << _lineNo
+ << " - Invalid called function, setting to unknown" << endl;
+
+ currentCalledFunction = _data->function(_emptyString,
+ currentCalledFile,
+ currentCalledObject);
+ }
+
+ currentCalledPartFunction =
+ currentCalledFunction->partFunction(_part,
+ currentCalledPartFile,
+ currentCalledPartObject);
+}
+
+
+void CachegrindLoader::clearPosition()
+{
+ currentPos = PositionSpec();
+
+ // current function/line
+ currentFunction = 0;
+ currentPartFunction = 0;
+ currentFunctionSource = 0;
+ currentFile = 0;
+ currentPartFile = 0;
+ currentObject = 0;
+ currentPartObject = 0;
+ currentLine = 0;
+ currentPartLine = 0;
+ currentInstr = 0;
+ currentPartInstr = 0;
+
+ // current call
+ currentCalledObject = 0;
+ currentCalledPartObject = 0;
+ currentCalledFile = 0;
+ currentCalledPartFile = 0;
+ currentCalledFunction = 0;
+ currentCalledPartFunction = 0;
+ currentCallCount = 0;
+
+ // current jump
+ currentJumpToFile = 0;
+ currentJumpToFunction = 0;
+ targetPos = PositionSpec();
+ jumpsFollowed = 0;
+ jumpsExecuted = 0;
+
+ subMapping = 0;
+}
+
+
+/**
+ * The main import function...
+ */
+bool CachegrindLoader::loadTraceInternal(TracePart* part)
+{
+ clearCompression();
+ clearPosition();
+
+ _part = part;
+ _data = part->data();
+ TQFile* pFile = part->file();
+
+ if (!pFile) return false;
+
+ _filename = pFile->name();
+
+ FixFile file(pFile);
+ if (!file.exists()) {
+ kdError() << "File doesn't exist\n" << endl;
+ return false;
+ }
+ kdDebug() << "Loading " << _filename << " ..." << endl;
+ TQString statusMsg = i18n("Loading %1").arg(_filename);
+ int statusProgress = 0;
+ emit updateStatus(statusMsg,statusProgress);
+
+
+#if USE_FIXCOST
+ // FixCost Memory Pool
+ FixPool* pool = _data->fixPool();
+#endif
+
+ _lineNo = 0;
+ FixString line;
+ char c;
+ bool totalsSet = false;
+
+ // current position
+ nextLineType = SelfCost;
+ // default if there's no "positions:" line
+ hasLineInfo = true;
+ hasAddrInfo = false;
+
+ while (file.nextLine(line)) {
+
+ _lineNo++;
+
+#if TRACE_LOADER
+ kdDebug() << "[CachegrindLoader] " << _filename << ":" << _lineNo
+ << " - '" << TQString(line) << "'" << endl;
+#endif
+
+ // if we cannot strip a character, this was an empty line
+ if (!line.first(c)) continue;
+
+ if (c <= '9') {
+
+ if (c == '#') continue;
+
+ // parse position(s)
+ if (!parsePosition(line, currentPos)) {
+ kdError() << _filename << ":" << _lineNo
+ << " - Invalid position specification ('"
+ << TQString(line) << "')" << endl;
+ continue;
+ }
+
+ // go through after big switch
+ }
+ else { // if (c > '9')
+
+ line.stripFirst(c);
+
+ /* in order of probability */
+ switch(c) {
+
+ case 'f':
+
+ // fl=, fi=, fe=
+ if (line.stripPrefix("l=") ||
+ line.stripPrefix("i=") ||
+ line.stripPrefix("e=")) {
+
+ setFile(line);
+ continue;
+ }
+
+ // fn=
+ if (line.stripPrefix("n=")) {
+
+ setFunction(line);
+
+ // on a new function, update status
+ int progress = (int)(100.0 * file.current() / file.len() +.5);
+ if (progress != statusProgress) {
+ statusProgress = progress;
+
+ /* When this signal is connected, it most probably
+ * should lead to GUI update. Thus, when multiple
+ * "long operations" (like file loading) are in progress,
+ * this can temporarly switch to another operation.
+ */
+ emit updateStatus(statusMsg,statusProgress);
+ }
+
+ continue;
+ }
+
+ break;
+
+ case 'c':
+ // cob=
+ if (line.stripPrefix("ob=")) {
+ setCalledObject(line);
+ continue;
+ }
+
+ // cfi= / cfl=
+ if (line.stripPrefix("fl=") ||
+ line.stripPrefix("fi=")) {
+ setCalledFile(line);
+ continue;
+ }
+
+ // cfn=
+ if (line.stripPrefix("fn=")) {
+
+ setCalledFunction(line);
+ continue;
+ }
+
+ // calls=
+ if (line.stripPrefix("alls=")) {
+ // ignore long lines...
+ line.stripUInt64(currentCallCount);
+ nextLineType = CallCost;
+ continue;
+ }
+
+ // cmd:
+ if (line.stripPrefix("md:")) {
+ TQString command = TQString(line).stripWhiteSpace();
+ if (!_data->command().isEmpty() &&
+ _data->command() != command) {
+
+ kdWarning() << _filename << ":" << _lineNo
+ << " - Redefined command, was '"
+ << _data->command()
+ << "'" << endl;
+ }
+ _data->setCommand(command);
+ continue;
+ }
+
+ // creator:
+ if (line.stripPrefix("reator:")) {
+ // ignore ...
+ continue;
+ }
+
+ break;
+
+ case 'j':
+
+ // jcnd=
+ if (line.stripPrefix("cnd=")) {
+ bool valid;
+
+ valid = line.stripUInt64(jumpsFollowed) &&
+ line.stripPrefix("/") &&
+ line.stripUInt64(jumpsExecuted) &&
+ parsePosition(line, targetPos);
+
+ if (!valid) {
+ kdError() << _filename << ":" << _lineNo
+ << " - Invalid jcnd line" << endl;
+ }
+ else
+ nextLineType = CondJump;
+ continue;
+ }
+
+ if (line.stripPrefix("ump=")) {
+ bool valid;
+
+ valid = line.stripUInt64(jumpsExecuted) &&
+ parsePosition(line, targetPos);
+
+ if (!valid) {
+ kdError() << _filename << ":" << _lineNo
+ << " - Invalid jump line" << endl;
+ }
+ else
+ nextLineType = BoringJump;
+ continue;
+ }
+
+ // jfi=
+ if (line.stripPrefix("fi=")) {
+ currentJumpToFile = compressedFile(line);
+ continue;
+ }
+
+ // jfn=
+ if (line.stripPrefix("fn=")) {
+
+ if (!currentJumpToFile) {
+ // !=0 as functions needs file
+ currentJumpToFile = currentFile;
+ }
+
+ currentJumpToFunction =
+ compressedFunction(line,
+ currentJumpToFile,
+ currentObject);
+ continue;
+ }
+
+ break;
+
+ case 'o':
+
+ // ob=
+ if (line.stripPrefix("b=")) {
+ setObject(line);
+ continue;
+ }
+
+ break;
+
+ case '#':
+ continue;
+
+ case 't':
+
+ // totals:
+ if (line.stripPrefix("otals:")) continue;
+
+ // thread:
+ if (line.stripPrefix("hread:")) {
+ part->setThreadID(TQString(line).toInt());
+ continue;
+ }
+
+ // timeframe (BB):
+ if (line.stripPrefix("imeframe (BB):")) {
+ part->setTimeframe(line);
+ continue;
+ }
+
+ break;
+
+ case 'd':
+
+ // desc:
+ if (line.stripPrefix("esc:")) {
+
+ line.stripSurroundingSpaces();
+
+ // desc: Trigger:
+ if (line.stripPrefix("Trigger:")) {
+ part->setTrigger(line);
+ }
+
+ continue;
+ }
+ break;
+
+ case 'e':
+
+ // events:
+ if (line.stripPrefix("vents:")) {
+ subMapping = _data->mapping()->subMapping(line);
+ part->setFixSubMapping(subMapping);
+ continue;
+ }
+
+ // event:<name>[=<formula>][:<long name>]
+ if (line.stripPrefix("vent:")) {
+ line.stripSurroundingSpaces();
+
+ FixString e, f, l;
+ if (!line.stripName(e)) {
+ kdError() << _filename << ":" << _lineNo
+ << " - Invalid event" << endl;
+ continue;
+ }
+ line.stripSpaces();
+ if (!line.stripFirst(c)) continue;
+
+ if (c=='=') f = line.stripUntil(':');
+ line.stripSpaces();
+
+ // add to known cost types
+ if (line.isEmpty()) line = e;
+ TraceCostType::add(new TraceCostType(e,line,f));
+ continue;
+ }
+ break;
+
+ case 'p':
+
+ // part:
+ if (line.stripPrefix("art:")) {
+ part->setPartNumber(TQString(line).toInt());
+ continue;
+ }
+
+ // pid:
+ if (line.stripPrefix("id:")) {
+ part->setProcessID(TQString(line).toInt());
+ continue;
+ }
+
+ // positions:
+ if (line.stripPrefix("ositions:")) {
+ TQString positions(line);
+ hasLineInfo = (positions.find("line")>=0);
+ hasAddrInfo = (positions.find("instr")>=0);
+ continue;
+ }
+ break;
+
+ case 'v':
+
+ // version:
+ if (line.stripPrefix("ersion:")) {
+ part->setVersion(line);
+ continue;
+ }
+ break;
+
+ case 's':
+
+ // summary:
+ if (line.stripPrefix("ummary:")) {
+ if (!subMapping) {
+ kdError() << "No event line found. Skipping '" << _filename << endl;
+ return false;
+ }
+
+ part->totals()->set(subMapping, line);
+ continue;
+ }
+
+ case 'r':
+
+ // rcalls= (deprecated)
+ if (line.stripPrefix("calls=")) {
+ // handle like normal calls: we need the sum of call count
+ // recursive cost is discarded in cycle detection
+ line.stripUInt64(currentCallCount);
+ nextLineType = CallCost;
+
+ kdDebug() << "WARNING: This trace dump was generated by an old "
+ "version\n of the call-tree skin. Use a new one!" << endl;
+
+ continue;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ kdError() << _filename << ":" << _lineNo
+ << " - Invalid line '" << c << TQString(line) << "'" << endl;
+ continue;
+ }
+
+ if (!subMapping) {
+ kdError() << "No event line found. Skipping '" << _filename << "'" << endl;
+ return false;
+ }
+
+ // for a cost line, we always need a current function
+ ensureFunction();
+
+
+#if USE_FIXCOST
+ if (!currentFunctionSource ||
+ (currentFunctionSource->file() != currentFile))
+ currentFunctionSource = currentFunction->sourceFile(currentFile,
+ true);
+#else
+ if (hasAddrInfo) {
+ if (!currentInstr ||
+ (currentInstr->addr() != currentPos.fromAddr)) {
+ currentInstr = currentFunction->instr(currentPos.fromAddr,
+ true);
+
+ if (!currentInstr) {
+ kdError() << _filename << ":" << _lineNo
+ << " - Invalid address "
+ << currentPos.fromAddr.toString() << endl;
+
+ continue;
+ }
+
+ currentPartInstr = currentInstr->partInstr(part,
+ currentPartFunction);
+ }
+ }
+
+ if (hasLineInfo) {
+ if (!currentLine ||
+ (currentLine->lineno() != currentPos.fromLine)) {
+
+ currentLine = currentFunction->line(currentFile,
+ currentPos.fromLine,
+ true);
+ currentPartLine = currentLine->partLine(part,
+ currentPartFunction);
+ }
+ if (hasAddrInfo && currentInstr)
+ currentInstr->setLine(currentLine);
+ }
+#endif
+
+#if TRACE_LOADER
+ kdDebug() << _filename << ":" << _lineNo
+ << endl << " currentInstr "
+ << (currentInstr ? currentInstr->toString().ascii() : ".")
+ << endl << " currentLine "
+ << (currentLine ? currentLine->toString().ascii() : ".")
+ << "( file " << currentFile->name() << ")"
+ << endl << " currentFunction "
+ << currentFunction->prettyName().ascii()
+ << endl << " currentCalled "
+ << (currentCalledFunction ? currentCalledFunction->prettyName().ascii() : ".")
+ << endl;
+#endif
+
+ // create cost item
+
+ if (nextLineType == SelfCost) {
+
+#if USE_FIXCOST
+ new (pool) FixCost(part, pool,
+ currentFunctionSource,
+ currentPos,
+ currentPartFunction,
+ line);
+#else
+ if (hasAddrInfo) {
+ TracePartInstr* partInstr;
+ partInstr = currentInstr->partInstr(part, currentPartFunction);
+
+ if (hasLineInfo) {
+ // we need to set <line> back after reading for the line
+ int l = line.len();
+ const char* s = line.ascii();
+
+ partInstr->addCost(subMapping, line);
+ line.set(s,l);
+ }
+ else
+ partInstr->addCost(subMapping, line);
+ }
+
+ if (hasLineInfo) {
+ TracePartLine* partLine;
+ partLine = currentLine->partLine(part, currentPartFunction);
+ partLine->addCost(subMapping, line);
+ }
+#endif
+
+ if (!line.isEmpty()) {
+ kdError() << _filename << ":" << _lineNo
+ << " - Garbage at end of cost line ('"
+ << TQString(line) << "')" << endl;
+ }
+ }
+ else if (nextLineType == CallCost) {
+ nextLineType = SelfCost;
+
+ TraceCall* calling = currentFunction->calling(currentCalledFunction);
+ TracePartCall* partCalling =
+ calling->partCall(part, currentPartFunction,
+ currentCalledPartFunction);
+
+#if USE_FIXCOST
+ FixCallCost* fcc;
+ fcc = new (pool) FixCallCost(part, pool,
+ currentFunctionSource,
+ hasLineInfo ? currentPos.fromLine : 0,
+ hasAddrInfo ? currentPos.fromAddr : Addr(0),
+ partCalling,
+ currentCallCount, line);
+ fcc->setMax(_data->callMax());
+#else
+ if (hasAddrInfo) {
+ TraceInstrCall* instrCall;
+ TracePartInstrCall* partInstrCall;
+
+ instrCall = calling->instrCall(currentInstr);
+ partInstrCall = instrCall->partInstrCall(part, partCalling);
+ partInstrCall->addCallCount(currentCallCount);
+
+ if (hasLineInfo) {
+ // we need to set <line> back after reading for the line
+ int l = line.len();
+ const char* s = line.ascii();
+
+ partInstrCall->addCost(subMapping, line);
+ line.set(s,l);
+ }
+ else
+ partInstrCall->addCost(subMapping, line);
+
+ // update maximum of call cost
+ _data->callMax()->maxCost(partInstrCall);
+ }
+
+ if (hasLineInfo) {
+ TraceLineCall* lineCall;
+ TracePartLineCall* partLineCall;
+
+ lineCall = calling->lineCall(currentLine);
+ partLineCall = lineCall->partLineCall(part, partCalling);
+
+ partLineCall->addCallCount(currentCallCount);
+ partLineCall->addCost(subMapping, line);
+
+ // update maximum of call cost
+ _data->callMax()->maxCost(partLineCall);
+ }
+#endif
+ currentCalledFile = 0;
+ currentCalledPartFile = 0;
+ currentCalledObject = 0;
+ currentCalledPartObject = 0;
+ currentCallCount = 0;
+
+ if (!line.isEmpty()) {
+ kdError() << _filename << ":" << _lineNo
+ << " - Garbage at end of call cost line ('"
+ << TQString(line) << "')" << endl;
+ }
+ }
+ else { // (nextLineType == BoringJump || nextLineType == CondJump)
+
+ TraceFunctionSource* targetSource;
+
+ if (!currentJumpToFunction)
+ currentJumpToFunction = currentFunction;
+
+ targetSource = (currentJumpToFile) ?
+ currentJumpToFunction->sourceFile(currentJumpToFile, true) :
+ currentFunctionSource;
+
+#if USE_FIXCOST
+ new (pool) FixJump(part, pool,
+ /* source */
+ hasLineInfo ? currentPos.fromLine : 0,
+ hasAddrInfo ? currentPos.fromAddr : 0,
+ currentPartFunction,
+ currentFunctionSource,
+ /* target */
+ hasLineInfo ? targetPos.fromLine : 0,
+ hasAddrInfo ? targetPos.fromAddr : Addr(0),
+ currentJumpToFunction,
+ targetSource,
+ (nextLineType == CondJump),
+ jumpsExecuted, jumpsFollowed);
+#endif
+
+ if (0) {
+ kdDebug() << _filename << ":" << _lineNo
+ << " - jump from 0x" << currentPos.fromAddr.toString()
+ << " (line " << currentPos.fromLine
+ << ") to 0x" << targetPos.fromAddr.toString()
+ << " (line " << targetPos.fromLine << ")" << endl;
+
+ if (nextLineType == BoringJump)
+ kdDebug() << " Boring Jump, count " << jumpsExecuted.pretty() << endl;
+ else
+ kdDebug() << " Cond. Jump, followed " << jumpsFollowed.pretty()
+ << ", executed " << jumpsExecuted.pretty() << endl;
+ }
+
+ nextLineType = SelfCost;
+ currentJumpToFunction = 0;
+ currentJumpToFile = 0;
+
+ if (!line.isEmpty()) {
+ kdError() << _filename << ":" << _lineNo
+ << " - Garbage at end of jump cost line ('"
+ << TQString(line) << "')" << endl;
+ }
+
+ }
+ }
+
+
+ emit updateStatus(statusMsg,100);
+
+ _part->invalidate();
+ if (!totalsSet) {
+ _part->totals()->clear();
+ _part->totals()->addCost(_part);
+ }
+
+ pFile->close();
+
+ return true;
+}
+