* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include "Model.h"
#include <new>
#include <stdio.h>
#include <stdlib.h>
#include <AutoDeleter.h>
#include <thread_defs.h>
static const char* const kThreadStateNames[] = {
"running",
"still running",
"preempted",
"ready",
"waiting",
"unknown"
};
const char*
thread_state_name(ThreadState state)
{
return kThreadStateNames[state];
}
const char*
wait_object_type_name(uint32 type)
{
switch (type) {
case THREAD_BLOCK_TYPE_SEMAPHORE:
return "semaphore";
case THREAD_BLOCK_TYPE_CONDITION_VARIABLE:
return "condition";
case THREAD_BLOCK_TYPE_MUTEX:
return "mutex";
case THREAD_BLOCK_TYPE_RW_LOCK:
return "rw lock";
case THREAD_BLOCK_TYPE_USER:
return "user";
case THREAD_BLOCK_TYPE_OTHER:
case THREAD_BLOCK_TYPE_OTHER_OBJECT:
return "other";
case THREAD_BLOCK_TYPE_SNOOZE:
return "snooze";
case THREAD_BLOCK_TYPE_SIGNAL:
return "signal";
default:
return "unknown";
}
}
Model::CPU::CPU()
:
fIdleTime(0)
{
}
void
Model::CPU::SetIdleTime(nanotime_t time)
{
fIdleTime = time;
}
Model::IORequest::IORequest(
system_profiler_io_request_scheduled* scheduledEvent,
system_profiler_io_request_finished* finishedEvent, size_t operationCount)
:
scheduledEvent(scheduledEvent),
finishedEvent(finishedEvent),
operationCount(operationCount)
{
}
Model::IORequest::~IORequest()
{
}
Model::IORequest*
Model::IORequest::Create(system_profiler_io_request_scheduled* scheduledEvent,
system_profiler_io_request_finished* finishedEvent, size_t operationCount)
{
void* memory = malloc(
sizeof(IORequest) + operationCount * sizeof(IOOperation));
if (memory == NULL)
return NULL;
return new(memory) IORequest(scheduledEvent, finishedEvent, operationCount);
}
void
Model::IORequest::Delete()
{
free(this);
}
Model::IOScheduler::IOScheduler(system_profiler_io_scheduler_added* event,
int32 index)
:
fAddedEvent(event),
fIndex(index)
{
}
Model::WaitObject::WaitObject(const system_profiler_wait_object_info* event)
:
fEvent(event),
fWaits(0),
fTotalWaitTime(0)
{
}
Model::WaitObject::~WaitObject()
{
}
void
Model::WaitObject::AddWait(nanotime_t waitTime)
{
fWaits++;
fTotalWaitTime += waitTime;
}
Model::WaitObjectGroup::WaitObjectGroup(WaitObject* waitObject)
:
fWaits(-1),
fTotalWaitTime(-1)
{
fWaitObjects.AddItem(waitObject);
}
Model::WaitObjectGroup::~WaitObjectGroup()
{
}
int64
Model::WaitObjectGroup::Waits()
{
if (fWaits < 0)
_ComputeWaits();
return fWaits;
}
nanotime_t
Model::WaitObjectGroup::TotalWaitTime()
{
if (fTotalWaitTime < 0)
_ComputeWaits();
return fTotalWaitTime;
}
void
Model::WaitObjectGroup::_ComputeWaits()
{
fWaits = 0;
fTotalWaitTime = 0;
for (int32 i = fWaitObjects.CountItems(); i-- > 0;) {
WaitObject* waitObject = fWaitObjects.ItemAt(i);
fWaits += waitObject->Waits();
fTotalWaitTime += waitObject->TotalWaitTime();
}
}
Model::ThreadWaitObject::ThreadWaitObject(WaitObject* waitObject)
:
fWaitObject(waitObject),
fWaits(0),
fTotalWaitTime(0)
{
}
Model::ThreadWaitObject::~ThreadWaitObject()
{
}
void
Model::ThreadWaitObject::AddWait(nanotime_t waitTime)
{
fWaits++;
fTotalWaitTime += waitTime;
fWaitObject->AddWait(waitTime);
}
Model::ThreadWaitObjectGroup::ThreadWaitObjectGroup(
ThreadWaitObject* threadWaitObject)
{
fWaitObjects.Add(threadWaitObject);
}
Model::ThreadWaitObjectGroup::~ThreadWaitObjectGroup()
{
}
bool
Model::ThreadWaitObjectGroup::GetThreadWaitObjects(
BObjectList<ThreadWaitObject>& objects)
{
ThreadWaitObjectList::ConstIterator it = fWaitObjects.GetIterator();
while (ThreadWaitObject* object = it.Next()) {
if (!objects.AddItem(object))
return false;
}
return true;
}
Model::Team::Team(const system_profiler_team_added* event, nanotime_t time)
:
fCreationEvent(event),
fCreationTime(time),
fDeletionTime(-1),
fThreads(10)
{
}
Model::Team::~Team()
{
}
bool
Model::Team::AddThread(Thread* thread)
{
return fThreads.BinaryInsert(thread, &Thread::CompareByCreationTimeID);
}
Model::Thread::Thread(Team* team, const system_profiler_thread_added* event,
nanotime_t time)
:
fEvents(NULL),
fEventCount(0),
fIORequests(NULL),
fIORequestCount(0),
fTeam(team),
fCreationEvent(event),
fCreationTime(time),
fDeletionTime(-1),
fRuns(0),
fTotalRunTime(0),
fMinRunTime(-1),
fMaxRunTime(-1),
fLatencies(0),
fTotalLatency(0),
fMinLatency(-1),
fMaxLatency(-1),
fReruns(0),
fTotalRerunTime(0),
fMinRerunTime(-1),
fMaxRerunTime(-1),
fWaits(0),
fTotalWaitTime(0),
fUnspecifiedWaitTime(0),
fIOCount(0),
fIOTime(0),
fPreemptions(0),
fIndex(-1),
fWaitObjectGroups(20)
{
}
Model::Thread::~Thread()
{
if (fIORequests != NULL) {
for (size_t i = 0; i < fIORequestCount; i++)
fIORequests[i]->Delete();
delete[] fIORequests;
}
delete[] fEvents;
}
void
Model::Thread::SetEvents(system_profiler_event_header** events,
size_t eventCount)
{
fEvents = events;
fEventCount = eventCount;
}
void
Model::Thread::SetIORequests(IORequest** requests, size_t requestCount)
{
fIORequests = requests;
fIORequestCount = requestCount;
}
size_t
Model::Thread::ClosestRequestStartIndex(nanotime_t minRequestStartTime) const
{
size_t lower = 0;
size_t upper = fIORequestCount;
while (lower < upper) {
size_t mid = (lower + upper) / 2;
IORequest* request = fIORequests[mid];
if (request->ScheduledTime() < minRequestStartTime)
lower = mid + 1;
else
upper = mid;
}
return lower;
}
Model::ThreadWaitObjectGroup*
Model::Thread::ThreadWaitObjectGroupFor(uint32 type, addr_t object) const
{
type_and_object key;
key.type = type;
key.object = object;
return fWaitObjectGroups.BinarySearchByKey(key,
&ThreadWaitObjectGroup::CompareWithTypeObject);
}
void
Model::Thread::AddRun(nanotime_t runTime)
{
fRuns++;
fTotalRunTime += runTime;
if (fMinRunTime < 0 || runTime < fMinRunTime)
fMinRunTime = runTime;
if (runTime > fMaxRunTime)
fMaxRunTime = runTime;
}
void
Model::Thread::AddRerun(nanotime_t runTime)
{
fReruns++;
fTotalRerunTime += runTime;
if (fMinRerunTime < 0 || runTime < fMinRerunTime)
fMinRerunTime = runTime;
if (runTime > fMaxRerunTime)
fMaxRerunTime = runTime;
}
void
Model::Thread::AddLatency(nanotime_t latency)
{
fLatencies++;
fTotalLatency += latency;
if (fMinLatency < 0 || latency < fMinLatency)
fMinLatency = latency;
if (latency > fMaxLatency)
fMaxLatency = latency;
}
void
Model::Thread::AddPreemption(nanotime_t runTime)
{
fPreemptions++;
}
void
Model::Thread::AddWait(nanotime_t waitTime)
{
fWaits++;
fTotalWaitTime += waitTime;
}
void
Model::Thread::AddUnspecifiedWait(nanotime_t waitTime)
{
fUnspecifiedWaitTime += waitTime;
}
Model::ThreadWaitObject*
Model::Thread::AddThreadWaitObject(WaitObject* waitObject,
ThreadWaitObjectGroup** _threadWaitObjectGroup)
{
ThreadWaitObject* threadWaitObject
= new(std::nothrow) ThreadWaitObject(waitObject);
if (threadWaitObject == NULL)
return NULL;
ThreadWaitObjectGroup* threadWaitObjectGroup
= ThreadWaitObjectGroupFor(waitObject->Type(), waitObject->Object());
if (threadWaitObjectGroup == NULL) {
threadWaitObjectGroup = new(std::nothrow) ThreadWaitObjectGroup(
threadWaitObject);
if (threadWaitObjectGroup == NULL) {
delete threadWaitObject;
return NULL;
}
if (!fWaitObjectGroups.BinaryInsert(threadWaitObjectGroup,
&ThreadWaitObjectGroup::CompareByTypeObject)) {
delete threadWaitObjectGroup;
return NULL;
}
} else {
threadWaitObjectGroup->AddWaitObject(threadWaitObject);
}
if (_threadWaitObjectGroup != NULL)
*_threadWaitObjectGroup = threadWaitObjectGroup;
return threadWaitObject;
}
void
Model::Thread::SetIOs(int64 count, nanotime_t time)
{
fIOCount = count;
fIOTime = time;
}
Model::SchedulingState::~SchedulingState()
{
Clear();
}
status_t
Model::SchedulingState::Init()
{
status_t error = fThreadStates.Init();
if (error != B_OK)
return error;
return B_OK;
}
status_t
Model::SchedulingState::Init(const CompactSchedulingState* state)
{
status_t error = Init();
if (error != B_OK)
return error;
if (state == NULL)
return B_OK;
fLastEventTime = state->LastEventTime();
for (int32 i = 0; const CompactThreadSchedulingState* compactThreadState
= state->ThreadStateAt(i); i++) {
ThreadSchedulingState* threadState
= new(std::nothrow) ThreadSchedulingState(*compactThreadState);
if (threadState == NULL)
return B_NO_MEMORY;
fThreadStates.Insert(threadState);
}
return B_OK;
}
void
Model::SchedulingState::Clear()
{
ThreadSchedulingState* state = fThreadStates.Clear(true);
while (state != NULL) {
ThreadSchedulingState* next = state->next;
DeleteThread(state);
state = next;
}
fLastEventTime = -1;
}
void
Model::SchedulingState::DeleteThread(ThreadSchedulingState* thread)
{
delete thread;
}
Model::CompactSchedulingState*
Model::CompactSchedulingState::Create(const SchedulingState& state,
off_t eventOffset)
{
nanotime_t lastEventTime = state.LastEventTime();
int32 threadCount = 0;
for (ThreadSchedulingStateTable::Iterator it
= state.ThreadStates().GetIterator();
ThreadSchedulingState* threadState = it.Next();) {
Thread* thread = threadState->thread;
if (thread->CreationTime() <= lastEventTime
&& (thread->DeletionTime() == -1
|| thread->DeletionTime() >= lastEventTime)) {
threadCount++;
}
}
CompactSchedulingState* compactState = (CompactSchedulingState*)malloc(
sizeof(CompactSchedulingState)
+ threadCount * sizeof(CompactThreadSchedulingState));
if (compactState == NULL)
return NULL;
compactState->fEventOffset = eventOffset;
compactState->fThreadCount = threadCount;
compactState->fLastEventTime = lastEventTime;
int32 threadIndex = 0;
for (ThreadSchedulingStateTable::Iterator it
= state.ThreadStates().GetIterator();
ThreadSchedulingState* threadState = it.Next();) {
Thread* thread = threadState->thread;
if (thread->CreationTime() <= lastEventTime
&& (thread->DeletionTime() == -1
|| thread->DeletionTime() >= lastEventTime)) {
compactState->fThreadStates[threadIndex++] = *threadState;
}
}
return compactState;
}
void
Model::CompactSchedulingState::Delete()
{
free(this);
}
Model::Model(const char* dataSourceName, void* eventData, size_t eventDataSize,
system_profiler_event_header** events, size_t eventCount)
:
fDataSourceName(dataSourceName),
fEventData(eventData),
fEvents(events),
fEventDataSize(eventDataSize),
fEventCount(eventCount),
fCPUCount(1),
fBaseTime(0),
fLastEventTime(0),
fIdleTime(0),
fCPUs(20),
fTeams(20),
fThreads(20),
fWaitObjectGroups(20),
fIOSchedulers(10),
fSchedulingStates(100)
{
}
Model::~Model()
{
for (int32 i = 0; CompactSchedulingState* state
= fSchedulingStates.ItemAt(i); i++) {
state->Delete();
}
delete[] fEvents;
free(fEventData);
for (int32 i = 0; void* data = fAssociatedData.ItemAt(i); i++)
free(data);
}
size_t
Model::ClosestEventIndex(nanotime_t eventTime) const
{
eventTime += fBaseTime;
size_t lower = 0;
size_t upper = CountEvents();
while (lower < upper) {
size_t mid = (lower + upper) / 2;
while (mid < upper) {
system_profiler_event_header* header = fEvents[mid];
switch (header->event) {
case B_SYSTEM_PROFILER_THREAD_SCHEDULED:
case B_SYSTEM_PROFILER_THREAD_ENQUEUED_IN_RUN_QUEUE:
case B_SYSTEM_PROFILER_THREAD_REMOVED_FROM_RUN_QUEUE:
break;
default:
mid++;
continue;
}
break;
}
if (mid == upper) {
lower = mid;
break;
}
system_profiler_thread_scheduling_event* event
= (system_profiler_thread_scheduling_event*)(fEvents[mid] + 1);
if (event->time < eventTime)
lower = mid + 1;
else
upper = mid;
}
return lower;
}
bool
Model::AddAssociatedData(void* data)
{
return fAssociatedData.AddItem(data);
}
void
Model::RemoveAssociatedData(void* data)
{
fAssociatedData.RemoveItem(data);
}
void
Model::LoadingFinished()
{
for (int32 i = 0; Thread* thread = fThreads.ItemAt(i); i++)
thread->SetIndex(i);
fIdleTime = 0;
for (int32 i = 0; CPU* cpu = CPUAt(i); i++)
fIdleTime += cpu->IdleTime();
}
void
Model::SetBaseTime(nanotime_t time)
{
fBaseTime = time;
}
void
Model::SetLastEventTime(nanotime_t time)
{
fLastEventTime = time;
}
bool
Model::SetCPUCount(int32 count)
{
fCPUCount = count;
fCPUs.MakeEmpty();
for (int32 i = 0; i < fCPUCount; i++) {
CPU* cpu = new(std::nothrow) CPU;
if (cpu == NULL || !fCPUs.AddItem(cpu)) {
delete cpu;
return false;
}
}
return true;
}
int32
Model::CountTeams() const
{
return fTeams.CountItems();
}
Model::Team*
Model::TeamAt(int32 index) const
{
return fTeams.ItemAt(index);
}
Model::Team*
Model::TeamByID(team_id id) const
{
return fTeams.BinarySearchByKey(id, &Team::CompareWithID);
}
Model::Team*
Model::AddTeam(const system_profiler_team_added* event, nanotime_t time)
{
Team* team = TeamByID(event->team);
if (team != NULL) {
fprintf(stderr, "Duplicate team: %" B_PRId32 "\n", event->team);
return team;
}
team = new(std::nothrow) Team(event, time);
if (team == NULL)
return NULL;
if (!fTeams.BinaryInsert(team, &Team::CompareByID)) {
delete team;
return NULL;
}
return team;
}
int32
Model::CountThreads() const
{
return fThreads.CountItems();
}
Model::Thread*
Model::ThreadAt(int32 index) const
{
return fThreads.ItemAt(index);
}
Model::Thread*
Model::ThreadByID(thread_id id) const
{
return fThreads.BinarySearchByKey(id, &Thread::CompareWithID);
}
Model::Thread*
Model::AddThread(const system_profiler_thread_added* event, nanotime_t time)
{
Thread* thread = ThreadByID(event->thread);
if (thread != NULL) {
fprintf(stderr, "Duplicate thread: %" B_PRId32 "\n", event->thread);
return thread;
}
Team* team = TeamByID(event->team);
if (team == NULL) {
fprintf(stderr, "No team for thread: %" B_PRId32 "\n", event->thread);
return NULL;
}
thread = new(std::nothrow) Thread(team, event, time);
if (thread == NULL)
return NULL;
ObjectDeleter<Thread> threadDeleter(thread);
if (!fThreads.BinaryInsert(thread, &Thread::CompareByID))
return NULL;
if (!team->AddThread(thread)) {
fThreads.RemoveItem(thread);
return NULL;
}
threadDeleter.Detach();
return thread;
}
Model::WaitObject*
Model::AddWaitObject(const system_profiler_wait_object_info* event,
WaitObjectGroup** _waitObjectGroup)
{
WaitObject* waitObject = new(std::nothrow) WaitObject(event);
if (waitObject == NULL)
return NULL;
WaitObjectGroup* waitObjectGroup
= WaitObjectGroupFor(waitObject->Type(), waitObject->Object());
if (waitObjectGroup == NULL) {
waitObjectGroup = new(std::nothrow) WaitObjectGroup(waitObject);
if (waitObjectGroup == NULL) {
delete waitObject;
return NULL;
}
if (!fWaitObjectGroups.BinaryInsert(waitObjectGroup,
&WaitObjectGroup::CompareByTypeObject)) {
delete waitObjectGroup;
return NULL;
}
} else {
waitObjectGroup->AddWaitObject(waitObject);
}
if (_waitObjectGroup != NULL)
*_waitObjectGroup = waitObjectGroup;
return waitObject;
}
int32
Model::CountWaitObjectGroups() const
{
return fWaitObjectGroups.CountItems();
}
Model::WaitObjectGroup*
Model::WaitObjectGroupAt(int32 index) const
{
return fWaitObjectGroups.ItemAt(index);
}
Model::WaitObjectGroup*
Model::WaitObjectGroupFor(uint32 type, addr_t object) const
{
type_and_object key;
key.type = type;
key.object = object;
return fWaitObjectGroups.BinarySearchByKey(key,
&WaitObjectGroup::CompareWithTypeObject);
}
Model::ThreadWaitObject*
Model::AddThreadWaitObject(thread_id threadID, WaitObject* waitObject,
ThreadWaitObjectGroup** _threadWaitObjectGroup)
{
Thread* thread = ThreadByID(threadID);
if (thread == NULL)
return NULL;
return thread->AddThreadWaitObject(waitObject, _threadWaitObjectGroup);
}
Model::ThreadWaitObjectGroup*
Model::ThreadWaitObjectGroupFor(thread_id threadID, uint32 type, addr_t object) const
{
Thread* thread = ThreadByID(threadID);
if (thread == NULL)
return NULL;
return thread->ThreadWaitObjectGroupFor(type, object);
}
int32
Model::CountIOSchedulers() const
{
return fIOSchedulers.CountItems();
}
Model::IOScheduler*
Model::IOSchedulerAt(int32 index) const
{
return fIOSchedulers.ItemAt(index);
}
Model::IOScheduler*
Model::IOSchedulerByID(int32 id) const
{
for (int32 i = 0; IOScheduler* scheduler = fIOSchedulers.ItemAt(i); i++) {
if (scheduler->ID() == id)
return scheduler;
}
return NULL;
}
Model::IOScheduler*
Model::AddIOScheduler(system_profiler_io_scheduler_added* event)
{
IOScheduler* scheduler = new(std::nothrow) IOScheduler(event,
fIOSchedulers.CountItems());
if (scheduler == NULL || !fIOSchedulers.AddItem(scheduler)) {
delete scheduler;
return NULL;
}
return scheduler;
}
bool
Model::AddSchedulingStateSnapshot(const SchedulingState& state,
off_t eventOffset)
{
CompactSchedulingState* compactState = CompactSchedulingState::Create(state,
eventOffset);
if (compactState == NULL)
return false;
if (!fSchedulingStates.AddItem(compactState)) {
compactState->Delete();
return false;
}
return true;
}
const Model::CompactSchedulingState*
Model::ClosestSchedulingState(nanotime_t eventTime) const
{
int32 index = fSchedulingStates.BinarySearchIndexByKey(eventTime,
&_CompareEventTimeSchedulingState);
if (index >= 0)
return fSchedulingStates.ItemAt(index);
index = -index - 1;
return index > 0 ? fSchedulingStates.ItemAt(index - 1) : NULL;
}
int
Model::_CompareEventTimeSchedulingState(const nanotime_t* time,
const CompactSchedulingState* state)
{
if (*time < state->LastEventTime())
return -1;
return *time == state->LastEventTime() ? 0 : 1;
}