root/src/bin/debug/profile/Thread.cpp
/*
 * Copyright 2008-2010, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */


#include "Thread.h"

#include <algorithm>
#include <new>

#include <debug_support.h>

#include "debug_utils.h"

#include "Image.h"
#include "Options.h"
#include "Team.h"


// #pragma mark - ThreadImage


ThreadImage::ThreadImage(Image* image, ImageProfileResult* result)
        :
        fImage(image),
        fResult(result)
{
        fImage->AcquireReference();
        fResult->AcquireReference();
}


ThreadImage::~ThreadImage()
{
        fImage->ReleaseReference();
        fResult->ReleaseReference();
}


// #pragma mark - Thread


Thread::Thread(Team* team, thread_id threadID, const char* name, bigtime_t initialCPUTime)
        :
        fTeam(team),
        fID(threadID),
        fName(name),
        fLastCPUTime(initialCPUTime),
        fSampleArea(-1),
        fSamples(NULL),
        fProfileResult(NULL),
        fLazyImages(true)
{
        fTeam->AcquireReference();
}


Thread::~Thread()
{
        if (fSampleArea >= 0)
                delete_area(fSampleArea);

        if (fProfileResult != NULL)
                fProfileResult->ReleaseReference();

        while (ThreadImage* image = fImages.RemoveHead())
                delete image;
        while (ThreadImage* image = fOldImages.RemoveHead())
                delete image;

        fTeam->ReleaseReference();
}


int32
Thread::EntityID() const
{
        return ID();
}


const char*
Thread::EntityName() const
{
        return Name();
}


const char*
Thread::EntityType() const
{
        return "thread";
}


void
Thread::SetProfileResult(ProfileResult* result)
{
        ProfileResult* oldResult = fProfileResult;

        fProfileResult = result;
        if (fProfileResult != NULL)
                fProfileResult->AcquireReference();

        if (oldResult)
                oldResult->ReleaseReference();
}


void
Thread::UpdateInfo(const char* name)
{
        fName = name;
}


void
Thread::SetSampleArea(area_id area, addr_t* samples)
{
        fSampleArea = area;
        fSamples = samples;
}


void
Thread::SetInterval(bigtime_t interval)
{
        fProfileResult->SetInterval(interval);
}


void
Thread::SetLazyImages(bool lazy)
{
        fLazyImages = lazy;
}


status_t
Thread::AddImage(Image* image)
{
        ImageProfileResult* result;
        status_t error = fProfileResult->GetImageProfileResult(
                image->GetSharedImage(), image->ID(), result);
        if (error != B_OK)
                return error;

        BReference<ImageProfileResult> resultReference(result, true);

        ThreadImage* threadImage = new(std::nothrow) ThreadImage(image, result);
        if (threadImage == NULL)
                return B_NO_MEMORY;

        if (fLazyImages)
                fNewImages.Add(threadImage);
        else
                fImages.Add(threadImage);

        return B_OK;
}


void
Thread::RemoveImage(Image* image)
{
        ImageList::Iterator it = fImages.GetIterator();
        while (ThreadImage* threadImage = it.Next()) {
                if (threadImage->GetImage() == image) {
                        it.Remove();
                        if (threadImage->Result()->TotalHits() > 0)
                                fOldImages.Add(threadImage);
                        else
                                delete threadImage;
                        break;
                }
        }
}


void
Thread::AddSamples(int32 count, int32 dropped, int32 stackDepth,
        bool variableStackDepth, int32 event)
{
        _SynchronizeImages(event);

        if (variableStackDepth) {
                addr_t* samples = fSamples;

                while (count > 0) {
                        addr_t sampleCount = *(samples++);

                        if (sampleCount >= B_DEBUG_PROFILE_EVENT_BASE) {
                                int32 eventParameterCount
                                        = sampleCount & B_DEBUG_PROFILE_EVENT_PARAMETER_MASK;
                                if (sampleCount == B_DEBUG_PROFILE_IMAGE_EVENT) {
                                        _SynchronizeImages((int32)samples[0]);
                                } else {
                                        fprintf(stderr, "unknown profile event: %#lx\n",
                                                sampleCount);
                                }

                                samples += eventParameterCount;
                                count -= eventParameterCount + 1;
                                continue;
                        }

                        fProfileResult->AddSamples(this, samples, sampleCount);

                        samples += sampleCount;
                        count -= sampleCount + 1;
                }
        } else {
                count = count / stackDepth * stackDepth;

                for (int32 i = 0; i < count; i += stackDepth)
                        fProfileResult->AddSamples(this, fSamples + i, stackDepth);
        }

        fProfileResult->AddDroppedTicks(dropped);
}


void
Thread::AddSamples(addr_t* samples, int32 sampleCount)
{
        fProfileResult->AddSamples(this, samples, sampleCount);
}


void
Thread::UpdateCPUTime(bigtime_t time)
{
        bigtime_t elapsed = time - fLastCPUTime;
        int64 expectedTicks = elapsed / fProfileResult->Interval();
        fLastCPUTime = time;

        fProfileResult->AddExpectedTicks(expectedTicks);
}


void
Thread::PrintResults()
{
        fProfileResult->PrintResults(this);
}


int32
Thread::CountImages() const
{
        return fImages.Count() + fOldImages.Count();
}


ImageProfileResult*
Thread::VisitImages(Visitor& visitor) const
{
        ImageList::ConstIterator it = fOldImages.GetIterator();
        while (ThreadImage* image = it.Next()) {
                if (visitor.VisitImage(image->Result()))
                        return image->Result();
        }

        it = fImages.GetIterator();
        while (ThreadImage* image = it.Next()) {
                if (visitor.VisitImage(image->Result()))
                        return image->Result();
        }

        return NULL;
}


ImageProfileResult*
Thread::FindImage(addr_t address, addr_t& _loadDelta) const
{
        ImageList::ConstIterator it = fImages.GetIterator();
        while (ThreadImage* image = it.Next()) {
                if (image->GetImage()->ContainsAddress(address)) {
                        _loadDelta = image->GetImage()->LoadDelta();
                        return image->Result();
                }
        }
        return NULL;
}


void
Thread::_SynchronizeImages(int32 event)
{
        // remove obsolete images
        ImageList::Iterator it = fImages.GetIterator();
        while (ThreadImage* image = it.Next()) {
                int32 deleted = image->GetImage()->DeletionEvent();
                if (deleted >= 0 && event >= deleted) {
                        it.Remove();
                        if (image->Result()->TotalHits() > 0)
                                fOldImages.Add(image);
                        else
                                delete image;
                }
        }

        // add new images
        it = fNewImages.GetIterator();
        while (ThreadImage* image = it.Next()) {
                if (image->GetImage()->CreationEvent() <= event) {
                        it.Remove();
                        int32 deleted = image->GetImage()->DeletionEvent();
                        if (deleted >= 0 && event >= deleted) {
                                // image already deleted
                                delete image;
                        } else
                                fImages.Add(image);
                }
        }
}