You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdesdk/tdecachegrind/tdecachegrind/cachegrindloader.cpp

1324 lines
32 KiB

/* 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 <tdelocale.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;
}