/* This file is part of KCachegrind.
   Copyright (c) 2002-2016 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 "tracedata.h"

#include <errno.h>
#include <stdlib.h>

#include <QFile>
#include <QDir>
#include <QFileInfo>
#include <QDebug>

#include "logger.h"
#include "loader.h"
#include "globalconfig.h"
#include "utils.h"
#include "fixcost.h"


#define TRACE_DEBUG      0
#define TRACE_ASSERTIONS 0




//---------------------------------------------------
// TraceJumpCost

TraceJumpCost::TraceJumpCost(ProfileContext* c)
    :CostItem(c)
{
    TraceJumpCost::clear();
}

TraceJumpCost::~TraceJumpCost()
{}

SubCost TraceJumpCost::executedCount()
{
    if (_dirty) update();

    return _executedCount;
}

SubCost TraceJumpCost::followedCount()
{
    if (_dirty) update();

    return _followedCount;
}

QString TraceJumpCost::costString(EventTypeSet*)
{
    if (_dirty) update();

    return QStringLiteral("%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();
}



//---------------------------------------------------
// TraceCallCost

TraceCallCost::TraceCallCost(ProfileContext* context)
    : ProfileCostArray(context)
{
    _callCount = 0;
}

TraceCallCost::~TraceCallCost()
{}


QString TraceCallCost::costString(EventTypeSet* m)
{
    return QStringLiteral("%1, Calls %2")
            .arg(ProfileCostArray::costString(m))
            .arg(_callCount.pretty());
}

QString TraceCallCost::prettyCallCount()
{
    return _callCount.pretty();
}

void TraceCallCost::clear()
{
    _callCount = 0;
    ProfileCostArray::clear();
}

SubCost TraceCallCost::callCount()
{
    if (_dirty) update();

    return _callCount;
}

void TraceCallCost::addCallCount(SubCost c)
{
    _callCount += c;

    invalidate();
}


//---------------------------------------------------
// TraceInclusiveCost

TraceInclusiveCost::TraceInclusiveCost(ProfileContext* context)
    : ProfileCostArray(context), _inclusive(context)
{}

TraceInclusiveCost::~TraceInclusiveCost()
{}

QString TraceInclusiveCost::costString(EventTypeSet* m)
{
    return QStringLiteral("%1, Inclusive %2")
            .arg(ProfileCostArray::costString(m))
            .arg(_inclusive.costString(m));
}

void TraceInclusiveCost::clear()
{
    _inclusive.clear();
    ProfileCostArray::clear();
}

ProfileCostArray* TraceInclusiveCost::inclusive()
{
    if (_dirty) update();

    return &_inclusive;
}

void TraceInclusiveCost::addInclusive(ProfileCostArray* c)
{
    _inclusive.addCost(c);

    invalidate();
}


//---------------------------------------------------
// TraceListCost

TraceListCost::TraceListCost(ProfileContext* context)
    : ProfileCostArray(context)
{
    _lastDep = nullptr;
}

TraceListCost::~TraceListCost()
{}

void TraceListCost::addDep(ProfileCostArray* dep)
{
#if TRACE_ASSERTIONS
    if (_deps.contains(dep)) {
        qDebug("addDep: %s already in list!",
               qPrintable(dep->fullName()));
        return;
    }
#endif

    _deps.append(dep);
    _lastDep = dep;
    invalidate();

#if TRACE_DEBUG
    qDebug("%s added\n %s (now %d)",
           qPrintable( fullName() ), qPrintable(dep->fullName()),
           _deps.count());
#endif
}

ProfileCostArray* TraceListCost::findDepFromPart(TracePart* part)
{
    if (_lastDep && _lastDep->part() == part)
        return _lastDep;

    foreach(ProfileCostArray* dep, _deps) {
        if (dep->part() == part) {
            _lastDep = dep;
            return dep;
        }
    }
    return nullptr;
}


void TraceListCost::update()
{
    if (!_dirty) return;

#if TRACE_DEBUG
    qDebug("update %s (count %d)",
           qPrintable( fullName() ), _deps.count());
#endif

    clear();
    foreach(ProfileCostArray* item, _deps) {
        if (onlyActiveParts())
            if (!item->part() || !item->part()->isActive()) continue;

        addCost(item);
    }

    _dirty = false;

#if TRACE_DEBUG
    qDebug("   > %s", qPrintable(costString(0)));
#endif
}



//---------------------------------------------------
// TraceJumpListCost

TraceJumpListCost::TraceJumpListCost(ProfileContext* context)
    : TraceJumpCost(context)
{
    _lastDep = nullptr;
}

TraceJumpListCost::~TraceJumpListCost()
{}

void TraceJumpListCost::addDep(TraceJumpCost* dep)
{
#if TRACE_ASSERTIONS
    if (_deps.contains(dep)) {
        qDebug("addDep: %s already in list!",
               qPrintable(dep->fullName()));
        return;
    }
#endif

    _deps.append(dep);
    _lastDep = dep;
    invalidate();

#if TRACE_DEBUG
    qDebug("%s added\n %s (now %d)",
           qPrintable( fullName() ), qPrintable(dep->fullName()),
           _deps.count());
#endif
}

TraceJumpCost* TraceJumpListCost::findDepFromPart(TracePart* part)
{
    if (_lastDep && _lastDep->part() == part)
        return _lastDep;

    foreach(TraceJumpCost* dep, _deps) {
        if (dep->part() == part) {
            _lastDep = dep;
            return dep;
        }
    }
    return nullptr;
}


void TraceJumpListCost::update()
{
    if (!_dirty) return;

#if TRACE_DEBUG
    qDebug("update %s (count %d)",
           qPrintable( fullName() ), _deps.count());
#endif

    clear();
    foreach(TraceJumpCost* item, _deps) {
        if (onlyActiveParts())
            if (!item->part() || !item->part()->isActive()) continue;

        addCost(item);
    }

    _dirty = false;

#if TRACE_DEBUG
    qDebug("   > %s", qPrintable(costString(0)));
#endif
}



//---------------------------------------------------
// TraceCallListCost

TraceCallListCost::TraceCallListCost(ProfileContext* context)
    : TraceCallCost(context)
{
    _lastDep = nullptr;
}

TraceCallListCost::~TraceCallListCost()
{}

void TraceCallListCost::addDep(TraceCallCost* dep)
{
#if TRACE_ASSERTIONS
    if (_deps.contains(dep)) {
        qDebug("addDep: %s already in list!",
               qPrintable(dep->fullName()));
        return;
    }
#endif

    _deps.append(dep);
    _lastDep = dep;
    invalidate();

#if TRACE_DEBUG
    qDebug("%s added\n %s (now %d)",
           qPrintable( fullName() ), qPrintable(dep->fullName()),
           _deps.count());
#endif
}

TraceCallCost* TraceCallListCost::findDepFromPart(TracePart* part)
{
    if (_lastDep && _lastDep->part() == part)
        return _lastDep;

    foreach(TraceCallCost* dep, _deps) {
        if (dep->part() == part) {
            _lastDep = dep;
            return dep;
        }
    }
    return nullptr;
}


void TraceCallListCost::update()
{
    if (!_dirty) return;

#if TRACE_DEBUG
    qDebug("update %s (count %d)",
           qPrintable( fullName() ), _deps.count());
#endif

    /* Without dependent cost items, assume fixed costs,
     * i.e. do not change cost */
    if (_deps.count()>0) {
        clear();
        foreach(TraceCallCost* item, _deps) {
            if (onlyActiveParts())
                if (!item->part() || !item->part()->isActive()) continue;

            addCost(item);
            addCallCount(item->callCount());
        }
    }

    _dirty = false;

#if TRACE_DEBUG
    qDebug("   > %s", qPrintable(costString(0)));
#endif
}


//---------------------------------------------------
// TraceInclusiveListCost

TraceInclusiveListCost::TraceInclusiveListCost(ProfileContext* context)
    : TraceInclusiveCost(context)
{
    _lastDep = nullptr;
}

TraceInclusiveListCost::~TraceInclusiveListCost()
{}


void TraceInclusiveListCost::addDep(TraceInclusiveCost* dep)
{
#if TRACE_ASSERTIONS
    if (_deps.contains(dep)) {
        qDebug("addDep: %s already in list!",
               qPrintable(dep->fullName()));
        return;
    }
#endif

    _deps.append(dep);
    _lastDep = dep;
    invalidate();

#if TRACE_DEBUG
    qDebug("%s added\n %s (now %d)",
           qPrintable( fullName() ), qPrintable(dep->fullName()),
           _deps.count());
#endif
}

TraceInclusiveCost* TraceInclusiveListCost::findDepFromPart(TracePart* part)
{
    if (_lastDep && _lastDep->part() == part)
        return _lastDep;

    foreach(TraceInclusiveCost* dep, _deps) {
        if (dep->part() == part) {
            _lastDep = dep;
            return dep;
        }
    }
    return nullptr;
}

void TraceInclusiveListCost::update()
{
    if (!_dirty) return;

#if TRACE_DEBUG
    qDebug("update %s (count %d)",
           qPrintable( fullName() ), _deps.count());
#endif

    clear();
    foreach(TraceInclusiveCost* item, _deps) {
        if (onlyActiveParts())
            if (!item->part() || !item->part()->isActive()) continue;

        addCost(item);
        addInclusive(item->inclusive());
    }

    _dirty = false;

#if TRACE_DEBUG
    qDebug("   > %s", qPrintable(costString(0)));
#endif
}



//---------------------------------------------------
// TracePartInstrJump

TracePartInstrJump::TracePartInstrJump(TraceInstrJump* instrJump,
                                       TracePartInstrJump* next)
    : TraceJumpCost(ProfileContext::context(ProfileContext::PartInstrJump))
{
    _dep = instrJump;
    _next = next;
}

TracePartInstrJump::~TracePartInstrJump()
{}


//---------------------------------------------------
// TracePartInstrCall

TracePartInstrCall::TracePartInstrCall(TraceInstrCall* instrCall)
    : TraceCallCost(ProfileContext::context(ProfileContext::PartInstrCall))
{
    _dep = instrCall;
}

TracePartInstrCall::~TracePartInstrCall()
{}



//---------------------------------------------------
// TracePartInstr

TracePartInstr::TracePartInstr(TraceInstr* instr)
    : ProfileCostArray(ProfileContext::context(ProfileContext::PartInstr))
{
    _dep = instr;
}

TracePartInstr::~TracePartInstr()
{}



//---------------------------------------------------
// TracePartLineJump

TracePartLineJump::TracePartLineJump(TraceLineJump* lineJump)
    : TraceJumpCost(ProfileContext::context(ProfileContext::PartLineJump))
{
    _dep = lineJump;
}

TracePartLineJump::~TracePartLineJump()
{}


//---------------------------------------------------
// TracePartLineCall

TracePartLineCall::TracePartLineCall(TraceLineCall* lineCall)
    : TraceCallCost(ProfileContext::context(ProfileContext::PartLineCall))
{
    _dep = lineCall;
}

TracePartLineCall::~TracePartLineCall()
{}


//---------------------------------------------------
// TracePartLine

TracePartLine::TracePartLine(TraceLine* line)
    : ProfileCostArray(ProfileContext::context(ProfileContext::PartLine))
{
    _dep = line;
}

TracePartLine::~TracePartLine()
{}




//---------------------------------------------------
// TracePartCall

TracePartCall::TracePartCall(TraceCall* call)
    : TraceCallListCost(ProfileContext::context(ProfileContext::PartCall))
{
    _dep = call;

    _firstFixCallCost = nullptr;
}

TracePartCall::~TracePartCall()
{}

bool TracePartCall::isRecursion()
{
    return call()->isRecursion();
}

void TracePartCall::update()
{
#if !USE_FIXCOST
    TraceCallListCost::update();
#else

    if (!_dirty) return;

#if TRACE_DEBUG
    qDebug("update %s", qPrintable( fullName() ));
#endif

    /* Without dependent cost items, assume fixed costs,
     * i.e. do not change cost */
    if (_firstFixCallCost) {
        clear();
        FixCallCost* item;
        for (item = _firstFixCallCost; item; item = item->nextCostOfPartCall())
            item->addTo(this);
    }

    _dirty = false;

#if TRACE_DEBUG
    qDebug("   > %s", qPrintable(costString(0)));
#endif

#endif // USE_FIXCOST
}


//---------------------------------------------------
// TracePartFunction

TracePartFunction::TracePartFunction(TraceFunction* function,
                                     TracePartObject* partObject,
                                     TracePartFile *partFile)
    : TraceInclusiveCost(ProfileContext::context(ProfileContext::PartFunction))
{
    _dep = function;
    _partObject = partObject;
    _partFile = partFile;
    _partClass = nullptr;

    _calledCount     = 0;
    _callingCount    = 0;
    _calledContexts  = 0;
    _callingContexts = 0;

    _firstFixCost = nullptr;
    _firstFixJump = nullptr;
}

TracePartFunction::~TracePartFunction()
{}

QString TracePartFunction::prettyCalledCount()
{
    return _calledCount.pretty();
}

QString TracePartFunction::prettyCallingCount()
{
    return _callingCount.pretty();
}

QString TracePartFunction::costString(EventTypeSet* m)
{
    update();

    QString res = TraceInclusiveCost::costString(m);
    res += QStringLiteral(", called from %1: %2")
           .arg(_calledContexts).arg(prettyCalledCount());
    res += QStringLiteral(", calling from %1: %2")
           .arg(_callingContexts).arg(prettyCallingCount());

    return res;
}


void TracePartFunction::addPartInstr(TracePartInstr* ref)
{
#if TRACE_ASSERTIONS
    if (_partInstr.contains(ref)) {
        qDebug("TracePartFunction::addPartInstr: %s already in list!",
               qPrintable(ref->name()));
        return;
    }
#endif

    _partInstr.append(ref);
    invalidate();

#if TRACE_DEBUG
    qDebug("%s added\n %s (now %d)",
           qPrintable( fullName() ), qPrintable(ref->fullName()),
           _partInstr.count());
#endif
}


void TracePartFunction::addPartLine(TracePartLine* ref)
{
#if TRACE_ASSERTIONS
    if (_partLines.contains(ref)) {
        qDebug("TracePartFunction::addPartLine: %s already in list!",
               qPrintable(ref->name()));
        return;
    }
#endif

    _partLines.append(ref);
    invalidate();

#if TRACE_DEBUG
    qDebug("%s added\n %s (now %d)",
           qPrintable( fullName() ), qPrintable(ref->fullName()),
           _partLines.count());
#endif
}


void TracePartFunction::addPartCaller(TracePartCall* ref)
{
#if TRACE_ASSERTIONS
    if (_partCallers.contains(ref)) {
        qDebug("TracePartFunction::addPartCaller: %s already in list!",
               qPrintable(ref->name()));
        return;
    }
#endif

    _partCallers.append(ref);
    invalidate();

#if TRACE_DEBUG
    qDebug("%s added Caller\n %s (now %d)",
           qPrintable( fullName() ), qPrintable(ref->fullName()),
           _partCallers.count());
#endif
}


void TracePartFunction::addPartCalling(TracePartCall* ref)
{
#if TRACE_ASSERTIONS
    if (_partCallings.contains(ref)) {
        qDebug("TracePartFunction::addPartCalling: %s already in list!",
               qPrintable(ref->name()));
        return;
    }
#endif

    _partCallings.append(ref);
    invalidate();

#if TRACE_DEBUG
    qDebug("%s added Calling\n %s (now %d)",
           qPrintable( fullName() ), qPrintable(ref->fullName()),
           _partCallings.count());
#endif
}

SubCost TracePartFunction::calledCount()
{
    if (_dirty) update();

    return _calledCount;
}

int TracePartFunction::calledContexts()
{
    if (_dirty) update();

    return _calledContexts;
}

SubCost TracePartFunction::callingCount()
{
    if (_dirty) update();

    return _callingCount;
}


int TracePartFunction::callingContexts()
{
    if (_dirty) update();

    return _callingContexts;
}


void TracePartFunction::update()
{
    if (!_dirty) return;

#if TRACE_DEBUG
    qDebug("TracePartFunction::update %s (Callers %d, Callings %d, lines %d)",
           qPrintable(name()), _partCallers.count(), _partCallings.count(),
           _partLines.count());
#endif

    _calledCount     = 0;
    _callingCount    = 0;
    _calledContexts  = 0;
    _callingContexts = 0;

    // To calculate context counts, we just use first real event type (FIXME?)
    EventType* e = data() ? data()->eventTypes()->realType(0) : nullptr;

    // calculate additional cost metrics
    foreach(TracePartCall* caller, _partCallers) {
        if (e && (caller->subCost(e) >0))
            _calledContexts++;

        SubCost c = caller->callCount();
        if (c>0) {
            _calledCount += c;
        }
    }
    foreach(TracePartCall* calling, _partCallings) {
        if (e && (calling->subCost(e)>0))
            _callingContexts++;

        SubCost c = calling->callCount();
        if (c>0) {
            _callingCount += c;
        }
    }

    // self cost
#if !USE_FIXCOST
    if (_partLines.count()>0) {
        ProfileCostArray::clear();

        foreach(TracePartLine* line, _partLines)
            addCost(line);
    }
#else
    if (_firstFixCost) {
        ProfileCostArray::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
        foreach(TracePartCall* caller, _partCallers) {
            // detect simple recursion (no cycle)
            if (caller->isRecursion()) continue;

            addInclusive(caller);
        }
    }
    else {
        // without caller info, use calling sum + line costs
        foreach(TracePartCall* calling, _partCallings) {
            // detect simple recursion (no cycle)
            if (calling->isRecursion()) continue;

            addInclusive(calling);
        }
        _dirty = false; // do not recurse!
        addInclusive(this);
    }

    _dirty = false;

#if TRACE_DEBUG
    qDebug("   > %s", qPrintable(costString(0)));
#endif
}



//---------------------------------------------------
// TracePartClass

TracePartClass::TracePartClass(TraceClass* cls)
    : TraceInclusiveListCost(ProfileContext::context(ProfileContext::PartClass))
{
    _dep = cls;
}

TracePartClass::~TracePartClass()
{}

QString TracePartClass::prettyName() const
{
    return QStringLiteral("%1 from %2")
            .arg( _dep->name().isEmpty() ? QStringLiteral("(global)") : _dep->name())
            .arg(part()->name());
}

//---------------------------------------------------
// TracePartFile

TracePartFile::TracePartFile(TraceFile* file)
    : TraceInclusiveListCost(ProfileContext::context(ProfileContext::PartFile))
{
    _dep = file;
}

TracePartFile::~TracePartFile()
{}


//---------------------------------------------------
// TracePartObject

TracePartObject::TracePartObject(TraceObject* object)
    : TraceInclusiveListCost(ProfileContext::context(ProfileContext::PartObject))
{
    _dep = object;
}

TracePartObject::~TracePartObject()
{}




//---------------------------------------------------
// TraceInstrJump

TraceInstrJump::TraceInstrJump(TraceInstr* instrFrom, TraceInstr* instrTo,
                               bool isCondJump)
    : TraceJumpCost(ProfileContext::context(ProfileContext::InstrJump))
{
    _first = nullptr;

    _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 = nullptr;

    // shortcut if recently used
    if (item &&
        (item->instrJump()==this) &&
        (item->part() == part)) return item;

    for(item = _first; item; item = item->next())
        if (item->part() == part)
            return item;

    item = new TracePartInstrJump(this, _first);
    item->setPosition(part);
    _first = item;
    return item;
}

void TraceInstrJump::update()
{
    if (!_dirty) return;

    clear();
    TracePartInstrJump* item;
    for (item = _first; item; item = item->next()) {
        if (!item->part() || !item->part()->isActive()) continue;

        addCost(item);
    }
    _dirty = false;

#if TRACE_DEBUG
    qDebug("updated %s", qPrintable( fullName() ));
#endif

#if TRACE_DEBUG
    qDebug("   > %s", qPrintable(costString(0)));
#endif
}

QString TraceInstrJump::name() const
{
    return QStringLiteral("jump at 0x%1 to 0x%2")
            .arg(_instrFrom->addr().toString())
            .arg(_instrTo->addr().toString());
}


//---------------------------------------------------
// TraceLineJump

TraceLineJump::TraceLineJump(TraceLine* lineFrom, TraceLine* lineTo,
                             bool isCondJump)
    : TraceJumpListCost(ProfileContext::context(ProfileContext::LineJump))
{
    _lineFrom = lineFrom;
    _lineTo   = lineTo;
    _isCondJump = isCondJump;
}

TraceLineJump::~TraceLineJump()
{
    // we are the owner of TracePartLineJump's generated in our factory
    qDeleteAll(_deps);
}


TracePartLineJump* TraceLineJump::partLineJump(TracePart* part)
{
    TracePartLineJump* item = (TracePartLineJump*) findDepFromPart(part);
    if (!item) {
        item = new TracePartLineJump(this);
        item->setPosition(part);
        addDep(item);
    }
    return item;
}


QString TraceLineJump::name() const
{
    return QStringLiteral("jump at %1 to %2")
            .arg(_lineFrom->prettyName())
            .arg(_lineTo->prettyName());
}


//---------------------------------------------------
// TraceInstrCall

TraceInstrCall::TraceInstrCall(TraceCall* call, TraceInstr* instr)
    : TraceCallListCost(ProfileContext::context(ProfileContext::InstrCall))
{
    _call  = call;
    _instr = instr;
}

TraceInstrCall::~TraceInstrCall()
{
    // we are the owner of TracePartInstrCall's generated in our factory
    qDeleteAll(_deps);
}


TracePartInstrCall* TraceInstrCall::partInstrCall(TracePart* part,
                                                  TracePartCall*)
{
    TracePartInstrCall* item = (TracePartInstrCall*) findDepFromPart(part);
    if (!item) {
        item = new TracePartInstrCall(this);
        item->setPosition(part);
        addDep(item);
        // instruction calls are not registered in function calls
        // as together with line calls calls are duplicated
        //partCall->addDep(item);
    }
    return item;
}


QString TraceInstrCall::name() const
{
    return QStringLiteral("%1 at %2").arg(_call->name()).arg(_instr->name());
}


//---------------------------------------------------
// TraceLineCall

TraceLineCall::TraceLineCall(TraceCall* call, TraceLine* line)
    : TraceCallListCost(ProfileContext::context(ProfileContext::LineCall))
{
    _call = call;

    _line = line;
}

TraceLineCall::~TraceLineCall()
{
    // we are the owner of TracePartLineCall's generated in our factory
    qDeleteAll(_deps);
}


TracePartLineCall* TraceLineCall::partLineCall(TracePart* part,
                                               TracePartCall* partCall)
{
    TracePartLineCall* item = (TracePartLineCall*) findDepFromPart(part);
    if (!item) {
        item = new TracePartLineCall(this);
        item->setPosition(part);
        addDep(item);
        partCall->addDep(item);
    }
    return item;
}


QString TraceLineCall::name() const
{
    return QStringLiteral("%1 at %2").arg(_call->name()).arg(_line->name());
}


//---------------------------------------------------
// TraceCall

TraceCall::TraceCall(TraceFunction* caller, TraceFunction* called)
    : TraceCallListCost(ProfileContext::context(ProfileContext::Call))
{
    _caller = caller;
    _called = called;
}


TraceCall::~TraceCall()
{
    // we are the owner of all items generated in our factories
    qDeleteAll(_deps);
    qDeleteAll(_lineCalls);
}

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)
{
    foreach(TraceInstrCall* icall, _instrCalls)
        if (icall->instr() == i)
            return icall;

    TraceInstrCall* icall = new TraceInstrCall(this, i);
    _instrCalls.append(icall);
    invalidate();

#if TRACE_DEBUG
    qDebug("Created %s [TraceCall::instrCall]", qPrintable(icall->fullName()));
#endif
    i->addInstrCall(icall);
    return icall;
}


TraceLineCall* TraceCall::lineCall(TraceLine* l)
{
    foreach(TraceLineCall* lcall, _lineCalls)
        if (lcall->line() == l)
            return lcall;

    TraceLineCall* lcall = new TraceLineCall(this, l);
    _lineCalls.append(lcall);
    invalidate();

#if TRACE_DEBUG
    qDebug("Created %s [TraceCall::lineCall]", qPrintable(lcall->fullName()));
#endif
    l->addLineCall(lcall);
    return lcall;
}


void TraceCall::invalidateDynamicCost()
{
    foreach(TraceLineCall* lc, _lineCalls)
        lc->invalidate();

    foreach(TraceInstrCall* ic, _instrCalls)
        ic->invalidate();

    invalidate();
}


QString TraceCall::name() const
{
    return QStringLiteral("%1 => %2")
            .arg(_caller->name())
            .arg(_called->name());
}

int TraceCall::inCycle()
{
    if (!_caller || !_called) return 0;
    if (!_caller->cycle()) return 0;
    if (_caller == _caller->cycle()) return 0;
    if (_caller->cycle() != _called->cycle()) return 0;

    return _caller->cycle()->cycleNo();
}

void TraceCall::update()
{
    if (!_dirty) return;

    // special handling for cycles
    if (_caller && _caller->cycle() && _caller==_caller->cycle()) {

        // we have no part calls: use inclusive cost of called function
        clear();
        if (_called)
            addCost(_called->inclusive());
        _dirty = false;
        return;
    }

    TraceCallListCost::update();
}

TraceFunction* TraceCall::caller(bool /*skipCycle*/) const
{
    return _caller;
}

TraceFunction* TraceCall::called(bool skipCycle) const
{
    if (!skipCycle && _called) {
        // if this is a call to a cycle member from outside of the cycle,
        // fake it to be a call to the whole cycle
        if (_called->cycle() && _caller &&
            (_caller->cycle() != _called->cycle()))
            return _called->cycle();
    }

    return _called;
}

QString TraceCall::callerName(bool skipCycle) const
{
    if (!_caller) return QObject::tr("(no caller)");

    if (!skipCycle) {
        // if this call goes into a cycle, add the entry function
        TraceFunctionCycle* c = _called->cycle();
        if (c && _caller && (_caller->cycle() != c)) {
            QString via = _called->prettyName();
            return QObject::tr("%1 via %2").arg(_caller->prettyName()).arg(via);
        }
    }

    return _caller->prettyName();
}

QString TraceCall::calledName(bool skipCycle) const
{
    if (!_called) return QObject::tr("(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(nullptr);
            QString via = _called->prettyName();
            _called->setCycle(c);
            return QObject::tr("%1 via %2").arg(c->name()).arg(via);
        }
    }
    return _called->prettyName();
}


//---------------------------------------------------
// TraceInstr

TraceInstr::TraceInstr()
    : TraceListCost(ProfileContext::context(ProfileContext::Instr))
{
    _addr = 0;
    _line = nullptr;
    _function = nullptr;
}

TraceInstr::~TraceInstr()
{
    // we are the owner of items generated in our factories
    qDeleteAll(_deps);
    qDeleteAll(_instrJumps);
}

bool TraceInstr::hasCost(EventType* ct)
{
    if (subCost(ct) >0)
        return true;

    foreach(TraceInstrCall* ic, _instrCalls)
        if (ic->subCost(ct) >0)
            return true;

    foreach(TraceInstrJump* ij, _instrJumps)
        if (ij->executedCount() >0)
            return true;

    return false;
}

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)
{
    foreach(TraceInstrJump* jump, _instrJumps)
        if (jump->instrTo() == to)
            return jump;

    TraceInstrJump* jump = new TraceInstrJump(this, to, isJmpCond);
    _instrJumps.append(jump);
    return jump;
}



void TraceInstr::addInstrCall(TraceInstrCall* instrCall)
{
#if TRACE_ASSERTIONS
    if (_instrCalls.contains(instrCall)) return;

    if (instrCall->instr() != this) {
        qDebug("Can not add instruction call to another instruction!");
        return;
    }
#endif

    _instrCalls.append(instrCall);
    invalidate();

#if TRACE_DEBUG
    qDebug("%s added\n %s (now %d)",
           qPrintable( fullName() ),
           qPrintable(instrCall->fullName()), _instrCalls.count());
#endif
}


QString TraceInstr::name() const
{
    return QStringLiteral("0x%1").arg(_addr.toString());
}

QString TraceInstr::prettyName() const
{
    return QStringLiteral("0x%1").arg(_addr.toString());
}


//---------------------------------------------------
// TraceLine

TraceLine::TraceLine()
    : TraceListCost(ProfileContext::context(ProfileContext::Line))
{
    _lineno = 0;
    _sourceFile = nullptr;
}

TraceLine::~TraceLine()
{
    // we are the owner of items generated in our factories
    qDeleteAll(_deps);
    qDeleteAll(_lineJumps);
}

bool TraceLine::hasCost(EventType* ct)
{
    if (subCost(ct) >0)
        return true;

    foreach(TraceLineCall* lc, _lineCalls)
        if (lc->subCost(ct) >0)
            return true;

    foreach(TraceLineJump* lj, _lineJumps)
        if (lj->executedCount() >0)
            return true;

    return false;
}

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)
{
    foreach(TraceLineJump* jump, _lineJumps)
        if (jump->lineTo() == to)
            return jump;

    TraceLineJump* jump = new TraceLineJump(this, to, isJmpCond);
    _lineJumps.append(jump);
    return jump;
}


void TraceLine::addLineCall(TraceLineCall* lineCall)
{
#if TRACE_ASSERTIONS
    if (_lineCalls.contains(lineCall)) return;

    if (lineCall->line() != this) {
        qDebug("Can not add line call to another line!");
        return;
    }
#endif

    TraceFunction* caller = lineCall->call()->caller();
    TraceFunction* function = _sourceFile->function();
    if (caller != function) {
        // We regard 2 functions as the same if they have
        // same class, name, object
        if ((caller->cls() != function->cls()) ||
            (caller->name() != function->name()) ||
            (caller->object() != function->object())) {

            qDebug("ERROR: Adding line call, line %d\n  of %s to\n  %s ?!",
                   lineCall->line()->lineno(),
                   qPrintable(caller->info()), qPrintable(function->info()));
        }
    }

    _lineCalls.append(lineCall);
    invalidate();

#if TRACE_DEBUG
    qDebug("%s added\n %s (now %d)",
           qPrintable( fullName() ),
           qPrintable(lineCall->fullName()), _lineCalls.count());
#endif
}


QString TraceLine::name() const
{
    QString fileShortName = _sourceFile->file()->shortName();
    if (fileShortName.isEmpty())
        return TraceFile::prettyEmptyName();

    return QStringLiteral("%1:%2")
            .arg(fileShortName).arg(_lineno);
}

QString TraceLine::prettyName() const
{
    return QStringLiteral("%1 [%2]")
            .arg(name()).arg(_sourceFile->function()->prettyName());
}

//---------------------------------------------------
// TraceCostItem

TraceCostItem::TraceCostItem(ProfileContext* context)
    : TraceInclusiveListCost(context)
{
}

TraceCostItem::~TraceCostItem()
{}


//---------------------------------------------------
// TraceFunctionSource

TraceFunctionSource::TraceFunctionSource(TraceFunction* function,
                                         TraceFile* file)
    : ProfileCostArray(ProfileContext::context(ProfileContext::FunctionSource))
{
    _file = file;
    _function = function;

    // the function is dependent from our cost sum
    _dep = _function;

    _lineMap = nullptr;
    _lineMapFilled = false;
    _line0 = nullptr;
}

TraceFunctionSource::~TraceFunctionSource()
{
    delete _lineMap;
    delete _line0;
}

QString TraceFunctionSource::name() const
{
    return QStringLiteral("%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 nullptr;
            _line0 = new TraceLine;
            _line0->setSourceFile(this);
            _line0->setLineno(0);
        }
        return _line0;
    }

    if (!createNew) {
        if (!_lineMap) return nullptr;
        TraceLineMap::Iterator it = _lineMap->find(lineno);
        if (it == _lineMap->end()) return nullptr;
        return &(it.value());
    }

    if (!_lineMap) _lineMap = new TraceLineMap;

    TraceLine& l = (*_lineMap)[lineno];
    if (!l.isValid()) {
        l.setSourceFile(this);
        l.setLineno(lineno);

#if TRACE_DEBUG
        qDebug("Created %s [TraceFunctionSource::line]",
               qPrintable(l.fullName()));
#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 = nullptr;
    TracePartLine* pl = nullptr;
    TraceLineCall* lc = nullptr;
    TracePartLineCall* plc = nullptr;

    /* go over all part objects for this function, and
     * - build TraceLines (the line map) using FixCost objects
     * - build TraceJumpLines using FixJump objects
     */
    foreach(TraceInclusiveCost* ic, _function->deps()) {
        TracePartFunction* pf = (TracePartFunction*) ic;

        if (0) qDebug("PartFunction %s:%d",
                      qPrintable(pf->function()->name()),
                      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 = nullptr;
            }
            if (!pl || pl->part() != fc->part())
                pl = l->partLine(fc->part(), pf);
            fc->addTo(pl);
        }

        TraceLine* to = nullptr;
        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;
            }

            // do not 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);
        }

        foreach(TracePartCall* pc, pf->partCallings()) {

            if (0) qDebug("PartCall %s:%d",
                          qPrintable(pc->call()->name()),
                          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 = nullptr;
                }
                if (!plc || plc->part() != fcc->part())
                    plc = lc->partLineCall(fcc->part(), pc);

                fcc->addTo(plc);
                if (0) qDebug("Add FixCallCost %s:%d/0x%s, CallCount %s",
                              qPrintable(fcc->functionSource()->file()->shortName()),
                              fcc->line(), qPrintable(fcc->addr().toString()),
                              qPrintable(fcc->callCount().pretty()));
            }
        }
    }

#endif

    return _lineMap;
}



//---------------------------------------------------
// TraceAssociation

TraceAssociation::TraceAssociation()
{
    _function = nullptr;
    _valid = false;
}

TraceAssociation::~TraceAssociation()
{
    // do not delete from TraceFunction
    if (_function) _function->removeAssociation(this);
}

bool TraceAssociation::isAssociated()
{
    if (!_function) return false;

    return _function->association(rtti())==this;
}

bool TraceAssociation::setFunction(TraceFunction* f)
{
    if (_function == f)
        return isAssociated();

    if (_function) {
        // do not delete ourself
        _function->removeAssociation(this);
    }

    _function = f;
    if (f && f->association(rtti()) == nullptr) {
        f->addAssociation(this);
        return true;
    }
    return false;
}

void TraceAssociation::clear(TraceData* d, int rtti)
{
    TraceFunctionMap::Iterator it;
    for ( it = d->functionMap().begin();
          it != d->functionMap().end(); ++it )
        (*it).removeAssociation(rtti);
}

void TraceAssociation::invalidate(TraceData* d, int rtti)
{
    TraceFunctionMap::Iterator it;
    for ( it = d->functionMap().begin();
          it != d->functionMap().end(); ++it )
        (*it).invalidateAssociation(rtti);
}


//---------------------------------------------------
// TraceFunction

TraceFunction::TraceFunction()
    : TraceCostItem(ProfileContext::context(ProfileContext::Function))
{
    _object = nullptr;
    _file = nullptr;
    _cls = nullptr;
    _cycle = nullptr;

    _calledCount     = 0;
    _callingCount    = 0;
    _calledContexts  = 0;
    _callingContexts = 0;

    _instrMap = nullptr;
    _instrMapFilled = false;
}


TraceFunction::~TraceFunction()
{
    qDeleteAll(_associations);

    // we are the owner of items generated in our factories
    qDeleteAll(_deps);
    qDeleteAll(_callings);
    qDeleteAll(_sourceFiles);

    delete _instrMap;
}

// no unique check is done!
void TraceFunction::addAssociation(TraceAssociation* a)
{
    if (!a) return;
    _associations.append(a);
}

void TraceFunction::removeAssociation(TraceAssociation* a)
{
    _associations.removeAll(a);
}

void TraceFunction::removeAssociation(int rtti, bool reallyDelete)
{
    if (rtti==0) {
        if (reallyDelete)
            qDeleteAll(_associations);
        _associations.clear();
        return;
    }

    foreach(TraceAssociation* a, _associations) {
        if (a->rtti() == rtti) {
            if (reallyDelete) delete a;
            _associations.removeAll(a);
            return;
        }
    }
}

void TraceFunction::invalidateAssociation(int rtti)
{
    foreach(TraceAssociation* a, _associations) {
        if ((rtti==0) || (a->rtti() == rtti))
            a->invalidate();
    }
}

TraceAssociation* TraceFunction::association(int rtti)
{
    foreach(TraceAssociation* a, _associations) {
        if (a->rtti() == rtti)
            return a;
    }
    return nullptr;
}

#if 0
// helper for prettyName
bool TraceFunction::isUniquePrefix(const QString& prefix) const
{
    TraceFunctionMap::ConstIterator it, it2;
    it = it2 = _myMapIterator;
    if (it != data()->functionBeginIterator()) {
        it2--;
        if ((*it2).name().startsWith(prefix)) return false;
    }
    if (it != data()->functionEndIterator()) {
        it++;
        if ((*it).name().startsWith(prefix)) return false;
    }
    return true;
}
#endif

QString TraceFunction::prettyName() const
{
    QString res = _name;

    if (_name.isEmpty())
        return prettyEmptyName();

    if (GlobalConfig::hideTemplates()) {

        res = QString();
        int d = 0;
        for(int i=0;i<_name.length();i++) {
            switch(_name[i].toLatin1()) {
            case '<':
                if (d<=0) res.append(_name[i]);
                d++;
                break;
            case '>':
                d--;
                // fall through
            default:
                if (d<=0) res.append(_name[i]);
                break;
            }
        }
    }
#if 0
    // TODO: make it a configuration, but disabled by default.
    //
    // Stripping parameter signature of C++ symbols is fine
    // if the function name is unique in the whole program.
    // However, we only can detect if it is unique in the profile,
    // which makes this "beautification" potentially confusing
    int p = _name.indexOf('(');
    if (p>0) {
        // handle C++ "operator()" correct
        if ( (p+2 < _name.size()) && (_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);
    }
#endif

    // cycle members
    if (_cycle) {
        if (_cycle != this)
            res = QStringLiteral("%1 <cycle %2>").arg(res).arg(_cycle->cycleNo());
        else
            res = QStringLiteral("<cycle %2>").arg(_cycle->cycleNo());
    }


    return res;
}

QString TraceFunction::formattedName() const
{
    // produce a "rich" name only if templates are hidden
    if (!GlobalConfig::hideTemplates() || _name.isEmpty()) return QString();

    // bold, but inside template parameters normal, function arguments italic
    QString rich(QStringLiteral("<b>"));
    int d = 0;
    for(int i=0;i<_name.length();i++) {
        switch(_name[i].toLatin1()) {
        case '&':
            rich.append("&amp;");
            break;
        case '<':
            d++;
            rich.append("&lt;");
            if (d==1)
                rich.append("</b>");
            break;
        case '>':
            d--;
            if (d==0)
                rich.append("<b>");
            rich.append("&gt; "); // add space to allow for line break
            break;
        case '(':
            rich.append("</b>(<i><b>");
            break;
        case ')':
            rich.append("</b></i>)<b>");
            break;
        default:
            rich.append(_name[i]);
            break;
        }
    }
    rich.append("</b>");
    return rich;
}

QString TraceFunction::prettyEmptyName()
{
    return QObject::tr("(unknown)");
}

/*
 * Returns location string: ELF object and source file(s).
 */
QString TraceFunction::location(int maxFiles) const
{
    QString loc;

    // add object file with address range
    if (_object) {
        loc = _object->shortName();

#if 0
        uint from = firstAddress();
        uint to = lastAddress();
        if (from != 0 && to != 0) {
            if (from == to)
                loc += QString(" (0x%1)").arg(to, 0, 16);
            else
                loc += QString(" (0x%1-0x%2)").arg(from, 0, 16).arg(to, 0, 16);
        }
#endif
    }

    // add all source files
    int filesAdded = 0;
    foreach(TraceFunctionSource* sourceFile, _sourceFiles) {
        if (!sourceFile->file() ||
            (sourceFile->file()->name().isEmpty()) )
            continue;

        if (!loc.isEmpty())
            loc += (filesAdded>0) ? ", " : ": ";
        filesAdded++;

        if ((maxFiles>0) && (filesAdded>maxFiles)) {
            loc += QLatin1String("...");
            break;
        }
        loc += sourceFile->file()->shortName();

#if 0
        from = sourceFile->firstLineno();
        to = sourceFile->lastLineno();
        if (from != 0 && to != 0) {
            if (from == to)
                loc += QString(" (%1)").arg(to);
            else
                loc += QString(" (%1-%2)").arg(from).arg(to);
        }
#endif
    }

    return loc;
}

// pretty version is allowed to mangle the string...
QString TraceFunction::prettyLocation(int maxFiles) const
{
    QString l = location(maxFiles);
    if (l.isEmpty()) return QObject::tr("(unknown)");

    return l;
}

void TraceFunction::addPrettyLocation(QString& s, int maxFiles) const
{
    QString l = location(maxFiles);
    if (l.isEmpty()) return;

    s += QStringLiteral(" (%1)").arg(l);
}

QString TraceFunction::prettyNameWithLocation(int maxFiles) const
{
    QString l = location(maxFiles);
    if (l.isEmpty()) return prettyName();

    return QStringLiteral("%1 (%2)").arg(prettyName()).arg(l);
}

QString TraceFunction::info() const
{
    QString l = location();
    if (l.isEmpty())
        return QStringLiteral("Function %1").arg(name());

    return QStringLiteral("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->constBegin();
    return (*it).addr();
}

Addr TraceFunction::lastAddress() const
{
    // ignore address 0 here
    if (!_instrMap || _instrMap->count() == 0) return 0;
    TraceInstrMap::ConstIterator it = _instrMap->constEnd();
    --it;
    return (*it).addr();
}

/* factory */
TraceInstr* TraceFunction::instr(Addr addr, bool createNew)
{
    // address 0 not allowed
    if (addr == Addr(0)) return nullptr;

    if (!createNew) {
        if (!_instrMap) return nullptr;
        TraceInstrMap::Iterator it = _instrMap->find(addr);
        if (it == _instrMap->end())
            return nullptr;
        return &(it.value());
    }

    if (!_instrMap) _instrMap = new TraceInstrMap;

    TraceInstr& i = (*_instrMap)[addr];
    if (!i.isValid()) {
        i.setAddr(addr);
        i.setFunction(this);

#if TRACE_DEBUG
        qDebug("Created %s [TraceFunction::instr]",
               qPrintable(i.fullName()));
#endif
    }
    return &i;
}

void TraceFunction::addCaller(TraceCall* caller)
{
#if TRACE_ASSERTIONS
    if (caller->called() != this) {
        qDebug("Can not add call to another line!\n");
        return;
    }

    if (_callers.contains(caller)) return;
#endif

    _callers.append(caller);
    invalidate();

#if TRACE_DEBUG
    qDebug("%s added Caller\n %s (now %d)",
           qPrintable( fullName() ), qPrintable(caller->fullName()), _callers.count());
#endif
}



TraceCall* TraceFunction::calling(TraceFunction* called)
{
    foreach(TraceCall* calling, _callings)
        if (calling->called() == called)
            return calling;

    TraceCall* calling = new TraceCall(this, called);
    _callings.append(calling);

    // we have to invalidate ourself so invalidations from item propagate up
    invalidate();

#if TRACE_DEBUG
    qDebug("Created %s [TraceFunction::calling]", qPrintable(calling->fullName()));
#endif
    called->addCaller(calling);
    return calling;
}

TraceFunctionSource* TraceFunction::sourceFile(TraceFile* file,
                                               bool createNew)
{
    if (!file) file = _file;

    foreach(TraceFunctionSource* sourceFile, _sourceFiles)
        if (sourceFile->file() == file)
            return sourceFile;

    if (!createNew) return nullptr;

    TraceFunctionSource* sourceFile = new TraceFunctionSource(this, file);
    _sourceFiles.append(sourceFile);

    // we have to invalidate ourself so invalidations from item propagate up
    invalidate();

#if TRACE_DEBUG
    qDebug("Created SourceFile %s [TraceFunction::line]",
           qPrintable(file->name()));
#endif
    file->addSourceFile(sourceFile);
    return sourceFile;
}

TraceLine* TraceFunction::line(TraceFile* file, uint lineno,
                               bool createNew)
{
    Q_ASSERT(file!=nullptr);

    TraceFunctionSource* sf = sourceFile(file, createNew);
    if (!sf)
        return nullptr;
    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()==nullptr && partObject) {
        item->setPartObject(partObject);
        partObject->addPartFunction(item);
    }

    return item;
}


SubCost TraceFunction::calledCount()
{
    if (_dirty) update();

    return _calledCount;
}

int TraceFunction::calledContexts()
{
    if (_dirty) update();

    return _calledContexts;
}

SubCost TraceFunction::callingCount()
{
    if (_dirty) update();

    return _callingCount;
}

int TraceFunction::callingContexts()
{
    if (_dirty) update();

    return _callingContexts;
}

QString TraceFunction::prettyCalledCount()
{
    return _calledCount.pretty();
}

QString TraceFunction::prettyCallingCount()
{
    return _callingCount.pretty();
}


TraceCallList TraceFunction::callers(bool skipCycle) const
{
    if (skipCycle) return _callers;

    // fake the callers for cycle members
    if (_cycle && (_cycle != this)) {
        TraceCallList l;

        // inner-cycle-callers
        foreach(TraceCall* c, _callers)
            if (c->caller()->cycle() == _cycle)
                l.append(c);

        // call from cycle itself
        foreach(TraceCall* c, _cycle->_callings)
            if (c->called() == this) {
                l.append(c);
                return l;
            }
    }

    return _callers;
}

const TraceCallList& TraceFunction::callings(bool /* skipCycle */) const
{
    return _callings;
}

void TraceFunction::invalidateDynamicCost()
{
    foreach(TraceCall* c, _callings)
        c->invalidateDynamicCost();

    foreach(TraceFunctionSource* sf, _sourceFiles)
        sf->invalidateDynamicCost();

    if (_instrMap) {
        TraceInstrMap::Iterator iit;
        for ( iit = _instrMap->begin();
              iit != _instrMap->end(); ++iit )
            (*iit).invalidate();
    }

    invalidate();
}

void TraceFunction::update()
{
    if (!_dirty) return;

#if TRACE_DEBUG
    qDebug("Update %s (Callers %d, sourceFiles %d, instrs %d)",
           qPrintable(_name), _callers.count(),
           _sourceFiles.count(), _instrMap ? _instrMap->count():0);
#endif

    _calledCount    = 0;
    _callingCount    = 0;
    _calledContexts  = 0;
    _callingContexts = 0;
    clear();

    // To calculate context counts, we just use first real event type (FIXME?)
    EventType* e = data() ? data()->eventTypes()->realType(0) : nullptr;

    // context count is NOT the sum of part contexts
    foreach(TraceCall *caller, _callers) {
        if (e && (caller->subCost(e) >0))
            _calledContexts++;
        _calledCount += caller->callCount();
    }

    foreach(TraceCall* callee, _callings) {
        if (e && (callee->subCost(e) >0))
            _callingContexts++;
        _callingCount += callee->callCount();
    }

    if (data()->inFunctionCycleUpdate() || !_cycle) {
        // usual case (no cycle member)
        foreach(TraceInclusiveCost* item, _deps) {
            if (!item->part() || !item->part()->isActive()) continue;

            addCost(item);
            addInclusive(item->inclusive());
        }
    }
    else {
        // this is a cycle or cycle member
        foreach(TraceCall* callee, _callings) {

            // ignore inner-cycle member calls for inclusive cost
            if ((_cycle != this) &&
                (callee->inCycle()>0))  continue;

            addInclusive(callee);
        }

        // self cost
        if (type() == ProfileContext::FunctionCycle) {
            // cycle: self cost is sum of cycle member self costs, but
            //        does not add to inclusive cost
            foreach(TraceFunction* m, ((TraceFunctionCycle*)this)->members())
                addCost(m);
        }
        else {
            // cycle member
            foreach(TraceInclusiveCost* item, _deps) {
                if (!item->part() || !item->part()->isActive()) continue;

                addCost(item);
            }
            _dirty = false; // do not recurse
            addInclusive(this);
        }
    }
    _dirty = false;

#if TRACE_DEBUG
    qDebug("> %s", qPrintable(costString(0)));
#endif
}

bool TraceFunction::isCycle()
{
    return _cycle == this;
}

bool TraceFunction::isCycleMember()
{
    return _cycle && (_cycle != this);
}

void TraceFunction::cycleReset()
{
    _cycle = nullptr;
    _cycleStackDown = nullptr;
    _cycleLow = 0;
}

// this does not mark functions calling themself !
void TraceFunction::cycleDFS(int d, int& pNo, TraceFunction** pTop)
{
    if (_cycleLow != 0) return;

    // 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 ?!
     */
    Q_ASSERT((data() != nullptr) && (data()->eventTypes()->realCount()>0));
    EventType* e = data()->eventTypes()->realType(0);

    SubCost base = 0;
    if (_callers.count()>0) {
        foreach(TraceCall* caller, _callers)
            if (caller->subCost(e) > base)
                base = caller->subCost(e);
    }
    else base = inclusive()->subCost(e);

    SubCost cutLimit = SubCost(base * GlobalConfig::cycleCut());

    if (0) {
        qDebug("%s (%d) Visiting %s",
               qPrintable(QString().fill(' ', d)),
               pNo, qPrintable(prettyName()));
        qDebug("%s       Cum. %s, Max Caller %s, cut limit %s",
               qPrintable(QString().fill(' ', d)),
               qPrintable(inclusive()->subCost(e).pretty()),
               qPrintable(base.pretty()),
               qPrintable(cutLimit.pretty()));
    }

    foreach(TraceCall *callee, _callings) {
        TraceFunction* called = callee->called();

        // cycle cut heuristic
        if (callee->subCost(e) < cutLimit) {
            if (0) qDebug("%s       Cut call to %s (cum. %s)",
                          qPrintable(QString().fill(' ', d)),
                          qPrintable(called->prettyName()),
                          qPrintable(callee->subCost(e).pretty()));

            continue;
        }

        if (called->_cycleLow==0) {
            // not visited yet
            called->cycleDFS(d+1, pNo, pTop);
            if (called->_cycleLow < _cycleLow)
                _cycleLow = called->_cycleLow;
        }
        else if (called->_cycleStackDown) {
            // backlink to same SCC (still in stack)
            if (called->_cycleLow < _cycleLow)
                _cycleLow = called->_cycleLow;

            if (0) qDebug("%s (low %d) Back to %s",
                          qPrintable(QString().fill(' ', d)),
                          _cycleLow, qPrintable(called->prettyName()));
        }
    }

    if (prefixNo == _cycleLow) {
        // this is the base of a SCC.

        if (*pTop == this) {
            *pTop = _cycleStackDown;
            _cycleStackDown = nullptr;
        }
        else {
            // a SCC with >1 members

            TraceFunctionCycle* cycle = data()->functionCycle(this);
            if (0) qDebug("Found Cycle %d with base %s:",
                          cycle->cycleNo(), qPrintable(prettyName()));
            while(*pTop) {
                TraceFunction* top = *pTop;
                cycle->add(top);

                // remove from stack
                *pTop = top->_cycleStackDown;
                top->_cycleStackDown = nullptr;

                if (0) qDebug("  %s", qPrintable(top->prettyName()));
                if (top == this) break;
            }
        }
    }
}


TraceInstrMap* TraceFunction::instrMap()
{
#if USE_FIXCOST

    if (_instrMapFilled) return _instrMap;
    _instrMapFilled = true;
    if (!_instrMap)
        _instrMap = new TraceInstrMap;

    TraceLine* l = nullptr;
    TraceInstr* i = nullptr;
    TracePartInstr* pi = nullptr;
    TraceInstrCall* ic = nullptr;
    TracePartInstrCall* pic = nullptr;

    foreach(TraceInclusiveCost* icost, deps()) {
        TracePartFunction* pf = (TracePartFunction*) icost;

        if (0) qDebug("PartFunction %s:%d",
                      qPrintable(pf->function()->name()),
                      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 = nullptr;
            }
            if (!pi || pi->part() != fc->part())
                pi = i->partInstr(fc->part(), pf);
            fc->addTo(pi);
        }

        TraceInstr* to = nullptr;
        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);
        }

        foreach(TracePartCall* pc, pf->partCallings()) {

            if (0) qDebug("PartCall %s:%d",
                          qPrintable(pc->call()->name()),
                          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 = nullptr;
                }
                if (!pic || pic->part() != fcc->part())
                    pic = ic->partInstrCall(fcc->part(), pc);

                fcc->addTo(pic);
                if (0) qDebug("Add FixCallCost %s:%d/0x%s, CallCount %s",
                              qPrintable(fcc->functionSource()->file()->shortName()),
                              fcc->line(), qPrintable(fcc->addr().toString()),
                              qPrintable(fcc->callCount().pretty()));
            }
        }
    }

#endif

    return _instrMap;
}



//---------------------------------------------------
// TraceFunctionCycle

TraceFunctionCycle::TraceFunctionCycle(TraceFunction* f, int n)
{
    _base = f;
    _cycleNo = n;
    _cycle = this;

    setContext(ProfileContext::context(ProfileContext::FunctionCycle));

    setPosition(f->data());
    setName(QStringLiteral("<cycle %1>").arg(n));

    // reset to attributes of base function
    setFile(_base->file());
    setClass(_base->cls());
    setObject(_base->object());
}

void TraceFunctionCycle::init()
{
    _members.clear();
    _callers.clear();
    // this deletes all TraceCall's to members
    _callings.clear();

    invalidate();
}

void TraceFunctionCycle::add(TraceFunction* f)
{
    _members.append(f);
}

void TraceFunctionCycle::setup()
{
    if (_members.isEmpty()) return;

    foreach(TraceFunction* f, _members) {

        // the cycle takes all outside callers from its members
        foreach(TraceCall* call, f->callers()) {
            if (_members.contains(call->caller())) continue;
            _callers.append(call);
        }

        // the cycle has a call to each member
        TraceCall* call = new TraceCall(this, f);
        call->invalidate();
        _callings.append(call);

        // now do some faking...
        f->setCycle(this);
    }
    invalidate();
}


//---------------------------------------------------
// TraceClass

TraceClass::TraceClass()
    : TraceCostItem(ProfileContext::context(ProfileContext::Class))
{}

TraceClass::~TraceClass()
{
    // we are the owner of items generated in our factory
    qDeleteAll(_deps);
}

QString TraceClass::prettyName() const
{
    if (_name.isEmpty())
        return prettyEmptyName();
    return _name;
}

QString TraceClass::prettyEmptyName()
{
    return QObject::tr("(global)");
}

TracePartClass* TraceClass::partClass(TracePart* part)
{
    TracePartClass* item = (TracePartClass*) findDepFromPart(part);
    if (!item) {
        item = new TracePartClass(this);
        item->setPosition(part);
        addDep(item);
    }
    return item;
}

void TraceClass::addFunction(TraceFunction* function)
{
#if TRACE_ASSERTIONS
    if (function->cls() != this) {
        qDebug("Can not add function to a class not enclosing this function\n");
        return;
    }

    if (_functions.contains(function)) return;
#endif

    _functions.append(function);

    invalidate();

#if TRACE_DEBUG
    qDebug("%s added\n %s (now %d)",
           qPrintable( fullName() ),
           qPrintable(function->fullName()), _functions.count());
#endif
}



//---------------------------------------------------
// TraceFile

TraceFile::TraceFile()
    : TraceCostItem(ProfileContext::context(ProfileContext::File))
{}

TraceFile::~TraceFile()
{
    // we are the owner of items generated in our factory
    qDeleteAll(_deps);
}

TracePartFile* TraceFile::partFile(TracePart* part)
{
    TracePartFile* item = (TracePartFile*) findDepFromPart(part);
    if (!item) {
        item = new TracePartFile(this);
        item->setPosition(part);
        addDep(item);
    }
    return item;
}

void TraceFile::addFunction(TraceFunction* function)
{
#if TRACE_ASSERTIONS
    if (function->file() != this) {
        qDebug("Can not add function to a file not enclosing this function\n");
        return;
    }

    if (_functions.contains(function)) return;
#endif

    _functions.append(function);

    invalidate();

#if TRACE_DEBUG
    qDebug("%s added\n %s (now %d)",
           qPrintable( fullName() ),
           qPrintable(function->fullName()), _functions.count());
#endif
}


void TraceFile::addSourceFile(TraceFunctionSource* sourceFile)
{
#if TRACE_ASSERTIONS
    if (sourceFile->file() != this) {
        qDebug("Can not add sourceFile to a file not having lines for it\n");
        return;
    }
#endif

    _sourceFiles.append(sourceFile);
    // not truly needed, as we do not use the sourceFiles for cost update
    invalidate();

#if TRACE_DEBUG
    qDebug("%s \n added SourceFile %s (now %d)",
           qPrintable( fullName() ), qPrintable(sourceFile->fullName()),
           _sourceFiles.count());
#endif
}



void TraceFile::setDirectory(const QString& dir)
{
    if (dir.endsWith('/'))
        _dir = dir.left(dir.length()-1);
    else
        _dir = dir;
}

QString TraceFile::directory()
{
    if (!_dir.isEmpty()) return _dir;

    int lastIndex = 0, index;
    while ( (index=_name.indexOf(QLatin1Char('/'), lastIndex)) >=0)
        lastIndex = index+1;

    if (lastIndex==0) return QString();

    // without ending "/"
    return _name.left(lastIndex-1);
}


QString TraceFile::shortName() const
{
    int lastIndex = 0, index;
    while ( (index=_name.indexOf(QLatin1Char('/'), lastIndex)) >=0)
        lastIndex = index+1;

    return _name.mid(lastIndex);
}

QString TraceFile::prettyName() const
{
    QString sn = shortName();

    if (sn.isEmpty())
        return prettyEmptyName();

    return sn;
}

QString TraceFile::prettyEmptyName()
{
    return QObject::tr("(unknown)");
}

QString TraceFile::prettyLongName() const
{
    if (_name.isEmpty())
        return prettyEmptyName();
    return _name;
}


//---------------------------------------------------
// TraceObject

TraceObject::TraceObject()
    : TraceCostItem(ProfileContext::context(ProfileContext::Object))
{}

TraceObject::~TraceObject()
{
    // we are the owner of items generated in our factory
    qDeleteAll(_deps);
}

TracePartObject* TraceObject::partObject(TracePart* part)
{
    TracePartObject* item = (TracePartObject*) findDepFromPart(part);
    if (!item) {
        item = new TracePartObject(this);
        item->setPosition(part);
        addDep(item);
    }
    return item;
}

void TraceObject::addFunction(TraceFunction* function)
{
#if TRACE_ASSERTIONS
    if (function->object() != this) {
        qDebug("Can not add function to an object not enclosing this function\n");
        return;
    }

    if (_functions.contains(function)) return;
#endif

    _functions.append(function);

    invalidate();

#if TRACE_DEBUG
    qDebug("%s added\n %s (now %d)",
           qPrintable( fullName() ),
           qPrintable(function->fullName()), _functions.count());
#endif
}

void TraceObject::setDirectory(const QString& dir)
{
    if (dir.endsWith('/'))
        _dir = dir.left(dir.length()-1);
    else
        _dir = dir;
}

QString TraceObject::directory()
{
    if (!_dir.isEmpty()) return _dir;

    int lastIndex = 0, index;
    while ( (index=_name.indexOf(QLatin1Char('/'), lastIndex)) >=0)
        lastIndex = index+1;

    if (lastIndex==0) return QString();

    // without ending "/"
    return _name.left(lastIndex-1);
}


QString TraceObject::shortName() const
{
    int lastIndex = 0, index;
    while ( (index=_name.indexOf(QLatin1Char('/'), lastIndex)) >=0)
        lastIndex = index+1;

    return _name.mid(lastIndex);
}

QString TraceObject::prettyName() const
{
    QString sn = shortName();

    if (sn.isEmpty())
        return prettyEmptyName();

    return sn;
}

QString TraceObject::prettyEmptyName()
{
    return QObject::tr("(unknown)");
}

//---------------------------------------------------
// TracePart

TracePart::TracePart(TraceData* data)
    : TraceListCost(ProfileContext::context(ProfileContext::Part))
{
    setPosition(data);

    _dep = data;
    _active = true;
    _number = 0;
    _tid = 0;
    _pid = 0;

    _eventTypeMapping = nullptr;
}

TracePart::~TracePart()
{
    delete _eventTypeMapping;
}

void TracePart::setPartNumber(int n)
{
    if (data()->maxPartNumber() <n) data()->setMaxPartNumber(n);
    _number = n;
}

void TracePart::setThreadID(int tid)
{
    if (data()->maxThreadID() <tid) data()->setMaxThreadID(tid);
    _tid = tid;
}

void TracePart::setProcessID(int pid)
{
    _pid = pid;
}



// strip path
QString TracePart::shortName() const
{
    int lastIndex = 0, index;
    while ( (index=_name.indexOf(QLatin1Char('/'), lastIndex)) >=0)
        lastIndex = index+1;

    return _name.mid(lastIndex);
}

QString TracePart::prettyName() const
{
    if (_pid==0) return shortName();
    QString name = QStringLiteral("PID %1").arg(_pid);
    if (_number>0)
        name += QStringLiteral(", section %2").arg(_number);
    if ((data()->maxThreadID()>1) && (_tid>0))
        name += QStringLiteral(", thread %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;
}

bool TracePart::operator<(const TracePart& p2) const
{
    if (processID() < p2.processID()) return true;

    if (processID() == p2.processID()) {
        if (partNumber() < p2.partNumber()) return true;

        if (partNumber() == p2.partNumber())
            return (threadID() < p2.threadID());
    }
    return false;
}


//---------------------------------------------------
// TraceData


// create vectors with reasonable default sizes, but not wasting memory
TraceData::TraceData(Logger* l)
    : ProfileCostArray(ProfileContext::context(ProfileContext::Data))
{
    _logger = l;
    init();
}

void TraceData::init()
{
    _functionCycleCount = 0;
    _inFunctionCycleUpdate = false;

    _maxThreadID = 0;
    _maxPartNumber = 0;
    _fixPool = nullptr;
    _dynPool = nullptr;

    _arch = ArchUnknown;
}

TraceData::~TraceData()
{
    qDeleteAll(_parts);

    delete _fixPool;
    delete _dynPool;
}

QString TraceData::shortTraceName() const
{
    int lastIndex = 0, index;
    while ( (index=_traceName.indexOf(QLatin1Char('/'), 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;
}

bool partLessThan(const TracePart* p1, const TracePart* p2)
{
    return *p1 < *p2;
}

/**
 * Load a list of files.
 * If only one file is given, it is assumed to be a prefix, and all
 * existing files with that prefix are loaded.
 *
 * Returns 0 if nothing found to load
 */
int TraceData::load(QStringList files)
{
    if (files.isEmpty()) return 0;

    _traceName = files[0];
    if (files.count() == 1) {
        QFileInfo finfo(_traceName);
        QString prefix = finfo.fileName();
        QDir dir = finfo.dir();
        if (finfo.isDir()) {
            prefix = QStringLiteral("callgrind.out");
            _traceName += QLatin1String("/callgrind.out");
        }

        files = dir.entryList(QStringList() << prefix + '*', QDir::Files);
        QStringList::Iterator it = files.begin();
        for (; it != files.end(); ++it ) {
            *it = dir.path() + '/' + *it;
        }
    }

    if (files.isEmpty()) {
        _traceName += ' ' + QObject::tr("(not found)");
        return 0;
    }

    QStringList::const_iterator it;
    int partsLoaded = 0;
    for (it = files.constBegin(); it != files.constEnd(); ++it ) {
        QFile file(*it);
        partsLoaded += internalLoad(&file, *it);
    }
    if (partsLoaded == 0) return 0;

    std::sort(_parts.begin(), _parts.end(), partLessThan);
    invalidateDynamicCost();
    updateFunctionCycles();

    return partsLoaded;
}

int TraceData::load(QString file)
{
    return load(QStringList(file));
}

int TraceData::load(QIODevice* file, const QString& filename)
{
    _traceName = filename;
    int partsLoaded = internalLoad(file, filename);
    if (partsLoaded>0) {
        invalidateDynamicCost();
        updateFunctionCycles();
    }
    return partsLoaded;
}

int TraceData::internalLoad(QIODevice* device, const QString& filename)
{
    if (!device->open( QIODevice::ReadOnly ) ) {
        _logger->loadStart(filename);
        _logger->loadFinished(QString::fromLocal8Bit(strerror( errno )));
        return 0;
    }

    Loader* l = Loader::matchingLoader(device);
    if (!l) {
        // special case empty file: ignore...
        if (device->size() == 0) return 0;

        _logger->loadStart(filename);
        _logger->loadFinished(QStringLiteral("Unknown file format"));
        return 0;
    }
    l->setLogger(_logger);

    int partsLoaded = l->load(this, device, filename);

    l->setLogger(nullptr);

    return partsLoaded;
}

bool TraceData::activateParts(const TracePartList& l)
{
    bool changed = false;

    foreach(TracePart* part, _parts)
        if (part->activate(l.contains(part)))
            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;

    foreach(TracePart* part, l) {
        if (_parts.contains(part))
            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);
}

void TraceData::addPart(TracePart* part)
{
    if (_parts.contains(part)>0) return;

    if ((part->partNumber()==0) &&
        (part->processID()==0)) {
        _maxPartNumber++;
        part->setPartNumber(_maxPartNumber);
    }
    _parts.append(part);
}

TracePart* TraceData::partWithName(const QString& name)
{
    foreach(TracePart* part, _parts)
        if (part->name() == name)
            return part;
    return nullptr;
}

QString TraceData::activePartRange()
{
    QString res;
    int r1=-1, r2=-1, count=0;
    foreach(TracePart* part, _parts) {
        count++;
        if (part->isActive()) {
            if (r1<0) { r1 = r2 = count; }
            else if (r2 == count-1) { r2 = count; }
            else {
                if (!res.isEmpty()) res += ';';
                if (r1==r2) res += QString::number(r1);
                else res += QStringLiteral("%1-%2").arg(r1).arg(r2);
                r1 = r2 = count;
            }
        }
    }
    if (r1>=0) {
        if (!res.isEmpty()) res += ';';
        if (r1==r2) res += QString::number(r1);
        else res += QStringLiteral("%1-%2").arg(r1).arg(r2);
    }

    return res;
}

void TraceData::invalidateDynamicCost()
{
    // invalidate all dynamic costs

    TraceObjectMap::Iterator oit;
    for ( oit = _objectMap.begin();
          oit != _objectMap.end(); ++oit )
        (*oit).invalidate();

    TraceClassMap::Iterator cit;
    for ( cit = _classMap.begin();
          cit != _classMap.end(); ++cit )
        (*cit).invalidate();

    TraceFileMap::Iterator fit;
    for ( fit = _fileMap.begin();
          fit != _fileMap.end(); ++fit )
        (*fit).invalidate();

    TraceFunctionMap::Iterator it;
    for ( it = _functionMap.begin();
          it != _functionMap.end(); ++it ) {
        (*it).invalidateDynamicCost();
    }

    invalidate();

}


TraceObject* TraceData::object(const QString& name)
{
    TraceObject& o = _objectMap[name];
    if (!o.data()) {
        // was created
        o.setPosition(this);
        o.setName(name);

#if TRACE_DEBUG
        qDebug("Created %s [TraceData::object]",
               qPrintable(o.fullName()));
#endif
    }
    return &o;
}


TraceFile* TraceData::file(const QString& name)
{
    TraceFile& f = _fileMap[name];
    if (!f.data()) {
        // was created
        f.setPosition(this);
        f.setName(name);

#if TRACE_DEBUG
        qDebug("Created %s [TraceData::file]",
               qPrintable(f.fullName()));
#endif
    }
    return &f;
}


// usually only called by function()
TraceClass* TraceData::cls(const QString& fnName, QString& shortName)
{
    int lastIndex = 0, index, pIndex;

    // we ignore any "::" after a '(' or a space
    pIndex=fnName.indexOf('(', 0);

#if 0
    int sIndex=fnName.find(" ", 0);
    if (sIndex>=0)
        if ((pIndex == -1) || (sIndex < pIndex))
            pIndex = sIndex;
#endif

    while ((index=fnName.indexOf(QLatin1String("::"), lastIndex)) >=0) {
        if (pIndex>=0 && pIndex<index) break;
        lastIndex = index+2;
    }

    QString clsName = (lastIndex < 3) ? QString() :
                                        fnName.left(lastIndex-2);
    shortName = fnName.mid(lastIndex);

    TraceClass& c = _classMap[clsName];
    if (!c.data()) {
        // was created
        c.setPosition(this);
        c.setName(clsName);

#if TRACE_DEBUG
        qDebug("Created %s [TraceData::cls]",
               qPrintable(c.fullName()));
#endif
    }
    return &c;
}


// name is inclusive class/namespace prefix
TraceFunction* TraceData::function(const QString& name,
                                   TraceFile* file, TraceObject* object)
{
    // strip class name
    QString shortName;
    TraceClass* c = cls(name, shortName);

    if (!file || !object || !c) {
        qDebug("ERROR - no file/object/class for %s ?!", qPrintable(name));
        return nullptr;
    }

    // Use object name and file name as part of key, to get distinct
    // function objects for functions with same name but defined in
    // different ELF objects or different files (this is possible e.g.
    // in C by using "static").
    //
    // Note about usage of this factory method by the Cachegrind loader:
    // that dump format does not explicitly specify the attribution
    // of functions to ELF objects and files. Rather, cost is attributed
    // to ELF object, source file and function. We use the first cost
    // seen for a function to bind an ELF object and source file to that
    // function. Callgrind always prints the cost of the instruction at
    // function entry first, so there, this strategy works.
    // But such an order is not enforced by the format. If the cost of
    // an inlined function from another source file would be printed first,
    // the attribution would go wrong. The format also allows cost of
    // the same function to be spread over the dump. With wrong
    // attributions, it can happen that cost of the same function is
    // interpreted as being from distinct functions.
    // For a correct solution, the format needs to be more expressive,
    // or the ordering of costs specified.
    // Previously, the file name was left out from the key.
    // The change was motivated by bug ID 3014067 (on SourceForge).
    QString key = name + file->shortName() + object->shortName();

    TraceFunctionMap::Iterator it;
    it = _functionMap.find(key);
    if (it == _functionMap.end()) {
        it = _functionMap.insert(key, TraceFunction());
        TraceFunction& f = it.value();

        f.setPosition(this);
        f.setName(name);
        f.setClass(c);
        f.setObject(object);
        f.setFile(file);
        //f.setMapIterator(it);

#if TRACE_DEBUG
        qDebug("Created %s [TraceData::function]\n  for %s, %s, %s",
               qPrintable(f.fullName()),
               qPrintable(c->fullName()), qPrintable(file->fullName()),
               object ? qPrintable(object->fullName()) : "(unknown object)");
#endif

        c->addFunction(&f);
        object->addFunction(&f);
        file->addFunction(&f);
    }

    return &(it.value());
}

TraceFunctionMap::Iterator TraceData::functionIterator(TraceFunction* f)
{

    // IMPORTANT: build as SAME key as used in function() above !!
    QString key;

    if (f->cls()) key = f->cls()->name() + "::";
    key += f->name();
    key += f->object()->shortName();

    return _functionMap.find(key);
}

TraceFunctionMap::ConstIterator TraceData::functionBeginIterator() const
{
    return _functionMap.begin();
}

TraceFunctionMap::ConstIterator TraceData::functionEndIterator() const
{
    return _functionMap.end();
}

// _maxCallCount is maintained globally, and only updated at loading
void TraceData::updateMaxCallCount(SubCost c)
{
    if (_maxCallCount < c)
        _maxCallCount = c;
}

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();

    foreach(TracePart* part, _parts) {
        _totals.addCost(part->totals());
        if (part->isActive())
            addCost(part->totals());
    }

    _dirty = false;
}

ProfileCostArray* TraceData::search(ProfileContext::Type t, QString name,
                                    EventType* ct, ProfileCostArray* parent)
{
    ProfileCostArray* result = nullptr;
    ProfileContext::Type pt;
    SubCost sc, scTop = 0;

    pt = parent ? parent->type() : ProfileContext::InvalidType;
    switch(t) {
    case ProfileContext::Function:
    {
        TraceFunction *f;
        TraceFunctionMap::Iterator it;
        for ( it = _functionMap.begin();
              it != _functionMap.end(); ++it ) {
            f = &(*it);

            if (f->name() != name) continue;

            if ((pt == ProfileContext::Class) && (parent != f->cls())) continue;
            if ((pt == ProfileContext::File) && (parent != f->file())) continue;
            if ((pt == ProfileContext::Object) && (parent != f->object())) continue;

            if (ct) {
                sc = f->inclusive()->subCost(ct);
                if (sc <= scTop) continue;
                scTop = sc;
            }

            result = f;
        }
    }
        break;

    case ProfileContext::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 ProfileContext::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 ProfileContext::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 ProfileContext::Instr:
        if (pt == ProfileContext::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 ProfileContext::Line:
    {
        TraceFunctionSourceList sList;
        if (pt == ProfileContext::Function)
            sList = ((TraceFunction*)parent)->sourceFiles();
        else if (pt == ProfileContext::FunctionSource)
            sList.append((TraceFunctionSource*) parent);
        else break;

        TraceLineMap* lineMap;
        TraceLine* line;
        TraceLineMap::Iterator it;
        foreach(TraceFunctionSource* fs, sList) {
            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;
    foreach(cycle, _functionCycles)
        if (cycle->base() == f)
            return cycle;

    _functionCycleCount++;
    cycle = new TraceFunctionCycle(f, _functionCycleCount);

    _functionCycles.append(cycle);
    return cycle;
}


void TraceData::updateFunctionCycles()
{
    //qDebug("Updating cycles...");

    // init cycle info
    foreach(TraceFunctionCycle* cycle, _functionCycles)
        cycle->init();

    TraceFunctionMap::Iterator it;
    for ( it = _functionMap.begin(); it != _functionMap.end(); ++it )
        (*it).cycleReset();

    if (!GlobalConfig::showCycles()) return;

    _inFunctionCycleUpdate = true;


#if 0
    int fCount = _functionMap.size(), fNo = 0, progress=0, p;
    QString msg = tr("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 = nullptr;
        (*it).cycleDFS(1, pNo, &stackTop);
    }

    // postprocess cycles
    foreach(TraceFunctionCycle* cycle, _functionCycles)
        cycle->setup();

    _inFunctionCycleUpdate = false;
    // we have to invalidate costs because cycles are now taken into account
    invalidateDynamicCost();

#if 0
    if (0) if (_topLevel) _topLevel->showStatus(QString(), 0);
#endif
}

void TraceData::updateObjectCycles()
{
}


void TraceData::updateClassCycles()
{
}


void TraceData::updateFileCycles()
{
}

