// Note: Callgrind sometimes gives different IDs for same file // (when references to same source file come from different ELF objects) TraceFile* CachegrindLoader::compressedFile(const QString& name) { if ((name[0] != '(') || !name[1].isDigit()) return _data->file(checkUnknown(name)); // compressed format using _fileVector int p = name.indexOf(')'); if (p<2) { error(QStringLiteral("Invalid compressed file ('%1')").arg(name)); return 0; } int index = name.midRef(1, p-1).toUInt(); TraceFile* f = 0; p++; while((name.length()>p) && name.at(p).isSpace()) p++; if (name.length()>p) { if (_fileVector.size() <= index) { int newSize = index * 2; #if TRACE_LOADER qDebug() << " CachegrindLoader::fileVector enlarged to " << newSize; #endif _fileVector.resize(newSize); } QString realName = checkUnknown(name.mid(p)); f = (TraceFile*) _fileVector.at(index); if (f && (f->name() != realName)) { error(QStringLiteral("Redefinition of compressed file index %1 (was '%2') to %3") .arg(index).arg(f->name()).arg(realName)); } f = _data->file(realName); _fileVector.replace(index, f); } else { if ((_fileVector.size() <= index) || ( (f=(TraceFile*)_fileVector.at(index)) == 0)) { error(QStringLiteral("Undefined compressed file index %1").arg(index)); return 0; } } return f; }
/** * The main import function... */ int CachegrindLoader::loadInternal(TraceData* data, QIODevice* device, const QString& filename) { if (!data || !device) return 0; _data = data; _filename = filename; _lineNo = 0; loadStart(_filename); FixFile file(device, _filename); if (!file.exists()) { loadFinished(QStringLiteral("File does not exist")); return 0; } int statusProgress = 0; #if USE_FIXCOST // FixCost Memory Pool FixPool* pool = _data->fixPool(); #endif _part = 0; partsAdded = 0; prepareNewPart(); FixString line; char c; // current position nextLineType = SelfCost; // default if there is no "positions:" line hasLineInfo = true; hasAddrInfo = false; while (file.nextLine(line)) { _lineNo++; #if TRACE_LOADER qDebug() << "[CachegrindLoader] " << _filename << ":" << _lineNo << " - '" << QString(line) << "'"; #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)) { error(QStringLiteral("Invalid position specification '%1'").arg(line)); continue; } // go through after big switch } else { // if (c > '9') line.stripFirst(c); /* in order of probability */ switch(c) { case 'f': // fl= if (line.stripPrefix("l=")) { setFile(line); // this is the default for new functions currentFunctionFile = currentFile; continue; } // fi=, fe= if (line.stripPrefix("i=") || line.stripPrefix("e=")) { setFile(line); continue; } // fn= if (line.stripPrefix("n=")) { if (currentFile != currentFunctionFile) currentFile = currentFunctionFile; 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. */ loadProgress(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:")) { QString command = QString(line).trimmed(); if (!_data->command().isEmpty() && _data->command() != command) { error(QStringLiteral("Redefined command, was '%1'").arg(_data->command())); } _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) { error(QStringLiteral("Invalid line after 'jcnd'")); } else nextLineType = CondJump; continue; } if (line.stripPrefix("ump=")) { bool valid; valid = line.stripUInt64(jumpsExecuted) && parsePosition(line, targetPos); if (!valid) { error(QStringLiteral("Invalid line after 'jump'")); } 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 'a': // "arch: arm" if (line.stripPrefix("rch: arm")) { TraceData::Arch a = _data->architecture(); if ((a != TraceData::ArchUnknown) && (a != TraceData::ArchARM)) { error(QStringLiteral("Redefined architecture!")); } _data->setArchitecture(TraceData::ArchARM); continue; } break; case 't': // totals: if (line.stripPrefix("otals:")) continue; // thread: if (line.stripPrefix("hread:")) { prepareNewPart(); _part->setThreadID(QString(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:")) { prepareNewPart(); mapping = _data->eventTypes()->createMapping(line); _part->setEventMapping(mapping); continue; } // event:<name>[=<formula>][:<long name>] if (line.stripPrefix("vent:")) { line.stripSurroundingSpaces(); FixString e, f, l; if (!line.stripName(e)) { error(QStringLiteral("Invalid event")); 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; EventType::add(new EventType(e,line,f)); continue; } break; case 'p': // part: if (line.stripPrefix("art:")) { prepareNewPart(); _part->setPartNumber(QString(line).toInt()); continue; } // pid: if (line.stripPrefix("id:")) { prepareNewPart(); _part->setProcessID(QString(line).toInt()); continue; } // positions: if (line.stripPrefix("ositions:")) { prepareNewPart(); QString positions(line); hasLineInfo = positions.contains(QStringLiteral("line")); hasAddrInfo = positions.contains(QStringLiteral("instr")); continue; } break; case 'v': // version: if (line.stripPrefix("ersion:")) { // ignore for now continue; } break; case 's': // summary: if (line.stripPrefix("ummary:")) { if (!mapping) { error(QStringLiteral("No event line found. Skipping file")); delete _part; return false; } _part->totals()->set(mapping, 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; warning(QStringLiteral("Old file format using deprecated 'rcalls'")); continue; } break; default: break; } error(QStringLiteral("Invalid line '%1%2'").arg(c).arg(line)); continue; } if (!mapping) { error(QStringLiteral("No event line found. Skipping file")); delete _part; return false; } // for a cost line, we always need a current function ensureFunction(); if (!currentFunctionSource || (currentFunctionSource->file() != currentFile)) { currentFunctionSource = currentFunction->sourceFile(currentFile, true); } #if !USE_FIXCOST if (hasAddrInfo) { if (!currentInstr || (currentInstr->addr() != currentPos.fromAddr)) { currentInstr = currentFunction->instr(currentPos.fromAddr, true); if (!currentInstr) { error(QString("Invalid address '%1'").arg(currentPos.fromAddr.toString())); continue; } currentPartInstr = currentInstr->partInstr(_part, currentPartFunction); } } if (hasLineInfo) { if (!currentLine || (currentLine->lineno() != currentPos.fromLine)) { currentLine = currentFunctionSource->line(currentPos.fromLine, true); currentPartLine = currentLine->partLine(_part, currentPartFunction); } if (hasAddrInfo && currentInstr) currentInstr->setLine(currentLine); } #endif #if TRACE_LOADER qDebug() << _filename << ":" << _lineNo; qDebug() << " currentInstr " << (currentInstr ? qPrintable(currentInstr->toString()) : "."); qDebug() << " currentLine " << (currentLine ? qPrintable(currentLine->toString()) : ".") << "( file " << currentFile->name() << ")"; qDebug() << " currentFunction " << qPrintable(currentFunction->prettyName()); qDebug() << " currentCalled " << (currentCalledFunction ? qPrintable(currentCalledFunction->prettyName()) : "."); #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(mapping, line); line.set(s,l); } else partInstr->addCost(mapping, line); } if (hasLineInfo) { TracePartLine* partLine; partLine = currentLine->partLine(_part, currentPartFunction); partLine->addCost(mapping, line); } #endif if (!line.isEmpty()) { error(QStringLiteral("Garbage at end of cost line ('%1')").arg(line)); } } 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()); _data->updateMaxCallCount(fcc->callCount()); #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(mapping, line); line.set(s,l); } else partInstrCall->addCost(mapping, line); // update maximum of call cost _data->callMax()->maxCost(partInstrCall); _data->updateMaxCallCount(partInstrCall->callCount()); } if (hasLineInfo) { TraceLineCall* lineCall; TracePartLineCall* partLineCall; lineCall = calling->lineCall(currentLine); partLineCall = lineCall->partLineCall(_part, partCalling); partLineCall->addCallCount(currentCallCount); partLineCall->addCost(mapping, line); // update maximum of call cost _data->callMax()->maxCost(partLineCall); _data->updateMaxCallCount(partLineCall->callCount()); } #endif currentCalledFile = 0; currentCalledPartFile = 0; currentCalledObject = 0; currentCalledPartObject = 0; currentCallCount = 0; if (!line.isEmpty()) { error(QStringLiteral("Garbage at end of call cost line ('%1')").arg(line)); } } 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); #else if (hasAddrInfo) { TraceInstr* jumpToInstr; TraceInstrJump* instrJump; TracePartInstrJump* partInstrJump; jumpToInstr = currentJumpToFunction->instr(targetPos.fromAddr, true); instrJump = currentInstr->instrJump(jumpToInstr, (nextLineType == CondJump)); partInstrJump = instrJump->partInstrJump(_part); partInstrJump->addExecutedCount(jumpsExecuted); if (nextLineType == CondJump) partInstrJump->addFollowedCount(jumpsFollowed); } if (hasLineInfo) { TraceLine* jumpToLine; TraceLineJump* lineJump; TracePartLineJump* partLineJump; jumpToLine = targetSource->line(targetPos.fromLine, true); lineJump = currentLine->lineJump(jumpToLine, (nextLineType == CondJump)); partLineJump = lineJump->partLineJump(_part); partLineJump->addExecutedCount(jumpsExecuted); if (nextLineType == CondJump) partLineJump->addFollowedCount(jumpsFollowed); } #endif if (0) { qDebug() << _filename << ":" << _lineNo << " - jump from 0x" << currentPos.fromAddr.toString() << " (line " << currentPos.fromLine << ") to 0x" << targetPos.fromAddr.toString() << " (line " << targetPos.fromLine << ")"; if (nextLineType == BoringJump) qDebug() << " Boring Jump, count " << jumpsExecuted.pretty(); else qDebug() << " Cond. Jump, followed " << jumpsFollowed.pretty() << ", executed " << jumpsExecuted.pretty(); } nextLineType = SelfCost; currentJumpToFunction = 0; currentJumpToFile = 0; if (!line.isEmpty()) { error(QStringLiteral("Garbage at end of jump cost line ('%1')").arg(line)); } } } loadFinished(); if (mapping) { _part->invalidate(); _part->totals()->clear(); _part->totals()->addCost(_part); data->addPart(_part); partsAdded++; } else { delete _part; } device->close(); return partsAdded; }