1342 lines
31 KiB
C++
1342 lines
31 KiB
C++
/*
|
|
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
|
|
* Distributed under the terms of the MIT License.
|
|
*/
|
|
|
|
|
|
#include "ModelLoader.h"
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include <algorithm>
|
|
#include <new>
|
|
|
|
#include <AutoDeleter.h>
|
|
#include <AutoLocker.h>
|
|
#include <DebugEventStream.h>
|
|
|
|
#include <system_profiler_defs.h>
|
|
#include <thread_defs.h>
|
|
|
|
#include "DataSource.h"
|
|
#include "MessageCodes.h"
|
|
#include "Model.h"
|
|
|
|
|
|
// add a scheduling state snapshot every x events
|
|
static const uint32 kSchedulingSnapshotInterval = 1024;
|
|
|
|
static const uint32 kMaxCPUCount = 1024;
|
|
|
|
|
|
struct SimpleWaitObjectInfo : system_profiler_wait_object_info {
|
|
SimpleWaitObjectInfo(uint32 type)
|
|
{
|
|
this->type = type;
|
|
object = 0;
|
|
referenced_object = 0;
|
|
name[0] = '\0';
|
|
}
|
|
};
|
|
|
|
|
|
static const SimpleWaitObjectInfo kSnoozeWaitObjectInfo(
|
|
THREAD_BLOCK_TYPE_SNOOZE);
|
|
static const SimpleWaitObjectInfo kSignalWaitObjectInfo(
|
|
THREAD_BLOCK_TYPE_SIGNAL);
|
|
|
|
|
|
// #pragma mark - CPUInfo
|
|
|
|
|
|
struct ModelLoader::CPUInfo {
|
|
nanotime_t idleTime;
|
|
|
|
CPUInfo()
|
|
:
|
|
idleTime(0)
|
|
{
|
|
}
|
|
};
|
|
|
|
|
|
// #pragma mark - IOOperation
|
|
|
|
|
|
struct ModelLoader::IOOperation : DoublyLinkedListLinkImpl<IOOperation> {
|
|
io_operation_started* startedEvent;
|
|
io_operation_finished* finishedEvent;
|
|
|
|
IOOperation(io_operation_started* startedEvent)
|
|
:
|
|
startedEvent(startedEvent),
|
|
finishedEvent(NULL)
|
|
{
|
|
}
|
|
};
|
|
|
|
|
|
// #pragma mark - IORequest
|
|
|
|
|
|
struct ModelLoader::IORequest : DoublyLinkedListLinkImpl<IORequest> {
|
|
io_request_scheduled* scheduledEvent;
|
|
io_request_finished* finishedEvent;
|
|
IOOperationList operations;
|
|
size_t operationCount;
|
|
IORequest* hashNext;
|
|
|
|
IORequest(io_request_scheduled* scheduledEvent)
|
|
:
|
|
scheduledEvent(scheduledEvent),
|
|
finishedEvent(NULL),
|
|
operationCount(0)
|
|
{
|
|
}
|
|
|
|
~IORequest()
|
|
{
|
|
while (IOOperation* operation = operations.RemoveHead())
|
|
delete operation;
|
|
}
|
|
|
|
void AddOperation(IOOperation* operation)
|
|
{
|
|
operations.Add(operation);
|
|
operationCount++;
|
|
}
|
|
|
|
IOOperation* FindOperation(void* address) const
|
|
{
|
|
for (IOOperationList::ConstReverseIterator it
|
|
= operations.GetReverseIterator();
|
|
IOOperation* operation = it.Next();) {
|
|
if (operation->startedEvent->operation == address)
|
|
return operation;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
Model::IORequest* CreateModelRequest() const
|
|
{
|
|
size_t operationCount = operations.Count();
|
|
|
|
Model::IORequest* modelRequest = Model::IORequest::Create(
|
|
scheduledEvent, finishedEvent, operationCount);
|
|
if (modelRequest == NULL)
|
|
return NULL;
|
|
|
|
size_t index = 0;
|
|
for (IOOperationList::ConstIterator it = operations.GetIterator();
|
|
IOOperation* operation = it.Next();) {
|
|
Model::IOOperation& modelOperation
|
|
= modelRequest->operations[index++];
|
|
modelOperation.startedEvent = operation->startedEvent;
|
|
modelOperation.finishedEvent = operation->finishedEvent;
|
|
}
|
|
|
|
return modelRequest;
|
|
}
|
|
};
|
|
|
|
|
|
// #pragma mark - IORequestHashDefinition
|
|
|
|
|
|
struct ModelLoader::IORequestHashDefinition {
|
|
typedef void* KeyType;
|
|
typedef IORequest ValueType;
|
|
|
|
size_t HashKey(KeyType key) const
|
|
{ return (size_t)key; }
|
|
|
|
size_t Hash(const IORequest* value) const
|
|
{ return HashKey(value->scheduledEvent->request); }
|
|
|
|
bool Compare(KeyType key, const IORequest* value) const
|
|
{ return key == value->scheduledEvent->request; }
|
|
|
|
IORequest*& GetLink(IORequest* value) const
|
|
{ return value->hashNext; }
|
|
};
|
|
|
|
|
|
// #pragma mark - ExtendedThreadSchedulingState
|
|
|
|
|
|
struct ModelLoader::ExtendedThreadSchedulingState
|
|
: Model::ThreadSchedulingState {
|
|
|
|
ExtendedThreadSchedulingState(Model::Thread* thread)
|
|
:
|
|
Model::ThreadSchedulingState(thread),
|
|
fEvents(NULL),
|
|
fEventIndex(0),
|
|
fEventCount(0)
|
|
{
|
|
}
|
|
|
|
~ExtendedThreadSchedulingState()
|
|
{
|
|
delete[] fEvents;
|
|
|
|
while (IORequest* request = fIORequests.RemoveHead())
|
|
delete request;
|
|
while (IORequest* request = fPendingIORequests.RemoveHead())
|
|
delete request;
|
|
}
|
|
|
|
system_profiler_event_header** Events() const
|
|
{
|
|
return fEvents;
|
|
}
|
|
|
|
size_t CountEvents() const
|
|
{
|
|
return fEventCount;
|
|
}
|
|
|
|
system_profiler_event_header** DetachEvents()
|
|
{
|
|
system_profiler_event_header** events = fEvents;
|
|
fEvents = NULL;
|
|
return events;
|
|
}
|
|
|
|
void IncrementEventCount()
|
|
{
|
|
fEventCount++;
|
|
}
|
|
|
|
void AddEvent(system_profiler_event_header* event)
|
|
{
|
|
fEvents[fEventIndex++] = event;
|
|
}
|
|
|
|
bool AllocateEventArray()
|
|
{
|
|
if (fEventCount == 0)
|
|
return true;
|
|
|
|
fEvents = new(std::nothrow) system_profiler_event_header*[fEventCount];
|
|
if (fEvents == NULL)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void AddIORequest(IORequest* request)
|
|
{
|
|
fPendingIORequests.Add(request);
|
|
}
|
|
|
|
void IORequestFinished(IORequest* request)
|
|
{
|
|
fPendingIORequests.Remove(request);
|
|
fIORequests.Add(request);
|
|
}
|
|
|
|
bool PrepareThreadIORequests(Model::IORequest**& _requests,
|
|
size_t& _requestCount)
|
|
{
|
|
fIORequests.MoveFrom(&fPendingIORequests);
|
|
size_t requestCount = fIORequests.Count();
|
|
|
|
if (requestCount == 0) {
|
|
_requests = NULL;
|
|
_requestCount = 0;
|
|
return true;
|
|
}
|
|
|
|
Model::IORequest** requests
|
|
= new(std::nothrow) Model::IORequest*[requestCount];
|
|
if (requests == NULL)
|
|
return false;
|
|
|
|
size_t index = 0;
|
|
while (IORequest* request = fIORequests.RemoveHead()) {
|
|
ObjectDeleter<IORequest> requestDeleter(request);
|
|
|
|
Model::IORequest* modelRequest = request->CreateModelRequest();
|
|
if (modelRequest == NULL) {
|
|
for (size_t i = 0; i < index; i++)
|
|
requests[i]->Delete();
|
|
delete requests;
|
|
return false;
|
|
}
|
|
|
|
requests[index++] = modelRequest;
|
|
}
|
|
|
|
_requests = requests;
|
|
_requestCount = requestCount;
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
system_profiler_event_header** fEvents;
|
|
size_t fEventIndex;
|
|
size_t fEventCount;
|
|
IORequestList fIORequests;
|
|
IORequestList fPendingIORequests;
|
|
};
|
|
|
|
|
|
// #pragma mark - ExtendedSchedulingState
|
|
|
|
|
|
struct ModelLoader::ExtendedSchedulingState : Model::SchedulingState {
|
|
inline ExtendedThreadSchedulingState* LookupThread(thread_id threadID) const
|
|
{
|
|
Model::ThreadSchedulingState* thread
|
|
= Model::SchedulingState::LookupThread(threadID);
|
|
return thread != NULL
|
|
? static_cast<ExtendedThreadSchedulingState*>(thread) : NULL;
|
|
}
|
|
|
|
|
|
protected:
|
|
virtual void DeleteThread(Model::ThreadSchedulingState* thread)
|
|
{
|
|
delete static_cast<ExtendedThreadSchedulingState*>(thread);
|
|
}
|
|
};
|
|
|
|
|
|
// #pragma mark - ModelLoader
|
|
|
|
|
|
inline void
|
|
ModelLoader::_UpdateLastEventTime(nanotime_t time)
|
|
{
|
|
if (fBaseTime < 0) {
|
|
fBaseTime = time;
|
|
fModel->SetBaseTime(time);
|
|
}
|
|
|
|
fState->SetLastEventTime(time - fBaseTime);
|
|
}
|
|
|
|
|
|
ModelLoader::ModelLoader(DataSource* dataSource,
|
|
const BMessenger& target, void* targetCookie)
|
|
:
|
|
AbstractModelLoader(target, targetCookie),
|
|
fModel(NULL),
|
|
fDataSource(dataSource),
|
|
fCPUInfos(NULL),
|
|
fState(NULL),
|
|
fIORequests(NULL)
|
|
{
|
|
}
|
|
|
|
|
|
ModelLoader::~ModelLoader()
|
|
{
|
|
delete[] fCPUInfos;
|
|
delete fDataSource;
|
|
delete fModel;
|
|
delete fState;
|
|
delete fIORequests;
|
|
}
|
|
|
|
|
|
Model*
|
|
ModelLoader::DetachModel()
|
|
{
|
|
AutoLocker<BLocker> locker(fLock);
|
|
|
|
if (fModel == NULL || fLoading)
|
|
return NULL;
|
|
|
|
Model* model = fModel;
|
|
fModel = NULL;
|
|
|
|
return model;
|
|
}
|
|
|
|
|
|
status_t
|
|
ModelLoader::PrepareForLoading()
|
|
{
|
|
if (fModel != NULL || fDataSource == NULL)
|
|
return B_BAD_VALUE;
|
|
|
|
// create and init the state
|
|
fState = new(std::nothrow) ExtendedSchedulingState;
|
|
if (fState == NULL)
|
|
return B_NO_MEMORY;
|
|
|
|
status_t error = fState->Init();
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
// create CPU info array
|
|
fCPUInfos = new(std::nothrow) CPUInfo[kMaxCPUCount];
|
|
if (fCPUInfos == NULL)
|
|
return B_NO_MEMORY;
|
|
|
|
// create IORequest hash table
|
|
fIORequests = new(std::nothrow) IORequestTable;
|
|
if (fIORequests == NULL || fIORequests->Init() != B_OK)
|
|
return B_NO_MEMORY;
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
status_t
|
|
ModelLoader::Load()
|
|
{
|
|
try {
|
|
return _Load();
|
|
} catch(...) {
|
|
return B_ERROR;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
ModelLoader::FinishLoading(bool success)
|
|
{
|
|
delete fState;
|
|
fState = NULL;
|
|
|
|
if (!success) {
|
|
delete fModel;
|
|
fModel = NULL;
|
|
}
|
|
|
|
delete[] fCPUInfos;
|
|
fCPUInfos = NULL;
|
|
|
|
delete fIORequests;
|
|
fIORequests = NULL;
|
|
}
|
|
|
|
|
|
status_t
|
|
ModelLoader::_Load()
|
|
{
|
|
// read the complete data into memory
|
|
void* eventData;
|
|
size_t eventDataSize;
|
|
status_t error = _ReadDebugEvents(&eventData, &eventDataSize);
|
|
if (error != B_OK)
|
|
return error;
|
|
MemoryDeleter eventDataDeleter(eventData);
|
|
|
|
// create a debug event array
|
|
system_profiler_event_header** events;
|
|
size_t eventCount;
|
|
error = _CreateDebugEventArray(eventData, eventDataSize, events,
|
|
eventCount);
|
|
if (error != B_OK)
|
|
return error;
|
|
ArrayDeleter<system_profiler_event_header*> eventsDeleter(events);
|
|
|
|
// get the data source name
|
|
BString dataSourceName;
|
|
fDataSource->GetName(dataSourceName);
|
|
|
|
// create a model
|
|
fModel = new(std::nothrow) Model(dataSourceName.String(), eventData,
|
|
eventDataSize, events, eventCount);
|
|
if (fModel == NULL)
|
|
return B_NO_MEMORY;
|
|
eventDataDeleter.Detach();
|
|
eventsDeleter.Detach();
|
|
|
|
// create a debug input stream
|
|
BDebugEventInputStream input;
|
|
error = input.SetTo(eventData, eventDataSize, false);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
// add the snooze and signal wait objects to the model
|
|
if (fModel->AddWaitObject(&kSnoozeWaitObjectInfo, NULL) == NULL
|
|
|| fModel->AddWaitObject(&kSignalWaitObjectInfo, NULL) == NULL) {
|
|
return B_NO_MEMORY;
|
|
}
|
|
|
|
// process the events
|
|
fMaxCPUIndex = 0;
|
|
fState->Clear();
|
|
fBaseTime = -1;
|
|
uint64 count = 0;
|
|
|
|
while (true) {
|
|
// get next event
|
|
uint32 event;
|
|
uint32 cpu;
|
|
const void* buffer;
|
|
off_t offset;
|
|
ssize_t bufferSize = input.ReadNextEvent(&event, &cpu, &buffer,
|
|
&offset);
|
|
if (bufferSize < 0)
|
|
return bufferSize;
|
|
if (buffer == NULL)
|
|
break;
|
|
|
|
// process the event
|
|
status_t error = _ProcessEvent(event, cpu, buffer, bufferSize);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
if (cpu > fMaxCPUIndex) {
|
|
if (cpu + 1 > kMaxCPUCount)
|
|
return B_BAD_DATA;
|
|
fMaxCPUIndex = cpu;
|
|
}
|
|
|
|
// periodically check whether we're supposed to abort
|
|
if (++count % 32 == 0) {
|
|
AutoLocker<BLocker> locker(fLock);
|
|
if (fAborted)
|
|
return B_ERROR;
|
|
}
|
|
|
|
// periodically add scheduling snapshots
|
|
if (count % kSchedulingSnapshotInterval == 0)
|
|
fModel->AddSchedulingStateSnapshot(*fState, offset);
|
|
}
|
|
|
|
if (!fModel->SetCPUCount(fMaxCPUIndex + 1))
|
|
return B_NO_MEMORY;
|
|
|
|
for (uint32 i = 0; i <= fMaxCPUIndex; i++)
|
|
fModel->CPUAt(i)->SetIdleTime(fCPUInfos[i].idleTime);
|
|
|
|
fModel->SetLastEventTime(fState->LastEventTime());
|
|
|
|
if (!_SetThreadEvents() || !_SetThreadIORequests())
|
|
return B_NO_MEMORY;
|
|
|
|
fModel->LoadingFinished();
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
status_t
|
|
ModelLoader::_ReadDebugEvents(void** _eventData, size_t* _size)
|
|
{
|
|
// get a BDataIO from the data source
|
|
BDataIO* io;
|
|
status_t error = fDataSource->CreateDataIO(&io);
|
|
if (error != B_OK)
|
|
return error;
|
|
ObjectDeleter<BDataIO> dataIOtDeleter(io);
|
|
|
|
// First we need to find out how large a buffer to allocate.
|
|
size_t size;
|
|
|
|
if (BPositionIO* positionIO = dynamic_cast<BPositionIO*>(io)) {
|
|
// it's a BPositionIO -- this makes things easier, since we know how
|
|
// many bytes to read
|
|
off_t currentPos = positionIO->Position();
|
|
if (currentPos < 0)
|
|
return currentPos;
|
|
|
|
off_t fileSize;
|
|
error = positionIO->GetSize(&fileSize);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
size = fileSize - currentPos;
|
|
} else {
|
|
// no BPositionIO -- we need to determine the total size by iteratively
|
|
// reading the whole data one time
|
|
|
|
// allocate a dummy buffer for reading
|
|
const size_t kBufferSize = 1024 * 1024;
|
|
void* buffer = malloc(kBufferSize);
|
|
if (buffer == NULL)
|
|
return B_NO_MEMORY;
|
|
MemoryDeleter bufferDeleter(buffer);
|
|
|
|
size = 0;
|
|
while (true) {
|
|
ssize_t bytesRead = io->Read(buffer, kBufferSize);
|
|
if (bytesRead < 0)
|
|
return bytesRead;
|
|
if (bytesRead == 0)
|
|
break;
|
|
|
|
size += bytesRead;
|
|
}
|
|
|
|
// we've got the size -- recreate the BDataIO
|
|
dataIOtDeleter.Delete();
|
|
error = fDataSource->CreateDataIO(&io);
|
|
if (error != B_OK)
|
|
return error;
|
|
dataIOtDeleter.SetTo(io);
|
|
}
|
|
|
|
// allocate the data buffer
|
|
void* data = malloc(size);
|
|
if (data == NULL)
|
|
return B_NO_MEMORY;
|
|
MemoryDeleter dataDeleter(data);
|
|
|
|
// read the data
|
|
ssize_t bytesRead = io->Read(data, size);
|
|
if (bytesRead < 0)
|
|
return bytesRead;
|
|
if ((size_t)bytesRead != size)
|
|
return B_FILE_ERROR;
|
|
|
|
dataDeleter.Detach();
|
|
*_eventData = data;
|
|
*_size = size;
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
status_t
|
|
ModelLoader::_CreateDebugEventArray(void* eventData, size_t eventDataSize,
|
|
system_profiler_event_header**& _events, size_t& _eventCount)
|
|
{
|
|
// count the events
|
|
BDebugEventInputStream input;
|
|
status_t error = input.SetTo(eventData, eventDataSize, false);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
size_t eventCount = 0;
|
|
while (true) {
|
|
// get next event
|
|
uint32 event;
|
|
uint32 cpu;
|
|
const void* buffer;
|
|
ssize_t bufferSize = input.ReadNextEvent(&event, &cpu, &buffer, NULL);
|
|
if (bufferSize < 0)
|
|
return bufferSize;
|
|
if (buffer == NULL)
|
|
break;
|
|
|
|
eventCount++;
|
|
}
|
|
|
|
// create the array
|
|
system_profiler_event_header** events = new(std::nothrow)
|
|
system_profiler_event_header*[eventCount];
|
|
if (events == NULL)
|
|
return B_NO_MEMORY;
|
|
|
|
// populate the array
|
|
error = input.SetTo(eventData, eventDataSize, false);
|
|
if (error != B_OK) {
|
|
delete[] events;
|
|
return error;
|
|
}
|
|
|
|
size_t eventIndex = 0;
|
|
while (true) {
|
|
// get next event
|
|
uint32 event;
|
|
uint32 cpu;
|
|
const void* buffer;
|
|
off_t offset;
|
|
input.ReadNextEvent(&event, &cpu, &buffer, &offset);
|
|
if (buffer == NULL)
|
|
break;
|
|
|
|
events[eventIndex++]
|
|
= (system_profiler_event_header*)((uint8*)eventData + offset);
|
|
}
|
|
|
|
_events = events;
|
|
_eventCount = eventCount;
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
status_t
|
|
ModelLoader::_ProcessEvent(uint32 event, uint32 cpu, const void* buffer,
|
|
size_t size)
|
|
{
|
|
switch (event) {
|
|
case B_SYSTEM_PROFILER_TEAM_ADDED:
|
|
_HandleTeamAdded((system_profiler_team_added*)buffer);
|
|
break;
|
|
|
|
case B_SYSTEM_PROFILER_TEAM_REMOVED:
|
|
_HandleTeamRemoved((system_profiler_team_removed*)buffer);
|
|
break;
|
|
|
|
case B_SYSTEM_PROFILER_TEAM_EXEC:
|
|
_HandleTeamExec((system_profiler_team_exec*)buffer);
|
|
break;
|
|
|
|
case B_SYSTEM_PROFILER_THREAD_ADDED:
|
|
_HandleThreadAdded((system_profiler_thread_added*)buffer);
|
|
break;
|
|
|
|
case B_SYSTEM_PROFILER_THREAD_REMOVED:
|
|
_HandleThreadRemoved((system_profiler_thread_removed*)buffer);
|
|
break;
|
|
|
|
case B_SYSTEM_PROFILER_THREAD_SCHEDULED:
|
|
_HandleThreadScheduled(cpu,
|
|
(system_profiler_thread_scheduled*)buffer);
|
|
break;
|
|
|
|
case B_SYSTEM_PROFILER_THREAD_ENQUEUED_IN_RUN_QUEUE:
|
|
_HandleThreadEnqueuedInRunQueue(
|
|
(thread_enqueued_in_run_queue*)buffer);
|
|
break;
|
|
|
|
case B_SYSTEM_PROFILER_THREAD_REMOVED_FROM_RUN_QUEUE:
|
|
_HandleThreadRemovedFromRunQueue(cpu,
|
|
(thread_removed_from_run_queue*)buffer);
|
|
break;
|
|
|
|
case B_SYSTEM_PROFILER_WAIT_OBJECT_INFO:
|
|
_HandleWaitObjectInfo((system_profiler_wait_object_info*)buffer);
|
|
break;
|
|
|
|
case B_SYSTEM_PROFILER_IO_SCHEDULER_ADDED:
|
|
_HandleIOSchedulerAdded(
|
|
(system_profiler_io_scheduler_added*)buffer);
|
|
break;
|
|
|
|
case B_SYSTEM_PROFILER_IO_SCHEDULER_REMOVED:
|
|
// not so interesting
|
|
break;
|
|
|
|
case B_SYSTEM_PROFILER_IO_REQUEST_SCHEDULED:
|
|
_HandleIORequestScheduled((io_request_scheduled*)buffer);
|
|
break;
|
|
case B_SYSTEM_PROFILER_IO_REQUEST_FINISHED:
|
|
_HandleIORequestFinished((io_request_finished*)buffer);
|
|
break;
|
|
case B_SYSTEM_PROFILER_IO_OPERATION_STARTED:
|
|
_HandleIOOperationStarted((io_operation_started*)buffer);
|
|
break;
|
|
case B_SYSTEM_PROFILER_IO_OPERATION_FINISHED:
|
|
_HandleIOOperationFinished((io_operation_finished*)buffer);
|
|
break;
|
|
|
|
default:
|
|
printf("unsupported event type %" B_PRIu32 ", size: %" B_PRIuSIZE
|
|
"\n", event, size);
|
|
return B_BAD_DATA;
|
|
}
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
bool
|
|
ModelLoader::_SetThreadEvents()
|
|
{
|
|
// allocate the threads' events arrays
|
|
for (int32 i = 0; Model::Thread* thread = fModel->ThreadAt(i); i++) {
|
|
ExtendedThreadSchedulingState* state
|
|
= fState->LookupThread(thread->ID());
|
|
if (!state->AllocateEventArray())
|
|
return false;
|
|
}
|
|
|
|
// fill the threads' event arrays
|
|
system_profiler_event_header** events = fModel->Events();
|
|
size_t eventCount = fModel->CountEvents();
|
|
for (size_t i = 0; i < eventCount; i++) {
|
|
system_profiler_event_header* header = events[i];
|
|
void* buffer = header + 1;
|
|
|
|
switch (header->event) {
|
|
case B_SYSTEM_PROFILER_THREAD_ADDED:
|
|
{
|
|
system_profiler_thread_added* event
|
|
= (system_profiler_thread_added*)buffer;
|
|
fState->LookupThread(event->thread)->AddEvent(header);
|
|
break;
|
|
}
|
|
|
|
case B_SYSTEM_PROFILER_THREAD_REMOVED:
|
|
{
|
|
system_profiler_thread_removed* event
|
|
= (system_profiler_thread_removed*)buffer;
|
|
fState->LookupThread(event->thread)->AddEvent(header);
|
|
break;
|
|
}
|
|
|
|
case B_SYSTEM_PROFILER_THREAD_SCHEDULED:
|
|
{
|
|
system_profiler_thread_scheduled* event
|
|
= (system_profiler_thread_scheduled*)buffer;
|
|
fState->LookupThread(event->thread)->AddEvent(header);
|
|
|
|
if (event->thread != event->previous_thread) {
|
|
fState->LookupThread(event->previous_thread)
|
|
->AddEvent(header);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case B_SYSTEM_PROFILER_THREAD_ENQUEUED_IN_RUN_QUEUE:
|
|
{
|
|
thread_enqueued_in_run_queue* event
|
|
= (thread_enqueued_in_run_queue*)buffer;
|
|
fState->LookupThread(event->thread)->AddEvent(header);
|
|
break;
|
|
}
|
|
|
|
case B_SYSTEM_PROFILER_THREAD_REMOVED_FROM_RUN_QUEUE:
|
|
{
|
|
thread_removed_from_run_queue* event
|
|
= (thread_removed_from_run_queue*)buffer;
|
|
fState->LookupThread(event->thread)->AddEvent(header);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// transfer the events arrays from the scheduling states to the thread
|
|
// objects
|
|
for (int32 i = 0; Model::Thread* thread = fModel->ThreadAt(i); i++) {
|
|
ExtendedThreadSchedulingState* state
|
|
= fState->LookupThread(thread->ID());
|
|
thread->SetEvents(state->Events(), state->CountEvents());
|
|
state->DetachEvents();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool
|
|
ModelLoader::_SetThreadIORequests()
|
|
{
|
|
for (int32 i = 0; Model::Thread* thread = fModel->ThreadAt(i); i++) {
|
|
ExtendedThreadSchedulingState* state
|
|
= fState->LookupThread(thread->ID());
|
|
Model::IORequest** requests;
|
|
size_t requestCount;
|
|
if (!state->PrepareThreadIORequests(requests, requestCount))
|
|
return false;
|
|
if (requestCount > 0)
|
|
_SetThreadIORequests(thread, requests, requestCount);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void
|
|
ModelLoader::_SetThreadIORequests(Model::Thread* thread,
|
|
Model::IORequest** requests, size_t requestCount)
|
|
{
|
|
// compute some totals
|
|
int64 ioCount = 0;
|
|
nanotime_t ioTime = 0;
|
|
|
|
// sort requests by scheduler and start time
|
|
std::sort(requests, requests + requestCount,
|
|
Model::IORequest::SchedulerTimeLess);
|
|
|
|
nanotime_t endTime = fBaseTime + fModel->LastEventTime();
|
|
|
|
// compute the summed up I/O times
|
|
nanotime_t ioStart = requests[0]->scheduledEvent->time;
|
|
nanotime_t previousEnd = requests[0]->finishedEvent != NULL
|
|
? requests[0]->finishedEvent->time : endTime;
|
|
int32 scheduler = requests[0]->scheduledEvent->scheduler;
|
|
|
|
for (size_t i = 1; i < requestCount; i++) {
|
|
system_profiler_io_request_scheduled* scheduledEvent
|
|
= requests[i]->scheduledEvent;
|
|
if (scheduledEvent->scheduler != scheduler
|
|
|| scheduledEvent->time >= previousEnd) {
|
|
ioCount++;
|
|
ioTime += previousEnd - ioStart;
|
|
ioStart = scheduledEvent->time;
|
|
}
|
|
|
|
previousEnd = requests[i]->finishedEvent != NULL
|
|
? requests[i]->finishedEvent->time : endTime;
|
|
}
|
|
|
|
ioCount++;
|
|
ioTime += previousEnd - ioStart;
|
|
|
|
// sort requests by start time
|
|
std::sort(requests, requests + requestCount, Model::IORequest::TimeLess);
|
|
|
|
// set the computed values
|
|
thread->SetIORequests(requests, requestCount);
|
|
thread->SetIOs(ioCount, ioTime);
|
|
}
|
|
|
|
|
|
void
|
|
ModelLoader::_HandleTeamAdded(system_profiler_team_added* event)
|
|
{
|
|
if (fModel->AddTeam(event, fState->LastEventTime()) == NULL)
|
|
throw std::bad_alloc();
|
|
}
|
|
|
|
|
|
void
|
|
ModelLoader::_HandleTeamRemoved(system_profiler_team_removed* event)
|
|
{
|
|
if (Model::Team* team = fModel->TeamByID(event->team))
|
|
team->SetDeletionTime(fState->LastEventTime());
|
|
else {
|
|
printf("Warning: Removed event for unknown team: %" B_PRId32 "\n",
|
|
event->team);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
ModelLoader::_HandleTeamExec(system_profiler_team_exec* event)
|
|
{
|
|
// TODO:...
|
|
}
|
|
|
|
|
|
void
|
|
ModelLoader::_HandleThreadAdded(system_profiler_thread_added* event)
|
|
{
|
|
_AddThread(event)->IncrementEventCount();
|
|
}
|
|
|
|
|
|
void
|
|
ModelLoader::_HandleThreadRemoved(system_profiler_thread_removed* event)
|
|
{
|
|
ExtendedThreadSchedulingState* thread = fState->LookupThread(event->thread);
|
|
if (thread == NULL) {
|
|
printf("Warning: Removed event for unknown thread: %" B_PRId32 "\n",
|
|
event->thread);
|
|
thread = _AddUnknownThread(event->thread);
|
|
}
|
|
|
|
thread->thread->SetDeletionTime(fState->LastEventTime());
|
|
thread->IncrementEventCount();
|
|
}
|
|
|
|
|
|
void
|
|
ModelLoader::_HandleThreadScheduled(uint32 cpu,
|
|
system_profiler_thread_scheduled* event)
|
|
{
|
|
_UpdateLastEventTime(event->time);
|
|
|
|
ExtendedThreadSchedulingState* thread = fState->LookupThread(event->thread);
|
|
if (thread == NULL) {
|
|
printf("Warning: Schedule event for unknown thread: %" B_PRId32 "\n",
|
|
event->thread);
|
|
thread = _AddUnknownThread(event->thread);
|
|
return;
|
|
}
|
|
|
|
nanotime_t diffTime = fState->LastEventTime() - thread->lastTime;
|
|
|
|
if (thread->state == READY) {
|
|
// thread scheduled after having been woken up
|
|
thread->thread->AddLatency(diffTime);
|
|
} else if (thread->state == PREEMPTED) {
|
|
// thread scheduled after having been preempted before
|
|
thread->thread->AddRerun(diffTime);
|
|
}
|
|
|
|
if (thread->state == STILL_RUNNING) {
|
|
// Thread was running and continues to run.
|
|
thread->state = RUNNING;
|
|
}
|
|
|
|
if (thread->state != RUNNING) {
|
|
thread->lastTime = fState->LastEventTime();
|
|
thread->state = RUNNING;
|
|
}
|
|
|
|
thread->IncrementEventCount();
|
|
|
|
// unscheduled thread
|
|
|
|
if (event->thread == event->previous_thread)
|
|
return;
|
|
|
|
thread = fState->LookupThread(event->previous_thread);
|
|
if (thread == NULL) {
|
|
printf("Warning: Schedule event for unknown previous thread: %" B_PRId32
|
|
"\n", event->previous_thread);
|
|
thread = _AddUnknownThread(event->previous_thread);
|
|
}
|
|
|
|
diffTime = fState->LastEventTime() - thread->lastTime;
|
|
|
|
if (thread->state == STILL_RUNNING) {
|
|
// thread preempted
|
|
thread->thread->AddPreemption(diffTime);
|
|
thread->thread->AddRun(diffTime);
|
|
if (thread->priority == 0)
|
|
_AddIdleTime(cpu, diffTime);
|
|
|
|
thread->lastTime = fState->LastEventTime();
|
|
thread->state = PREEMPTED;
|
|
} else if (thread->state == RUNNING) {
|
|
// thread starts waiting (it hadn't been added to the run
|
|
// queue before being unscheduled)
|
|
thread->thread->AddRun(diffTime);
|
|
if (thread->priority == 0)
|
|
_AddIdleTime(cpu, diffTime);
|
|
|
|
if (event->previous_thread_state == B_THREAD_WAITING) {
|
|
addr_t waitObject = event->previous_thread_wait_object;
|
|
switch (event->previous_thread_wait_object_type) {
|
|
case THREAD_BLOCK_TYPE_SNOOZE:
|
|
case THREAD_BLOCK_TYPE_SIGNAL:
|
|
waitObject = 0;
|
|
break;
|
|
case THREAD_BLOCK_TYPE_SEMAPHORE:
|
|
case THREAD_BLOCK_TYPE_CONDITION_VARIABLE:
|
|
case THREAD_BLOCK_TYPE_MUTEX:
|
|
case THREAD_BLOCK_TYPE_RW_LOCK:
|
|
case THREAD_BLOCK_TYPE_OTHER:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
_AddThreadWaitObject(thread,
|
|
event->previous_thread_wait_object_type, waitObject);
|
|
}
|
|
|
|
thread->lastTime = fState->LastEventTime();
|
|
thread->state = WAITING;
|
|
} else if (thread->state == UNKNOWN) {
|
|
uint32 threadState = event->previous_thread_state;
|
|
if (threadState == B_THREAD_WAITING
|
|
|| threadState == B_THREAD_SUSPENDED) {
|
|
thread->lastTime = fState->LastEventTime();
|
|
thread->state = WAITING;
|
|
} else if (threadState == B_THREAD_READY) {
|
|
thread->lastTime = fState->LastEventTime();
|
|
thread->state = PREEMPTED;
|
|
}
|
|
}
|
|
|
|
thread->IncrementEventCount();
|
|
}
|
|
|
|
|
|
void
|
|
ModelLoader::_HandleThreadEnqueuedInRunQueue(
|
|
thread_enqueued_in_run_queue* event)
|
|
{
|
|
_UpdateLastEventTime(event->time);
|
|
|
|
ExtendedThreadSchedulingState* thread = fState->LookupThread(event->thread);
|
|
if (thread == NULL) {
|
|
printf("Warning: Enqueued in run queue event for unknown thread: %"
|
|
B_PRId32 "\n", event->thread);
|
|
thread = _AddUnknownThread(event->thread);
|
|
}
|
|
|
|
if (thread->state == RUNNING || thread->state == STILL_RUNNING) {
|
|
// Thread was running and is reentered into the run queue. This
|
|
// is done by the scheduler, if the thread remains ready.
|
|
thread->state = STILL_RUNNING;
|
|
} else {
|
|
// Thread was waiting and is ready now.
|
|
nanotime_t diffTime = fState->LastEventTime() - thread->lastTime;
|
|
if (thread->waitObject != NULL) {
|
|
thread->waitObject->AddWait(diffTime);
|
|
thread->waitObject = NULL;
|
|
thread->thread->AddWait(diffTime);
|
|
} else if (thread->state != UNKNOWN)
|
|
thread->thread->AddUnspecifiedWait(diffTime);
|
|
|
|
thread->lastTime = fState->LastEventTime();
|
|
thread->state = READY;
|
|
}
|
|
|
|
thread->priority = event->priority;
|
|
|
|
thread->IncrementEventCount();
|
|
}
|
|
|
|
|
|
void
|
|
ModelLoader::_HandleThreadRemovedFromRunQueue(uint32 cpu,
|
|
thread_removed_from_run_queue* event)
|
|
{
|
|
_UpdateLastEventTime(event->time);
|
|
|
|
ExtendedThreadSchedulingState* thread = fState->LookupThread(event->thread);
|
|
if (thread == NULL) {
|
|
printf("Warning: Removed from run queue event for unknown thread: "
|
|
"%" B_PRId32 "\n", event->thread);
|
|
thread = _AddUnknownThread(event->thread);
|
|
}
|
|
|
|
// This really only happens when the thread priority is changed
|
|
// while the thread is ready.
|
|
|
|
nanotime_t diffTime = fState->LastEventTime() - thread->lastTime;
|
|
if (thread->state == RUNNING) {
|
|
// This should never happen.
|
|
thread->thread->AddRun(diffTime);
|
|
if (thread->priority == 0)
|
|
_AddIdleTime(cpu, diffTime);
|
|
} else if (thread->state == READY || thread->state == PREEMPTED) {
|
|
// Not really correct, but the case is rare and we keep it
|
|
// simple.
|
|
thread->thread->AddUnspecifiedWait(diffTime);
|
|
}
|
|
|
|
thread->lastTime = fState->LastEventTime();
|
|
thread->state = WAITING;
|
|
|
|
thread->IncrementEventCount();
|
|
}
|
|
|
|
|
|
void
|
|
ModelLoader::_HandleWaitObjectInfo(system_profiler_wait_object_info* event)
|
|
{
|
|
if (fModel->AddWaitObject(event, NULL) == NULL)
|
|
throw std::bad_alloc();
|
|
}
|
|
|
|
|
|
void
|
|
ModelLoader::_HandleIOSchedulerAdded(system_profiler_io_scheduler_added* event)
|
|
{
|
|
Model::IOScheduler* scheduler = fModel->IOSchedulerByID(event->scheduler);
|
|
if (scheduler != NULL) {
|
|
printf("Warning: Duplicate added event for I/O scheduler %" B_PRId32
|
|
"\n", event->scheduler);
|
|
return;
|
|
}
|
|
|
|
if (fModel->AddIOScheduler(event) == NULL)
|
|
throw std::bad_alloc();
|
|
}
|
|
|
|
|
|
void
|
|
ModelLoader::_HandleIORequestScheduled(io_request_scheduled* event)
|
|
{
|
|
IORequest* request = fIORequests->Lookup(event->request);
|
|
if (request != NULL) {
|
|
printf("Warning: Duplicate schedule event for I/O request %p\n",
|
|
event->request);
|
|
return;
|
|
}
|
|
|
|
ExtendedThreadSchedulingState* thread = fState->LookupThread(event->thread);
|
|
if (thread == NULL) {
|
|
printf("Warning: I/O request for unknown thread %" B_PRId32 "\n",
|
|
event->thread);
|
|
thread = _AddUnknownThread(event->thread);
|
|
}
|
|
|
|
if (fModel->IOSchedulerByID(event->scheduler) == NULL) {
|
|
printf("Warning: I/O requests for unknown scheduler %" B_PRId32 "\n",
|
|
event->scheduler);
|
|
// TODO: Add state for unknown scheduler, as we do for threads.
|
|
return;
|
|
}
|
|
|
|
request = new(std::nothrow) IORequest(event);
|
|
if (request == NULL)
|
|
throw std::bad_alloc();
|
|
|
|
fIORequests->Insert(request);
|
|
thread->AddIORequest(request);
|
|
}
|
|
|
|
|
|
void
|
|
ModelLoader::_HandleIORequestFinished(io_request_finished* event)
|
|
{
|
|
IORequest* request = fIORequests->Lookup(event->request);
|
|
if (request == NULL)
|
|
return;
|
|
|
|
request->finishedEvent = event;
|
|
|
|
fIORequests->Remove(request);
|
|
fState->LookupThread(request->scheduledEvent->thread)
|
|
->IORequestFinished(request);
|
|
}
|
|
|
|
|
|
void
|
|
ModelLoader::_HandleIOOperationStarted(io_operation_started* event)
|
|
{
|
|
IORequest* request = fIORequests->Lookup(event->request);
|
|
if (request == NULL) {
|
|
printf("Warning: I/O request for operation %p not found\n",
|
|
event->operation);
|
|
return;
|
|
}
|
|
|
|
IOOperation* operation = new(std::nothrow) IOOperation(event);
|
|
if (operation == NULL)
|
|
throw std::bad_alloc();
|
|
|
|
request->AddOperation(operation);
|
|
}
|
|
|
|
|
|
void
|
|
ModelLoader::_HandleIOOperationFinished(io_operation_finished* event)
|
|
{
|
|
IORequest* request = fIORequests->Lookup(event->request);
|
|
if (request == NULL) {
|
|
printf("Warning: I/O request for operation %p not found\n",
|
|
event->operation);
|
|
return;
|
|
}
|
|
|
|
IOOperation* operation = request->FindOperation(event->operation);
|
|
if (operation == NULL) {
|
|
printf("Warning: operation %p not found\n", event->operation);
|
|
return;
|
|
}
|
|
|
|
operation->finishedEvent = event;
|
|
}
|
|
|
|
|
|
ModelLoader::ExtendedThreadSchedulingState*
|
|
ModelLoader::_AddThread(system_profiler_thread_added* event)
|
|
{
|
|
// do we know the thread already?
|
|
ExtendedThreadSchedulingState* info = fState->LookupThread(event->thread);
|
|
if (info != NULL) {
|
|
printf("Warning: Duplicate thread added event for thread %" B_PRId32
|
|
"\n", event->thread);
|
|
return info;
|
|
}
|
|
|
|
// add the thread to the model
|
|
Model::Thread* thread = fModel->AddThread(event, fState->LastEventTime());
|
|
if (thread == NULL)
|
|
throw std::bad_alloc();
|
|
|
|
// create and add a ThreadSchedulingState
|
|
info = new(std::nothrow) ExtendedThreadSchedulingState(thread);
|
|
if (info == NULL)
|
|
throw std::bad_alloc();
|
|
|
|
// TODO: The priority is missing from the system_profiler_thread_added
|
|
// struct. For now guess at least whether this is an idle thread.
|
|
if (strncmp(event->name, "idle thread", strlen("idle thread")) == 0)
|
|
info->priority = 0;
|
|
else
|
|
info->priority = B_NORMAL_PRIORITY;
|
|
|
|
fState->InsertThread(info);
|
|
|
|
return info;
|
|
}
|
|
|
|
|
|
ModelLoader::ExtendedThreadSchedulingState*
|
|
ModelLoader::_AddUnknownThread(thread_id threadID)
|
|
{
|
|
// create a dummy "add thread" event
|
|
system_profiler_thread_added* event = (system_profiler_thread_added*)
|
|
malloc(sizeof(system_profiler_thread_added));
|
|
if (event == NULL)
|
|
throw std::bad_alloc();
|
|
|
|
if (!fModel->AddAssociatedData(event)) {
|
|
free(event);
|
|
throw std::bad_alloc();
|
|
}
|
|
|
|
try {
|
|
event->team = _AddUnknownTeam()->ID();
|
|
event->thread = threadID;
|
|
snprintf(event->name, sizeof(event->name), "unknown thread %" B_PRId32,
|
|
threadID);
|
|
|
|
// add the thread to the model
|
|
ExtendedThreadSchedulingState* state = _AddThread(event);
|
|
return state;
|
|
} catch (...) {
|
|
throw;
|
|
}
|
|
}
|
|
|
|
Model::Team*
|
|
ModelLoader::_AddUnknownTeam()
|
|
{
|
|
team_id teamID = 0;
|
|
Model::Team* team = fModel->TeamByID(teamID);
|
|
if (team != NULL)
|
|
return team;
|
|
|
|
// create a dummy "add team" event
|
|
static const char* const kUnknownThreadsTeamName = "unknown threads";
|
|
size_t nameLength = strlen(kUnknownThreadsTeamName);
|
|
|
|
system_profiler_team_added* event = (system_profiler_team_added*)
|
|
malloc(sizeof(system_profiler_team_added) + nameLength);
|
|
if (event == NULL)
|
|
throw std::bad_alloc();
|
|
|
|
event->team = teamID;
|
|
event->args_offset = nameLength;
|
|
strlcpy(event->name, kUnknownThreadsTeamName, nameLength + 1);
|
|
|
|
// add the team to the model
|
|
team = fModel->AddTeam(event, fState->LastEventTime());
|
|
if (team == NULL)
|
|
throw std::bad_alloc();
|
|
|
|
return team;
|
|
}
|
|
|
|
|
|
void
|
|
ModelLoader::_AddThreadWaitObject(ExtendedThreadSchedulingState* thread,
|
|
uint32 type, addr_t object)
|
|
{
|
|
Model::WaitObjectGroup* waitObjectGroup
|
|
= fModel->WaitObjectGroupFor(type, object);
|
|
if (waitObjectGroup == NULL) {
|
|
// The algorithm should prevent this case.
|
|
printf("ModelLoader::_AddThreadWaitObject(): Unknown wait object: type:"
|
|
" %" B_PRIu32 ", " "object: %#" B_PRIxADDR "\n", type, object);
|
|
return;
|
|
}
|
|
|
|
Model::WaitObject* waitObject = waitObjectGroup->MostRecentWaitObject();
|
|
|
|
Model::ThreadWaitObjectGroup* threadWaitObjectGroup
|
|
= fModel->ThreadWaitObjectGroupFor(thread->ID(), type, object);
|
|
|
|
if (threadWaitObjectGroup == NULL
|
|
|| threadWaitObjectGroup->MostRecentWaitObject() != waitObject) {
|
|
Model::ThreadWaitObject* threadWaitObject
|
|
= fModel->AddThreadWaitObject(thread->ID(), waitObject,
|
|
&threadWaitObjectGroup);
|
|
if (threadWaitObject == NULL)
|
|
throw std::bad_alloc();
|
|
}
|
|
|
|
thread->waitObject = threadWaitObjectGroup->MostRecentThreadWaitObject();
|
|
}
|
|
|
|
|
|
void
|
|
ModelLoader::_AddIdleTime(uint32 cpu, nanotime_t time)
|
|
{
|
|
fCPUInfos[cpu].idleTime += time;
|
|
}
|