2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
14 #include <AutoDeleter.h>
16 #include <thread_defs.h>
20 static const char* const kThreadStateNames
[] = {
31 thread_state_name(ThreadState state
)
33 return kThreadStateNames
[state
];
38 wait_object_type_name(uint32 type
)
41 case THREAD_BLOCK_TYPE_SEMAPHORE
:
43 case THREAD_BLOCK_TYPE_CONDITION_VARIABLE
:
45 case THREAD_BLOCK_TYPE_MUTEX
:
47 case THREAD_BLOCK_TYPE_RW_LOCK
:
49 case THREAD_BLOCK_TYPE_OTHER
:
51 case THREAD_BLOCK_TYPE_SNOOZE
:
53 case THREAD_BLOCK_TYPE_SIGNAL
:
72 Model::CPU::SetIdleTime(nanotime_t time
)
78 // #pragma mark - IORequest
81 Model::IORequest::IORequest(
82 system_profiler_io_request_scheduled
* scheduledEvent
,
83 system_profiler_io_request_finished
* finishedEvent
, size_t operationCount
)
85 scheduledEvent(scheduledEvent
),
86 finishedEvent(finishedEvent
),
87 operationCount(operationCount
)
92 Model::IORequest::~IORequest()
97 /*static*/ Model::IORequest
*
98 Model::IORequest::Create(system_profiler_io_request_scheduled
* scheduledEvent
,
99 system_profiler_io_request_finished
* finishedEvent
, size_t operationCount
)
101 void* memory
= malloc(
102 sizeof(IORequest
) + operationCount
* sizeof(IOOperation
));
106 return new(memory
) IORequest(scheduledEvent
, finishedEvent
, operationCount
);
111 Model::IORequest::Delete()
117 // #pragma mark - IOScheduler
120 Model::IOScheduler::IOScheduler(system_profiler_io_scheduler_added
* event
,
129 // #pragma mark - WaitObject
132 Model::WaitObject::WaitObject(const system_profiler_wait_object_info
* event
)
141 Model::WaitObject::~WaitObject()
147 Model::WaitObject::AddWait(nanotime_t waitTime
)
150 fTotalWaitTime
+= waitTime
;
154 // #pragma mark - WaitObjectGroup
157 Model::WaitObjectGroup::WaitObjectGroup(WaitObject
* waitObject
)
162 fWaitObjects
.AddItem(waitObject
);
166 Model::WaitObjectGroup::~WaitObjectGroup()
172 Model::WaitObjectGroup::Waits()
182 Model::WaitObjectGroup::TotalWaitTime()
184 if (fTotalWaitTime
< 0)
187 return fTotalWaitTime
;
192 Model::WaitObjectGroup::_ComputeWaits()
197 for (int32 i
= fWaitObjects
.CountItems(); i
-- > 0;) {
198 WaitObject
* waitObject
= fWaitObjects
.ItemAt(i
);
200 fWaits
+= waitObject
->Waits();
201 fTotalWaitTime
+= waitObject
->TotalWaitTime();
206 // #pragma mark - ThreadWaitObject
209 Model::ThreadWaitObject::ThreadWaitObject(WaitObject
* waitObject
)
211 fWaitObject(waitObject
),
218 Model::ThreadWaitObject::~ThreadWaitObject()
224 Model::ThreadWaitObject::AddWait(nanotime_t waitTime
)
227 fTotalWaitTime
+= waitTime
;
229 fWaitObject
->AddWait(waitTime
);
233 // #pragma mark - ThreadWaitObjectGroup
236 Model::ThreadWaitObjectGroup::ThreadWaitObjectGroup(
237 ThreadWaitObject
* threadWaitObject
)
239 fWaitObjects
.Add(threadWaitObject
);
243 Model::ThreadWaitObjectGroup::~ThreadWaitObjectGroup()
249 Model::ThreadWaitObjectGroup::GetThreadWaitObjects(
250 BObjectList
<ThreadWaitObject
>& objects
)
252 ThreadWaitObjectList::Iterator it
= fWaitObjects
.GetIterator();
253 while (ThreadWaitObject
* object
= it
.Next()) {
254 if (!objects
.AddItem(object
))
262 // #pragma mark - Team
265 Model::Team::Team(const system_profiler_team_added
* event
, nanotime_t time
)
267 fCreationEvent(event
),
281 Model::Team::AddThread(Thread
* thread
)
283 return fThreads
.BinaryInsert(thread
, &Thread::CompareByCreationTimeID
);
287 // #pragma mark - Thread
290 Model::Thread::Thread(Team
* team
, const system_profiler_thread_added
* event
,
298 fCreationEvent(event
),
315 fUnspecifiedWaitTime(0),
320 fWaitObjectGroups(20, true)
325 Model::Thread::~Thread()
327 if (fIORequests
!= NULL
) {
328 for (size_t i
= 0; i
< fIORequestCount
; i
++)
329 fIORequests
[i
]->Delete();
331 delete[] fIORequests
;
339 Model::Thread::SetEvents(system_profiler_event_header
** events
,
343 fEventCount
= eventCount
;
348 Model::Thread::SetIORequests(IORequest
** requests
, size_t requestCount
)
350 fIORequests
= requests
;
351 fIORequestCount
= requestCount
;
356 Model::Thread::ClosestRequestStartIndex(nanotime_t minRequestStartTime
) const
359 size_t upper
= fIORequestCount
;
360 while (lower
< upper
) {
361 size_t mid
= (lower
+ upper
) / 2;
362 IORequest
* request
= fIORequests
[mid
];
364 if (request
->ScheduledTime() < minRequestStartTime
)
374 Model::ThreadWaitObjectGroup
*
375 Model::Thread::ThreadWaitObjectGroupFor(uint32 type
, addr_t object
) const
381 return fWaitObjectGroups
.BinarySearchByKey(key
,
382 &ThreadWaitObjectGroup::CompareWithTypeObject
);
387 Model::Thread::AddRun(nanotime_t runTime
)
390 fTotalRunTime
+= runTime
;
392 if (fMinRunTime
< 0 || runTime
< fMinRunTime
)
393 fMinRunTime
= runTime
;
394 if (runTime
> fMaxRunTime
)
395 fMaxRunTime
= runTime
;
400 Model::Thread::AddRerun(nanotime_t runTime
)
403 fTotalRerunTime
+= runTime
;
405 if (fMinRerunTime
< 0 || runTime
< fMinRerunTime
)
406 fMinRerunTime
= runTime
;
407 if (runTime
> fMaxRerunTime
)
408 fMaxRerunTime
= runTime
;
413 Model::Thread::AddLatency(nanotime_t latency
)
416 fTotalLatency
+= latency
;
418 if (fMinLatency
< 0 || latency
< fMinLatency
)
419 fMinLatency
= latency
;
420 if (latency
> fMaxLatency
)
421 fMaxLatency
= latency
;
426 Model::Thread::AddPreemption(nanotime_t runTime
)
433 Model::Thread::AddWait(nanotime_t waitTime
)
436 fTotalWaitTime
+= waitTime
;
441 Model::Thread::AddUnspecifiedWait(nanotime_t waitTime
)
443 fUnspecifiedWaitTime
+= waitTime
;
447 Model::ThreadWaitObject
*
448 Model::Thread::AddThreadWaitObject(WaitObject
* waitObject
,
449 ThreadWaitObjectGroup
** _threadWaitObjectGroup
)
451 // create a thread wait object
452 ThreadWaitObject
* threadWaitObject
453 = new(std::nothrow
) ThreadWaitObject(waitObject
);
454 if (threadWaitObject
== NULL
)
457 // find the thread wait object group
458 ThreadWaitObjectGroup
* threadWaitObjectGroup
459 = ThreadWaitObjectGroupFor(waitObject
->Type(), waitObject
->Object());
460 if (threadWaitObjectGroup
== NULL
) {
461 // doesn't exist yet -- create
462 threadWaitObjectGroup
= new(std::nothrow
) ThreadWaitObjectGroup(
464 if (threadWaitObjectGroup
== NULL
) {
465 delete threadWaitObject
;
470 if (!fWaitObjectGroups
.BinaryInsert(threadWaitObjectGroup
,
471 &ThreadWaitObjectGroup::CompareByTypeObject
)) {
472 delete threadWaitObjectGroup
;
476 // exists -- just add the object
477 threadWaitObjectGroup
->AddWaitObject(threadWaitObject
);
480 if (_threadWaitObjectGroup
!= NULL
)
481 *_threadWaitObjectGroup
= threadWaitObjectGroup
;
483 return threadWaitObject
;
488 Model::Thread::SetIOs(int64 count
, nanotime_t time
)
495 // #pragma mark - SchedulingState
498 Model::SchedulingState::~SchedulingState()
505 Model::SchedulingState::Init()
507 status_t error
= fThreadStates
.Init();
516 Model::SchedulingState::Init(const CompactSchedulingState
* state
)
518 status_t error
= Init();
525 fLastEventTime
= state
->LastEventTime();
526 for (int32 i
= 0; const CompactThreadSchedulingState
* compactThreadState
527 = state
->ThreadStateAt(i
); i
++) {
528 ThreadSchedulingState
* threadState
529 = new(std::nothrow
) ThreadSchedulingState(*compactThreadState
);
530 if (threadState
== NULL
)
533 fThreadStates
.Insert(threadState
);
541 Model::SchedulingState::Clear()
543 ThreadSchedulingState
* state
= fThreadStates
.Clear(true);
544 while (state
!= NULL
) {
545 ThreadSchedulingState
* next
= state
->next
;
554 Model::SchedulingState::DeleteThread(ThreadSchedulingState
* thread
)
560 // #pragma mark - CompactSchedulingState
563 /*static*/ Model::CompactSchedulingState
*
564 Model::CompactSchedulingState::Create(const SchedulingState
& state
,
567 nanotime_t lastEventTime
= state
.LastEventTime();
569 // count the active threads
570 int32 threadCount
= 0;
571 for (ThreadSchedulingStateTable::Iterator it
572 = state
.ThreadStates().GetIterator();
573 ThreadSchedulingState
* threadState
= it
.Next();) {
574 Thread
* thread
= threadState
->thread
;
575 if (thread
->CreationTime() <= lastEventTime
576 && (thread
->DeletionTime() == -1
577 || thread
->DeletionTime() >= lastEventTime
)) {
582 CompactSchedulingState
* compactState
= (CompactSchedulingState
*)malloc(
583 sizeof(CompactSchedulingState
)
584 + threadCount
* sizeof(CompactThreadSchedulingState
));
585 if (compactState
== NULL
)
588 // copy the state info
589 compactState
->fEventOffset
= eventOffset
;
590 compactState
->fThreadCount
= threadCount
;
591 compactState
->fLastEventTime
= lastEventTime
;
593 int32 threadIndex
= 0;
594 for (ThreadSchedulingStateTable::Iterator it
595 = state
.ThreadStates().GetIterator();
596 ThreadSchedulingState
* threadState
= it
.Next();) {
597 Thread
* thread
= threadState
->thread
;
598 if (thread
->CreationTime() <= lastEventTime
599 && (thread
->DeletionTime() == -1
600 || thread
->DeletionTime() >= lastEventTime
)) {
601 compactState
->fThreadStates
[threadIndex
++] = *threadState
;
610 Model::CompactSchedulingState::Delete()
616 // #pragma mark - Model
619 Model::Model(const char* dataSourceName
, void* eventData
, size_t eventDataSize
,
620 system_profiler_event_header
** events
, size_t eventCount
)
622 fDataSourceName(dataSourceName
),
623 fEventData(eventData
),
625 fEventDataSize(eventDataSize
),
626 fEventCount(eventCount
),
634 fWaitObjectGroups(20, true),
635 fIOSchedulers(10, true),
636 fSchedulingStates(100)
643 for (int32 i
= 0; CompactSchedulingState
* state
644 = fSchedulingStates
.ItemAt(i
); i
++) {
652 for (int32 i
= 0; void* data
= fAssociatedData
.ItemAt(i
); i
++)
658 Model::ClosestEventIndex(nanotime_t eventTime
) const
660 // The events themselves are unmodified and use an absolute time.
661 eventTime
+= fBaseTime
;
663 // Binary search the event. Since not all events have a timestamp, we have
664 // to do a bit of iteration, too.
666 size_t upper
= CountEvents();
667 while (lower
< upper
) {
668 size_t mid
= (lower
+ upper
) / 2;
669 while (mid
< upper
) {
670 system_profiler_event_header
* header
= fEvents
[mid
];
671 switch (header
->event
) {
672 case B_SYSTEM_PROFILER_THREAD_SCHEDULED
:
673 case B_SYSTEM_PROFILER_THREAD_ENQUEUED_IN_RUN_QUEUE
:
674 case B_SYSTEM_PROFILER_THREAD_REMOVED_FROM_RUN_QUEUE
:
689 system_profiler_thread_scheduling_event
* event
690 = (system_profiler_thread_scheduling_event
*)(fEvents
[mid
] + 1);
691 if (event
->time
< eventTime
)
702 Model::AddAssociatedData(void* data
)
704 return fAssociatedData
.AddItem(data
);
709 Model::RemoveAssociatedData(void* data
)
711 fAssociatedData
.RemoveItem(data
);
716 Model::LoadingFinished()
718 // set the thread indices
719 for (int32 i
= 0; Thread
* thread
= fThreads
.ItemAt(i
); i
++)
722 // compute the total idle time
724 for (int32 i
= 0; CPU
* cpu
= CPUAt(i
); i
++)
725 fIdleTime
+= cpu
->IdleTime();
730 Model::SetBaseTime(nanotime_t time
)
737 Model::SetLastEventTime(nanotime_t time
)
739 fLastEventTime
= time
;
744 Model::SetCPUCount(int32 count
)
750 for (int32 i
= 0; i
< fCPUCount
; i
++) {
751 CPU
* cpu
= new(std::nothrow
) CPU
;
752 if (cpu
== NULL
|| !fCPUs
.AddItem(cpu
)) {
763 Model::CountTeams() const
765 return fTeams
.CountItems();
770 Model::TeamAt(int32 index
) const
772 return fTeams
.ItemAt(index
);
777 Model::TeamByID(team_id id
) const
779 return fTeams
.BinarySearchByKey(id
, &Team::CompareWithID
);
784 Model::AddTeam(const system_profiler_team_added
* event
, nanotime_t time
)
786 Team
* team
= TeamByID(event
->team
);
788 fprintf(stderr
, "Duplicate team: %" B_PRId32
"\n", event
->team
);
789 // TODO: User feedback!
793 team
= new(std::nothrow
) Team(event
, time
);
797 if (!fTeams
.BinaryInsert(team
, &Team::CompareByID
)) {
807 Model::CountThreads() const
809 return fThreads
.CountItems();
814 Model::ThreadAt(int32 index
) const
816 return fThreads
.ItemAt(index
);
821 Model::ThreadByID(thread_id id
) const
823 return fThreads
.BinarySearchByKey(id
, &Thread::CompareWithID
);
828 Model::AddThread(const system_profiler_thread_added
* event
, nanotime_t time
)
830 // check whether we do already know the thread
831 Thread
* thread
= ThreadByID(event
->thread
);
832 if (thread
!= NULL
) {
833 fprintf(stderr
, "Duplicate thread: %" B_PRId32
"\n", event
->thread
);
834 // TODO: User feedback!
839 Team
* team
= TeamByID(event
->team
);
841 fprintf(stderr
, "No team for thread: %" B_PRId32
"\n", event
->thread
);
845 // create the thread and add it
846 thread
= new(std::nothrow
) Thread(team
, event
, time
);
849 ObjectDeleter
<Thread
> threadDeleter(thread
);
851 if (!fThreads
.BinaryInsert(thread
, &Thread::CompareByID
))
854 if (!team
->AddThread(thread
)) {
855 fThreads
.RemoveItem(thread
);
859 threadDeleter
.Detach();
865 Model::AddWaitObject(const system_profiler_wait_object_info
* event
,
866 WaitObjectGroup
** _waitObjectGroup
)
868 // create a wait object
869 WaitObject
* waitObject
= new(std::nothrow
) WaitObject(event
);
870 if (waitObject
== NULL
)
873 // find the wait object group
874 WaitObjectGroup
* waitObjectGroup
875 = WaitObjectGroupFor(waitObject
->Type(), waitObject
->Object());
876 if (waitObjectGroup
== NULL
) {
877 // doesn't exist yet -- create
878 waitObjectGroup
= new(std::nothrow
) WaitObjectGroup(waitObject
);
879 if (waitObjectGroup
== NULL
) {
885 if (!fWaitObjectGroups
.BinaryInsert(waitObjectGroup
,
886 &WaitObjectGroup::CompareByTypeObject
)) {
887 delete waitObjectGroup
;
891 // exists -- just add the object
892 waitObjectGroup
->AddWaitObject(waitObject
);
895 if (_waitObjectGroup
!= NULL
)
896 *_waitObjectGroup
= waitObjectGroup
;
903 Model::CountWaitObjectGroups() const
905 return fWaitObjectGroups
.CountItems();
909 Model::WaitObjectGroup
*
910 Model::WaitObjectGroupAt(int32 index
) const
912 return fWaitObjectGroups
.ItemAt(index
);
916 Model::WaitObjectGroup
*
917 Model::WaitObjectGroupFor(uint32 type
, addr_t object
) const
923 return fWaitObjectGroups
.BinarySearchByKey(key
,
924 &WaitObjectGroup::CompareWithTypeObject
);
928 Model::ThreadWaitObject
*
929 Model::AddThreadWaitObject(thread_id threadID
, WaitObject
* waitObject
,
930 ThreadWaitObjectGroup
** _threadWaitObjectGroup
)
932 Thread
* thread
= ThreadByID(threadID
);
936 return thread
->AddThreadWaitObject(waitObject
, _threadWaitObjectGroup
);
940 Model::ThreadWaitObjectGroup
*
941 Model::ThreadWaitObjectGroupFor(thread_id threadID
, uint32 type
, addr_t object
) const
943 Thread
* thread
= ThreadByID(threadID
);
947 return thread
->ThreadWaitObjectGroupFor(type
, object
);
952 Model::CountIOSchedulers() const
954 return fIOSchedulers
.CountItems();
959 Model::IOSchedulerAt(int32 index
) const
961 return fIOSchedulers
.ItemAt(index
);
966 Model::IOSchedulerByID(int32 id
) const
968 for (int32 i
= 0; IOScheduler
* scheduler
= fIOSchedulers
.ItemAt(i
); i
++) {
969 if (scheduler
->ID() == id
)
978 Model::AddIOScheduler(system_profiler_io_scheduler_added
* event
)
980 IOScheduler
* scheduler
= new(std::nothrow
) IOScheduler(event
,
981 fIOSchedulers
.CountItems());
982 if (scheduler
== NULL
|| !fIOSchedulers
.AddItem(scheduler
)) {
992 Model::AddSchedulingStateSnapshot(const SchedulingState
& state
,
995 CompactSchedulingState
* compactState
= CompactSchedulingState::Create(state
,
997 if (compactState
== NULL
)
1000 if (!fSchedulingStates
.AddItem(compactState
)) {
1001 compactState
->Delete();
1009 const Model::CompactSchedulingState
*
1010 Model::ClosestSchedulingState(nanotime_t eventTime
) const
1012 int32 index
= fSchedulingStates
.BinarySearchIndexByKey(eventTime
,
1013 &_CompareEventTimeSchedulingState
);
1015 return fSchedulingStates
.ItemAt(index
);
1019 return index
> 0 ? fSchedulingStates
.ItemAt(index
- 1) : NULL
;
1024 Model::_CompareEventTimeSchedulingState(const nanotime_t
* time
,
1025 const CompactSchedulingState
* state
)
1027 if (*time
< state
->LastEventTime())
1029 return *time
== state
->LastEventTime() ? 0 : 1;