/*
    This file is part of KCachegrind.

    SPDX-FileCopyrightText: 2002-2016 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>

    SPDX-License-Identifier: GPL-2.0-only
*/


#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()
{
}

