void CostListItem::update() { if (!_costItem) return; TraceData* d = _costItem->data(); double total = d->subCost(_eventType); if (total == 0.0) { setText(0, QString("---")); setIcon(0, QPixmap()); return; } _pure = _costItem->subCost(_eventType); double pure = 100.0 * _pure / total; QString str; if (GlobalConfig::showPercentage()) str = QString("%1").arg(pure, 0, 'f', GlobalConfig::percentPrecision()); else str = _costItem->prettySubCost(_eventType); if (_skipped) { // special handling for skip entries... setText(0, QString("< %1").arg(str)); return; } setText(0, str); setIcon(0, costPixmap(_eventType, _costItem, total, false)); }
// 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::setFunction(const QString& name) { ensureFile(); ensureObject(); currentFunction = compressedFunction( name, currentFile, currentObject); if (!currentFunction) { error(QStringLiteral("Invalid function specification, setting to unknown")); currentFunction = _data->function(_emptyString, currentFile, currentObject); } currentPartFunction = currentFunction->partFunction(_part, currentPartFile, currentPartObject); currentFunctionSource = 0; currentLine = 0; currentPartLine = 0; }
void CachegrindLoader::setCalledFunction(const QString& 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) { error(QStringLiteral("Invalid called function, setting to unknown")); currentCalledFunction = _data->function(_emptyString, currentCalledFile, currentCalledObject); } currentCalledPartFunction = currentCalledFunction->partFunction(_part, currentCalledPartFile, currentCalledPartObject); }
// 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); }
MicroProfilerSection::~MicroProfilerSection() { if (!isProfiling_ || !profiling.isProfiling_) { return; } auto endTime = nowNs(); auto endNumProfileSections = profileSections; myTraceData.addTime(name_, endTime - startTime_, endNumProfileSections - startNumProfileSections_ - 1); }
// 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; }
TraceObject* CachegrindLoader::compressedObject(const QString& name) { if ((name[0] != '(') || !name[1].isDigit()) return _data->object(checkUnknown(name)); // compressed format using _objectVector int p = name.indexOf(')'); if (p<2) { error(QStringLiteral("Invalid compressed ELF object ('%1')").arg(name)); return 0; } int index = name.midRef(1, p-1).toInt(); TraceObject* o = 0; p++; while((name.length()>p) && name.at(p).isSpace()) p++; if (name.length()>p) { if (_objectVector.size() <= index) { int newSize = index * 2; #if TRACE_LOADER qDebug() << " CachegrindLoader: objectVector enlarged to " << newSize; #endif _objectVector.resize(newSize); } QString realName = checkUnknown(name.mid(p)); o = (TraceObject*) _objectVector.at(index); if (o && (o->name() != realName)) { error(QStringLiteral("Redefinition of compressed ELF object index %1 (was '%2') to %3") .arg(index).arg(o->name()).arg(realName)); } o = _data->object(realName); _objectVector.replace(index, o); } else { if ((_objectVector.size() <= index) || ( (o=(TraceObject*)_objectVector.at(index)) == 0)) { error(QStringLiteral("Undefined compressed ELF object index %1").arg(index)); return 0; } } return o; }
void CachegrindLoader::setCalledObject(const QString& name) { currentCalledObject = compressedObject(name); if (!currentCalledObject) { error(QStringLiteral("Invalid specification of called ELF object, setting to unknown")); currentCalledObject = _data->object(_emptyString); } currentCalledPartObject = currentCalledObject->partObject(_part); }
void CachegrindLoader::setCalledFile(const QString& name) { currentCalledFile = compressedFile(name); if (!currentCalledFile) { error(QStringLiteral("Invalid specification of called file, setting to unknown")); currentCalledFile = _data->file(_emptyString); } currentCalledPartFile = currentCalledFile->partFile(_part); }
void CachegrindLoader::setObject(const QString& name) { currentObject = compressedObject(name); if (!currentObject) { error(QString("Invalid ELF object specification, setting to unknown")); currentObject = _data->object(_emptyString); } currentPartObject = currentObject->partObject(_part); currentFunction = 0; currentPartFunction = 0; }
void CachegrindLoader::setFile(const QString& name) { currentFile = compressedFile(name); if (!currentFile) { error(QString("Invalid file specification, setting to unknown")); currentFile = _data->file(_emptyString); } currentPartFile = currentFile->partFile(_part); currentLine = 0; currentPartLine = 0; }
// make sure that a valid function is set, at least dummy with empty name void CachegrindLoader::ensureFunction() { if (currentFunction) return; error(QStringLiteral("Function not specified, setting to unknown")); ensureFile(); ensureObject(); currentFunction = _data->function(_emptyString, currentFile, currentObject); currentPartFunction = currentFunction->partFunction(_part, currentPartFile, currentPartObject); }
void CachegrindLoader::prepareNewPart() { if (_part) { // really new part needed? if (mapping == 0) return; // yes _part->invalidate(); _part->totals()->clear(); _part->totals()->addCost(_part); _data->addPart(_part); partsAdded++; } clearCompression(); clearPosition(); _part = new TracePart(_data); _part->setName(_filename); }
void EventTypeItem::update() { TraceData* d = _costItem ? _costItem->data() : 0; double total = d ? ((double)d->subCost(_eventType)) : 0.0; if (total == 0.0) { setText(1, "-"); setIcon(1, QIcon()); setText(2, "-"); setIcon(2, QIcon()); return; } TraceFunction* f = (_costItem && _costItem->type()==ProfileContext::Function) ? (TraceFunction*)_costItem : 0; ProfileCostArray* selfTotalCost = f ? f->data() : d; if (f && GlobalConfig::showExpanded()) { ProfileCostArray* parent = 0; switch(_groupType) { case ProfileContext::Object: parent = f->object(); break; case ProfileContext::Class: parent = f->cls(); break; case ProfileContext::File: parent = f->file(); break; case ProfileContext::FunctionCycle: parent = f->cycle(); break; default: break; } if (parent) selfTotalCost = parent; } if (_costItem && _costItem->type()==ProfileContext::FunctionCycle) { f = (TraceFunction*)_costItem; selfTotalCost = f->data(); } double selfTotal = selfTotalCost->subCost(_eventType); // for all cost items there is a self cost _pure = _costItem ? _costItem->subCost(_eventType) : SubCost(0); double pure = 100.0 * _pure / selfTotal; if (GlobalConfig::showPercentage()) { setText(2, QString("%1") .arg(pure, 0, 'f', GlobalConfig::percentPrecision())); } else if (_costItem) setText(2, _costItem->prettySubCost(_eventType)); setIcon(2, QIcon(costPixmap(_eventType, _costItem, selfTotal, false))); if (!f) { setText(1, "-"); setIcon(1, QIcon()); return; } _sum = f->inclusive()->subCost(_eventType); double sum = 100.0 * _sum / total; if (GlobalConfig::showPercentage()) { setText(1, QString("%1") .arg(sum, 0, 'f', GlobalConfig::percentPrecision())); } else setText(1, _sum.pretty()); setIcon(1, QIcon(costPixmap(_eventType, f->inclusive(), total, false))); }
namespace react { #if !MICRO_PROFILER_STUB_IMPLEMENTATION struct TraceData { TraceData(); ~TraceData(); void addTime(MicroProfilerName name, uint_fast64_t time, uint_fast32_t internalClockCalls); std::thread::id threadId_; uint_fast64_t startTime_; std::atomic_uint_fast64_t times_[MicroProfilerName::__LENGTH__] = {}; std::atomic_uint_fast32_t calls_[MicroProfilerName::__LENGTH__] = {}; std::atomic_uint_fast32_t childProfileSections_[MicroProfilerName::__LENGTH__] = {}; }; struct ProfilingImpl { std::mutex mutex_; std::vector<TraceData*> allTraceData_; bool isProfiling_ = false; uint_fast64_t startTime_; uint_fast64_t endTime_; uint_fast64_t clockOverhead_; uint_fast64_t profileSectionOverhead_; }; static ProfilingImpl profiling; thread_local TraceData myTraceData; thread_local uint_fast32_t profileSections = 0; static uint_fast64_t nowNs() { struct timespec time; clock_gettime(CLOCK_REALTIME, &time); return uint_fast64_t(1000000000) * time.tv_sec + time.tv_nsec; } static uint_fast64_t diffNs(uint_fast64_t start, uint_fast64_t end) { return end - start; } static std::string formatTimeNs(uint_fast64_t timeNs) { std::ostringstream out; out.precision(2); if (timeNs < 1000) { out << timeNs << "ns"; } else if (timeNs < 1000000) { out << timeNs / 1000.0 << "us"; } else { out << std::fixed << timeNs / 1000000.0 << "ms"; } return out.str(); } MicroProfilerSection::MicroProfilerSection(MicroProfilerName name) : isProfiling_(profiling.isProfiling_), name_(name), startNumProfileSections_(profileSections) { if (!isProfiling_) { return; } profileSections++; startTime_ = nowNs(); } MicroProfilerSection::~MicroProfilerSection() { if (!isProfiling_ || !profiling.isProfiling_) { return; } auto endTime = nowNs(); auto endNumProfileSections = profileSections; myTraceData.addTime(name_, endTime - startTime_, endNumProfileSections - startNumProfileSections_ - 1); } TraceData::TraceData() : threadId_(std::this_thread::get_id()) { std::lock_guard<std::mutex> lock(profiling.mutex_); profiling.allTraceData_.push_back(this); } TraceData::~TraceData() { std::lock_guard<std::mutex> lock(profiling.mutex_); auto& infos = profiling.allTraceData_; infos.erase(std::remove(infos.begin(), infos.end(), this), infos.end()); } void TraceData::addTime(MicroProfilerName name, uint_fast64_t time, uint_fast32_t childprofileSections) { times_[name] += time; calls_[name]++; childProfileSections_[name] += childprofileSections; } static void printReport() { LOG(ERROR) << "======= MICRO PROFILER REPORT ======="; LOG(ERROR) << "- Total Time: " << formatTimeNs(diffNs(profiling.startTime_, profiling.endTime_)); LOG(ERROR) << "- Clock Overhead: " << formatTimeNs(profiling.clockOverhead_); LOG(ERROR) << "- Profiler Section Overhead: " << formatTimeNs(profiling.profileSectionOverhead_); for (auto info : profiling.allTraceData_) { LOG(ERROR) << "--- Thread ID 0x" << std::hex << info->threadId_ << " ---"; for (int i = 0; i < MicroProfilerName::__LENGTH__; i++) { if (info->times_[i] > 0) { auto totalTime = info->times_[i].load(); auto calls = info->calls_[i].load(); auto clockOverhead = profiling.clockOverhead_ * calls + profiling.profileSectionOverhead_ * info->childProfileSections_[i].load(); if (totalTime < clockOverhead) { LOG(ERROR) << "- " << MicroProfiler::profilingNameToString(static_cast<MicroProfilerName>(i)) << ": " << "ERROR: Total time was " << totalTime << "ns but clock overhead was calculated to be " << clockOverhead << "ns!"; } else { auto correctedTime = totalTime - clockOverhead; auto timePerCall = correctedTime / calls; LOG(ERROR) << "- " << MicroProfiler::profilingNameToString(static_cast<MicroProfilerName>(i)) << ": " << formatTimeNs(correctedTime) << " (" << calls << " calls, " << formatTimeNs(timePerCall) << "/call)"; } } } } } static void clearProfiling() { CHECK(!profiling.isProfiling_) << "Trying to clear profiling but profiling was already started!"; for (auto info : profiling.allTraceData_) { for (unsigned int i = 0; i < MicroProfilerName::__LENGTH__; i++) { info->times_[i] = 0; info->calls_[i] = 0; info->childProfileSections_[i] = 0; } } } static uint_fast64_t calculateClockOverhead() { int numCalls = 1000000; uint_fast64_t start = nowNs(); for (int i = 0; i < numCalls; i++) { nowNs(); } uint_fast64_t end = nowNs(); return (end - start) / numCalls; } static uint_fast64_t calculateProfileSectionOverhead() { int numCalls = 1000000; uint_fast64_t start = nowNs(); profiling.isProfiling_ = true; for (int i = 0; i < numCalls; i++) { MICRO_PROFILER_SECTION(static_cast<MicroProfilerName>(0)); } uint_fast64_t end = nowNs(); profiling.isProfiling_ = false; return (end - start) / numCalls; } void MicroProfiler::startProfiling() { CHECK(!profiling.isProfiling_) << "Trying to start profiling but profiling was already started!"; profiling.clockOverhead_ = calculateClockOverhead(); profiling.profileSectionOverhead_ = calculateProfileSectionOverhead(); std::lock_guard<std::mutex> lock(profiling.mutex_); clearProfiling(); profiling.startTime_ = nowNs(); profiling.isProfiling_ = true; } void MicroProfiler::stopProfiling() { CHECK(profiling.isProfiling_) << "Trying to stop profiling but profiling hasn't been started!"; profiling.isProfiling_ = false; profiling.endTime_ = nowNs(); std::lock_guard<std::mutex> lock(profiling.mutex_); printReport(); clearProfiling(); } bool MicroProfiler::isProfiling() { return profiling.isProfiling_; } void MicroProfiler::runInternalBenchmark() { MicroProfiler::startProfiling(); for (int i = 0; i < 1000000; i++) { MICRO_PROFILER_SECTION_NAMED(outer, __INTERNAL_BENCHMARK_OUTER); { MICRO_PROFILER_SECTION_NAMED(inner, __INTERNAL_BENCHMARK_INNER); } } MicroProfiler::stopProfiling(); } #else void MicroProfiler::startProfiling() { CHECK(false) << "This platform has a stub implementation of the micro profiler and cannot collect traces"; } void MicroProfiler::stopProfiling() { } bool MicroProfiler::isProfiling() { return false; } void MicroProfiler::runInternalBenchmark() { } #endif } }
/** * 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; }
// 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 QString& 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.indexOf(')'); if (p<2) { error(QStringLiteral("Invalid compressed function ('%1')").arg(name)); return 0; } int index = name.midRef(1, p-1).toUInt(); TraceFunction* f = 0; p++; while((name.length()>p) && name.at(p).isSpace()) p++; if (name.length()>p) { if (_functionVector.size() <= index) { int newSize = index * 2; #if TRACE_LOADER qDebug() << " CachegrindLoader::functionVector enlarged to " << newSize; #endif _functionVector.resize(newSize); } QString realName = checkUnknown(name.mid(p)); f = (TraceFunction*) _functionVector.at(index); if (f && (f->name() != realName)) { error(QStringLiteral("Redefinition of compressed function index %1 (was '%2') to %3") .arg(index).arg(f->name()).arg(realName)); } f = _data->function(realName, file, object); _functionVector.replace(index, f); #if TRACE_LOADER qDebug() << "compressedFunction: Inserted at Index " << index << "\n " << f->fullName() << "\n in " << f->cls()->fullName() << "\n in " << f->file()->fullName() << "\n in " << f->object()->fullName(); #endif } else { if ((_functionVector.size() <= index) || ( (f=(TraceFunction*)_functionVector.at(index)) == 0)) { error(QStringLiteral("Undefined compressed function index %1").arg(index)); 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; }
void CostTypeItem::update() { TraceData* d = _costItem ? _costItem->data() : 0; double total = d ? ((double)d->subCost(_costType)) : 0.0; if (total == 0.0) { setText(1, "-"); setPixmap(1, QPixmap()); setText(2, "-"); setPixmap(2, QPixmap()); return; } TraceFunction* f = (_costItem->type()==TraceCost::Function) ? (TraceFunction*)_costItem : 0; TraceCost* selfTotalCost = f ? f->data() : d; if (f && Configuration::showExpanded()) { switch(_groupType) { case TraceCost::Object: selfTotalCost = f->object(); break; case TraceCost::Class: selfTotalCost = f->cls(); break; case TraceCost::File: selfTotalCost = f->file(); break; case TraceCost::FunctionCycle: selfTotalCost = f->cycle(); break; default: break; } } if (_costItem->type()==TraceCost::FunctionCycle) { f = (TraceFunction*)_costItem; selfTotalCost = f->data(); } double selfTotal = selfTotalCost->subCost(_costType); // for all cost items there's a self cost _pure = _costItem ? _costItem->subCost(_costType) : SubCost(0); double pure = 100.0 * _pure / selfTotal; if (Configuration::showPercentage()) { setText(2, QString("%1") .arg(pure, 0, 'f', Configuration::percentPrecision())); } else setText(2, _costItem->prettySubCost(_costType)); setPixmap(2, costPixmap(_costType, _costItem, selfTotal, false)); if (!f) { setText(1, "-"); setPixmap(1, QPixmap()); return; } _sum = f->inclusive()->subCost(_costType); double sum = 100.0 * _sum / total; if (Configuration::showPercentage()) { setText(1, QString("%1") .arg(sum, 0, 'f', Configuration::percentPrecision())); } else setText(1, _sum.pretty()); setPixmap(1, costPixmap(_costType, f->inclusive(), total, false)); }