summaryrefslogtreecommitdiffstats
path: root/kcachegrind/kcachegrind/tracedata.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kcachegrind/kcachegrind/tracedata.cpp')
-rw-r--r--kcachegrind/kcachegrind/tracedata.cpp5072
1 files changed, 5072 insertions, 0 deletions
diff --git a/kcachegrind/kcachegrind/tracedata.cpp b/kcachegrind/kcachegrind/tracedata.cpp
new file mode 100644
index 00000000..85f4f2e1
--- /dev/null
+++ b/kcachegrind/kcachegrind/tracedata.cpp
@@ -0,0 +1,5072 @@
+/* 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., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+
+#include <stdlib.h>
+
+#include <qfile.h>
+#include <qdir.h>
+#include <qfileinfo.h>
+#include <qregexp.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+
+#include "tracedata.h"
+#include "toplevel.h"
+#include "loader.h"
+#include "configuration.h"
+#include "utils.h"
+#include "fixcost.h"
+
+
+#define TRACE_DEBUG 0
+#define TRACE_ASSERTIONS 0
+
+const int TraceCost::MaxRealIndex = MaxRealIndexValue;
+const int TraceCost::InvalidIndex = -1;
+
+//---------------------------------------------------
+// Addr
+
+bool Addr::set(FixString& s)
+{
+ return s.stripUInt64(_v);
+}
+
+int Addr::set(const char *s)
+{
+ int n = 0;
+ _v = 0;
+
+ while((n<16) && *s) {
+ if ((*s>='0') && (*s<='9'))
+ _v = 16*_v + (*s-'0');
+ else if ((*s>='a') && (*s<='f'))
+ _v = 16*_v + 10 + (*s-'a');
+ else if ((*s>='A') && (*s<='F'))
+ _v = 16*_v + 10 + (*s-'A');
+ else break;
+ s++;
+ n++;
+ }
+
+ return n;
+}
+
+
+QString Addr::toString() const
+{
+ if (_v == 0) return QString("0");
+
+ uint64 n = _v;
+ QString hex;
+#if (QT_VERSION-0 >= 0x030200)
+ hex.reserve(16);
+#endif
+
+ while(n>0) {
+ int d = (n & 15);
+ hex = QChar((d<10) ? ('0'+d) : ('A'-10+d)) + hex;
+ n /= 16;
+ }
+
+ return hex;
+}
+
+QString Addr::pretty() const
+{
+ if (_v == 0) return QString("0");
+
+ uint64 n = _v;
+ int p = 0;
+ QString hex;
+#if (QT_VERSION-0 >= 0x030200)
+ hex.reserve(20);
+#endif
+
+ while(n>0) {
+ int d = (n & 15);
+ if ((p>0) && ((p%4)==0)) hex = " " + hex;
+ hex = QChar((d<10) ? ('0'+d) : ('A'-10+d)) + hex;
+ n /= 16;
+ p++;
+ }
+
+ return hex;
+}
+
+bool Addr::isInRange(Addr a, int distance)
+{
+ uint64 diff = (a._v > _v) ? (a._v - _v) : (_v - a._v);
+ uint64 dist = (distance<0) ? distance : -distance;
+ return (diff < dist);
+}
+
+//---------------------------------------------------
+// TraceItem
+
+QString* TraceItem::_typeName = 0;
+QString* TraceItem::_i18nTypeName = 0;
+
+TraceItem::TraceItem()
+{
+ _position = 0;
+ _dep = 0;
+ _dirty = true;
+}
+
+TraceItem::~TraceItem()
+{}
+
+void TraceItem::cleanup()
+{
+ if (_typeName) {
+ delete [] _typeName;
+ _typeName = 0;
+ }
+ if (_i18nTypeName) {
+ delete [] _i18nTypeName;
+ _i18nTypeName = 0;
+ }
+}
+
+QString TraceItem::typeName(CostType t)
+{
+ if (!_typeName) {
+ _typeName = new QString [MaxCostType+1];
+ QString* strs = _typeName;
+ for(int i=0;i<=MaxCostType;i++)
+ strs[i] = QString("?");
+
+ strs[Item] = I18N_NOOP("Abstract Item");
+ strs[Cost] = I18N_NOOP("Cost Item");
+ strs[PartLine] = I18N_NOOP("Part Source Line");
+ strs[Line] = I18N_NOOP("Source Line");
+ strs[PartLineCall] = I18N_NOOP("Part Line Call");
+ strs[LineCall] = I18N_NOOP("Line Call");
+ strs[PartLineJump] = I18N_NOOP("Part Jump");
+ strs[LineJump] = I18N_NOOP("Jump");
+ strs[PartInstr] = I18N_NOOP("Part Instruction");
+ strs[Instr] = I18N_NOOP("Instruction");
+ strs[PartInstrJump] = I18N_NOOP("Part Instruction Jump");
+ strs[InstrJump] = I18N_NOOP("Instruction Jump");
+ strs[PartInstrCall] = I18N_NOOP("Part Instruction Call");
+ strs[InstrCall] = I18N_NOOP("Instruction Call");
+ strs[PartCall] = I18N_NOOP("Part Call");
+ strs[Call] = I18N_NOOP("Call");
+ strs[PartFunction] = I18N_NOOP("Part Function");
+ strs[FunctionSource] = I18N_NOOP("Function Source File");
+ strs[Function] = I18N_NOOP("Function");
+ strs[FunctionCycle] = I18N_NOOP("Function Cycle");
+ strs[PartClass] = I18N_NOOP("Part Class");
+ strs[Class] = I18N_NOOP("Class");
+ strs[PartFile] = I18N_NOOP("Part Source File");
+ strs[File] = I18N_NOOP("Source File");
+ strs[PartObject] = I18N_NOOP("Part ELF Object");
+ strs[Object] = I18N_NOOP("ELF Object");
+ strs[Part] = I18N_NOOP("Profile Part");
+ strs[Data] = I18N_NOOP("Program Trace");
+ }
+ if (t<0 || t> MaxCostType) t = MaxCostType;
+ return _typeName[t];
+}
+
+TraceItem::CostType TraceItem::costType(QString s)
+{
+ // This is the default cost Type
+ if (s.isEmpty()) return Function;
+
+ CostType type;
+ for (int i=0; i<MaxCostType;i++) {
+ type = (CostType) i;
+ if (typeName(type) == s)
+ return type;
+ }
+ return NoCostType;
+}
+
+// all strings of typeName() are translatable because of I18N_NOOP there
+QString TraceItem::i18nTypeName(CostType t)
+{
+ if (!_i18nTypeName) {
+ _i18nTypeName = new QString [MaxCostType+1];
+ for(int i=0;i<=MaxCostType;i++)
+ _i18nTypeName[i] = i18n(typeName((CostType)i).utf8().data());
+ }
+ if (t<0 || t> MaxCostType) t = MaxCostType;
+ return _i18nTypeName[t];
+}
+
+TraceItem::CostType TraceItem::i18nCostType(QString s)
+{
+ // This is the default cost Type
+ if (s.isEmpty()) return Function;
+
+ CostType type;
+ for (int i=0; i<MaxCostType;i++) {
+ type = (CostType) i;
+ if (i18nTypeName(type) == s)
+ return type;
+ }
+ return NoCostType;
+}
+
+
+void TraceItem::clear()
+{
+ invalidate();
+}
+
+
+QString TraceItem::costString(TraceCostMapping*)
+{
+ return QString("(no cost)");
+}
+
+QString TraceItem::name() const
+{
+ if (part()) {
+ return i18n("%1 from %2")
+ .arg(_dep->name())
+ .arg(part()->name());
+ }
+
+ if (_dep)
+ return _dep->name();
+
+ return i18n("(unknown)");
+}
+
+QString TraceItem::prettyName() const
+{
+ if (name().isEmpty()) return i18n("(unknown)");
+ return name();
+}
+
+
+QString TraceItem::fullName() const
+{
+ return QString("%1 %2")
+ .arg(typeName(type())).arg(prettyName());
+}
+
+QString TraceItem::toString()
+{
+ return QString("%1\n [%3]").arg(fullName()).arg(costString(0));
+}
+
+void TraceItem::invalidate()
+{
+ if (_dirty) return;
+ _dirty = true;
+
+ if (_dep)
+ _dep->invalidate();
+}
+
+void TraceItem::update()
+{
+ _dirty = false;
+}
+
+TracePart* TraceItem::part()
+{
+ return _position ? _position->part() : 0;
+}
+
+const TracePart* TraceItem::part() const
+{
+ return _position ? _position->part() : 0;
+}
+
+TraceData* TraceItem::data()
+{
+ return _position ? _position->data() : 0;
+}
+
+const TraceData* TraceItem::data() const
+{
+ return _position ? _position->data() : 0;
+}
+
+
+//---------------------------------------------------
+// TraceCost
+
+TraceCost::TraceCost()
+ : TraceItem()
+{
+ _cachedType = 0; // no virtual value cached
+
+ TraceCost::clear();
+}
+
+TraceCost::~TraceCost()
+{}
+
+
+void TraceCost::clear()
+{
+ // simple set usage count to 0
+ _count = 0;
+
+ TraceItem::clear();
+}
+
+
+
+void TraceCost::set(TraceSubMapping* sm, const char* s)
+{
+ if (!sm) return;
+ if (!s) {
+ if (_count>0) clear();
+ return;
+ }
+
+ while(*s == ' ') s++;
+
+ if (sm->isIdentity()) {
+ int i = 0;
+ while(i<sm->count()) {
+ if (!_cost[i].set(&s)) break;
+ i++;
+ }
+ _count = i;
+ }
+ else {
+ int i = 0, maxIndex = 0, index;
+ while(1) {
+ index = sm->realIndex(i);
+ if (maxIndex<index) maxIndex=index;
+ if (index == TraceCost::InvalidIndex) break;
+ if (!_cost[index].set(&s)) break;
+ i++;
+ }
+ // we have to set all costs of unused indexes till maxIndex to zero
+ for(i=sm->firstUnused(); i<=maxIndex; i=sm->nextUnused(i))
+ _cost[i] = 0;
+ _count = maxIndex;
+ }
+ // a cost change has to be propagated (esp. in subclasses)
+ invalidate();
+}
+
+void TraceCost::set(TraceSubMapping* sm, FixString & s)
+{
+ if (!sm) return;
+
+ s.stripSpaces();
+
+ if (sm->isIdentity()) {
+ int i = 0;
+ while(i<sm->count()) {
+ if (!s.stripUInt64(_cost[i])) break;
+ i++;
+ }
+ _count = i;
+ }
+ else {
+ int i = 0, maxIndex = 0, index;
+ while(1) {
+ index = sm->realIndex(i);
+ if (maxIndex<index) maxIndex=index;
+ if (index == TraceCost::InvalidIndex) break;
+ if (!s.stripUInt64(_cost[index])) break;
+ i++;
+ }
+ // we have to set all costs of unused indexes till maxIndex to zero
+ for(i=sm->firstUnused(); i<=maxIndex; i=sm->nextUnused(i))
+ _cost[i] = 0;
+ _count = maxIndex+1;
+ }
+ invalidate();
+}
+
+
+void TraceCost::addCost(TraceSubMapping* sm, const char* s)
+{
+ if (!sm || !s) return;
+
+ SubCost v;
+
+ if (sm->isIdentity()) {
+ int i = 0;
+ while(i<sm->count()) {
+ if (!v.set(&s)) break;
+ if (i<_count)
+ _cost[i] += v;
+ else
+ _cost[i] = v;
+ i++;
+ }
+ if (i > _count) _count = i;
+ }
+ else {
+ int i = 0, maxIndex = 0, index;
+ while(1) {
+ if (!v.set(&s)) break;
+ index = sm->realIndex(i);
+ if (maxIndex<index) maxIndex=index;
+ if (index == TraceCost::InvalidIndex) break;
+ if (index<_count)
+ _cost[index] += v;
+ else
+ _cost[index] = v;
+ i++;
+ }
+ if (maxIndex >= _count) {
+ /* we have to set all costs of unused indexes in the interval
+ * [_count;maxIndex] to zero */
+ for(i=sm->nextUnused(_count-1); i<=maxIndex; i=sm->nextUnused(i))
+ _cost[i] = 0;
+ _count = maxIndex+1;
+ }
+ }
+
+ // a cost change has to be propagated (esp. in subclasses)
+ invalidate();
+
+#if TRACE_DEBUG
+ _dirty = false; // don't recurse !
+ qDebug("%s\n now %s", fullName().ascii(),
+ TraceCost::costString(0).ascii());
+ _dirty = true; // because of invalidate()
+#endif
+}
+
+void TraceCost::addCost(TraceSubMapping* sm, FixString & s)
+{
+ if (!sm) return;
+
+ s.stripSpaces();
+
+ SubCost v;
+
+ if (sm->isIdentity()) {
+ int i = 0;
+ while(i<sm->count()) {
+ if (!s.stripUInt64(v)) break;
+ if (i<_count)
+ _cost[i] += v;
+ else
+ _cost[i] = v;
+ i++;
+ }
+ if (i > _count) _count = i;
+ }
+ else {
+ int i = 0, maxIndex = 0, index;
+ while(1) {
+ if (!s.stripUInt64(v)) break;
+ index = sm->realIndex(i);
+ if (maxIndex<index) maxIndex=index;
+ if (index == TraceCost::InvalidIndex) break;
+ if (index<_count)
+ _cost[index] += v;
+ else
+ _cost[index] = v;
+ i++;
+ }
+ if (maxIndex >= _count) {
+ /* we have to set all costs of unused indexes in the interval
+ * [_count;maxIndex] to zero */
+ for(i=sm->nextUnused(_count-1); i<=maxIndex; i=sm->nextUnused(i))
+ _cost[i] = 0;
+ _count = maxIndex+1;
+ }
+ }
+
+ invalidate();
+
+#if TRACE_DEBUG
+ _dirty = false; // don't recurse !
+ qDebug("%s\n now %s", fullName().ascii(),
+ TraceCost::costString(0).ascii());
+ _dirty = true; // because of invalidate()
+#endif
+}
+
+
+// update each subcost to be maximum of old and given costs
+void TraceCost::maxCost(TraceSubMapping* sm, FixString & s)
+{
+ if (!sm) return;
+
+ s.stripSpaces();
+
+ SubCost v;
+
+ if (sm->isIdentity()) {
+ int i = 0;
+ while(i<sm->count()) {
+ if (!s.stripUInt64(v)) break;
+ if (i<_count) {
+ if (v>_cost[i]) _cost[i] = v;
+ }
+ else
+ _cost[i] = v;
+ i++;
+ }
+ if (i > _count) _count = i;
+ }
+ else {
+ int i = 0, maxIndex = 0, index;
+ while(1) {
+ if (!s.stripUInt64(v)) break;
+ index = sm->realIndex(i);
+ if (maxIndex<index) maxIndex=index;
+ if (index == TraceCost::InvalidIndex) break;
+ if (index<_count) {
+ if (v>_cost[index]) _cost[index] = v;
+ }
+ else
+ _cost[index] = v;
+ i++;
+ }
+ if (maxIndex >= _count) {
+ /* we have to set all costs of unused indexes in the interval
+ * [_count;maxIndex] to zero */
+ for(i=sm->nextUnused(_count-1); i<=maxIndex; i=sm->nextUnused(i))
+ _cost[i] = 0;
+ _count = maxIndex+1;
+ }
+ }
+
+ invalidate();
+
+#if TRACE_DEBUG
+ _dirty = false; // don't recurse !
+ qDebug("%s\n now %s", fullName().ascii(),
+ TraceCost::costString(0).ascii());
+ _dirty = true; // because of invalidate()
+#endif
+}
+
+
+void TraceCost::addCost(TraceCost* item)
+{
+ int i;
+
+ if (!item) return;
+
+ // we have to update the other item if needed
+ // because we access the item costs directly
+ if (item->_dirty) item->update();
+
+ if (item->_count < _count) {
+ for (i = 0; i<item->_count; i++)
+ _cost[i] += item->_cost[i];
+ }
+ else {
+ for (i = 0; i<_count; i++)
+ _cost[i] += item->_cost[i];
+ for (; i<item->_count; i++)
+ _cost[i] = item->_cost[i];
+ _count = item->_count;
+ }
+
+ // a cost change has to be propagated (esp. in subclasses)
+ invalidate();
+
+#if TRACE_DEBUG
+ _dirty = false; // don't recurse !
+ qDebug("%s added cost item\n %s\n now %s",
+ fullName().ascii(), item->fullName().ascii(),
+ TraceCost::costString(0).ascii());
+ _dirty = true; // because of invalidate()
+#endif
+}
+
+void TraceCost::maxCost(TraceCost* item)
+{
+ int i;
+
+ if (!item) return;
+
+ // we have to update the other item if needed
+ // because we access the item costs directly
+ if (item->_dirty) item->update();
+
+ if (item->_count < _count) {
+ for (i = 0; i<item->_count; i++)
+ if (_cost[i] < item->_cost[i]) _cost[i] = item->_cost[i];
+ }
+ else {
+ for (i = 0; i<_count; i++)
+ if (_cost[i] < item->_cost[i]) _cost[i] = item->_cost[i];
+ for (; i<item->_count; i++)
+ _cost[i] = item->_cost[i];
+ _count = item->_count;
+ }
+
+ // a cost change has to be propagated (esp. in subclasses)
+ invalidate();
+
+#if TRACE_DEBUG
+ _dirty = false; // don't recurse !
+ qDebug("%s added cost item\n %s\n now %s",
+ fullName().ascii(), item->fullName().ascii(),
+ TraceCost::costString(0).ascii());
+ _dirty = true; // because of invalidate()
+#endif
+}
+
+void TraceCost::addCost(int type, SubCost value)
+{
+ if (type<0 || type>=MaxRealIndex) return;
+ if (type<_count)
+ _cost[type] += value;
+ else {
+ for(int i=_count;i<type;i++)
+ _cost[i] = 0;
+ _cost[type] = value;
+ _count = type+1;
+ }
+
+ // a cost change has to be propagated (esp. in subclasses)
+ invalidate();
+}
+
+void TraceCost::maxCost(int type, SubCost value)
+{
+ if (type<0 || type>=MaxRealIndex) return;
+ if (type<_count) {
+ if (value>_cost[type]) _cost[type] = value;
+ }
+ else {
+ for(int i=_count;i<type;i++)
+ _cost[i] = 0;
+ _cost[type] = value;
+ _count = type+1;
+ }
+
+ // a cost change has to be propagated (esp. in subclasses)
+ invalidate();
+}
+
+
+TraceCost TraceCost::diff(TraceCost* item)
+{
+ TraceCost res;
+
+ // we have to update the other item if needed
+ // because we access the item costs directly
+ if (item->_dirty) item->update();
+
+ int maxCount = (item->_count > _count) ? item->_count : _count;
+
+ res._count = maxCount;
+ for (int i=0; i<maxCount;i++)
+ res._cost[i] = item->subCost(i) - subCost(i);
+
+ return res;
+}
+
+QString TraceCost::costString(TraceCostMapping* m)
+{
+ QString res;
+
+ if (_dirty) update();
+
+ int maxIndex = m ? m->realCount() : TraceCost::MaxRealIndex;
+ for (int i = 0; i<maxIndex; i++) {
+ if (!res.isEmpty()) res += ", ";
+ if (m) res += m->type(i)->name() + " ";
+
+ res += subCost(i).pretty();
+ }
+ return res;
+}
+
+
+void TraceCost::invalidate()
+{
+ if (_dirty) return;
+ _dirty = true;
+ _cachedType = 0; // cached value is invalid, too
+
+ if (_dep)
+ _dep->invalidate();
+}
+
+void TraceCost::update()
+{
+ _dirty = false;
+}
+
+// this is only for real types
+SubCost TraceCost::subCost(int idx)
+{
+ if (idx<0) return 0;
+
+ /* update if needed as cost could be calculated dynamically in subclasses
+ * this can change _count !! */
+ if (_dirty) update();
+ if (idx>=_count) return 0;
+
+ return _cost[idx];
+}
+
+SubCost TraceCost::subCost(TraceCostType* t)
+{
+ if (!t) return 0;
+ if (_cachedType != t) {
+ _cachedType = t;
+ _cachedCost = t->subCost(this);
+ }
+ return _cachedCost;
+}
+
+QString TraceCost::prettySubCost(TraceCostType* t)
+{
+ return subCost(t).pretty();
+}
+
+
+
+//---------------------------------------------------
+// TraceJumpCost
+
+TraceJumpCost::TraceJumpCost()
+ :TraceItem()
+{
+ TraceJumpCost::clear();
+}
+
+TraceJumpCost::~TraceJumpCost()
+{}
+
+SubCost TraceJumpCost::executedCount()
+{
+ if (_dirty) update();
+
+ return _executedCount;
+}
+
+SubCost TraceJumpCost::followedCount()
+{
+ if (_dirty) update();
+
+ return _followedCount;
+}
+
+QString TraceJumpCost::costString(TraceCostMapping*)
+{
+ if (_dirty) update();
+
+ return QString("%1/%2")
+ .arg(_followedCount.pretty())
+ .arg(_executedCount.pretty());
+}
+
+void TraceJumpCost::clear()
+{
+ _followedCount = 0;
+ _executedCount = 0;
+}
+
+void TraceJumpCost::addCost(TraceJumpCost* item)
+{
+ if (item->_dirty) item->update();
+
+ _followedCount += item->followedCount();
+ _executedCount += item->executedCount();
+}
+
+
+//---------------------------------------------------
+// TraceCostType
+
+QPtrList<TraceCostType>* TraceCostType::_knownTypes = 0;
+
+TraceCostType::TraceCostType(QString name, QString longName, QString formula)
+{
+ _name = name;
+ _longName = longName;
+ _formula = formula;
+ _mapping = 0;
+ _realIndex = TraceCost::InvalidIndex;
+ _parsed = false;
+ _inParsing = false;
+
+ for (int i=0; i<TraceCost::MaxRealIndex;i++)
+ _coefficient[i] = 0;
+}
+
+void TraceCostType::setFormula(QString formula)
+{
+ _formula = formula;
+ _realIndex = TraceCost::InvalidIndex;
+ _parsed = false;
+}
+
+void TraceCostType::setMapping(TraceCostMapping* m)
+{
+ _parsed = false;
+ _mapping = m;
+}
+
+// setting the index to TraceCost::MaxRealIndex makes it a
+// real type with unspecified index
+void TraceCostType::setRealIndex(int i)
+{
+ if (i<0 || i>TraceCost::MaxRealIndex)
+ i=TraceCost::InvalidIndex;
+
+ _realIndex = i;
+ _formula = QString::null;
+}
+
+// checks for existing types and sets coefficients
+bool TraceCostType::parseFormula()
+{
+ if (_parsed) return true;
+ if (_inParsing) {
+ qDebug("TraceCostType::parseFormula: Recursion detected.");
+ return false;
+ }
+
+ if (!_mapping) {
+ qDebug("TraceCostType::parseFormula: No mapping set!");
+ return false;
+ }
+
+ _inParsing = true;
+
+ for (int i=0; i<TraceCost::MaxRealIndex;i++)
+ _coefficient[i] = 0;
+
+ QRegExp rx( "((?:\\+|\\-)?)\\s*(\\d*)\\s*\\*?\\s*(\\w+)" );
+
+ int factor, pos;
+ QString costName;
+ TraceCostType* costType;
+
+ pos = 0;
+ while (1) {
+ pos = rx.search(_formula, pos);
+ if (pos<0) break;
+ pos += rx.matchedLength();
+ if (rx.cap(0).isEmpty()) break;
+
+ //qDebug("parseFormula: matched '%s','%s','%s'",
+ // rx.cap(1).ascii(), rx.cap(2).ascii(), rx.cap(3).ascii());
+
+ costName = rx.cap(3);
+ costType = _mapping->type(costName);
+ if (!costType) {
+ // qDebug("Cost type '%s': In formula cost '%s' unknown.",
+ // _name.ascii(), costName.ascii());
+
+ _inParsing = false;
+ return false;
+ }
+
+ factor = (rx.cap(2).isEmpty()) ? 1 : rx.cap(2).toInt();
+ if (rx.cap(1) == "-") factor = -factor;
+
+ if (costType->isReal())
+ _coefficient[costType->realIndex()] += factor;
+ else {
+ costType->parseFormula();
+ for (int i=0; i<TraceCost::MaxRealIndex;i++)
+ _coefficient[i] += factor * costType->_coefficient[i];
+ }
+ }
+
+ _inParsing = false;
+ _parsed = true;
+
+ return true;
+}
+
+QString TraceCostType::parsedFormula()
+{
+ QString res;
+
+ if (!parseFormula()) return res;
+
+ for (int i=0; i<TraceCost::MaxRealIndex;i++) {
+ int c = _coefficient[i];
+ if (c == 0) continue;
+
+ if (!res.isEmpty()) {
+ res += " ";
+ if (c>0) res += "+ ";
+ }
+ if (c<0) { res += "- "; c = -c; }
+ res += QString::number(c);
+
+ TraceCostType* t = _mapping->type(i);
+ if (!t) continue;
+
+ if (!t->name().isEmpty())
+ res += QString(" * %1").arg(t->name());
+ }
+
+ return res;
+}
+
+SubCost TraceCostType::subCost(TraceCost* c)
+{
+ if (_realIndex != TraceCost::InvalidIndex)
+ return c->subCost(_realIndex);
+
+ if (!_parsed) {
+ if (!parseFormula()) return 0;
+ }
+ SubCost res = 0;
+
+ int rc = _mapping->realCount();
+ for (int i = 0;i<rc;i++)
+ if (_coefficient[i] != 0)
+ res += _coefficient[i] * c->subCost(i);
+
+ return res;
+}
+
+int TraceCostType::histCost(TraceCost* c, double total, double* hist)
+{
+ if (total == 0.0) return 0;
+
+ if (!_parsed) {
+ if (!parseFormula()) return 0;
+ }
+
+ int rc = _mapping->realCount();
+ for (int i = 0;i<rc;i++) {
+ if (_coefficient[i] != 0)
+ hist[i] = _coefficient[i] * c->subCost(i) / total;
+ else
+ hist[i] = 0.0;
+ }
+
+ return rc;
+}
+
+
+
+
+TraceCostType* TraceCostType::knownRealType(QString n)
+{
+ if (!_knownTypes) return 0;
+
+ TraceCostType* t;
+ for (t=_knownTypes->first();t;t=_knownTypes->next())
+ if (t->isReal() && (t->name() == n)) {
+ TraceCostType* type = new TraceCostType(*t);
+ return type;
+ }
+
+ return 0;
+}
+
+TraceCostType* TraceCostType::knownVirtualType(QString n)
+{
+ if (!_knownTypes) return 0;
+
+ TraceCostType* t;
+ for (t=_knownTypes->first();t;t=_knownTypes->next())
+ if (!t->isReal() && (t->name() == n)) {
+ TraceCostType* type = new TraceCostType(*t);
+ return type;
+ }
+
+ return 0;
+}
+
+// we take ownership
+void TraceCostType::add(TraceCostType* t)
+{
+ if (!t) return;
+
+ t->setMapping(0);
+
+ if (!_knownTypes)
+ _knownTypes = new QPtrList<TraceCostType>;
+
+ /* Already known? */
+ TraceCostType* kt;
+ for (kt=_knownTypes->first();kt;kt=_knownTypes->next())
+ if (kt->name() == t->name()) break;
+
+ if (kt) {
+ // Overwrite old type
+ if (!t->longName().isEmpty() &&
+ (t->longName() != t->name())) kt->setLongName(t->longName());
+ if (!t->formula().isEmpty()) kt->setFormula(t->formula());
+
+ delete t;
+ }
+ else {
+ if (t->longName().isEmpty()) t->setLongName(t->name());
+ _knownTypes->append(t);
+ }
+}
+
+
+int TraceCostType::knownTypeCount()
+{
+ if (!_knownTypes) return 0;
+
+ return _knownTypes->count();
+}
+
+bool TraceCostType::remove(QString n)
+{
+ if (!_knownTypes) return false;
+
+ TraceCostType* t;
+ for (t=_knownTypes->first();t;t=_knownTypes->next())
+ if (!t->isReal() && (t->name() == n)) {
+ _knownTypes->removeRef(t);
+ delete t;
+ return true;
+ }
+
+ return false;
+}
+
+TraceCostType* TraceCostType::knownType(int i)
+{
+ if (!_knownTypes) return 0;
+ if (i<0 || i>=(int)_knownTypes->count()) return 0;
+
+ return _knownTypes->at(i);
+}
+
+QColor TraceCostType::color()
+{
+ if (!_mapping) return QColor();
+ return _mapping->realColors()[_realIndex];
+}
+
+
+//---------------------------------------------------
+// TraceCostMapping
+
+TraceCostMapping::TraceCostMapping()
+{
+ _realCount = 0;
+ _virtualCount = 0;
+ for (int i=0;i<TraceCost::MaxRealIndex;i++) _real[i] = 0;
+ for (int i=0;i<TraceCost::MaxRealIndex;i++) _virtual[i] = 0;
+}
+
+TraceCostMapping::~TraceCostMapping()
+{
+ for (int i=0;i<TraceCost::MaxRealIndex;i++)
+ if (_real[i]) delete _real[i];
+
+ for (int i=0;i<TraceCost::MaxRealIndex;i++)
+ if (_virtual[i]) delete _virtual[i];
+}
+
+TraceSubMapping* TraceCostMapping::subMapping(QString types, bool create)
+{
+ // first check if there's enough space in the mapping
+ int newCount = 0;
+ int pos = 0, pos2, len = types.length();
+
+ while (1) {
+ // skip space
+ while((pos<len) && types[pos].isSpace()) pos++;
+
+ pos2 = pos;
+ while((pos2<len) && !types[pos2].isSpace()) pos2++;
+ if (pos2 == pos) break;
+
+ if (realIndex(types.mid(pos,pos2-pos)) == TraceCost::InvalidIndex)
+ newCount++;
+
+ pos = pos2;
+ }
+
+ if (!create && (newCount>0)) return 0;
+
+ if (newCount+_realCount > TraceCost::MaxRealIndex) {
+ kdDebug() << "TraceCostMapping::subMapping: No space for "
+ << newCount << " sub costs." << endl;
+ return 0;
+ }
+
+ TraceSubMapping* sm = new TraceSubMapping(this);
+
+ pos = 0;
+ while (1) {
+ // skip space
+ while((pos<len) && types[pos].isSpace()) pos++;
+
+ pos2 = pos;
+ while((pos2<len) && !types[pos2].isSpace()) pos2++;
+ if (pos2 == pos) break;
+
+ sm->append(addReal(types.mid(pos,pos2-pos)));
+
+ pos = pos2;
+ }
+
+ return sm;
+}
+
+
+int TraceCostMapping::addReal(QString t)
+{
+ int index = realIndex(t);
+ if (index>=0) return index;
+
+ TraceCostType* ct = TraceCostType::knownRealType(t);
+ if (!ct) ct = new TraceCostType(t, t);
+
+ // make it real
+ ct->setRealIndex();
+
+ return add(ct);
+}
+
+// add a cost type to a mapping
+// this transfers ownership of the type!
+int TraceCostMapping::add(TraceCostType* ct)
+{
+ if (!ct) return TraceCost::InvalidIndex;
+
+ ct->setMapping(this);
+
+ if (ct->isReal()) {
+ if (_realCount >= TraceCost::MaxRealIndex) {
+ qDebug("WARNING: Maximum for real cost types reached (on adding '%s')",
+ ct->name().ascii());
+ return TraceCost::InvalidIndex;
+ }
+ _real[_realCount] = ct;
+ ct->setRealIndex(_realCount);
+ _realColor[_realCount] = Configuration::costTypeColor(ct);
+
+ _realCount++;
+ return _realCount-1;
+ }
+
+ if (_virtualCount >= TraceCost::MaxRealIndex) {
+ qDebug("WARNING: Maximum for virtual cost types reached (on adding '%s')",
+ ct->name().ascii());
+ return TraceCost::InvalidIndex;
+ }
+ _virtual[_virtualCount] = ct;
+ _virtualCount++;
+ return _virtualCount-1;
+}
+
+// we delete the type: t is invalid when returning true!
+bool TraceCostMapping::remove(TraceCostType* t)
+{
+ if (!t) return false;
+ if (t->mapping() != this) return false;
+
+ // don't delete real types
+ if (t->isReal()) return false;
+
+ int i;
+ for(i=0;i<_virtualCount;i++)
+ if (_virtual[i] == t) break;
+
+ // not found?
+ if (i == _virtualCount) return false;
+
+ // delete known type with same name
+ TraceCostType::remove(t->name());
+
+ // delete this type
+ _virtual[i] = 0;
+ delete t;
+ if (i+1 == _virtualCount) {
+ // we can reuse the last index
+ _virtualCount--;
+ }
+ return true;
+}
+
+
+TraceCostType* TraceCostMapping::realType(int t)
+{
+ if (t<0 || t>=_realCount) return 0;
+ return _real[t];
+}
+
+TraceCostType* TraceCostMapping::virtualType(int t)
+{
+ if (t<0 || t>=_virtualCount) return 0;
+ return _virtual[t];
+}
+
+
+TraceCostType* TraceCostMapping::type(int t)
+{
+ if (t<0) return 0;
+ if (t<_realCount) return _real[t];
+
+ t -= TraceCost::MaxRealIndex;
+ if (t<0) return 0;
+ if (t<_virtualCount) return _virtual[t];
+
+ return 0;
+}
+
+TraceCostType* TraceCostMapping::type(QString name)
+{
+ for (int i=0;i<_realCount;i++)
+ if (_real[i] && (_real[i]->name() == name))
+ return _real[i];
+
+ for (int i=0;i<_virtualCount;i++)
+ if (_virtual[i] && (_virtual[i]->name() == name))
+ return _virtual[i];
+
+ return 0;
+}
+
+TraceCostType* TraceCostMapping::typeForLong(QString name)
+{
+ for (int i=0;i<_realCount;i++)
+ if (_real[i] && (_real[i]->longName() == name))
+ return _real[i];
+
+ for (int i=0;i<_virtualCount;i++)
+ if (_virtual[i] && (_virtual[i]->longName() == name))
+ return _virtual[i];
+
+ return 0;
+}
+
+
+int TraceCostMapping::realIndex(QString name)
+{
+ for (int i=0;i<_realCount;i++)
+ if (_real[i] && (_real[i]->name() == name))
+ return i;
+
+ return TraceCost::InvalidIndex;
+}
+
+int TraceCostMapping::index(QString name)
+{
+ for (int i=0;i<_realCount;i++)
+ if (_real[i] && (_real[i]->name() == name))
+ return i;
+
+ for (int i=0;i<_virtualCount;i++)
+ if (_virtual[i] && (_virtual[i]->name() == name))
+ return TraceCost::MaxRealIndex + 1 + i;
+
+ return TraceCost::InvalidIndex;
+}
+
+int TraceCostMapping::addKnownVirtualTypes()
+{
+ int addCount = 0;
+ int addDiff, i;
+ int knownCount = TraceCostType::knownTypeCount();
+
+ while (1) {
+ addDiff = 0;
+ for (i=0; i<knownCount; i++) {
+ TraceCostType* t = TraceCostType::knownType(i);
+ if (t->isReal()) continue;
+ if (index(t->name()) != TraceCost::InvalidIndex) continue;
+ t->setMapping(this);
+ if (t->parseFormula()) {
+ addDiff++;
+ add(new TraceCostType(t->name(), t->longName(), t->formula()));
+ }
+ t->setMapping(0);
+ }
+ if (addDiff == 0) break;
+ addCount += addDiff;
+ }
+ return addCount;
+}
+
+
+//---------------------------------------------------
+// TraceSubMapping
+
+TraceSubMapping::TraceSubMapping(TraceCostMapping* mapping)
+{
+ _mapping = mapping;
+ clear();
+}
+
+void TraceSubMapping::clear()
+{
+ _count = 0;
+ _isIdentity = true;
+ _firstUnused = 0;
+ for(int i=0;i<TraceCost::MaxRealIndex;i++) {
+ _realIndex[i] = TraceCost::InvalidIndex;
+ _nextUnused[i] = i+1;
+ }
+}
+
+bool TraceSubMapping::append(QString type, bool create)
+{
+ if (!_mapping) return false;
+ int index = create ? _mapping->addReal(type) : _mapping->realIndex(type);
+
+ return append(index);
+}
+
+bool TraceSubMapping::append(int type)
+{
+ if (!_mapping) return false;
+ if ((type<0) || (type >= _mapping->realCount())) return false;
+
+ if ( _count >= TraceCost::MaxRealIndex) return false;
+
+ _realIndex[_count] = type;
+
+ if (_isIdentity && (_count != type)) _isIdentity = false;
+ if (type == _firstUnused)
+ _firstUnused = _nextUnused[type];
+ for(int i=0;i<type;i++)
+ if (_nextUnused[i] == type)
+ _nextUnused[i]=_nextUnused[type];
+
+ _count++;
+ return true;
+}
+
+
+//---------------------------------------------------
+// TraceCallCost
+
+TraceCallCost::TraceCallCost()
+{
+ _callCount = 0;
+}
+
+TraceCallCost::~TraceCallCost()
+{}
+
+
+QString TraceCallCost::costString(TraceCostMapping* m)
+{
+ return QString("%1, Calls %2")
+ .arg(TraceCost::costString(m))
+ .arg(_callCount.pretty());
+}
+
+QString TraceCallCost::prettyCallCount()
+{
+ return _callCount.pretty();
+}
+
+void TraceCallCost::clear()
+{
+ _callCount = 0;
+ TraceCost::clear();
+}
+
+SubCost TraceCallCost::callCount()
+{
+ if (_dirty) update();
+
+ return _callCount;
+}
+
+void TraceCallCost::addCallCount(SubCost c)
+{
+ _callCount += c;
+
+ invalidate();
+}
+
+
+//---------------------------------------------------
+// TraceInclusiveCost
+
+TraceInclusiveCost::TraceInclusiveCost()
+{}
+
+TraceInclusiveCost::~TraceInclusiveCost()
+{}
+
+QString TraceInclusiveCost::costString(TraceCostMapping* m)
+{
+ return QString("%1, Inclusive %2")
+ .arg(TraceCost::costString(m))
+ .arg(_inclusive.costString(m));
+}
+
+void TraceInclusiveCost::clear()
+{
+ _inclusive.clear();
+ TraceCost::clear();
+}
+
+TraceCost* TraceInclusiveCost::inclusive()
+{
+ if (_dirty) update();
+
+ return &_inclusive;
+}
+
+void TraceInclusiveCost::addInclusive(TraceCost* c)
+{
+ _inclusive.addCost(c);
+
+ invalidate();
+}
+
+
+//---------------------------------------------------
+// TraceListCost
+
+TraceListCost::TraceListCost()
+{
+ _lastDep = 0;
+}
+
+TraceListCost::~TraceListCost()
+{}
+
+void TraceListCost::addDep(TraceCost* dep)
+{
+#if TRACE_ASSERTIONS
+ if (_deps.findRef(dep)>=0) {
+ qDebug("addDep: %s already in list!",
+ dep->fullName().ascii());
+ return;
+ }
+#endif
+
+ _deps.append(dep);
+ _lastDep = dep;
+ invalidate();
+
+#if TRACE_DEBUG
+ qDebug("%s added\n %s (now %d)",
+ fullName().ascii(), dep->fullName().ascii(),
+ _deps.count());
+#endif
+}
+
+TraceCost* TraceListCost::findDepFromPart(TracePart* part)
+{
+ if (_lastDep && _lastDep->part() == part)
+ return _lastDep;
+
+ TraceCost* dep;
+ for (dep = _deps.first(); dep; dep = _deps.next())
+ if (dep->part() == part) {
+ _lastDep = dep;
+ return dep;
+ }
+ return 0;
+}
+
+
+void TraceListCost::update()
+{
+ if (!_dirty) return;
+
+#if TRACE_DEBUG
+ qDebug("update %s (count %d)",
+ fullName().ascii(), _deps.count());
+#endif
+
+ clear();
+ TraceCost* item;
+ for (item = _deps.first(); item; item = _deps.next()) {
+ if (onlyActiveParts())
+ if (!item->part() || !item->part()->isActive()) continue;
+
+ addCost(item);
+ }
+
+ _dirty = false;
+
+#if TRACE_DEBUG
+ qDebug(" > %s", costString(0).ascii());
+#endif
+}
+
+
+
+//---------------------------------------------------
+// TraceJumpListCost
+
+TraceJumpListCost::TraceJumpListCost()
+{
+ _lastDep = 0;
+}
+
+TraceJumpListCost::~TraceJumpListCost()
+{}
+
+void TraceJumpListCost::addDep(TraceJumpCost* dep)
+{
+#if TRACE_ASSERTIONS
+ if (_deps.findRef(dep)>=0) {
+ qDebug("addDep: %s already in list!",
+ dep->fullName().ascii());
+ return;
+ }
+#endif
+
+ _deps.append(dep);
+ _lastDep = dep;
+ invalidate();
+
+#if TRACE_DEBUG
+ qDebug("%s added\n %s (now %d)",
+ fullName().ascii(), dep->fullName().ascii(),
+ _deps.count());
+#endif
+}
+
+TraceJumpCost* TraceJumpListCost::findDepFromPart(TracePart* part)
+{
+ if (_lastDep && _lastDep->part() == part)
+ return _lastDep;
+
+ TraceJumpCost* dep;
+ for (dep = _deps.first(); dep; dep = _deps.next())
+ if (dep->part() == part) {
+ _lastDep = dep;
+ return dep;
+ }
+ return 0;
+}
+
+
+void TraceJumpListCost::update()
+{
+ if (!_dirty) return;
+
+#if TRACE_DEBUG
+ qDebug("update %s (count %d)",
+ fullName().ascii(), _deps.count());
+#endif
+
+ clear();
+ TraceJumpCost* item;
+ for (item = _deps.first(); item; item = _deps.next()) {
+ if (onlyActiveParts())
+ if (!item->part() || !item->part()->isActive()) continue;
+
+ addCost(item);
+ }
+
+ _dirty = false;
+
+#if TRACE_DEBUG
+ qDebug(" > %s", costString(0).ascii());
+#endif
+}
+
+
+
+//---------------------------------------------------
+// TraceCallListCost
+
+TraceCallListCost::TraceCallListCost()
+{
+ _lastDep = 0;
+}
+
+TraceCallListCost::~TraceCallListCost()
+{}
+
+void TraceCallListCost::addDep(TraceCallCost* dep)
+{
+#if TRACE_ASSERTIONS
+ if (_deps.findRef(dep)>=0) {
+ qDebug("addDep: %s already in list!",
+ dep->fullName().ascii());
+ return;
+ }
+#endif
+
+ _deps.append(dep);
+ _lastDep = dep;
+ invalidate();
+
+#if TRACE_DEBUG
+ qDebug("%s added\n %s (now %d)",
+ fullName().ascii(), dep->fullName().ascii(),
+ _deps.count());
+#endif
+}
+
+TraceCallCost* TraceCallListCost::findDepFromPart(TracePart* part)
+{
+ if (_lastDep && _lastDep->part() == part)
+ return _lastDep;
+
+ TraceCallCost* dep;
+ for (dep = _deps.first(); dep; dep = _deps.next())
+ if (dep->part() == part) {
+ _lastDep = dep;
+ return dep;
+ }
+ return 0;
+}
+
+
+void TraceCallListCost::update()
+{
+ if (!_dirty) return;
+
+#if TRACE_DEBUG
+ qDebug("update %s (count %d)",
+ fullName().ascii(), _deps.count());
+#endif
+
+ /* Without dependent cost items, assume fixed costs,
+ * i.e. don't change cost */
+ if (_deps.count()>0) {
+ clear();
+ TraceCallCost* item;
+ for (item = _deps.first(); item; item = _deps.next()) {
+ if (onlyActiveParts())
+ if (!item->part() || !item->part()->isActive()) continue;
+
+ addCost(item);
+ addCallCount(item->callCount());
+ }
+ }
+
+ _dirty = false;
+
+#if TRACE_DEBUG
+ qDebug(" > %s", costString(0).ascii());
+#endif
+}
+
+
+//---------------------------------------------------
+// TraceInclusiveListCost
+
+TraceInclusiveListCost::TraceInclusiveListCost()
+{
+ _lastDep = 0;
+}
+
+TraceInclusiveListCost::~TraceInclusiveListCost()
+{}
+
+
+void TraceInclusiveListCost::addDep(TraceInclusiveCost* dep)
+{
+#if TRACE_ASSERTIONS
+ if (_deps.findRef(dep)>=0) {
+ qDebug("addDep: %s already in list!",
+ dep->fullName().ascii());
+ return;
+ }
+#endif
+
+ _deps.append(dep);
+ _lastDep = dep;
+ invalidate();
+
+#if TRACE_DEBUG
+ qDebug("%s added\n %s (now %d)",
+ fullName().ascii(), dep->fullName().ascii(),
+ _deps.count());
+#endif
+}
+
+TraceInclusiveCost* TraceInclusiveListCost::findDepFromPart(TracePart* part)
+{
+ if (_lastDep && _lastDep->part() == part)
+ return _lastDep;
+
+ TraceInclusiveCost* dep;
+ for (dep = _deps.first(); dep; dep = _deps.next())
+ if (dep->part() == part) {
+ _lastDep = dep;
+ return dep;
+ }
+ return 0;
+}
+
+void TraceInclusiveListCost::update()
+{
+ if (!_dirty) return;
+
+#if TRACE_DEBUG
+ qDebug("update %s (count %d)",
+ fullName().ascii(), _deps.count());
+#endif
+
+ clear();
+ TraceInclusiveCost* item;
+ for (item = _deps.first(); item; item = _deps.next()) {
+ if (onlyActiveParts())
+ if (!item->part() || !item->part()->isActive()) continue;
+
+ addCost(item);
+ addInclusive(item->inclusive());
+ }
+
+ _dirty = false;
+
+#if TRACE_DEBUG
+ qDebug(" > %s", costString(0).ascii());
+#endif
+}
+
+
+
+//---------------------------------------------------
+// TracePartInstrJump
+
+TracePartInstrJump::TracePartInstrJump(TraceInstrJump* instrJump,
+ TracePartInstrJump* next)
+{
+ _dep = instrJump;
+ _next = next;
+}
+
+TracePartInstrJump::~TracePartInstrJump()
+{}
+
+
+//---------------------------------------------------
+// TracePartInstrCall
+
+TracePartInstrCall::TracePartInstrCall(TraceInstrCall* instrCall)
+{
+ _dep = instrCall;
+}
+
+TracePartInstrCall::~TracePartInstrCall()
+{}
+
+
+
+//---------------------------------------------------
+// TracePartInstr
+
+TracePartInstr::TracePartInstr(TraceInstr* instr)
+{
+ _dep = instr;
+}
+
+TracePartInstr::~TracePartInstr()
+{}
+
+
+
+//---------------------------------------------------
+// TracePartLineJump
+
+TracePartLineJump::TracePartLineJump(TraceLineJump* lineJump)
+{
+ _dep = lineJump;
+}
+
+TracePartLineJump::~TracePartLineJump()
+{}
+
+
+//---------------------------------------------------
+// TracePartLineCall
+
+TracePartLineCall::TracePartLineCall(TraceLineCall* lineCall)
+{
+ _dep = lineCall;
+}
+
+TracePartLineCall::~TracePartLineCall()
+{}
+
+
+//---------------------------------------------------
+// TracePartLine
+
+TracePartLine::TracePartLine(TraceLine* line)
+{
+ _dep = line;
+}
+
+TracePartLine::~TracePartLine()
+{}
+
+
+
+
+//---------------------------------------------------
+// TracePartCall
+
+TracePartCall::TracePartCall(TraceCall* call)
+{
+ _dep = call;
+
+ _firstFixCallCost = 0;
+}
+
+TracePartCall::~TracePartCall()
+{}
+
+bool TracePartCall::isRecursion()
+{
+ return call()->isRecursion();
+}
+
+void TracePartCall::update()
+{
+#if !USE_FIXCOST
+ TraceCallListCost::update();
+#else
+
+ if (!_dirty) return;
+
+#if TRACE_DEBUG
+ qDebug("update %s", fullName().ascii());
+#endif
+
+ /* Without dependent cost items, assume fixed costs,
+ * i.e. don't change cost */
+ if (_firstFixCallCost) {
+ clear();
+ FixCallCost* item;
+ for (item = _firstFixCallCost; item; item = item->nextCostOfPartCall())
+ item->addTo(this);
+ }
+
+ _dirty = false;
+
+#if TRACE_DEBUG
+ qDebug(" > %s", costString(0).ascii());
+#endif
+
+#endif // USE_FIXCOST
+}
+
+
+//---------------------------------------------------
+// TracePartFunction
+
+TracePartFunction::TracePartFunction(TraceFunction* function,
+ TracePartObject* partObject,
+ TracePartFile *partFile)
+{
+ _dep = function;
+ _partObject = partObject;
+ _partFile = partFile;
+ _partClass = 0;
+
+ _calledCount = 0;
+ _callingCount = 0;
+ _calledContexts = 0;
+ _callingContexts = 0;
+
+ _firstFixCost = 0;
+ _firstFixJump = 0;
+}
+
+TracePartFunction::~TracePartFunction()
+{}
+
+QString TracePartFunction::prettyCalledCount()
+{
+ return _calledCount.pretty();
+}
+
+QString TracePartFunction::prettyCallingCount()
+{
+ return _callingCount.pretty();
+}
+
+QString TracePartFunction::costString(TraceCostMapping* m)
+{
+ update();
+
+ QString res = TraceInclusiveCost::costString(m);
+ res += QString(", called from %1: %2")
+ .arg(_calledContexts).arg(prettyCalledCount());
+ res += QString(", calling from %1: %2")
+ .arg(_callingContexts).arg(prettyCallingCount());
+
+ return res;
+}
+
+
+void TracePartFunction::addPartInstr(TracePartInstr* ref)
+{
+#if TRACE_ASSERTIONS
+ if (_partInstr.findRef(ref)>=0) {
+ qDebug("TracePartFunction::addPartInstr: %s already in list!",
+ ref->name().ascii());
+ return;
+ }
+#endif
+
+ _partInstr.append(ref);
+ invalidate();
+
+#if TRACE_DEBUG
+ qDebug("%s added\n %s (now %d)",
+ fullName().ascii(), ref->fullName().ascii(),
+ _partInstr.count());
+#endif
+}
+
+
+void TracePartFunction::addPartLine(TracePartLine* ref)
+{
+#if TRACE_ASSERTIONS
+ if (_partLines.findRef(ref)>=0) {
+ qDebug("TracePartFunction::addPartLine: %s already in list!",
+ ref->name().ascii());
+ return;
+ }
+#endif
+
+ _partLines.append(ref);
+ invalidate();
+
+#if TRACE_DEBUG
+ qDebug("%s added\n %s (now %d)",
+ fullName().ascii(), ref->fullName().ascii(),
+ _partLines.count());
+#endif
+}
+
+
+void TracePartFunction::addPartCaller(TracePartCall* ref)
+{
+#if TRACE_ASSERTIONS
+ if (_partCallers.findRef(ref)>=0) {
+ qDebug("TracePartFunction::addPartCaller: %s already in list!",
+ ref->name().ascii());
+ return;
+ }
+#endif
+
+ _partCallers.append(ref);
+ invalidate();
+
+#if TRACE_DEBUG
+ qDebug("%s added Caller\n %s (now %d)",
+ fullName().ascii(), ref->fullName().ascii(),
+ _partCallers.count());
+#endif
+}
+
+
+void TracePartFunction::addPartCalling(TracePartCall* ref)
+{
+#if TRACE_ASSERTIONS
+ if (_partCallings.findRef(ref)>=0) {
+ qDebug("TracePartFunction::addPartCalling: %s already in list!",
+ ref->name().ascii());
+ return;
+ }
+#endif
+
+ _partCallings.append(ref);
+ invalidate();
+
+#if TRACE_DEBUG
+ qDebug("%s added Calling\n %s (now %d)",
+ fullName().ascii(), ref->fullName().ascii(),
+ _partCallings.count());
+#endif
+}
+
+SubCost TracePartFunction::calledCount()
+{
+ if (_dirty) update();
+
+ return _calledCount;
+}
+
+int TracePartFunction::calledContexts()
+{
+ if (_dirty) update();
+
+ return _calledContexts;
+}
+
+SubCost TracePartFunction::callingCount()
+{
+ if (_dirty) update();
+
+ return _callingCount;
+}
+
+
+int TracePartFunction::callingContexts()
+{
+ if (_dirty) update();
+
+ return _callingContexts;
+}
+
+
+void TracePartFunction::update()
+{
+ if (!_dirty) return;
+
+#if TRACE_DEBUG
+ qDebug("TracePartFunction::update %s (Callers %d, Callings %d, lines %d)",
+ name().ascii(), _partCallers.count(), _partCallings.count(),
+ _partLines.count());
+#endif
+
+ _calledCount = 0;
+ _callingCount = 0;
+ _calledContexts = 0;
+ _callingContexts = 0;
+
+ // calculate additional cost metrics
+ TracePartCall *caller, *calling;
+ for (caller=_partCallers.first();caller;caller=_partCallers.next()) {
+
+ // FIXME
+ if (caller->subCost(0)>0)
+ _calledContexts++;
+
+ SubCost c = caller->callCount();
+ if (c>0) {
+ _calledCount += c;
+ }
+ }
+ for (calling=_partCallings.first();calling;calling=_partCallings.next()) {
+ // FIXME
+ if (calling->subCost(0)>0)
+ _callingContexts++;
+
+ SubCost c = calling->callCount();
+ if (c>0) {
+ _callingCount += c;
+ }
+ }
+
+ // self cost
+#if !USE_FIXCOST
+ if (_partLines.count()>0) {
+ TraceCost::clear();
+
+ TracePartLine* line;
+ for (line = _partLines.first(); line; line = _partLines.next())
+ addCost(line);
+ }
+#else
+ if (_firstFixCost) {
+ TraceCost::clear();
+
+ FixCost* item;
+ for (item = _firstFixCost; item; item = item->nextCostOfPartFunction())
+ item->addTo(this);
+ }
+#endif
+
+
+ /* There are two possibilities to calculate inclusive cost:
+ * 1) sum of call costs to this function
+ * 2) sum of call costs from this function + self cost
+ *
+ * 1) is wrong if a function was called spontaneous, but also by a call.
+ * This eventually can happen with thread/process startup functions,
+ * and signal handlers.
+ *
+ * 2) is wrong with "skipped PLT" and the calltree skin, because
+ * cost of PLT is attributed to called function (?)
+ *
+ * For now, do 1) if there are callers, otherwise 2).
+ * Should this be fixed to take the maximum of 1) and 2) ?
+ */
+ _inclusive.clear();
+ if (_calledCount>0) {
+ // inclusive cost: if possible, use caller sums
+ for (caller=_partCallers.first();caller;caller=_partCallers.next()) {
+ // detect simple recursion (no cycle)
+ if (caller->isRecursion()) continue;
+
+ addInclusive(caller);
+ }
+ }
+ else {
+ // without caller info, use calling sum + line costs
+ for (calling=_partCallings.first();calling;calling=_partCallings.next()) {
+ // detect simple recursion (no cycle)
+ if (calling->isRecursion()) continue;
+
+ addInclusive(calling);
+ }
+ _dirty = false; // don't recurse!
+ addInclusive(this);
+ }
+
+ _dirty = false;
+
+#if TRACE_DEBUG
+ qDebug(" > %s", costString(0).ascii());
+#endif
+}
+
+
+
+//---------------------------------------------------
+// TracePartClass
+
+TracePartClass::TracePartClass(TraceClass* cls)
+{
+ _dep = cls;
+}
+
+TracePartClass::~TracePartClass()
+{}
+
+QString TracePartClass::prettyName() const
+{
+ return QString("%1 from %2")
+ .arg( _dep->name().isEmpty() ? QString("(global)") : _dep->name())
+ .arg(part()->name());
+}
+
+//---------------------------------------------------
+// TracePartFile
+
+TracePartFile::TracePartFile(TraceFile* file)
+{
+ _dep = file;
+}
+
+TracePartFile::~TracePartFile()
+{}
+
+
+//---------------------------------------------------
+// TracePartObject
+
+TracePartObject::TracePartObject(TraceObject* object)
+{
+ _dep = object;
+}
+
+TracePartObject::~TracePartObject()
+{}
+
+
+
+
+//---------------------------------------------------
+// TraceInstrJump
+
+TraceInstrJump::TraceInstrJump(TraceInstr* instrFrom, TraceInstr* instrTo,
+ bool isCondJump)
+{
+ _first = 0;
+
+ _instrFrom = instrFrom;
+ _instrTo = instrTo;
+ _isCondJump = isCondJump;
+}
+
+TraceInstrJump::~TraceInstrJump()
+{
+ // we are the owner of the TracePartInstrJump's generated in our factory
+ TracePartInstrJump* item = _first, *next;
+ while(item) {
+ next = item->next();
+ delete item;
+ item = next;
+ }
+}
+
+TracePartInstrJump* TraceInstrJump::partInstrJump(TracePart* part)
+{
+ static TracePartInstrJump* item = 0;
+
+ // shortcut
+ if (item && (item->instrJump()==this) && (item->part() == part)) return item;
+
+ for(item = _first; item; item = item->next())
+ if (item->part() == part) break;
+
+ if (!item) {
+ item = new TracePartInstrJump(this, _first);
+ item->setPosition(part);
+ _first = item;
+ }
+ return item;
+}
+
+void TraceInstrJump::update()
+{
+ if (!_dirty) return;
+
+ clear();
+ TracePartInstrJump* item;
+ for (item = _first; item; item = item->next()) {
+ if (!item->part() || !item->part()->isActive()) continue;
+
+ addCost(item);
+ }
+ _dirty = false;
+
+#if TRACE_DEBUG
+ qDebug("updated %s", fullName().ascii());
+#endif
+
+#if TRACE_DEBUG
+ qDebug(" > %s", costString(0).ascii());
+#endif
+}
+
+QString TraceInstrJump::name() const
+{
+ return QString("jump at 0x%1 to 0x%2")
+ .arg(_instrFrom->addr().toString())
+ .arg(_instrTo->addr().toString());
+}
+
+
+//---------------------------------------------------
+// TraceInstrJumpList
+
+
+int TraceInstrJumpList::compareItems ( Item item1, Item item2 )
+{
+ TraceInstrJump* ij1 = (TraceInstrJump*) item1;
+ TraceInstrJump* ij2 = (TraceInstrJump*) item2;
+
+ Addr addr1Low = ij1->instrFrom()->addr();
+ Addr addr2Low = ij2->instrFrom()->addr();
+ Addr addr1High = ij1->instrTo()->addr();
+ Addr addr2High = ij2->instrTo()->addr();
+ Addr t;
+
+ if (addr1Low > addr1High) {
+ t = addr1Low;
+ addr1Low = addr1High;
+ addr1High = t;
+ }
+
+ if (addr2Low > addr2High) {
+ t = addr2Low;
+ addr2Low = addr2High;
+ addr2High = t;
+ }
+
+ if (_sortLow) {
+ // we sort according to smallest instruction address
+ if (addr1Low != addr2Low) return (addr1Low > addr2Low) ? 1:-1;
+ // jump ends come before jump starts
+ if (addr1Low == ij1->instrTo()->addr()) return -1;
+ if (addr2Low == ij2->instrTo()->addr()) return 1;
+ return (addr1High > addr2High) ? 1:-1;
+ }
+
+ // we sort according to highest instruction address
+ if (addr1High != addr2High) return (addr1High > addr2High) ? 1:-1;
+ // jump ends come before jump starts
+ if (addr1High == ij1->instrTo()->addr()) return -1;
+ if (addr2High == ij2->instrTo()->addr()) return 1;
+ return (addr1Low > addr2Low) ? 1:-1;
+}
+
+
+//---------------------------------------------------
+// TraceLineJump
+
+TraceLineJump::TraceLineJump(TraceLine* lineFrom, TraceLine* lineTo,
+ bool isCondJump)
+{
+ // we are the owner of TracePartLineJump's generated in our factory
+ _deps.setAutoDelete(true);
+
+ _lineFrom = lineFrom;
+ _lineTo = lineTo;
+ _isCondJump = isCondJump;
+}
+
+TraceLineJump::~TraceLineJump()
+{}
+
+
+TracePartLineJump* TraceLineJump::partLineJump(TracePart* part)
+{
+ TracePartLineJump* item = (TracePartLineJump*) findDepFromPart(part);
+ if (!item) {
+ item = new TracePartLineJump(this);
+ item->setPosition(part);
+ addDep(item);
+ }
+ return item;
+}
+
+
+QString TraceLineJump::name() const
+{
+ return QString("jump at %1 to %2")
+ .arg(_lineFrom->prettyName())
+ .arg(_lineTo->prettyName());
+}
+
+
+//---------------------------------------------------
+// TraceLineJumpList
+
+
+int TraceLineJumpList::compareItems ( Item item1, Item item2 )
+{
+ TraceLineJump* lj1 = (TraceLineJump*) item1;
+ TraceLineJump* lj2 = (TraceLineJump*) item2;
+
+ uint line1Low = lj1->lineFrom()->lineno();
+ uint line2Low = lj2->lineFrom()->lineno();
+ uint line1High = lj1->lineTo()->lineno();
+ uint line2High = lj2->lineTo()->lineno();
+ uint t;
+
+ if (line1Low > line1High) {
+ t = line1Low; line1Low = line1High; line1High = t;
+ }
+ if (line2Low > line2High) {
+ t = line2Low; line2Low = line2High; line2High = t;
+ }
+
+ if (_sortLow) {
+ // we sort according to smallest line number
+ if (line1Low != line2Low) return line1Low - line2Low;
+ // jump ends come before jump starts
+ if (line1Low == lj1->lineTo()->lineno()) return -1;
+ if (line2Low == lj2->lineTo()->lineno()) return 1;
+ return line1High - line2High;
+ }
+
+ // we sort according to highest line number
+ if (line1High != line2High) return line1High - line2High;
+ // jump ends come before jump starts
+ if (line1High == lj1->lineTo()->lineno()) return -1;
+ if (line2High == lj2->lineTo()->lineno()) return 1;
+ return line1Low - line2Low;
+}
+
+
+//---------------------------------------------------
+// TraceInstrCall
+
+TraceInstrCall::TraceInstrCall(TraceCall* call, TraceInstr* instr)
+{
+ // we are the owner of TracePartInstrCall's generated in our factory
+ _deps.setAutoDelete(true);
+
+ _call = call;
+ _instr = instr;
+}
+
+TraceInstrCall::~TraceInstrCall()
+{}
+
+
+TracePartInstrCall* TraceInstrCall::partInstrCall(TracePart* part,
+ TracePartCall*)
+{
+ TracePartInstrCall* item = (TracePartInstrCall*) findDepFromPart(part);
+ if (!item) {
+ item = new TracePartInstrCall(this);
+ item->setPosition(part);
+ addDep(item);
+ // instruction calls are not registered in function calls
+ // as together with line calls calls are duplicated
+ //partCall->addDep(item);
+ }
+ return item;
+}
+
+
+QString TraceInstrCall::name() const
+{
+ return QString("%1 at %2").arg(_call->name()).arg(_instr->name());
+}
+
+
+//---------------------------------------------------
+// TraceLineCall
+
+TraceLineCall::TraceLineCall(TraceCall* call, TraceLine* line)
+{
+ // we are the owner of TracePartLineCall's generated in our factory
+ _deps.setAutoDelete(true);
+
+ _call = call;
+ _line = line;
+}
+
+TraceLineCall::~TraceLineCall()
+{}
+
+
+TracePartLineCall* TraceLineCall::partLineCall(TracePart* part,
+ TracePartCall* partCall)
+{
+ TracePartLineCall* item = (TracePartLineCall*) findDepFromPart(part);
+ if (!item) {
+ item = new TracePartLineCall(this);
+ item->setPosition(part);
+ addDep(item);
+ partCall->addDep(item);
+ }
+ return item;
+}
+
+
+QString TraceLineCall::name() const
+{
+ return QString("%1 at %2").arg(_call->name()).arg(_line->name());
+}
+
+
+//---------------------------------------------------
+// TraceCall
+
+TraceCall::TraceCall(TraceFunction* caller, TraceFunction* called)
+{
+ // we are the owner of all items generated in our factory
+ _deps.setAutoDelete(true);
+ _lineCalls.setAutoDelete(true);
+
+ _caller = caller;
+ _called = called;
+}
+
+
+TraceCall::~TraceCall()
+{}
+
+TracePartCall* TraceCall::partCall(TracePart* part,
+ TracePartFunction* partCaller,
+ TracePartFunction* partCalling)
+{
+ TracePartCall* item = (TracePartCall*) findDepFromPart(part);
+ if (!item) {
+ item = new TracePartCall(this);
+ item->setPosition(part);
+ addDep(item);
+ partCaller->addPartCalling(item);
+ partCalling->addPartCaller(item);
+ }
+ return item;
+}
+
+TraceInstrCall* TraceCall::instrCall(TraceInstr* i)
+{
+ TraceInstrCall* icall;
+ for (icall=_instrCalls.first();icall;icall=_instrCalls.next())
+ if (icall->instr() == i)
+ break;
+
+ if (!icall) {
+ icall = new TraceInstrCall(this, i);
+
+ _instrCalls.append(icall);
+ invalidate();
+
+#if TRACE_DEBUG
+ qDebug("Created %s [TraceCall::instrCall]", icall->fullName().ascii());
+#endif
+ i->addInstrCall(icall);
+ }
+ return icall;
+}
+
+
+TraceLineCall* TraceCall::lineCall(TraceLine* l)
+{
+ TraceLineCall* lcall;
+ for (lcall=_lineCalls.first();lcall;lcall=_lineCalls.next())
+ if (lcall->line() == l)
+ break;
+
+ if (!lcall) {
+ lcall = new TraceLineCall(this, l);
+
+ _lineCalls.append(lcall);
+ invalidate();
+
+#if TRACE_DEBUG
+ qDebug("Created %s [TraceCall::lineCall]", lcall->fullName().ascii());
+#endif
+ l->addLineCall(lcall);
+ }
+ return lcall;
+}
+
+
+void TraceCall::invalidateDynamicCost()
+{
+ TraceLineCall* lc;
+ for (lc=_lineCalls.first();lc;lc=_lineCalls.next())
+ lc->invalidate();
+
+ TraceInstrCall* ic;
+ for (ic=_instrCalls.first();ic;ic=_instrCalls.next())
+ ic->invalidate();
+
+ invalidate();
+}
+
+
+QString TraceCall::name() const
+{
+ return QString("%1 => %2")
+ .arg(_caller->name())
+ .arg(_called->name());
+}
+
+int TraceCall::inCycle()
+{
+ if (!_caller || !_called) return 0;
+ if (!_caller->cycle()) return 0;
+ if (_caller == _caller->cycle()) return 0;
+ if (_caller->cycle() != _called->cycle()) return 0;
+
+ return _caller->cycle()->cycleNo();
+}
+
+void TraceCall::update()
+{
+ if (!_dirty) return;
+
+ // special handling for cycles
+ if (_caller && _caller->cycle() && _caller==_caller->cycle()) {
+
+ // we have no part calls: use inclusive cost of called function
+ clear();
+ if (_called)
+ addCost(_called->inclusive());
+ _dirty = false;
+ return;
+ }
+
+ TraceCallListCost::update();
+}
+
+TraceFunction* TraceCall::caller(bool /*skipCycle*/) const
+{
+ return _caller;
+}
+
+TraceFunction* TraceCall::called(bool skipCycle) const
+{
+ if (!skipCycle && _called) {
+ // if this is a call to a cycle member from outside of the cycle,
+ // fake it to be a call to the whole cycle
+ if (_called->cycle() && _caller &&
+ (_caller->cycle() != _called->cycle()))
+ return _called->cycle();
+ }
+
+ return _called;
+}
+
+QString TraceCall::callerName(bool skipCycle) const
+{
+ if (!_caller) return i18n("(no caller)");
+
+ if (!skipCycle) {
+ // if this call goes into a cycle, add the entry function
+ TraceFunctionCycle* c = _called->cycle();
+ if (c && _caller && (_caller->cycle() != c)) {
+ QString via = _called->prettyName();
+ return i18n("%1 via %2").arg(_caller->prettyName()).arg(via);
+ }
+ }
+
+ return _caller->prettyName();
+}
+
+QString TraceCall::calledName(bool skipCycle) const
+{
+ if (!_called) return i18n("(no callee)");
+
+ if (!skipCycle) {
+ // if this call goes into a cycle, add the entry function
+ TraceFunctionCycle* c = _called->cycle();
+ if (c && _caller && (_caller->cycle() != c)) {
+ // HACK to get rid of cycle postfix...
+ _called->setCycle(0);
+ QString via = _called->prettyName();
+ _called->setCycle(c);
+ return i18n("%1 via %2").arg(c->name()).arg(via);
+ }
+ }
+ return _called->prettyName();
+}
+
+
+//---------------------------------------------------
+// TraceInstr
+
+TraceInstr::TraceInstr()
+{
+ // we are the owner of TracePartInstr's generated in our factory
+ _deps.setAutoDelete(true);
+ _instrJumps.setAutoDelete(true);
+
+ _addr = 0;
+ _line = 0;
+ _function = 0;
+}
+
+TraceInstr::~TraceInstr()
+{}
+
+bool TraceInstr::hasCost(TraceCostType* ct)
+{
+ bool res = subCost(ct) > 0;
+ if (!res) {
+ TraceInstrCall* ic;
+ for(ic=_instrCalls.first();ic;ic=_instrCalls.next())
+ if (ic->subCost(ct) > 0) break;
+ res = (ic != 0);
+ if (!res) {
+ TraceInstrJump* ij;
+ for(ij=_instrJumps.first();ij;ij=_instrJumps.next())
+ if (ij->executedCount() > 0) break;
+ res = (ij != 0);
+ }
+ }
+
+ return res;
+}
+
+TracePartInstr* TraceInstr::partInstr(TracePart* part,
+ TracePartFunction* partFunction)
+{
+ TracePartInstr* item = (TracePartInstr*) findDepFromPart(part);
+ if (!item) {
+ item = new TracePartInstr(this);
+ item->setPosition(part);
+ addDep(item);
+ //part->addDep(item);
+ partFunction->addPartInstr(item);
+ }
+ return item;
+}
+
+TraceInstrJump* TraceInstr::instrJump(TraceInstr* to, bool isJmpCond)
+{
+ TraceInstrJump* jump;
+ for (jump=_instrJumps.first();jump;jump=_instrJumps.next())
+ if (jump->instrTo() == to)
+ break;
+
+ if (!jump) {
+ jump = new TraceInstrJump(this, to, isJmpCond);
+
+ _instrJumps.append(jump);
+ }
+ return jump;
+}
+
+
+
+void TraceInstr::addInstrCall(TraceInstrCall* instrCall)
+{
+#if TRACE_ASSERTIONS
+ if (_instrCalls.findRef(instrCall)>=0) return;
+
+ if (instrCall->instr() != this) {
+ qDebug("Can't add instruction call to another instruction!");
+ return;
+ }
+#endif
+
+ _instrCalls.append(instrCall);
+ invalidate();
+
+#if TRACE_DEBUG
+ qDebug("%s added\n %s (now %d)",
+ fullName().ascii(),
+ instrCall->fullName().ascii(), _instrCalls.count());
+#endif
+}
+
+
+QString TraceInstr::name() const
+{
+ return QString("0x%1").arg(_addr.toString());
+}
+
+QString TraceInstr::prettyName() const
+{
+ return QString("0x%1").arg(_addr.toString());
+}
+
+
+//---------------------------------------------------
+// TraceLine
+
+TraceLine::TraceLine()
+{
+ // we are the owner of TracePartLine's generated in our factory
+ _deps.setAutoDelete(true);
+ _lineJumps.setAutoDelete(true);
+
+ _lineno = 0;
+ _sourceFile = 0;
+}
+
+TraceLine::~TraceLine()
+{}
+
+bool TraceLine::hasCost(TraceCostType* ct)
+{
+ bool res = subCost(ct) > 0;
+ if (!res) {
+ TraceLineCall* lc;
+ for(lc=_lineCalls.first();lc;lc=_lineCalls.next())
+ if (lc->subCost(ct) > 0) break;
+ res = (lc != 0);
+ if (!res) {
+ TraceLineJump* lj;
+ for(lj=_lineJumps.first();lj;lj=_lineJumps.next())
+ if (lj->executedCount() > 0) break;
+ res = (lj != 0);
+ }
+ }
+
+ return res;
+}
+
+TracePartLine* TraceLine::partLine(TracePart* part,
+ TracePartFunction* partFunction)
+{
+ TracePartLine* item = (TracePartLine*) findDepFromPart(part);
+ if (!item) {
+ item = new TracePartLine(this);
+ item->setPosition(part);
+ addDep(item);
+#if !USE_FIXCOST
+ part->addDep(item);
+#endif
+ partFunction->addPartLine(item);
+ }
+ return item;
+}
+
+TraceLineJump* TraceLine::lineJump(TraceLine* to, bool isJmpCond)
+{
+ TraceLineJump* jump;
+ for (jump=_lineJumps.first();jump;jump=_lineJumps.next())
+ if (jump->lineTo() == to)
+ break;
+
+ if (!jump) {
+ jump = new TraceLineJump(this, to, isJmpCond);
+
+ _lineJumps.append(jump);
+ }
+ return jump;
+}
+
+
+void TraceLine::addLineCall(TraceLineCall* lineCall)
+{
+#if TRACE_ASSERTIONS
+ if (_lineCalls.findRef(lineCall)>=0) return;
+
+ if (lineCall->line() != this) {
+ qDebug("Can't add line call to another line!");
+ return;
+ }
+#endif
+
+ TraceFunction* caller = lineCall->call()->caller();
+ TraceFunction* function = _sourceFile->function();
+ if (caller != function) {
+ // We regard 2 functions as the same if they have
+ // same class, name, object
+ if ((caller->cls() != function->cls()) ||
+ (caller->name() != function->name()) ||
+ (caller->object() != function->object())) {
+
+ qDebug("ERROR: Adding line call, line %d\n of %s to\n %s ?!",
+ lineCall->line()->lineno(),
+ caller->info().ascii(), function->info().ascii());
+ }
+ }
+
+ _lineCalls.append(lineCall);
+ invalidate();
+
+#if TRACE_DEBUG
+ qDebug("%s added\n %s (now %d)",
+ fullName().ascii(),
+ lineCall->fullName().ascii(), _lineCalls.count());
+#endif
+}
+
+
+QString TraceLine::name() const
+{
+ QString fileShortName = _sourceFile->file()->shortName();
+ if (fileShortName.isEmpty())
+ return i18n("(unknown)");
+
+ return QString("%1:%2")
+ .arg(fileShortName).arg(_lineno);
+}
+
+QString TraceLine::prettyName() const
+{
+ return QString("%1 [%2]")
+ .arg(name()).arg(_sourceFile->function()->prettyName());
+}
+
+//---------------------------------------------------
+// TraceCostItem
+
+TraceCostItem::TraceCostItem()
+{
+}
+
+TraceCostItem::~TraceCostItem()
+{}
+
+
+//---------------------------------------------------
+// TraceFunctionSource
+
+TraceFunctionSource::TraceFunctionSource(TraceFunction* function,
+ TraceFile* file)
+{
+ _file = file;
+ _function = function;
+
+ // the function is dependent from our cost sum
+ _dep = _function;
+
+ _lineMap = 0;
+ _lineMapFilled = false;
+ _line0 = 0;
+}
+
+TraceFunctionSource::~TraceFunctionSource()
+{
+ if (_lineMap) delete _lineMap;
+ if (_line0) delete _line0;
+}
+
+QString TraceFunctionSource::name() const
+{
+ return QString("%1 for %2").arg(_file->name()).arg(_function->name());
+}
+
+uint TraceFunctionSource::firstLineno()
+{
+ // lazy generate the map if not done up to now
+ TraceLineMap* map = lineMap();
+ // ignore line 0 here
+ if (!map || map->count() == 0) return 0;
+ TraceLineMap::Iterator it = map->begin();
+ return (*it).lineno();
+}
+
+uint TraceFunctionSource::lastLineno()
+{
+ // lazy generate the map if not done up to now
+ TraceLineMap* map = lineMap();
+ // ignore line 0 here
+ if (!map || map->count() == 0) return 0;
+ TraceLineMap::Iterator it = map->end();
+ --it;
+ return (*it).lineno();
+}
+
+/* factory */
+TraceLine* TraceFunctionSource::line(uint lineno, bool createNew)
+{
+ if (lineno == 0) {
+ if (!_line0) {
+ if (!createNew) return 0;
+ _line0 = new TraceLine;
+ _line0->setSourceFile(this);
+ _line0->setLineno(0);
+ }
+ return _line0;
+ }
+
+ if (!createNew) {
+ if (!_lineMap) return 0;
+ TraceLineMap::Iterator it = _lineMap->find(lineno);
+ if (it == _lineMap->end()) return 0;
+ return &(it.data());
+ }
+
+ if (!_lineMap) _lineMap = new TraceLineMap;
+
+ TraceLine& l = (*_lineMap)[lineno];
+ if (!l.isValid()) {
+ l.setSourceFile(this);
+ l.setLineno(lineno);
+
+#if TRACE_DEBUG
+ qDebug("Created %s [TraceFunctionSource::line]",
+ l.fullName().ascii());
+#endif
+ }
+ return &l;
+}
+
+void TraceFunctionSource::update()
+{
+ if (!_dirty) return;
+
+ clear();
+
+ // no need to create lineMap if not already created
+ if (_lineMap) {
+ TraceLineMap::Iterator lit;
+ for ( lit = _lineMap->begin();
+ lit != _lineMap->end(); ++lit )
+ addCost( &(*lit) );
+ }
+
+ _dirty = false;
+}
+
+void TraceFunctionSource::invalidateDynamicCost()
+{
+ // no need to create lineMap if not already created
+ if (_lineMap) {
+ TraceLineMap::Iterator lit;
+ for ( lit = _lineMap->begin();
+ lit != _lineMap->end(); ++lit )
+ (*lit).invalidate();
+ }
+
+ invalidate();
+}
+
+TraceLineMap* TraceFunctionSource::lineMap()
+{
+#if USE_FIXCOST
+
+ if (_lineMapFilled) return _lineMap;
+ _lineMapFilled = true;
+ if (!_lineMap)
+ _lineMap = new TraceLineMap;
+
+ TraceLine* l = 0;
+ TracePartLine* pl = 0;
+ TraceLineCall* lc = 0;
+ TracePartLineCall* plc = 0;
+
+ /* go over all part objects for this function, and
+ * - build TraceLines (the line map) using FixCost objects
+ * - build TraceJumpLines using FixJump objects
+ */
+ TraceInclusiveCostList pfList = _function->deps();
+ TracePartFunction* pf = (TracePartFunction*) pfList.first();
+ for(; pf; pf = (TracePartFunction*) pfList.next()) {
+
+ if (0) qDebug("PartFunction %s:%d",
+ pf->function()->name().ascii(), pf->part()->partNumber());
+
+ FixCost* fc = pf->firstFixCost();
+ for(; fc; fc = fc->nextCostOfPartFunction()) {
+ if (fc->line() == 0) continue;
+ if (fc->functionSource() != this) continue;
+
+ if (!l || l->lineno() != fc->line()) {
+ l = &(*_lineMap)[fc->line()];
+ if (!l->isValid()) {
+ l->setSourceFile(this);
+ l->setLineno(fc->line());
+ }
+ pl = 0;
+ }
+ if (!pl || pl->part() != fc->part())
+ pl = l->partLine(fc->part(), pf);
+ fc->addTo(pl);
+ }
+
+ TraceLine* to = 0;
+ TraceLineJump* lj;
+ TracePartLineJump* plj;
+ FixJump* fj = pf->firstFixJump();
+ for(; fj; fj = fj->nextJumpOfPartFunction()) {
+ if (fj->line() == 0) continue;
+ if (fj->source() != this) continue;
+ if (!fj->targetSource()) {
+ // be robust against buggy loaders
+ continue;
+ }
+
+ // don't display jumps to same or following line
+ if ((fj->line() == fj->targetLine()) ||
+ (fj->line()+1 == fj->targetLine())) continue;
+
+ if (!l || l->lineno() != fj->line()) {
+ l = &(*_lineMap)[fj->line()];
+ if (!l->isValid()) {
+ l->setSourceFile(this);
+ l->setLineno(fj->line());
+ }
+ }
+
+ to = fj->targetSource()->line(fj->targetLine(), true);
+
+ lj = l->lineJump(to, fj->isCondJump());
+ plj = lj->partLineJump(fj->part());
+
+ fj->addTo(plj);
+ }
+
+
+ TracePartCallList pcList = pf->partCallings();
+ TracePartCall* pc = pcList.first();
+ for(; pc; pc = pcList.next()) {
+
+ if (0) qDebug("PartCall %s:%d",
+ pc->call()->name().ascii(),
+ pf->part()->partNumber());
+
+ FixCallCost* fcc = pc->firstFixCallCost();
+ for(; fcc; fcc = fcc->nextCostOfPartCall()) {
+ if (fcc->line() == 0) continue;
+ if (fcc->functionSource() != this) continue;
+
+ if (!l || l->lineno() != fcc->line()) {
+ l = &(*_lineMap)[fcc->line()];
+ if (!l->isValid()) {
+ l->setSourceFile(this);
+ l->setLineno(fcc->line());
+ }
+ }
+ if (!lc || lc->call() != pc->call() || lc->line() != l) {
+ lc = pc->call()->lineCall(l);
+ plc = 0;
+ }
+ if (!plc || plc->part() != fcc->part())
+ plc = lc->partLineCall(fcc->part(), pc);
+
+ fcc->addTo(plc);
+ if (0) qDebug("Add FixCallCost %s:%d/0x%s, CallCount %s",
+ fcc->functionSource()->file()->shortName().ascii(),
+ fcc->line(), fcc->addr().toString().ascii(),
+ fcc->callCount().pretty().ascii());
+ }
+ }
+ }
+
+#endif
+
+ return _lineMap;
+}
+
+
+
+//---------------------------------------------------
+// TraceAssoziation
+
+TraceAssoziation::TraceAssoziation()
+{
+ _function = 0;
+ _valid = false;
+}
+
+TraceAssoziation::~TraceAssoziation()
+{
+ // don't delete from TraceFunction
+ if (_function) _function->removeAssoziation(this);
+}
+
+bool TraceAssoziation::isAssoziated()
+{
+ if (!_function) return false;
+
+ return _function->assoziation(rtti())==this;
+}
+
+bool TraceAssoziation::setFunction(TraceFunction* f)
+{
+ if (_function == f)
+ return isAssoziated();
+
+ if (_function) {
+ // don't delete ourself
+ _function->removeAssoziation(this);
+ }
+
+ _function = f;
+ if (f && f->assoziation(rtti()) == 0) {
+ f->addAssoziation(this);
+ return true;
+ }
+ return false;
+}
+
+void TraceAssoziation::clear(TraceData* d, int rtti)
+{
+ TraceFunctionMap::Iterator it;
+ for ( it = d->functionMap().begin();
+ it != d->functionMap().end(); ++it )
+ (*it).removeAssoziation(rtti);
+}
+
+void TraceAssoziation::invalidate(TraceData* d, int rtti)
+{
+ TraceFunctionMap::Iterator it;
+ for ( it = d->functionMap().begin();
+ it != d->functionMap().end(); ++it )
+ (*it).invalidateAssoziation(rtti);
+}
+
+
+//---------------------------------------------------
+// TraceFunction
+
+TraceFunction::TraceFunction()
+{
+ _object = 0;
+ _file = 0;
+ _cls = 0;
+ _cycle = 0;
+
+ // we are the owner of items generated in our factory
+ _deps.setAutoDelete(true);
+ _callings.setAutoDelete(true);
+ _sourceFiles.setAutoDelete(true);
+
+ _calledCount = 0;
+ _callingCount = 0;
+ _calledContexts = 0;
+ _callingContexts = 0;
+
+ _instrMap = 0;
+ _instrMapFilled = false;
+}
+
+
+TraceFunction::~TraceFunction()
+{
+ _assoziations.setAutoDelete(true);
+ _assoziations.clear();
+
+ if (_instrMap) delete _instrMap;
+}
+
+// no unique check is done!
+void TraceFunction::addAssoziation(TraceAssoziation* a)
+{
+ if (!a) return;
+ _assoziations.append(a);
+}
+
+void TraceFunction::removeAssoziation(TraceAssoziation* a)
+{
+ _assoziations.removeRef(a);
+}
+
+void TraceFunction::removeAssoziation(int rtti, bool reallyDelete)
+{
+ if (rtti==0) {
+ if (reallyDelete)
+ _assoziations.setAutoDelete(true);
+ _assoziations.clear();
+ _assoziations.setAutoDelete(false);
+ return;
+ }
+
+ TraceAssoziation* a;
+ for (a=_assoziations.first();a;a=_assoziations.next())
+ if (a->rtti() == rtti) {
+ if (reallyDelete) delete a;
+ _assoziations.remove();
+ return;
+ }
+}
+
+void TraceFunction::invalidateAssoziation(int rtti)
+{
+ TraceAssoziation* a;
+ for (a=_assoziations.first();a;a=_assoziations.next())
+ if ((rtti==0) || (a->rtti() == rtti))
+ a->invalidate();
+}
+
+TraceAssoziation* TraceFunction::assoziation(int rtti)
+{
+ TraceAssoziation* a;
+ for (a=_assoziations.first();a;a=_assoziations.next())
+ if (a->rtti() == rtti)
+ return a;
+ return 0;
+}
+
+
+// helper for prettyName
+bool TraceFunction::isUniquePrefix(QString prefix) const
+{
+ TraceFunctionMap::ConstIterator it, it2;
+ it = it2 = _myMapIterator;
+ if (it != data()->functionBeginIterator()) {
+ it2--;
+ if ((*it2).name().startsWith(prefix)) return false;
+ }
+ if (it != data()->functionEndIterator()) {
+ it++;
+ if ((*it).name().startsWith(prefix)) return false;
+ }
+ return true;
+}
+
+
+QString TraceFunction::prettyName() const
+{
+ QString res = _name;
+
+ if (_name.isEmpty())
+ return i18n("(unknown)");
+
+ int p = _name.find('(');
+ if (p>0) {
+ // handle C++ "operator()" correct
+ if ((_name[p+1] == ')') && (_name[p+2] == '(')) p+=2;
+
+ // we have a C++ symbol with argument types:
+ // check for unique function name (inclusive '(' !)
+ if (isUniquePrefix(_name.left(p+1)))
+ res = _name.left(p);
+ }
+
+ // cycle members
+ if (_cycle) {
+ if (_cycle != this)
+ res = QString("%1 <cycle %2>").arg(res).arg(_cycle->cycleNo());
+ else
+ res = QString("<cycle %2>").arg(_cycle->cycleNo());
+ }
+
+
+ return res;
+}
+
+/*
+ * Returns location string: ELF object and source file(s).
+ */
+QString TraceFunction::location(int maxFiles) const
+{
+ QString loc;
+
+ // add object file with address range
+ if (_object) {
+ loc = _object->shortName();
+
+#if 0
+ uint from = firstAddress();
+ uint to = lastAddress();
+ if (from != 0 && to != 0) {
+ if (from == to)
+ loc += QString(" (0x%1)").arg(to, 0, 16);
+ else
+ loc += QString(" (0x%1-0x%2)").arg(from, 0, 16).arg(to, 0, 16);
+ }
+#endif
+ }
+
+ // add all source files
+ int filesAdded = 0;
+ TraceFunctionSourceList list = _sourceFiles;
+ TraceFunctionSource* sourceFile = list.first();
+ for (;sourceFile;sourceFile=list.next()) {
+ if (!sourceFile->file() ||
+ (sourceFile->file()->name().isEmpty()) )
+ continue;
+
+ if (!loc.isEmpty())
+ loc += (filesAdded>0) ? ", " : ": ";
+ filesAdded++;
+
+ if ((maxFiles>0) && (filesAdded>maxFiles)) {
+ loc += "...";
+ break;
+ }
+ loc += sourceFile->file()->shortName();
+
+#if 0
+ from = sourceFile->firstLineno();
+ to = sourceFile->lastLineno();
+ if (from != 0 && to != 0) {
+ if (from == to)
+ loc += QString(" (%1)").arg(to);
+ else
+ loc += QString(" (%1-%2)").arg(from).arg(to);
+ }
+#endif
+ }
+
+ return loc;
+}
+
+// pretty version is allowed to mangle the string...
+QString TraceFunction::prettyLocation(int maxFiles) const
+{
+ QString l = location(maxFiles);
+ if (l.isEmpty()) return i18n("(unknown)");
+
+ return l;
+}
+
+void TraceFunction::addPrettyLocation(QString& s, int maxFiles) const
+{
+ QString l = location(maxFiles);
+ if (l.isEmpty()) return;
+
+ s += QString(" (%1)").arg(l);
+}
+
+QString TraceFunction::prettyNameWithLocation(int maxFiles) const
+{
+ QString l = location(maxFiles);
+ if (l.isEmpty()) return prettyName();
+
+ return QString("%1 (%2)").arg(prettyName()).arg(l);
+}
+
+QString TraceFunction::info() const
+{
+ QString l = location();
+ if (l.isEmpty())
+ return QString("Function %1").arg(name());
+
+ return QString("Function %1 (location %2)")
+ .arg(name()).arg(l);
+}
+
+
+Addr TraceFunction::firstAddress() const
+{
+ // ignore address 0 here
+ if (!_instrMap || _instrMap->count() == 0) return 0;
+ TraceInstrMap::ConstIterator it = _instrMap->begin();
+ return (*it).addr();
+}
+
+Addr TraceFunction::lastAddress() const
+{
+ // ignore address 0 here
+ if (!_instrMap || _instrMap->count() == 0) return 0;
+ TraceInstrMap::ConstIterator it = _instrMap->end();
+ --it;
+ return (*it).addr();
+}
+
+/* factory */
+TraceInstr* TraceFunction::instr(Addr addr, bool createNew)
+{
+ // address 0 not allowed
+ if (addr == Addr(0)) return 0;
+
+ if (!createNew) {
+ if (!_instrMap) return 0;
+ TraceInstrMap::Iterator it = _instrMap->find(addr);
+ if (it == _instrMap->end())
+ return 0;
+ return &(it.data());
+ }
+
+ if (!_instrMap) _instrMap = new TraceInstrMap;
+
+ TraceInstr& i = (*_instrMap)[addr];
+ if (!i.isValid()) {
+ i.setAddr(addr);
+ i.setFunction(this);
+
+#if TRACE_DEBUG
+ qDebug("Created %s [TraceFunction::instr]",
+ i.fullName().ascii());
+#endif
+ }
+ return &i;
+}
+
+void TraceFunction::addCaller(TraceCall* caller)
+{
+#if TRACE_ASSERTIONS
+ if (caller->called() != this) {
+ qDebug("Can't add call to another line!\n");
+ return;
+ }
+
+ if (_callers.findRef(caller)>=0) return;
+#endif
+
+ _callers.append(caller);
+ invalidate();
+
+#if TRACE_DEBUG
+ qDebug("%s added Caller\n %s (now %d)",
+ fullName().ascii(), caller->fullName().ascii(), _callers.count());
+#endif
+}
+
+
+
+TraceCall* TraceFunction::calling(TraceFunction* called)
+{
+ TraceCallMap::Iterator it = _callingMap.find(called);
+ TraceCall* calling = (it == _callingMap.end()) ? 0 : it.data();
+
+ if (!calling) {
+ calling = new TraceCall(this, called);
+
+ _callingMap.insert(called, calling);
+ _callings.append(calling);
+
+ // we have to invalidate ourself so invalidations from item propagate up
+ invalidate();
+
+#if TRACE_DEBUG
+ qDebug("Created %s [TraceFunction::calling]", calling->fullName().ascii());
+#endif
+ called->addCaller(calling);
+ }
+ return calling;
+}
+
+TraceFunctionSource* TraceFunction::sourceFile(TraceFile* file,
+ bool createNew)
+{
+ if (!file) file = _file;
+
+ TraceFunctionSource* sourceFile = _sourceFiles.first();
+ for (;sourceFile;sourceFile=_sourceFiles.next())
+ if (sourceFile->file() == file) break;
+
+ if (!sourceFile && createNew) {
+ sourceFile = new TraceFunctionSource(this, file);
+
+ _sourceFiles.append(sourceFile);
+
+ // we have to invalidate ourself so invalidations from item propagate up
+ invalidate();
+
+#if TRACE_DEBUG
+ qDebug("Created SourceFile %s [TraceFunction::line]",
+ file->name().ascii());
+#endif
+ file->addSourceFile(sourceFile);
+ }
+ return sourceFile;
+}
+
+TraceLine* TraceFunction::line(TraceFile* file, uint lineno,
+ bool createNew)
+{
+ Q_ASSERT(file!=0);
+
+ TraceFunctionSource* sf = sourceFile(file, createNew);
+ if (!sf)
+ return 0;
+ else
+ return sf->line(lineno, createNew);
+}
+
+
+TracePartFunction* TraceFunction::partFunction(TracePart* part,
+ TracePartFile* partFile,
+ TracePartObject* partObject)
+{
+ TracePartFunction* item = (TracePartFunction*) findDepFromPart(part);
+ if (!item) {
+ item = new TracePartFunction(this, partObject, partFile);
+ item->setPosition(part);
+ addDep(item);
+#if USE_FIXCOST
+ part->addDep(item);
+#endif
+
+ if (_cls) {
+ TracePartClass* partClass = _cls->partClass(part);
+ partClass->addPartFunction(item);
+ item->setPartClass(partClass);
+ }
+
+ partFile->addPartFunction(item);
+ if (partObject)
+ partObject->addPartFunction(item);
+ }
+ else if (item->partObject()==0 && partObject) {
+ item->setPartObject(partObject);
+ partObject->addPartFunction(item);
+ }
+
+ return item;
+}
+
+
+SubCost TraceFunction::calledCount()
+{
+ if (_dirty) update();
+
+ return _calledCount;
+}
+
+int TraceFunction::calledContexts()
+{
+ if (_dirty) update();
+
+ return _calledContexts;
+}
+
+SubCost TraceFunction::callingCount()
+{
+ if (_dirty) update();
+
+ return _callingCount;
+}
+
+int TraceFunction::callingContexts()
+{
+ if (_dirty) update();
+
+ return _callingContexts;
+}
+
+QString TraceFunction::prettyCalledCount()
+{
+ return _calledCount.pretty();
+}
+
+QString TraceFunction::prettyCallingCount()
+{
+ return _callingCount.pretty();
+}
+
+
+TraceCallList TraceFunction::callers(bool skipCycle) const
+{
+ if (skipCycle) return _callers;
+
+ // fake the callers for cycle members
+ if (_cycle && (_cycle != this)) {
+ TraceCallList l;
+ TraceCall* c;
+
+ // inner-cycle-callers
+ TraceCallList list=_callers;
+ for (c=list.first();c;c=list.next())
+ if (c->caller()->cycle() == _cycle)
+ l.append(c);
+
+ // call from cycle itself
+ for (c=_cycle->_callings.first();c;c=_cycle->_callings.next())
+ if (c->called() == this) {
+ l.append(c);
+ return l;
+ }
+ }
+
+ return _callers;
+}
+
+const TraceCallList& TraceFunction::callings(bool /* skipCycle */) const
+{
+ return _callings;
+}
+
+void TraceFunction::invalidateDynamicCost()
+{
+ TraceCall* c;
+ for (c=_callings.first();c;c=_callings.next())
+ c->invalidateDynamicCost();
+
+ TraceFunctionSource* sf;
+ for (sf=_sourceFiles.first();sf;sf=_sourceFiles.next())
+ sf->invalidateDynamicCost();
+
+ if (_instrMap) {
+ TraceInstrMap::Iterator iit;
+ for ( iit = _instrMap->begin();
+ iit != _instrMap->end(); ++iit )
+ (*iit).invalidate();
+ }
+
+ invalidate();
+}
+
+void TraceFunction::update()
+{
+ if (!_dirty) return;
+
+#if TRACE_DEBUG
+ qDebug("Update %s (Callers %d, sourceFiles %d, instrs %d)",
+ _name.ascii(), _callers.count(),
+ _sourceFiles.count(), _instrMap ? _instrMap->count():0);
+#endif
+
+ _calledCount = 0;
+ _callingCount = 0;
+ _calledContexts = 0;
+ _callingContexts = 0;
+ clear();
+
+ // context count is NOT the sum of part contexts
+ TraceCall *caller, *calling;
+ for (caller=_callers.first();caller;caller=_callers.next()) {
+ // FIXME
+ if (caller->subCost(0)>0)
+ _calledContexts++;
+ _calledCount += caller->callCount();
+ }
+
+ for (calling=_callings.first();calling;calling=_callings.next()) {
+ // FIXME
+ if (calling->subCost(0)>0) _callingContexts++;
+ _callingCount += calling->callCount();
+ }
+
+ if (data()->inFunctionCycleUpdate() || !_cycle) {
+ // usual case (no cycle member)
+ TraceInclusiveCost* item;
+ for (item=_deps.first();item;item=_deps.next()) {
+ if (!item->part() || !item->part()->isActive()) continue;
+
+ addCost(item);
+ addInclusive(item->inclusive());
+ }
+ }
+ else {
+ // this is a cycle or cycle member
+ for (calling=_callings.first();calling;calling=_callings.next()) {
+
+ // ignore inner-cycle member calls for inclusive cost
+ if ((_cycle != this) &&
+ (calling->inCycle()>0)) continue;
+
+ addInclusive(calling);
+ }
+
+ // self cost
+ if (type() == FunctionCycle) {
+ // cycle: self cost is sum of cycle member self costs, but
+ // doesn't add to inclusive cost
+ TraceFunctionList mList = ((TraceFunctionCycle*)this)->members();
+ TraceFunction* m;
+ for (m=mList.first();m;m=mList.next())
+ addCost(m);
+ }
+ else {
+ // cycle member
+ TraceInclusiveCost* item;
+ for (item=_deps.first();item;item=_deps.next()) {
+ if (!item->part() || !item->part()->isActive()) continue;
+
+ addCost(item);
+ }
+ _dirty = false; // don't recurse
+ addInclusive(this);
+ }
+ }
+ _dirty = false;
+
+#if TRACE_DEBUG
+ qDebug("> %s", costString(0).ascii());
+#endif
+}
+
+bool TraceFunction::isCycle()
+{
+ return _cycle == this;
+}
+
+bool TraceFunction::isCycleMember()
+{
+ return _cycle && (_cycle != this);
+}
+
+void TraceFunction::cycleReset()
+{
+ _cycle = 0;
+ _cycleStackDown = 0;
+ _cycleLow = 0;
+}
+
+// this doesn't mark functions calling themself !
+void TraceFunction::cycleDFS(int d, int& pNo, TraceFunction** pTop)
+{
+ if (_cycleLow != 0) return;
+
+ if (0)
+ qDebug("%s D%02d > %s (%d)",
+ QString().fill(' ', d).ascii(), d, prettyName().ascii(), pNo+1);
+
+
+
+ // initialize with prefix order
+ pNo++;
+ int prefixNo = pNo;
+ _cycleLow = prefixNo;
+
+ // put myself on stack
+ _cycleStackDown = *pTop;
+ *pTop = this;
+
+ /* cycle cut heuristic:
+ * skip calls for cycle detection if they make less than _cycleCut
+ * percent of the cost of the function.
+ * FIXME: Which cost type to use for this heuristic ?!
+ */
+
+ SubCost base = 0;
+ if (_callers.count()>0) {
+ TraceCallList l = _callers;
+ TraceCall *caller;
+
+ for (caller=l.first();caller;caller=l.next())
+ if (caller->subCost(0) > base)
+ base = caller->subCost(0);
+ }
+ else base = inclusive()->subCost(0);
+
+ SubCost cutLimit = SubCost(base * Configuration::cycleCut());
+
+ if (0)
+ qDebug("%s Cum. %s, Max Caller %s, cut limit %s",
+ QString().fill(' ', d).ascii(),
+ inclusive()->subCost(0).pretty().ascii(),
+ base.pretty().ascii(),
+ cutLimit.pretty().ascii());
+
+ TraceCall *calling;
+ TraceCallList l = _callings;
+ for (calling=l.first();calling;calling=l.next()) {
+ TraceFunction* called = calling->called();
+
+ // cycle cut heuristic
+ if (calling->subCost(0) < cutLimit) {
+ if (0) qDebug("%s Cut call to %s (cum. %s)",
+ QString().fill(' ', d).ascii(),
+ called->prettyName().ascii(),
+ calling->subCost(0).pretty().ascii());
+
+ continue;
+ }
+
+ if (called->_cycleLow==0) {
+ // not visited yet
+ called->cycleDFS(d+1, pNo, pTop);
+ if (called->_cycleLow < _cycleLow)
+ _cycleLow = called->_cycleLow;
+ }
+ else if (called->_cycleStackDown) {
+ // backlink to same SCC (still in stack)
+ if (called->_cycleLow < _cycleLow)
+ _cycleLow = called->_cycleLow;
+
+ if (0)
+ qDebug("%s D%02d - %s (%d)",
+ QString().fill(' ', d+1).ascii(), d+1,
+ called->prettyName().ascii(), called->_cycleLow);
+ }
+ else {
+ if (0)
+ qDebug("%s D%02d - %s (%d) [Not on stack]",
+ QString().fill(' ', d+1).ascii(), d+1,
+ called->prettyName().ascii(), called->_cycleLow);
+ }
+ }
+
+ if (prefixNo == _cycleLow) {
+ // this is the base of a SCC.
+
+ if (*pTop == this) {
+ *pTop = _cycleStackDown;
+ _cycleStackDown = 0;
+ }
+ else {
+ // a SCC with >1 members
+
+ TraceFunctionCycle* cycle = data()->functionCycle(this);
+ if (0) qDebug("BASE CYC %d %s",
+ cycle->cycleNo(), prettyName().ascii());
+ while(*pTop) {
+ TraceFunction* top = *pTop;
+ cycle->add(top);
+
+ // remove from stack
+ *pTop = top->_cycleStackDown;
+ top->_cycleStackDown = 0;
+
+ if (0) qDebug("CYC %s", top->prettyName().ascii());
+ if (top == this) break;
+ }
+ }
+ }
+ if (0)
+ qDebug("%s D%02d < %s (%d)",
+ QString().fill(' ', d).ascii(), d,
+ prettyName().ascii(), _cycleLow);
+}
+
+
+TraceInstrMap* TraceFunction::instrMap()
+{
+#if USE_FIXCOST
+
+ if (_instrMapFilled) return _instrMap;
+ _instrMapFilled = true;
+ if (!_instrMap)
+ _instrMap = new TraceInstrMap;
+
+ TraceLine* l = 0;
+ TraceInstr* i = 0;
+ TracePartInstr* pi = 0;
+ TraceInstrCall* ic = 0;
+ TracePartInstrCall* pic = 0;
+
+ TraceInclusiveCostList pfList = deps();
+ TracePartFunction* pf = (TracePartFunction*) pfList.first();
+ for(; pf; pf = (TracePartFunction*) pfList.next()) {
+
+ if (0) qDebug("PartFunction %s:%d",
+ pf->function()->name().ascii(), pf->part()->partNumber());
+
+ FixCost* fc = pf->firstFixCost();
+ for(; fc; fc = fc->nextCostOfPartFunction()) {
+ if (fc->addr() == 0) continue;
+
+ if (!l || (l->lineno() != fc->line()) ||
+ (l->functionSource() != fc->functionSource()))
+ l = fc->functionSource()->line(fc->line(),true);
+
+ if (!i || i->addr() != fc->addr()) {
+ i = &(*_instrMap)[fc->addr()];
+ if (!i->isValid()) {
+ i->setFunction(this);
+ i->setAddr(fc->addr());
+ i->setLine(l);
+ }
+ pi = 0;
+ }
+ if (!pi || pi->part() != fc->part())
+ pi = i->partInstr(fc->part(), pf);
+ fc->addTo(pi);
+ }
+
+ TraceInstr* to = 0;
+ TraceInstrJump* ij;
+ TracePartInstrJump* pij;
+ FixJump* fj = pf->firstFixJump();
+ for(; fj; fj = fj->nextJumpOfPartFunction()) {
+ if (fj->addr() == 0) continue;
+
+ if (!l || (l->lineno() != fj->line()) ||
+ (l->functionSource() != fj->source()))
+ l = fj->source()->line(fj->line(),true);
+
+ if (!i || i->addr() != fj->addr()) {
+ i = &(*_instrMap)[fj->addr()];
+ if (!i->isValid()) {
+ i->setFunction(this);
+ i->setAddr(fj->addr());
+ i->setLine(l);
+ }
+ }
+
+ to = fj->targetFunction()->instr(fj->targetAddr(), true);
+
+ ij = i->instrJump(to, fj->isCondJump());
+ pij = ij->partInstrJump(fj->part());
+
+ fj->addTo(pij);
+ }
+
+ TracePartCallList pcList = pf->partCallings();
+ TracePartCall* pc = pcList.first();
+ for(; pc; pc = pcList.next()) {
+
+ if (0) qDebug("PartCall %s:%d",
+ pc->call()->name().ascii(),
+ pf->part()->partNumber());
+
+ FixCallCost* fcc = pc->firstFixCallCost();
+ for(; fcc; fcc = fcc->nextCostOfPartCall()) {
+ if (fcc->addr() == 0) continue;
+
+ if (!l || (l->lineno() != fcc->line()) ||
+ (l->functionSource() != fcc->functionSource()))
+ l = fcc->functionSource()->line(fcc->line(),true);
+
+ if (!i || i->addr() != fcc->addr()) {
+ i = &(*_instrMap)[fcc->addr()];
+ if (!i->isValid()) {
+ i->setFunction(this);
+ i->setAddr(fcc->addr());
+ i->setLine(l);
+ }
+ }
+ if (!ic || ic->call() != pc->call() || ic->instr() != i) {
+ ic = pc->call()->instrCall(i);
+ pic = 0;
+ }
+ if (!pic || pic->part() != fcc->part())
+ pic = ic->partInstrCall(fcc->part(), pc);
+
+ fcc->addTo(pic);
+ if (0) qDebug("Add FixCallCost %s:%d/0x%s, CallCount %s",
+ fcc->functionSource()->file()->shortName().ascii(),
+ fcc->line(), fcc->addr().toString().ascii(),
+ fcc->callCount().pretty().ascii());
+ }
+ }
+ }
+
+#endif
+
+ return _instrMap;
+}
+
+
+
+//---------------------------------------------------
+// TraceFunctionCycle
+
+TraceFunctionCycle::TraceFunctionCycle(TraceFunction* f, int n)
+{
+ _base = f;
+ _cycleNo = n;
+ _cycle = this;
+
+ setPosition(f->data());
+ setName(QString("<cycle %1>").arg(n));
+
+ // reset to attributes of base function
+ setFile(_base->file());
+ setClass(_base->cls());
+ setObject(_base->object());
+}
+
+void TraceFunctionCycle::init()
+{
+ _members.clear();
+ _memberSet.clear();
+ _callers.clear();
+ // this deletes all TraceCall's to members
+ _callings.clear();
+
+ invalidate();
+}
+
+void TraceFunctionCycle::add(TraceFunction* f)
+{
+ _members.append(f);
+ _memberSet.insert(f,1);
+}
+
+void TraceFunctionCycle::setup()
+{
+ if (_members.count()==0) return;
+
+ TraceFunction* f;
+ for (f=_members.first();f;f=_members.next()) {
+
+ // the cycle takes all outside callers from its members
+ TraceCall *call;
+ TraceCallList l = f->callers();
+ for (call=l.first();call;call=l.next()) {
+ if ( _memberSet.contains(call->caller()) ) continue;
+ _callers.append(call);
+ }
+
+ // the cycle has a call to each member
+ call = new TraceCall(this, f);
+ call->invalidate();
+ _callings.append(call);
+
+ // now do some faking...
+ f->setCycle(this);
+ }
+ invalidate();
+}
+
+
+//---------------------------------------------------
+// TraceClass
+
+TraceClass::TraceClass()
+{
+ // we are the owner of items generated in our factory
+ _deps.setAutoDelete(true);
+}
+
+TraceClass::~TraceClass()
+{}
+
+QString TraceClass::prettyName() const
+{
+ if (_name.isEmpty())
+ return QString("(global)");
+ return _name;
+}
+
+TracePartClass* TraceClass::partClass(TracePart* part)
+{
+ TracePartClass* item = (TracePartClass*) findDepFromPart(part);
+ if (!item) {
+ item = new TracePartClass(this);
+ item->setPosition(part);
+ addDep(item);
+ }
+ return item;
+}
+
+void TraceClass::addFunction(TraceFunction* function)
+{
+#if TRACE_ASSERTIONS
+ if (function->cls() != this) {
+ qDebug("Can't add function to a class not enclosing this function\n");
+ return;
+ }
+
+ if (_functions.findRef(function)>=0) return;
+#endif
+
+ _functions.append(function);
+
+ invalidate();
+
+#if TRACE_DEBUG
+ qDebug("%s added\n %s (now %d)",
+ fullName().ascii(),
+ function->fullName().ascii(), _functions.count());
+#endif
+}
+
+
+
+//---------------------------------------------------
+// TraceFile
+
+TraceFile::TraceFile()
+{
+ // we are the owner of items generated in our factory
+ _deps.setAutoDelete(true);
+}
+
+TraceFile::~TraceFile()
+{}
+
+TracePartFile* TraceFile::partFile(TracePart* part)
+{
+ TracePartFile* item = (TracePartFile*) findDepFromPart(part);
+ if (!item) {
+ item = new TracePartFile(this);
+ item->setPosition(part);
+ addDep(item);
+ }
+ return item;
+}
+
+void TraceFile::addFunction(TraceFunction* function)
+{
+#if TRACE_ASSERTIONS
+ if (function->file() != this) {
+ qDebug("Can't add function to a file not enclosing this function\n");
+ return;
+ }
+
+ if (_functions.findRef(function)>=0) return;
+#endif
+
+ _functions.append(function);
+
+ invalidate();
+
+#if TRACE_DEBUG
+ qDebug("%s added\n %s (now %d)",
+ fullName().ascii(),
+ function->fullName().ascii(), _functions.count());
+#endif
+}
+
+
+void TraceFile::addSourceFile(TraceFunctionSource* sourceFile)
+{
+#if TRACE_ASSERTIONS
+ if (sourceFile->file() != this) {
+ qDebug("Can't add sourceFile to a file not having lines for it\n");
+ return;
+ }
+#endif
+
+ _sourceFiles.append(sourceFile);
+ // not truely needed, as we don't use the sourceFiles for cost update
+ invalidate();
+
+#if TRACE_DEBUG
+ qDebug("%s \n added SourceFile %s (now %d)",
+ fullName().ascii(), sourceFile->fullName().ascii(),
+ _sourceFiles.count());
+#endif
+}
+
+
+
+void TraceFile::setDirectory(const QString& dir)
+{
+ if (dir.endsWith("/"))
+ _dir = dir.left(dir.length()-1);
+ else
+ _dir = dir;
+}
+
+QString TraceFile::directory()
+{
+ if (!_dir.isEmpty()) return _dir;
+
+ int lastIndex = 0, index;
+ while ( (index=_name.find("/", lastIndex)) >=0)
+ lastIndex = index+1;
+
+ if (lastIndex==0) return QString::null;
+
+ // without ending "/"
+ return _name.left(lastIndex-1);
+}
+
+
+QString TraceFile::shortName() const
+{
+ int lastIndex = 0, index;
+ while ( (index=_name.find("/", lastIndex)) >=0)
+ lastIndex = index+1;
+
+ return _name.mid(lastIndex);
+}
+
+QString TraceFile::prettyName() const
+{
+ QString sn = shortName();
+
+ if (sn.isEmpty())
+ return i18n("(unknown)");
+
+ return sn;
+}
+
+QString TraceFile::prettyLongName() const
+{
+ if (_name.isEmpty())
+ return i18n("(unknown)");
+ return _name;
+}
+
+
+//---------------------------------------------------
+// TraceObject
+
+TraceObject::TraceObject()
+{
+ // we are the owner of items generated in our factory
+ _deps.setAutoDelete(true);
+}
+
+TraceObject::~TraceObject()
+{}
+
+TracePartObject* TraceObject::partObject(TracePart* part)
+{
+ TracePartObject* item = (TracePartObject*) findDepFromPart(part);
+ if (!item) {
+ item = new TracePartObject(this);
+ item->setPosition(part);
+ addDep(item);
+ }
+ return item;
+}
+
+void TraceObject::addFunction(TraceFunction* function)
+{
+#if TRACE_ASSERTIONS
+ if (function->object() != this) {
+ qDebug("Can't add function to an object not enclosing this function\n");
+ return;
+ }
+
+ if (_functions.findRef(function)>=0) return;
+#endif
+
+ _functions.append(function);
+
+ invalidate();
+
+#if TRACE_DEBUG
+ qDebug("%s added\n %s (now %d)",
+ fullName().ascii(),
+ function->fullName().ascii(), _functions.count());
+#endif
+}
+
+// strip path
+void TraceObject::setName(const QString& name)
+{
+ _name = name;
+
+ int lastIndex = 0, index;
+ while ( (index=_name.find("/", lastIndex)) >=0)
+ lastIndex = index+1;
+
+ _shortName = _name.mid(lastIndex);
+}
+
+QString TraceObject::prettyName() const
+{
+ if (_shortName.isEmpty())
+ return i18n("(unknown)");
+
+ return _shortName;
+}
+
+//---------------------------------------------------
+// TracePart
+
+TracePart::TracePart(TraceData* data, QFile* file)
+{
+ setPosition(data);
+
+ _dep = data;
+ _file = file;
+ if (_file)
+ _name = _file->name();
+ _active = true;
+
+ _number = 0;
+ _tid = 0;
+ _pid = 0;
+
+ _fixSubMapping = 0;
+}
+
+TracePart::~TracePart()
+{
+ delete _file;
+
+ delete _fixSubMapping;
+}
+
+void TracePart::setPartNumber(int n)
+{
+ if (data()->maxPartNumber() <n) data()->setMaxPartNumber(n);
+ _number = n;
+}
+
+void TracePart::setThreadID(int tid)
+{
+ if (data()->maxThreadID() <tid) data()->setMaxThreadID(tid);
+ _tid = tid;
+}
+
+void TracePart::setProcessID(int pid)
+{
+ _pid = pid;
+}
+
+
+
+// strip path
+QString TracePart::shortName() const
+{
+ int lastIndex = 0, index;
+ while ( (index=_name.find("/", lastIndex)) >=0)
+ lastIndex = index+1;
+
+ return _name.mid(lastIndex);
+}
+
+QString TracePart::prettyName() const
+{
+ QString name = QString("%1.%2").arg(_pid).arg(_number);
+ if (data()->maxThreadID()>1)
+ name += QString("-%3").arg(_tid);
+ return name;
+}
+
+bool TracePart::activate(bool active)
+{
+ if (_active == active) return false;
+ _active = active;
+
+ // to be done by the client of this function
+ // data()->invalidateDynamicCost();
+ // So better use the TraceData functions...
+
+ return true;
+}
+
+
+
+//---------------------------------------------------
+// TracePartList
+
+int TracePartList::compareItems ( Item item1, Item item2 )
+{
+ TracePart* p1 = (TracePart*) item1;
+ TracePart* p2 = (TracePart*) item2;
+ int mTID = p1->data()->maxThreadID()+1;
+ int mNum = p1->data()->maxPartNumber()+1;
+
+ return
+ (p1->processID() - p2->processID()) * mTID * mNum +
+ (p1->partNumber() - p2->partNumber()) * mTID +
+ (p1->threadID() - p2->threadID());
+}
+
+QString TracePartList::names() const
+{
+ QString res;
+ TracePart* p;
+ TracePartList l = *this;
+ for (p=l.first();p;p=l.next()) {
+ if (!res.isEmpty()) res += ", ";
+ res += p->shortName();
+ }
+
+ return res;
+}
+
+
+
+//---------------------------------------------------
+// TraceData
+
+
+// create vectors with reasonable default sizes, but not wasting memory
+TraceData::TraceData(TopLevel* top)
+{
+ _topLevel = top;
+ init();
+}
+
+TraceData::TraceData(const QString& base)
+{
+ _topLevel = 0;
+ init();
+ load(base);
+}
+
+void TraceData::init()
+{
+ _parts.setAutoDelete(true);
+
+ _functionCycleCount = 0;
+ _inFunctionCycleUpdate = false;
+
+ _maxThreadID = 0;
+ _maxPartNumber = 0;
+ _fixPool = 0;
+ _dynPool = 0;
+}
+
+TraceData::~TraceData()
+{
+ if (_fixPool) delete _fixPool;
+ if (_dynPool) delete _dynPool;
+}
+
+QString TraceData::shortTraceName() const
+{
+ int lastIndex = 0, index;
+ while ( (index=_traceName.find("/", lastIndex)) >=0)
+ lastIndex = index+1;
+
+ return _traceName.mid(lastIndex);
+}
+
+FixPool* TraceData::fixPool()
+{
+ if (!_fixPool)
+ _fixPool = new FixPool();
+
+ return _fixPool;
+}
+
+DynPool* TraceData::dynPool()
+{
+ if (!_dynPool)
+ _dynPool = new DynPool();
+
+ return _dynPool;
+}
+
+
+/**
+ * Two cases:
+ *
+ * - <base> is a directory: Load first profile data file available
+ * - <base> is a file name without part/thread suffixes
+ */
+void TraceData::load(const QString& base)
+{
+ bool baseExisting = true;
+
+ _traceName = base;
+ QFileInfo finfo(base);
+ QString file = finfo.fileName();
+ QDir dir = finfo.dir();
+
+ if (!finfo.exists()) {
+ baseExisting = false;
+ }
+ else if (finfo.isDir()) {
+ // search for first profile data file in directory
+ dir = QDir(base);
+
+ QStringList prefixList;
+ prefixList << "callgrind.out" << "cachegrind.out";
+ for ( QStringList::Iterator it = prefixList.begin();
+ it != prefixList.end(); ++it ) {
+ file = *it;
+
+ // search for ".pid"
+ QStringList strList = dir.entryList(file+".*", QDir::Files);
+ if (strList.count()>0) {
+ int l = file.length();
+ file = strList.first();
+ l++;
+ while(file[l] >= '0' && file[l] <= '9') l++;
+ file = file.left(l);
+ break;
+ }
+ }
+
+ _traceName = dir.path() + "/" + file;
+ }
+
+ QStringList strList;
+ strList += dir.entryList(file+".*", QDir::Files);
+ strList += dir.entryList(file+"-*", QDir::Files);
+
+ baseExisting = QFile::exists(_traceName);
+ if (baseExisting)
+ strList << file;
+
+ if (strList.count() == 0) {
+ _traceName = base + "/" + file + " " + i18n("(not found)");
+ return;
+ }
+
+
+ // try to guess pid from file name
+ unsigned int pos = file.length();
+ unsigned int pid = 0, f=1;
+ pos--;
+ while(pos>0) {
+ if (file[pos] < '0' || file[pos] > '9') break;
+ pid += f * (file[pos].latin1() - '0');
+ pos--;
+ f *= 10;
+ }
+
+ QStringList::Iterator it;
+ unsigned int maxNumber = 0;
+ for (it = strList.begin(); it != strList.end(); ++it ) {
+ TracePart* p = addPart( dir.path(), *it );
+
+ if (!p) {
+ kdDebug() << "Error loading " << *it << endl;
+ continue;
+ }
+
+ const QString& str = *it;
+ unsigned int pos = file.length();
+
+ // try to guess part number from file name
+ unsigned int n = 0;
+ if ((str.length() > pos) && (str[pos] == '.')) {
+ pos++;
+ while(str.length()>pos) {
+ if (str[pos] < '0' || str[pos] > '9') break;
+ n = 10*n + (str[pos++] - '0');
+ }
+ }
+
+ // try to guess thread number from file name
+ unsigned int t = 0;
+ if ((str.length() > pos) && (str[pos] == '-')) {
+ pos++;
+ while(str.length()>pos) {
+ if (str[pos] < '0' || str[pos] > '9') break;
+ t = 10*t + (str[pos++] - '0');
+ }
+ }
+
+ //qDebug("File %s: Part %d, Thread %d", (*it).ascii(), n, t);
+
+ if (p->partNumber()>0) n = p->partNumber();
+ if (n>maxNumber) maxNumber = n;
+ if (n==0) n = maxNumber+1;
+ p->setPartNumber(n);
+
+ if (p->threadID()==0) p->setThreadID(t);
+ if (p->processID()==0) p->setProcessID(pid);
+
+ _parts.append(p);
+ }
+ _parts.sort();
+
+ invalidateDynamicCost();
+ updateFunctionCycles();
+
+ // clear loading messages from status bar
+ if (_topLevel) _topLevel->showStatus(QString::null, 0);
+}
+
+TracePart* TraceData::addPart(const QString& dir, const QString& name)
+{
+ QString filename = QString("%1/%2").arg(dir).arg(name);
+#if TRACE_DEBUG
+ qDebug("TraceData::addPart('%s')", filename.ascii());
+#endif
+
+ QFile* file = new QFile(filename);
+
+ Loader* l = Loader::matchingLoader(file);
+ if (!l) return 0;
+
+ if (_topLevel)
+ _topLevel->connect(l, SIGNAL(updateStatus(QString, int)),
+ SLOT(showStatus(QString, int)));
+
+ TracePart* part = new TracePart(this, file);
+
+ if (! l->loadTrace(part)) {
+ delete part;
+ part = 0;
+ }
+
+ if (_topLevel) l->disconnect(_topLevel);
+
+ return part;
+}
+
+bool TraceData::activateParts(const TracePartList& l)
+{
+ bool changed = false;
+
+ TracePart* part;
+ for (part=_parts.first();part;part=_parts.next())
+ if (part->activate(l.containsRef(part)>0))
+ changed = true;
+
+ if (changed) {
+ // because active parts have changed, throw away calculated
+ // costs...
+ invalidateDynamicCost();
+ updateFunctionCycles();
+ }
+
+ return changed;
+}
+
+
+bool TraceData::activateParts(TracePartList l, bool active)
+{
+ bool changed = false;
+
+ TracePart* part;
+ for (part=l.first();part;part=l.next())
+ if (_parts.findRef(part)>=0)
+ if (part->activate(active))
+ changed = true;
+
+ if (changed) {
+ invalidateDynamicCost();
+ updateFunctionCycles();
+ }
+
+ return changed;
+}
+
+bool TraceData::activatePart(TracePart* p, bool active)
+{
+ return p->activate(active);
+}
+
+bool TraceData::activateAll(bool active)
+{
+ return activateParts(_parts, active);
+}
+
+
+TracePart* TraceData::part(QString& name)
+{
+ TracePart* part;
+ for (part=_parts.first();part;part=_parts.next())
+ if (part->name() == name)
+ return part;
+ return 0;
+}
+
+QString TraceData::activePartRange()
+{
+ QString res;
+ int r1=-1, r2=-1, count=1;
+ TracePart* part;
+ for (part=_parts.first();part;part=_parts.next(), count++)
+ if (part->isActive()) {
+ if (r1<0) { r1 = r2 = count; }
+ else if (r2 == count-1) { r2 = count; }
+ else {
+ if (!res.isEmpty()) res += ";";
+ if (r1==r2) res += QString::number(r1);
+ else res += QString("%1-%2").arg(r1).arg(r2);
+ r1 = r2 = count;
+ }
+ }
+ if (r1>=0) {
+ if (!res.isEmpty()) res += ";";
+ if (r1==r2) res += QString::number(r1);
+ else res += QString("%1-%2").arg(r1).arg(r2);
+ }
+
+ return res;
+}
+
+void TraceData::invalidateDynamicCost()
+{
+ // invalidate all dynamic costs
+
+ TraceObjectMap::Iterator oit;
+ for ( oit = _objectMap.begin();
+ oit != _objectMap.end(); ++oit )
+ (*oit).invalidate();
+
+ TraceClassMap::Iterator cit;
+ for ( cit = _classMap.begin();
+ cit != _classMap.end(); ++cit )
+ (*cit).invalidate();
+
+ TraceFileMap::Iterator fit;
+ for ( fit = _fileMap.begin();
+ fit != _fileMap.end(); ++fit )
+ (*fit).invalidate();
+
+ TraceFunctionMap::Iterator it;
+ for ( it = _functionMap.begin();
+ it != _functionMap.end(); ++it ) {
+ (*it).invalidateDynamicCost();
+ }
+
+ invalidate();
+
+}
+
+
+TraceObject* TraceData::object(const QString& name)
+{
+ TraceObject& o = _objectMap[name];
+ if (!o.data()) {
+ // was created
+ o.setPosition(this);
+ o.setName(name);
+
+#if TRACE_DEBUG
+ qDebug("Created %s [TraceData::object]",
+ o.fullName().ascii());
+#endif
+ }
+ return &o;
+}
+
+
+TraceFile* TraceData::file(const QString& name)
+{
+ TraceFile& f = _fileMap[name];
+ if (!f.data()) {
+ // was created
+ f.setPosition(this);
+ f.setName(name);
+
+#if TRACE_DEBUG
+ qDebug("Created %s [TraceData::file]",
+ f.fullName().ascii());
+#endif
+ }
+ return &f;
+}
+
+
+// usually only called by function()
+TraceClass* TraceData::cls(const QString& fnName, QString& shortName)
+{
+ int lastIndex = 0, index, pIndex;
+
+ // we ignore any "::" after a '(' or a space
+ pIndex=fnName.find("(", 0);
+
+#if 0
+ int sIndex=fnName.find(" ", 0);
+ if (sIndex>=0)
+ if ((pIndex == -1) || (sIndex < pIndex))
+ pIndex = sIndex;
+#endif
+
+ while ((index=fnName.find("::", lastIndex)) >=0) {
+ if (pIndex>=0 && pIndex<index) break;
+ lastIndex = index+2;
+ }
+
+ QString clsName = (lastIndex < 3) ? QString::null :
+ fnName.left(lastIndex-2);
+ shortName = fnName.mid(lastIndex);
+
+ TraceClass& c = _classMap[clsName];
+ if (!c.data()) {
+ // was created
+ c.setPosition(this);
+ c.setName(clsName);
+
+#if TRACE_DEBUG
+ qDebug("Created %s [TraceData::cls]",
+ c.fullName().ascii());
+#endif
+ }
+ return &c;
+}
+
+
+// name is inclusive class/namespace prefix
+TraceFunction* TraceData::function(const QString& name,
+ TraceFile* file, TraceObject* object)
+{
+ // strip class name
+ QString shortName;
+ TraceClass* c = cls(name, shortName);
+
+ if (!file || !object || !c) {
+ qDebug("ERROR - no file/object/class for %s ?!", name.ascii());
+ return 0;
+ }
+
+ // Don't use file in key: A function can go over many files
+ // (inlined parts), but still is ONE function.
+ QString key = name + object->shortName();
+
+ TraceFunctionMap::Iterator it;
+ it = _functionMap.find(key);
+ if (it == _functionMap.end()) {
+ it = _functionMap.insert(key, TraceFunction());
+ TraceFunction& f = it.data();
+
+ f.setPosition(this);
+ f.setName(name);
+ f.setClass(c);
+ f.setObject(object);
+ f.setFile(file);
+ f.setMapIterator(it);
+
+#if TRACE_DEBUG
+ qDebug("Created %s [TraceData::function]\n for %s, %s, %s",
+ f.fullName().ascii(),
+ c->fullName().ascii(), file->fullName().ascii(),
+ object ? object->fullName().ascii() : "(unknown object)");
+#endif
+
+ c->addFunction(&f);
+ object->addFunction(&f);
+ file->addFunction(&f);
+ }
+
+ return &(it.data());
+}
+
+TraceFunctionMap::Iterator TraceData::functionIterator(TraceFunction* f)
+{
+
+ // IMPORTANT: build as SAME key as used in function() above !!
+ QString key;
+
+ if (f->cls()) key = f->cls()->name() + "::";
+ key += f->name();
+ key += f->object()->shortName();
+
+ return _functionMap.find(key);
+}
+
+TraceFunctionMap::ConstIterator TraceData::functionBeginIterator() const
+{
+ return _functionMap.begin();
+}
+
+TraceFunctionMap::ConstIterator TraceData::functionEndIterator() const
+{
+ return _functionMap.end();
+}
+
+
+void TraceData::resetSourceDirs()
+{
+ TraceFileMap::Iterator fit;
+ for ( fit = _fileMap.begin();
+ fit != _fileMap.end(); ++fit )
+ (*fit).resetDirectory();
+}
+
+void TraceData::update()
+{
+ if (!_dirty) return;
+
+ clear();
+ _totals.clear();
+
+ TracePart* part;
+ for (part=_parts.first();part;part=_parts.next()) {
+ _totals.addCost(part->totals());
+ if (part->isActive())
+ addCost(part->totals());
+ }
+
+ _dirty = false;
+}
+
+TraceCost* TraceData::search(TraceItem::CostType t, QString name,
+ TraceCostType* ct, TraceCost* parent)
+{
+ TraceCost* result = 0;
+ TraceItem::CostType pt = parent ? parent->type() : NoCostType;
+ SubCost sc, scTop = 0;
+
+ switch(t) {
+ case Function:
+ {
+ TraceFunction *f;
+ TraceFunctionMap::Iterator it;
+ for ( it = _functionMap.begin();
+ it != _functionMap.end(); ++it ) {
+ f = &(*it);
+
+ if (f->name() != name) continue;
+
+ if ((pt == Class) && (parent != f->cls())) continue;
+ if ((pt == File) && (parent != f->file())) continue;
+ if ((pt == Object) && (parent != f->object())) continue;
+
+ if (ct) {
+ sc = f->inclusive()->subCost(ct);
+ if (sc <= scTop) continue;
+ scTop = sc;
+ }
+
+ result = f;
+ }
+ }
+ break;
+
+ case File:
+ {
+ TraceFile *f;
+ TraceFileMap::Iterator it;
+ for ( it = _fileMap.begin();
+ it != _fileMap.end(); ++it ) {
+ f = &(*it);
+ if (f->name() != name) continue;
+ if (ct) {
+ sc = f->subCost(ct);
+ if (sc <= scTop) continue;
+ scTop = sc;
+ }
+ result = f;
+ }
+ }
+ break;
+
+ case Class:
+ {
+ TraceClass *c;
+ TraceClassMap::Iterator it;
+ for ( it = _classMap.begin();
+ it != _classMap.end(); ++it ) {
+ c = &(*it);
+ if (c->name() != name) continue;
+ if (ct) {
+ sc = c->subCost(ct);
+ if (sc <= scTop) continue;
+ scTop = sc;
+ }
+ result = c;
+ }
+ }
+ break;
+
+ case Object:
+ {
+ TraceObject *o;
+ TraceObjectMap::Iterator it;
+ for ( it = _objectMap.begin();
+ it != _objectMap.end(); ++it ) {
+ o = &(*it);
+ if (o->name() != name) continue;
+ if (ct) {
+ sc = o->subCost(ct);
+ if (sc <= scTop) continue;
+ scTop = sc;
+ }
+ result = o;
+ }
+ }
+ break;
+
+ case Instr:
+ if (pt == Function) {
+ TraceInstrMap* instrMap = ((TraceFunction*)parent)->instrMap();
+ if (!instrMap) break;
+
+ TraceInstr *instr;
+ TraceInstrMap::Iterator it;
+ for ( it = instrMap->begin();
+ it != instrMap->end(); ++it ) {
+ instr = &(*it);
+ if (instr->name() != name) continue;
+ result = instr;
+ }
+ }
+ break;
+
+ case Line:
+ {
+ TraceFunctionSourceList sList;
+ if (pt == Function)
+ sList = ((TraceFunction*)parent)->sourceFiles();
+ else if (pt == FunctionSource)
+ sList.append((TraceFunctionSource*) parent);
+ else break;
+
+ TraceLineMap* lineMap;
+ TraceLine* line;
+ TraceLineMap::Iterator it;
+ TraceFunctionSource* fs;
+ for(fs = sList.first(); fs; fs = sList.next()) {
+ lineMap = fs->lineMap();
+ if (!lineMap) continue;
+
+ for ( it = lineMap->begin();
+ it != lineMap->end(); ++it ) {
+ line = &(*it);
+ if (line->name() != name) continue;
+ result = line;
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return result;
+}
+
+
+TraceFunctionCycle* TraceData::functionCycle(TraceFunction* f)
+{
+ TraceFunctionCycle* cycle;
+ for (cycle=_functionCycles.first();cycle;cycle=_functionCycles.next())
+ if (cycle->base() == f) return cycle;
+
+ _functionCycleCount++;
+ cycle = new TraceFunctionCycle(f, _functionCycleCount);
+
+ _functionCycles.append(cycle);
+ return cycle;
+}
+
+
+void TraceData::updateFunctionCycles()
+{
+ //qDebug("Updating cycles...");
+
+ // init cycle info
+ TraceFunctionCycle* cycle;
+ for (cycle=_functionCycles.first();cycle;cycle=_functionCycles.next())
+ cycle->init();
+
+ TraceFunctionMap::Iterator it;
+ for ( it = _functionMap.begin(); it != _functionMap.end(); ++it )
+ (*it).cycleReset();
+
+ if (!Configuration::showCycles()) return;
+
+ _inFunctionCycleUpdate = true;
+
+
+#if 0
+ int fCount = _functionMap.size(), fNo = 0, progress=0, p;
+ QString msg = i18n("Recalculating Function Cycles...");
+ if (_topLevel) _topLevel->showStatus(msg,0);
+#endif
+
+ // DFS and collapse strong connected components (Tarjan)
+ int pNo = 0;
+ TraceFunction* stackTop;
+ for ( it = _functionMap.begin(); it != _functionMap.end(); ++it ) {
+
+#if 0
+ if (_topLevel) {
+ fNo++;
+ p = 100*fNo/fCount;
+ if (p> progress) {
+ progress = p;
+ _topLevel->showStatus(msg, p);
+ }
+ }
+#endif
+
+ stackTop = 0;
+ (*it).cycleDFS(1, pNo, &stackTop);
+ }
+
+ // postprocess cycles
+ for (cycle=_functionCycles.first();cycle;cycle=_functionCycles.next())
+ cycle->setup();
+
+ _inFunctionCycleUpdate = false;
+ // we have to invalidate costs because cycles are now taken into account
+ invalidateDynamicCost();
+
+#if 0
+ if (0) if (_topLevel) _topLevel->showStatus(QString::null,0);
+#endif
+}
+
+void TraceData::updateObjectCycles()
+{
+}
+
+
+void TraceData::updateClassCycles()
+{
+}
+
+
+void TraceData::updateFileCycles()
+{
+}
+
+