You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5069 lines
106 KiB
5069 lines
106 KiB
/* This file is part of KCachegrind.
|
|
Copyright (C) 2002, 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
|
|
|
|
KCachegrind is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public
|
|
License as published by the Free Software Foundation, version 2.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; see the file COPYING. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <tqfile.h>
|
|
#include <tqdir.h>
|
|
#include <tqfileinfo.h>
|
|
#include <tqregexp.h>
|
|
|
|
#include <tdelocale.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;
|
|
}
|
|
|
|
|
|
TQString Addr::toString() const
|
|
{
|
|
if (_v == 0) return TQString("0");
|
|
|
|
uint64 n = _v;
|
|
TQString hex;
|
|
hex.reserve(16);
|
|
|
|
while(n>0) {
|
|
int d = (n & 15);
|
|
hex = TQChar((d<10) ? ('0'+d) : ('A'-10+d)) + hex;
|
|
n /= 16;
|
|
}
|
|
|
|
return hex;
|
|
}
|
|
|
|
TQString Addr::pretty() const
|
|
{
|
|
if (_v == 0) return TQString("0");
|
|
|
|
uint64 n = _v;
|
|
int p = 0;
|
|
TQString hex;
|
|
hex.reserve(20);
|
|
|
|
while(n>0) {
|
|
int d = (n & 15);
|
|
if ((p>0) && ((p%4)==0)) hex = " " + hex;
|
|
hex = TQChar((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
|
|
|
|
TQString* TraceItem::_typeName = 0;
|
|
TQString* 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;
|
|
}
|
|
}
|
|
|
|
TQString TraceItem::typeName(CostType t)
|
|
{
|
|
if (!_typeName) {
|
|
_typeName = new TQString [MaxCostType+1];
|
|
TQString* strs = _typeName;
|
|
for(int i=0;i<=MaxCostType;i++)
|
|
strs[i] = TQString("?");
|
|
|
|
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(TQString 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
|
|
TQString TraceItem::i18nTypeName(CostType t)
|
|
{
|
|
if (!_i18nTypeName) {
|
|
_i18nTypeName = new TQString [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(TQString 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();
|
|
}
|
|
|
|
|
|
TQString TraceItem::costString(TraceCostMapping*)
|
|
{
|
|
return TQString("(no cost)");
|
|
}
|
|
|
|
TQString TraceItem::name() const
|
|
{
|
|
if (part()) {
|
|
return i18n("%1 from %2")
|
|
.arg(_dep->name())
|
|
.arg(part()->name());
|
|
}
|
|
|
|
if (_dep)
|
|
return _dep->name();
|
|
|
|
return i18n("(unknown)");
|
|
}
|
|
|
|
TQString TraceItem::prettyName() const
|
|
{
|
|
if (name().isEmpty()) return i18n("(unknown)");
|
|
return name();
|
|
}
|
|
|
|
|
|
TQString TraceItem::fullName() const
|
|
{
|
|
return TQString("%1 %2")
|
|
.arg(typeName(type())).arg(prettyName());
|
|
}
|
|
|
|
TQString TraceItem::toString()
|
|
{
|
|
return TQString("%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 !
|
|
tqDebug("%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 !
|
|
tqDebug("%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 !
|
|
tqDebug("%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 !
|
|
tqDebug("%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 !
|
|
tqDebug("%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;
|
|
}
|
|
|
|
TQString TraceCost::costString(TraceCostMapping* m)
|
|
{
|
|
TQString 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;
|
|
}
|
|
|
|
TQString 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;
|
|
}
|
|
|
|
TQString TraceJumpCost::costString(TraceCostMapping*)
|
|
{
|
|
if (_dirty) update();
|
|
|
|
return TQString("%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
|
|
|
|
TQPtrList<TraceCostType>* TraceCostType::_knownTypes = 0;
|
|
|
|
TraceCostType::TraceCostType(TQString name, TQString longName, TQString 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(TQString 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 = TQString();
|
|
}
|
|
|
|
// checks for existing types and sets coefficients
|
|
bool TraceCostType::parseFormula()
|
|
{
|
|
if (_parsed) return true;
|
|
if (_inParsing) {
|
|
tqDebug("TraceCostType::parseFormula: Recursion detected.");
|
|
return false;
|
|
}
|
|
|
|
if (!_mapping) {
|
|
tqDebug("TraceCostType::parseFormula: No mapping set!");
|
|
return false;
|
|
}
|
|
|
|
_inParsing = true;
|
|
|
|
for (int i=0; i<TraceCost::MaxRealIndex;i++)
|
|
_coefficient[i] = 0;
|
|
|
|
TQRegExp rx( "((?:\\+|\\-)?)\\s*(\\d*)\\s*\\*?\\s*(\\w+)" );
|
|
|
|
int factor, pos;
|
|
TQString 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;
|
|
|
|
//tqDebug("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) {
|
|
// tqDebug("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;
|
|
}
|
|
|
|
TQString TraceCostType::parsedFormula()
|
|
{
|
|
TQString 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 += TQString::number(c);
|
|
|
|
TraceCostType* t = _mapping->type(i);
|
|
if (!t) continue;
|
|
|
|
if (!t->name().isEmpty())
|
|
res += TQString(" * %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(TQString 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(TQString 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 TQPtrList<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(TQString 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);
|
|
}
|
|
|
|
TQColor TraceCostType::color()
|
|
{
|
|
if (!_mapping) return TQColor();
|
|
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(TQString 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(TQString 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) {
|
|
tqDebug("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) {
|
|
tqDebug("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(TQString 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(TQString 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(TQString name)
|
|
{
|
|
for (int i=0;i<_realCount;i++)
|
|
if (_real[i] && (_real[i]->name() == name))
|
|
return i;
|
|
|
|
return TraceCost::InvalidIndex;
|
|
}
|
|
|
|
int TraceCostMapping::index(TQString 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(TQString 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()
|
|
{}
|
|
|
|
|
|
TQString TraceCallCost::costString(TraceCostMapping* m)
|
|
{
|
|
return TQString("%1, Calls %2")
|
|
.arg(TraceCost::costString(m))
|
|
.arg(_callCount.pretty());
|
|
}
|
|
|
|
TQString 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()
|
|
{}
|
|
|
|
TQString TraceInclusiveCost::costString(TraceCostMapping* m)
|
|
{
|
|
return TQString("%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) {
|
|
tqDebug("addDep: %s already in list!",
|
|
dep->fullName().ascii());
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
_deps.append(dep);
|
|
_lastDep = dep;
|
|
invalidate();
|
|
|
|
#if TRACE_DEBUG
|
|
tqDebug("%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
|
|
tqDebug("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
|
|
tqDebug(" > %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) {
|
|
tqDebug("addDep: %s already in list!",
|
|
dep->fullName().ascii());
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
_deps.append(dep);
|
|
_lastDep = dep;
|
|
invalidate();
|
|
|
|
#if TRACE_DEBUG
|
|
tqDebug("%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
|
|
tqDebug("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
|
|
tqDebug(" > %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) {
|
|
tqDebug("addDep: %s already in list!",
|
|
dep->fullName().ascii());
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
_deps.append(dep);
|
|
_lastDep = dep;
|
|
invalidate();
|
|
|
|
#if TRACE_DEBUG
|
|
tqDebug("%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
|
|
tqDebug("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
|
|
tqDebug(" > %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) {
|
|
tqDebug("addDep: %s already in list!",
|
|
dep->fullName().ascii());
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
_deps.append(dep);
|
|
_lastDep = dep;
|
|
invalidate();
|
|
|
|
#if TRACE_DEBUG
|
|
tqDebug("%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
|
|
tqDebug("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
|
|
tqDebug(" > %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
|
|
tqDebug("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
|
|
tqDebug(" > %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()
|
|
{}
|
|
|
|
TQString TracePartFunction::prettyCalledCount()
|
|
{
|
|
return _calledCount.pretty();
|
|
}
|
|
|
|
TQString TracePartFunction::prettyCallingCount()
|
|
{
|
|
return _callingCount.pretty();
|
|
}
|
|
|
|
TQString TracePartFunction::costString(TraceCostMapping* m)
|
|
{
|
|
update();
|
|
|
|
TQString res = TraceInclusiveCost::costString(m);
|
|
res += TQString(", called from %1: %2")
|
|
.arg(_calledContexts).arg(prettyCalledCount());
|
|
res += TQString(", calling from %1: %2")
|
|
.arg(_callingContexts).arg(prettyCallingCount());
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
void TracePartFunction::addPartInstr(TracePartInstr* ref)
|
|
{
|
|
#if TRACE_ASSERTIONS
|
|
if (_partInstr.findRef(ref)>=0) {
|
|
tqDebug("TracePartFunction::addPartInstr: %s already in list!",
|
|
ref->name().ascii());
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
_partInstr.append(ref);
|
|
invalidate();
|
|
|
|
#if TRACE_DEBUG
|
|
tqDebug("%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) {
|
|
tqDebug("TracePartFunction::addPartLine: %s already in list!",
|
|
ref->name().ascii());
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
_partLines.append(ref);
|
|
invalidate();
|
|
|
|
#if TRACE_DEBUG
|
|
tqDebug("%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) {
|
|
tqDebug("TracePartFunction::addPartCaller: %s already in list!",
|
|
ref->name().ascii());
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
_partCallers.append(ref);
|
|
invalidate();
|
|
|
|
#if TRACE_DEBUG
|
|
tqDebug("%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) {
|
|
tqDebug("TracePartFunction::addPartCalling: %s already in list!",
|
|
ref->name().ascii());
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
_partCallings.append(ref);
|
|
invalidate();
|
|
|
|
#if TRACE_DEBUG
|
|
tqDebug("%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
|
|
tqDebug("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
|
|
tqDebug(" > %s", costString(0).ascii());
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------
|
|
// TracePartClass
|
|
|
|
TracePartClass::TracePartClass(TraceClass* cls)
|
|
{
|
|
_dep = cls;
|
|
}
|
|
|
|
TracePartClass::~TracePartClass()
|
|
{}
|
|
|
|
TQString TracePartClass::prettyName() const
|
|
{
|
|
return TQString("%1 from %2")
|
|
.arg( _dep->name().isEmpty() ? TQString("(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
|
|
tqDebug("updated %s", fullName().ascii());
|
|
#endif
|
|
|
|
#if TRACE_DEBUG
|
|
tqDebug(" > %s", costString(0).ascii());
|
|
#endif
|
|
}
|
|
|
|
TQString TraceInstrJump::name() const
|
|
{
|
|
return TQString("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;
|
|
}
|
|
|
|
|
|
TQString TraceLineJump::name() const
|
|
{
|
|
return TQString("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;
|
|
}
|
|
|
|
|
|
TQString TraceInstrCall::name() const
|
|
{
|
|
return TQString("%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;
|
|
}
|
|
|
|
|
|
TQString TraceLineCall::name() const
|
|
{
|
|
return TQString("%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
|
|
tqDebug("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
|
|
tqDebug("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();
|
|
}
|
|
|
|
|
|
TQString TraceCall::name() const
|
|
{
|
|
return TQString("%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;
|
|
}
|
|
|
|
TQString 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)) {
|
|
TQString via = _called->prettyName();
|
|
return i18n("%1 via %2").arg(_caller->prettyName()).arg(via);
|
|
}
|
|
}
|
|
|
|
return _caller->prettyName();
|
|
}
|
|
|
|
TQString 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);
|
|
TQString 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) {
|
|
tqDebug("Can't add instruction call to another instruction!");
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
_instrCalls.append(instrCall);
|
|
invalidate();
|
|
|
|
#if TRACE_DEBUG
|
|
tqDebug("%s added\n %s (now %d)",
|
|
fullName().ascii(),
|
|
instrCall->fullName().ascii(), _instrCalls.count());
|
|
#endif
|
|
}
|
|
|
|
|
|
TQString TraceInstr::name() const
|
|
{
|
|
return TQString("0x%1").arg(_addr.toString());
|
|
}
|
|
|
|
TQString TraceInstr::prettyName() const
|
|
{
|
|
return TQString("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) {
|
|
tqDebug("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())) {
|
|
|
|
tqDebug("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
|
|
tqDebug("%s added\n %s (now %d)",
|
|
fullName().ascii(),
|
|
lineCall->fullName().ascii(), _lineCalls.count());
|
|
#endif
|
|
}
|
|
|
|
|
|
TQString TraceLine::name() const
|
|
{
|
|
TQString fileShortName = _sourceFile->file()->shortName();
|
|
if (fileShortName.isEmpty())
|
|
return i18n("(unknown)");
|
|
|
|
return TQString("%1:%2")
|
|
.arg(fileShortName).arg(_lineno);
|
|
}
|
|
|
|
TQString TraceLine::prettyName() const
|
|
{
|
|
return TQString("%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;
|
|
}
|
|
|
|
TQString TraceFunctionSource::name() const
|
|
{
|
|
return TQString("%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
|
|
tqDebug("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) tqDebug("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) tqDebug("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) tqDebug("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(TQString 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;
|
|
}
|
|
|
|
|
|
TQString TraceFunction::prettyName() const
|
|
{
|
|
TQString 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 = TQString("%1 <cycle %2>").arg(res).arg(_cycle->cycleNo());
|
|
else
|
|
res = TQString("<cycle %2>").arg(_cycle->cycleNo());
|
|
}
|
|
|
|
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* Returns location string: ELF object and source file(s).
|
|
*/
|
|
TQString TraceFunction::location(int maxFiles) const
|
|
{
|
|
TQString 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 += TQString(" (0x%1)").arg(to, 0, 16);
|
|
else
|
|
loc += TQString(" (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 += TQString(" (%1)").arg(to);
|
|
else
|
|
loc += TQString(" (%1-%2)").arg(from).arg(to);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return loc;
|
|
}
|
|
|
|
// pretty version is allowed to mangle the string...
|
|
TQString TraceFunction::prettyLocation(int maxFiles) const
|
|
{
|
|
TQString l = location(maxFiles);
|
|
if (l.isEmpty()) return i18n("(unknown)");
|
|
|
|
return l;
|
|
}
|
|
|
|
void TraceFunction::addPrettyLocation(TQString& s, int maxFiles) const
|
|
{
|
|
TQString l = location(maxFiles);
|
|
if (l.isEmpty()) return;
|
|
|
|
s += TQString(" (%1)").arg(l);
|
|
}
|
|
|
|
TQString TraceFunction::prettyNameWithLocation(int maxFiles) const
|
|
{
|
|
TQString l = location(maxFiles);
|
|
if (l.isEmpty()) return prettyName();
|
|
|
|
return TQString("%1 (%2)").arg(prettyName()).arg(l);
|
|
}
|
|
|
|
TQString TraceFunction::info() const
|
|
{
|
|
TQString l = location();
|
|
if (l.isEmpty())
|
|
return TQString("Function %1").arg(name());
|
|
|
|
return TQString("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
|
|
tqDebug("Created %s [TraceFunction::instr]",
|
|
i.fullName().ascii());
|
|
#endif
|
|
}
|
|
return &i;
|
|
}
|
|
|
|
void TraceFunction::addCaller(TraceCall* caller)
|
|
{
|
|
#if TRACE_ASSERTIONS
|
|
if (caller->called() != this) {
|
|
tqDebug("Can't add call to another line!\n");
|
|
return;
|
|
}
|
|
|
|
if (_callers.findRef(caller)>=0) return;
|
|
#endif
|
|
|
|
_callers.append(caller);
|
|
invalidate();
|
|
|
|
#if TRACE_DEBUG
|
|
tqDebug("%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
|
|
tqDebug("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
|
|
tqDebug("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;
|
|
}
|
|
|
|
TQString TraceFunction::prettyCalledCount()
|
|
{
|
|
return _calledCount.pretty();
|
|
}
|
|
|
|
TQString 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
|
|
tqDebug("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
|
|
tqDebug("> %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)
|
|
tqDebug("%s D%02d > %s (%d)",
|
|
TQString().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)
|
|
tqDebug("%s Cum. %s, Max Caller %s, cut limit %s",
|
|
TQString().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) tqDebug("%s Cut call to %s (cum. %s)",
|
|
TQString().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)
|
|
tqDebug("%s D%02d - %s (%d)",
|
|
TQString().fill(' ', d+1).ascii(), d+1,
|
|
called->prettyName().ascii(), called->_cycleLow);
|
|
}
|
|
else {
|
|
if (0)
|
|
tqDebug("%s D%02d - %s (%d) [Not on stack]",
|
|
TQString().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) tqDebug("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) tqDebug("CYC %s", top->prettyName().ascii());
|
|
if (top == this) break;
|
|
}
|
|
}
|
|
}
|
|
if (0)
|
|
tqDebug("%s D%02d < %s (%d)",
|
|
TQString().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) tqDebug("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) tqDebug("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) tqDebug("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(TQString("<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()
|
|
{}
|
|
|
|
TQString TraceClass::prettyName() const
|
|
{
|
|
if (_name.isEmpty())
|
|
return TQString("(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) {
|
|
tqDebug("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
|
|
tqDebug("%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) {
|
|
tqDebug("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
|
|
tqDebug("%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) {
|
|
tqDebug("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
|
|
tqDebug("%s \n added SourceFile %s (now %d)",
|
|
fullName().ascii(), sourceFile->fullName().ascii(),
|
|
_sourceFiles.count());
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
void TraceFile::setDirectory(const TQString& dir)
|
|
{
|
|
if (dir.endsWith("/"))
|
|
_dir = dir.left(dir.length()-1);
|
|
else
|
|
_dir = dir;
|
|
}
|
|
|
|
TQString TraceFile::directory()
|
|
{
|
|
if (!_dir.isEmpty()) return _dir;
|
|
|
|
int lastIndex = 0, index;
|
|
while ( (index=_name.find("/", lastIndex)) >=0)
|
|
lastIndex = index+1;
|
|
|
|
if (lastIndex==0) return TQString();
|
|
|
|
// without ending "/"
|
|
return _name.left(lastIndex-1);
|
|
}
|
|
|
|
|
|
TQString TraceFile::shortName() const
|
|
{
|
|
int lastIndex = 0, index;
|
|
while ( (index=_name.find("/", lastIndex)) >=0)
|
|
lastIndex = index+1;
|
|
|
|
return _name.mid(lastIndex);
|
|
}
|
|
|
|
TQString TraceFile::prettyName() const
|
|
{
|
|
TQString sn = shortName();
|
|
|
|
if (sn.isEmpty())
|
|
return i18n("(unknown)");
|
|
|
|
return sn;
|
|
}
|
|
|
|
TQString 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) {
|
|
tqDebug("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
|
|
tqDebug("%s added\n %s (now %d)",
|
|
fullName().ascii(),
|
|
function->fullName().ascii(), _functions.count());
|
|
#endif
|
|
}
|
|
|
|
// strip path
|
|
void TraceObject::setName(const TQString& name)
|
|
{
|
|
_name = name;
|
|
|
|
int lastIndex = 0, index;
|
|
while ( (index=_name.find("/", lastIndex)) >=0)
|
|
lastIndex = index+1;
|
|
|
|
_shortName = _name.mid(lastIndex);
|
|
}
|
|
|
|
TQString TraceObject::prettyName() const
|
|
{
|
|
if (_shortName.isEmpty())
|
|
return i18n("(unknown)");
|
|
|
|
return _shortName;
|
|
}
|
|
|
|
//---------------------------------------------------
|
|
// TracePart
|
|
|
|
TracePart::TracePart(TraceData* data, TQFile* 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
|
|
TQString TracePart::shortName() const
|
|
{
|
|
int lastIndex = 0, index;
|
|
while ( (index=_name.find("/", lastIndex)) >=0)
|
|
lastIndex = index+1;
|
|
|
|
return _name.mid(lastIndex);
|
|
}
|
|
|
|
TQString TracePart::prettyName() const
|
|
{
|
|
TQString name = TQString("%1.%2").arg(_pid).arg(_number);
|
|
if (data()->maxThreadID()>1)
|
|
name += TQString("-%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());
|
|
}
|
|
|
|
TQString TracePartList::names() const
|
|
{
|
|
TQString 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 TQString& 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;
|
|
}
|
|
|
|
TQString 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 TQString& base)
|
|
{
|
|
bool baseExisting = true;
|
|
|
|
_traceName = base;
|
|
TQFileInfo finfo(base);
|
|
TQString file = finfo.fileName();
|
|
TQDir dir = finfo.dir();
|
|
|
|
if (!finfo.exists()) {
|
|
baseExisting = false;
|
|
}
|
|
else if (finfo.isDir()) {
|
|
// search for first profile data file in directory
|
|
dir = TQDir(base);
|
|
|
|
TQStringList prefixList;
|
|
prefixList << "callgrind.out" << "cachegrind.out";
|
|
for ( TQStringList::Iterator it = prefixList.begin();
|
|
it != prefixList.end(); ++it ) {
|
|
file = *it;
|
|
|
|
// search for ".pid"
|
|
TQStringList strList = dir.entryList(file+".*", TQDir::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;
|
|
}
|
|
|
|
TQStringList strList;
|
|
strList += dir.entryList(file+".*", TQDir::Files);
|
|
strList += dir.entryList(file+"-*", TQDir::Files);
|
|
|
|
baseExisting = TQFile::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;
|
|
}
|
|
|
|
TQStringList::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 TQString& 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 ((int)str.at(pos) < '0' || (int)str.at(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 ((int)str.at(pos) < '0' || (int)str.at(pos) > '9') break;
|
|
t = 10*t + (str[pos++] - '0');
|
|
}
|
|
}
|
|
|
|
//tqDebug("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(TQString(), 0);
|
|
}
|
|
|
|
TracePart* TraceData::addPart(const TQString& dir, const TQString& name)
|
|
{
|
|
TQString filename = TQString("%1/%2").arg(dir).arg(name);
|
|
#if TRACE_DEBUG
|
|
tqDebug("TraceData::addPart('%s')", filename.ascii());
|
|
#endif
|
|
|
|
TQFile* file = new TQFile(filename);
|
|
|
|
Loader* l = Loader::matchingLoader(file);
|
|
if (!l) return 0;
|
|
|
|
if (_topLevel)
|
|
_topLevel->connect(l, TQT_SIGNAL(updateStatus(TQString, int)),
|
|
TQT_SLOT(showStatus(TQString, 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(TQString& name)
|
|
{
|
|
TracePart* part;
|
|
for (part=_parts.first();part;part=_parts.next())
|
|
if (part->name() == name)
|
|
return part;
|
|
return 0;
|
|
}
|
|
|
|
TQString TraceData::activePartRange()
|
|
{
|
|
TQString 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 += TQString::number(r1);
|
|
else res += TQString("%1-%2").arg(r1).arg(r2);
|
|
r1 = r2 = count;
|
|
}
|
|
}
|
|
if (r1>=0) {
|
|
if (!res.isEmpty()) res += ";";
|
|
if (r1==r2) res += TQString::number(r1);
|
|
else res += TQString("%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 TQString& name)
|
|
{
|
|
TraceObject& o = _objectMap[name];
|
|
if (!o.data()) {
|
|
// was created
|
|
o.setPosition(this);
|
|
o.setName(name);
|
|
|
|
#if TRACE_DEBUG
|
|
tqDebug("Created %s [TraceData::object]",
|
|
o.fullName().ascii());
|
|
#endif
|
|
}
|
|
return &o;
|
|
}
|
|
|
|
|
|
TraceFile* TraceData::file(const TQString& name)
|
|
{
|
|
TraceFile& f = _fileMap[name];
|
|
if (!f.data()) {
|
|
// was created
|
|
f.setPosition(this);
|
|
f.setName(name);
|
|
|
|
#if TRACE_DEBUG
|
|
tqDebug("Created %s [TraceData::file]",
|
|
f.fullName().ascii());
|
|
#endif
|
|
}
|
|
return &f;
|
|
}
|
|
|
|
|
|
// usually only called by function()
|
|
TraceClass* TraceData::cls(const TQString& fnName, TQString& 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;
|
|
}
|
|
|
|
TQString clsName = (lastIndex < 3) ? TQString() :
|
|
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
|
|
tqDebug("Created %s [TraceData::cls]",
|
|
c.fullName().ascii());
|
|
#endif
|
|
}
|
|
return &c;
|
|
}
|
|
|
|
|
|
// name is inclusive class/namespace prefix
|
|
TraceFunction* TraceData::function(const TQString& name,
|
|
TraceFile* file, TraceObject* object)
|
|
{
|
|
// strip class name
|
|
TQString shortName;
|
|
TraceClass* c = cls(name, shortName);
|
|
|
|
if (!file || !object || !c) {
|
|
tqDebug("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.
|
|
TQString 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
|
|
tqDebug("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 !!
|
|
TQString 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, TQString 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()
|
|
{
|
|
//tqDebug("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;
|
|
TQString 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(TQString(),0);
|
|
#endif
|
|
}
|
|
|
|
void TraceData::updateObjectCycles()
|
|
{
|
|
}
|
|
|
|
|
|
void TraceData::updateClassCycles()
|
|
{
|
|
}
|
|
|
|
|
|
void TraceData::updateFileCycles()
|
|
{
|
|
}
|
|
|
|
|