xref: /haiku/src/apps/debuganalyzer/model/Model.cpp (revision 9a6a20d4689307142a7ed26a1437ba47e244e73f)
1 /*
2  * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "Model.h"
8 
9 #include <new>
10 
11 #include <stdio.h>
12 #include <stdlib.h>
13 
14 #include <AutoDeleter.h>
15 
16 #include <thread_defs.h>
17 
18 
19 
20 static const char* const kThreadStateNames[] = {
21 	"running",
22 	"still running",
23 	"preempted",
24 	"ready",
25 	"waiting",
26 	"unknown"
27 };
28 
29 
30 const char*
31 thread_state_name(ThreadState state)
32 {
33 	return kThreadStateNames[state];
34 }
35 
36 
37 const char*
38 wait_object_type_name(uint32 type)
39 {
40 	switch (type) {
41 		case THREAD_BLOCK_TYPE_SEMAPHORE:
42 			return "semaphore";
43 		case THREAD_BLOCK_TYPE_CONDITION_VARIABLE:
44 			return "condition";
45 		case THREAD_BLOCK_TYPE_MUTEX:
46 			return "mutex";
47 		case THREAD_BLOCK_TYPE_RW_LOCK:
48 			return "rw lock";
49 		case THREAD_BLOCK_TYPE_USER:
50 			return "user";
51 		case THREAD_BLOCK_TYPE_OTHER:
52 		case THREAD_BLOCK_TYPE_OTHER_OBJECT:
53 			return "other";
54 		case THREAD_BLOCK_TYPE_SNOOZE:
55 			return "snooze";
56 		case THREAD_BLOCK_TYPE_SIGNAL:
57 			return "signal";
58 		default:
59 			return "unknown";
60 	}
61 }
62 
63 
64 // #pragma mark - CPU
65 
66 
67 Model::CPU::CPU()
68 	:
69 	fIdleTime(0)
70 {
71 }
72 
73 
74 void
75 Model::CPU::SetIdleTime(nanotime_t time)
76 {
77 	fIdleTime = time;
78 }
79 
80 
81 // #pragma mark - IORequest
82 
83 
84 Model::IORequest::IORequest(
85 	system_profiler_io_request_scheduled* scheduledEvent,
86 	system_profiler_io_request_finished* finishedEvent, size_t operationCount)
87 	:
88 	scheduledEvent(scheduledEvent),
89 	finishedEvent(finishedEvent),
90 	operationCount(operationCount)
91 {
92 }
93 
94 
95 Model::IORequest::~IORequest()
96 {
97 }
98 
99 
100 /*static*/ Model::IORequest*
101 Model::IORequest::Create(system_profiler_io_request_scheduled* scheduledEvent,
102 	system_profiler_io_request_finished* finishedEvent, size_t operationCount)
103 {
104 	void* memory = malloc(
105 		sizeof(IORequest) + operationCount * sizeof(IOOperation));
106 	if (memory == NULL)
107 		return NULL;
108 
109 	return new(memory) IORequest(scheduledEvent, finishedEvent, operationCount);
110 }
111 
112 
113 void
114 Model::IORequest::Delete()
115 {
116 	free(this);
117 }
118 
119 
120 // #pragma mark - IOScheduler
121 
122 
123 Model::IOScheduler::IOScheduler(system_profiler_io_scheduler_added* event,
124 	int32 index)
125 	:
126 	fAddedEvent(event),
127 	fIndex(index)
128 {
129 }
130 
131 
132 // #pragma mark - WaitObject
133 
134 
135 Model::WaitObject::WaitObject(const system_profiler_wait_object_info* event)
136 	:
137 	fEvent(event),
138 	fWaits(0),
139 	fTotalWaitTime(0)
140 {
141 }
142 
143 
144 Model::WaitObject::~WaitObject()
145 {
146 }
147 
148 
149 void
150 Model::WaitObject::AddWait(nanotime_t waitTime)
151 {
152 	fWaits++;
153 	fTotalWaitTime += waitTime;
154 }
155 
156 
157 // #pragma mark - WaitObjectGroup
158 
159 
160 Model::WaitObjectGroup::WaitObjectGroup(WaitObject* waitObject)
161 	:
162 	fWaits(-1),
163 	fTotalWaitTime(-1)
164 {
165 	fWaitObjects.AddItem(waitObject);
166 }
167 
168 
169 Model::WaitObjectGroup::~WaitObjectGroup()
170 {
171 }
172 
173 
174 int64
175 Model::WaitObjectGroup::Waits()
176 {
177 	if (fWaits < 0)
178 		_ComputeWaits();
179 
180 	return fWaits;
181 }
182 
183 
184 nanotime_t
185 Model::WaitObjectGroup::TotalWaitTime()
186 {
187 	if (fTotalWaitTime < 0)
188 		_ComputeWaits();
189 
190 	return fTotalWaitTime;
191 }
192 
193 
194 void
195 Model::WaitObjectGroup::_ComputeWaits()
196 {
197 	fWaits = 0;
198 	fTotalWaitTime = 0;
199 
200 	for (int32 i = fWaitObjects.CountItems(); i-- > 0;) {
201 		WaitObject* waitObject = fWaitObjects.ItemAt(i);
202 
203 		fWaits += waitObject->Waits();
204 		fTotalWaitTime += waitObject->TotalWaitTime();
205 	}
206 }
207 
208 
209 // #pragma mark - ThreadWaitObject
210 
211 
212 Model::ThreadWaitObject::ThreadWaitObject(WaitObject* waitObject)
213 	:
214 	fWaitObject(waitObject),
215 	fWaits(0),
216 	fTotalWaitTime(0)
217 {
218 }
219 
220 
221 Model::ThreadWaitObject::~ThreadWaitObject()
222 {
223 }
224 
225 
226 void
227 Model::ThreadWaitObject::AddWait(nanotime_t waitTime)
228 {
229 	fWaits++;
230 	fTotalWaitTime += waitTime;
231 
232 	fWaitObject->AddWait(waitTime);
233 }
234 
235 
236 // #pragma mark - ThreadWaitObjectGroup
237 
238 
239 Model::ThreadWaitObjectGroup::ThreadWaitObjectGroup(
240 	ThreadWaitObject* threadWaitObject)
241 {
242 	fWaitObjects.Add(threadWaitObject);
243 }
244 
245 
246 Model::ThreadWaitObjectGroup::~ThreadWaitObjectGroup()
247 {
248 }
249 
250 
251 bool
252 Model::ThreadWaitObjectGroup::GetThreadWaitObjects(
253 	BObjectList<ThreadWaitObject>& objects)
254 {
255 	ThreadWaitObjectList::ConstIterator it = fWaitObjects.GetIterator();
256 	while (ThreadWaitObject* object = it.Next()) {
257 		if (!objects.AddItem(object))
258 			return false;
259 	}
260 
261 	return true;
262 }
263 
264 
265 // #pragma mark - Team
266 
267 
268 Model::Team::Team(const system_profiler_team_added* event, nanotime_t time)
269 	:
270 	fCreationEvent(event),
271 	fCreationTime(time),
272 	fDeletionTime(-1),
273 	fThreads(10)
274 {
275 }
276 
277 
278 Model::Team::~Team()
279 {
280 }
281 
282 
283 bool
284 Model::Team::AddThread(Thread* thread)
285 {
286 	return fThreads.BinaryInsert(thread, &Thread::CompareByCreationTimeID);
287 }
288 
289 
290 // #pragma mark - Thread
291 
292 
293 Model::Thread::Thread(Team* team, const system_profiler_thread_added* event,
294 	nanotime_t time)
295 	:
296 	fEvents(NULL),
297 	fEventCount(0),
298 	fIORequests(NULL),
299 	fIORequestCount(0),
300 	fTeam(team),
301 	fCreationEvent(event),
302 	fCreationTime(time),
303 	fDeletionTime(-1),
304 	fRuns(0),
305 	fTotalRunTime(0),
306 	fMinRunTime(-1),
307 	fMaxRunTime(-1),
308 	fLatencies(0),
309 	fTotalLatency(0),
310 	fMinLatency(-1),
311 	fMaxLatency(-1),
312 	fReruns(0),
313 	fTotalRerunTime(0),
314 	fMinRerunTime(-1),
315 	fMaxRerunTime(-1),
316 	fWaits(0),
317 	fTotalWaitTime(0),
318 	fUnspecifiedWaitTime(0),
319 	fIOCount(0),
320 	fIOTime(0),
321 	fPreemptions(0),
322 	fIndex(-1),
323 	fWaitObjectGroups(20, true)
324 {
325 }
326 
327 
328 Model::Thread::~Thread()
329 {
330 	if (fIORequests != NULL) {
331 		for (size_t i = 0; i < fIORequestCount; i++)
332 			fIORequests[i]->Delete();
333 
334 		delete[] fIORequests;
335 	}
336 
337 	delete[] fEvents;
338 }
339 
340 
341 void
342 Model::Thread::SetEvents(system_profiler_event_header** events,
343 	size_t eventCount)
344 {
345 	fEvents = events;
346 	fEventCount = eventCount;
347 }
348 
349 
350 void
351 Model::Thread::SetIORequests(IORequest** requests, size_t requestCount)
352 {
353 	fIORequests = requests;
354 	fIORequestCount = requestCount;
355 }
356 
357 
358 size_t
359 Model::Thread::ClosestRequestStartIndex(nanotime_t minRequestStartTime) const
360 {
361 	size_t lower = 0;
362 	size_t upper = fIORequestCount;
363 	while (lower < upper) {
364 		size_t mid = (lower + upper) / 2;
365 		IORequest* request = fIORequests[mid];
366 
367 		if (request->ScheduledTime() < minRequestStartTime)
368 			lower = mid + 1;
369 		else
370 			upper = mid;
371 	}
372 
373 	return lower;
374 }
375 
376 
377 Model::ThreadWaitObjectGroup*
378 Model::Thread::ThreadWaitObjectGroupFor(uint32 type, addr_t object) const
379 {
380 	type_and_object key;
381 	key.type = type;
382 	key.object = object;
383 
384 	return fWaitObjectGroups.BinarySearchByKey(key,
385 		&ThreadWaitObjectGroup::CompareWithTypeObject);
386 }
387 
388 
389 void
390 Model::Thread::AddRun(nanotime_t runTime)
391 {
392 	fRuns++;
393 	fTotalRunTime += runTime;
394 
395 	if (fMinRunTime < 0 || runTime < fMinRunTime)
396 		fMinRunTime = runTime;
397 	if (runTime > fMaxRunTime)
398 		fMaxRunTime = runTime;
399 }
400 
401 
402 void
403 Model::Thread::AddRerun(nanotime_t runTime)
404 {
405 	fReruns++;
406 	fTotalRerunTime += runTime;
407 
408 	if (fMinRerunTime < 0 || runTime < fMinRerunTime)
409 		fMinRerunTime = runTime;
410 	if (runTime > fMaxRerunTime)
411 		fMaxRerunTime = runTime;
412 }
413 
414 
415 void
416 Model::Thread::AddLatency(nanotime_t latency)
417 {
418 	fLatencies++;
419 	fTotalLatency += latency;
420 
421 	if (fMinLatency < 0 || latency < fMinLatency)
422 		fMinLatency = latency;
423 	if (latency > fMaxLatency)
424 		fMaxLatency = latency;
425 }
426 
427 
428 void
429 Model::Thread::AddPreemption(nanotime_t runTime)
430 {
431 	fPreemptions++;
432 }
433 
434 
435 void
436 Model::Thread::AddWait(nanotime_t waitTime)
437 {
438 	fWaits++;
439 	fTotalWaitTime += waitTime;
440 }
441 
442 
443 void
444 Model::Thread::AddUnspecifiedWait(nanotime_t waitTime)
445 {
446 	fUnspecifiedWaitTime += waitTime;
447 }
448 
449 
450 Model::ThreadWaitObject*
451 Model::Thread::AddThreadWaitObject(WaitObject* waitObject,
452 	ThreadWaitObjectGroup** _threadWaitObjectGroup)
453 {
454 	// create a thread wait object
455 	ThreadWaitObject* threadWaitObject
456 		= new(std::nothrow) ThreadWaitObject(waitObject);
457 	if (threadWaitObject == NULL)
458 		return NULL;
459 
460 	// find the thread wait object group
461 	ThreadWaitObjectGroup* threadWaitObjectGroup
462 		= ThreadWaitObjectGroupFor(waitObject->Type(), waitObject->Object());
463 	if (threadWaitObjectGroup == NULL) {
464 		// doesn't exist yet -- create
465 		threadWaitObjectGroup = new(std::nothrow) ThreadWaitObjectGroup(
466 			threadWaitObject);
467 		if (threadWaitObjectGroup == NULL) {
468 			delete threadWaitObject;
469 			return NULL;
470 		}
471 
472 		// add to the list
473 		if (!fWaitObjectGroups.BinaryInsert(threadWaitObjectGroup,
474 				&ThreadWaitObjectGroup::CompareByTypeObject)) {
475 			delete threadWaitObjectGroup;
476 			return NULL;
477 		}
478 	} else {
479 		// exists -- just add the object
480 		threadWaitObjectGroup->AddWaitObject(threadWaitObject);
481 	}
482 
483 	if (_threadWaitObjectGroup != NULL)
484 		*_threadWaitObjectGroup = threadWaitObjectGroup;
485 
486 	return threadWaitObject;
487 }
488 
489 
490 void
491 Model::Thread::SetIOs(int64 count, nanotime_t time)
492 {
493 	fIOCount = count;
494 	fIOTime = time;
495 }
496 
497 
498 // #pragma mark - SchedulingState
499 
500 
501 Model::SchedulingState::~SchedulingState()
502 {
503 	Clear();
504 }
505 
506 
507 status_t
508 Model::SchedulingState::Init()
509 {
510 	status_t error = fThreadStates.Init();
511 	if (error != B_OK)
512 		return error;
513 
514 	return B_OK;
515 }
516 
517 
518 status_t
519 Model::SchedulingState::Init(const CompactSchedulingState* state)
520 {
521 	status_t error = Init();
522 	if (error != B_OK)
523 		return error;
524 
525 	if (state == NULL)
526 		return B_OK;
527 
528 	fLastEventTime = state->LastEventTime();
529 	for (int32 i = 0; const CompactThreadSchedulingState* compactThreadState
530 			= state->ThreadStateAt(i); i++) {
531 		ThreadSchedulingState* threadState
532 			= new(std::nothrow) ThreadSchedulingState(*compactThreadState);
533 		if (threadState == NULL)
534 			return B_NO_MEMORY;
535 
536 		fThreadStates.Insert(threadState);
537 	}
538 
539 	return B_OK;
540 }
541 
542 
543 void
544 Model::SchedulingState::Clear()
545 {
546 	ThreadSchedulingState* state = fThreadStates.Clear(true);
547 	while (state != NULL) {
548 		ThreadSchedulingState* next = state->next;
549 		DeleteThread(state);
550 		state = next;
551 	}
552 
553 	fLastEventTime = -1;
554 }
555 
556 void
557 Model::SchedulingState::DeleteThread(ThreadSchedulingState* thread)
558 {
559 	delete thread;
560 }
561 
562 
563 // #pragma mark - CompactSchedulingState
564 
565 
566 /*static*/ Model::CompactSchedulingState*
567 Model::CompactSchedulingState::Create(const SchedulingState& state,
568 	off_t eventOffset)
569 {
570 	nanotime_t lastEventTime = state.LastEventTime();
571 
572 	// count the active threads
573 	int32 threadCount = 0;
574 	for (ThreadSchedulingStateTable::Iterator it
575 				= state.ThreadStates().GetIterator();
576 			ThreadSchedulingState* threadState = it.Next();) {
577 		Thread* thread = threadState->thread;
578 		if (thread->CreationTime() <= lastEventTime
579 			&& (thread->DeletionTime() == -1
580 				|| thread->DeletionTime() >= lastEventTime)) {
581 			threadCount++;
582 		}
583 	}
584 
585 	CompactSchedulingState* compactState = (CompactSchedulingState*)malloc(
586 		sizeof(CompactSchedulingState)
587 			+ threadCount * sizeof(CompactThreadSchedulingState));
588 	if (compactState == NULL)
589 		return NULL;
590 
591 	// copy the state info
592 	compactState->fEventOffset = eventOffset;
593 	compactState->fThreadCount = threadCount;
594 	compactState->fLastEventTime = lastEventTime;
595 
596 	int32 threadIndex = 0;
597 	for (ThreadSchedulingStateTable::Iterator it
598 				= state.ThreadStates().GetIterator();
599 			ThreadSchedulingState* threadState = it.Next();) {
600 		Thread* thread = threadState->thread;
601 		if (thread->CreationTime() <= lastEventTime
602 			&& (thread->DeletionTime() == -1
603 				|| thread->DeletionTime() >= lastEventTime)) {
604 			compactState->fThreadStates[threadIndex++] = *threadState;
605 		}
606 	}
607 
608 	return compactState;
609 }
610 
611 
612 void
613 Model::CompactSchedulingState::Delete()
614 {
615 	free(this);
616 }
617 
618 
619 // #pragma mark - Model
620 
621 
622 Model::Model(const char* dataSourceName, void* eventData, size_t eventDataSize,
623 	system_profiler_event_header** events, size_t eventCount)
624 	:
625 	fDataSourceName(dataSourceName),
626 	fEventData(eventData),
627 	fEvents(events),
628 	fEventDataSize(eventDataSize),
629 	fEventCount(eventCount),
630 	fCPUCount(1),
631 	fBaseTime(0),
632 	fLastEventTime(0),
633 	fIdleTime(0),
634 	fCPUs(20, true),
635 	fTeams(20, true),
636 	fThreads(20, true),
637 	fWaitObjectGroups(20, true),
638 	fIOSchedulers(10, true),
639 	fSchedulingStates(100)
640 {
641 }
642 
643 
644 Model::~Model()
645 {
646 	for (int32 i = 0; CompactSchedulingState* state
647 		= fSchedulingStates.ItemAt(i); i++) {
648 		state->Delete();
649 	}
650 
651 	delete[] fEvents;
652 
653 	free(fEventData);
654 
655 	for (int32 i = 0; void* data = fAssociatedData.ItemAt(i); i++)
656 		free(data);
657 }
658 
659 
660 size_t
661 Model::ClosestEventIndex(nanotime_t eventTime) const
662 {
663 	// The events themselves are unmodified and use an absolute time.
664 	eventTime += fBaseTime;
665 
666 	// Binary search the event. Since not all events have a timestamp, we have
667 	// to do a bit of iteration, too.
668 	size_t lower = 0;
669 	size_t upper = CountEvents();
670 	while (lower < upper) {
671 		size_t mid = (lower + upper) / 2;
672 		while (mid < upper) {
673 			system_profiler_event_header* header = fEvents[mid];
674 			switch (header->event) {
675 				case B_SYSTEM_PROFILER_THREAD_SCHEDULED:
676 				case B_SYSTEM_PROFILER_THREAD_ENQUEUED_IN_RUN_QUEUE:
677 				case B_SYSTEM_PROFILER_THREAD_REMOVED_FROM_RUN_QUEUE:
678 					break;
679 				default:
680 					mid++;
681 					continue;
682 			}
683 
684 			break;
685 		}
686 
687 		if (mid == upper) {
688 			lower = mid;
689 			break;
690 		}
691 
692 		system_profiler_thread_scheduling_event* event
693 			= (system_profiler_thread_scheduling_event*)(fEvents[mid] + 1);
694 		if (event->time < eventTime)
695 			lower = mid + 1;
696 		else
697 			upper = mid;
698 	}
699 
700 	return lower;
701 }
702 
703 
704 bool
705 Model::AddAssociatedData(void* data)
706 {
707 	return fAssociatedData.AddItem(data);
708 }
709 
710 
711 void
712 Model::RemoveAssociatedData(void* data)
713 {
714 	fAssociatedData.RemoveItem(data);
715 }
716 
717 
718 void
719 Model::LoadingFinished()
720 {
721 	// set the thread indices
722 	for (int32 i = 0; Thread* thread = fThreads.ItemAt(i); i++)
723 		thread->SetIndex(i);
724 
725 	// compute the total idle time
726 	fIdleTime = 0;
727 	for (int32 i = 0; CPU* cpu = CPUAt(i); i++)
728 		fIdleTime += cpu->IdleTime();
729 }
730 
731 
732 void
733 Model::SetBaseTime(nanotime_t time)
734 {
735 	fBaseTime = time;
736 }
737 
738 
739 void
740 Model::SetLastEventTime(nanotime_t time)
741 {
742 	fLastEventTime = time;
743 }
744 
745 
746 bool
747 Model::SetCPUCount(int32 count)
748 {
749 	fCPUCount = count;
750 
751 	fCPUs.MakeEmpty();
752 
753 	for (int32 i = 0; i < fCPUCount; i++) {
754 		CPU* cpu = new(std::nothrow) CPU;
755 		if (cpu == NULL || !fCPUs.AddItem(cpu)) {
756 			delete cpu;
757 			return false;
758 		}
759 	}
760 
761 	return true;
762 }
763 
764 
765 int32
766 Model::CountTeams() const
767 {
768 		return fTeams.CountItems();
769 }
770 
771 
772 Model::Team*
773 Model::TeamAt(int32 index) const
774 {
775 	return fTeams.ItemAt(index);
776 }
777 
778 
779 Model::Team*
780 Model::TeamByID(team_id id) const
781 {
782 	return fTeams.BinarySearchByKey(id, &Team::CompareWithID);
783 }
784 
785 
786 Model::Team*
787 Model::AddTeam(const system_profiler_team_added* event, nanotime_t time)
788 {
789 	Team* team = TeamByID(event->team);
790 	if (team != NULL) {
791 		fprintf(stderr, "Duplicate team: %" B_PRId32 "\n", event->team);
792 		// TODO: User feedback!
793 		return team;
794 	}
795 
796 	team = new(std::nothrow) Team(event, time);
797 	if (team == NULL)
798 		return NULL;
799 
800 	if (!fTeams.BinaryInsert(team, &Team::CompareByID)) {
801 		delete team;
802 		return NULL;
803 	}
804 
805 	return team;
806 }
807 
808 
809 int32
810 Model::CountThreads() const
811 {
812 	return fThreads.CountItems();
813 }
814 
815 
816 Model::Thread*
817 Model::ThreadAt(int32 index) const
818 {
819 	return fThreads.ItemAt(index);
820 }
821 
822 
823 Model::Thread*
824 Model::ThreadByID(thread_id id) const
825 {
826 	return fThreads.BinarySearchByKey(id, &Thread::CompareWithID);
827 }
828 
829 
830 Model::Thread*
831 Model::AddThread(const system_profiler_thread_added* event, nanotime_t time)
832 {
833 	// check whether we do already know the thread
834 	Thread* thread = ThreadByID(event->thread);
835 	if (thread != NULL) {
836 		fprintf(stderr, "Duplicate thread: %" B_PRId32 "\n", event->thread);
837 		// TODO: User feedback!
838 		return thread;
839 	}
840 
841 	// get its team
842 	Team* team = TeamByID(event->team);
843 	if (team == NULL) {
844 		fprintf(stderr, "No team for thread: %" B_PRId32 "\n", event->thread);
845 		return NULL;
846 	}
847 
848 	// create the thread and add it
849 	thread = new(std::nothrow) Thread(team, event, time);
850 	if (thread == NULL)
851 		return NULL;
852 	ObjectDeleter<Thread> threadDeleter(thread);
853 
854 	if (!fThreads.BinaryInsert(thread, &Thread::CompareByID))
855 		return NULL;
856 
857 	if (!team->AddThread(thread)) {
858 		fThreads.RemoveItem(thread);
859 		return NULL;
860 	}
861 
862 	threadDeleter.Detach();
863 	return thread;
864 }
865 
866 
867 Model::WaitObject*
868 Model::AddWaitObject(const system_profiler_wait_object_info* event,
869 	WaitObjectGroup** _waitObjectGroup)
870 {
871 	// create a wait object
872 	WaitObject* waitObject = new(std::nothrow) WaitObject(event);
873 	if (waitObject == NULL)
874 		return NULL;
875 
876 	// find the wait object group
877 	WaitObjectGroup* waitObjectGroup
878 		= WaitObjectGroupFor(waitObject->Type(), waitObject->Object());
879 	if (waitObjectGroup == NULL) {
880 		// doesn't exist yet -- create
881 		waitObjectGroup = new(std::nothrow) WaitObjectGroup(waitObject);
882 		if (waitObjectGroup == NULL) {
883 			delete waitObject;
884 			return NULL;
885 		}
886 
887 		// add to the list
888 		if (!fWaitObjectGroups.BinaryInsert(waitObjectGroup,
889 				&WaitObjectGroup::CompareByTypeObject)) {
890 			delete waitObjectGroup;
891 			return NULL;
892 		}
893 	} else {
894 		// exists -- just add the object
895 		waitObjectGroup->AddWaitObject(waitObject);
896 	}
897 
898 	if (_waitObjectGroup != NULL)
899 		*_waitObjectGroup = waitObjectGroup;
900 
901 	return waitObject;
902 }
903 
904 
905 int32
906 Model::CountWaitObjectGroups() const
907 {
908 	return fWaitObjectGroups.CountItems();
909 }
910 
911 
912 Model::WaitObjectGroup*
913 Model::WaitObjectGroupAt(int32 index) const
914 {
915 	return fWaitObjectGroups.ItemAt(index);
916 }
917 
918 
919 Model::WaitObjectGroup*
920 Model::WaitObjectGroupFor(uint32 type, addr_t object) const
921 {
922 	type_and_object key;
923 	key.type = type;
924 	key.object = object;
925 
926 	return fWaitObjectGroups.BinarySearchByKey(key,
927 		&WaitObjectGroup::CompareWithTypeObject);
928 }
929 
930 
931 Model::ThreadWaitObject*
932 Model::AddThreadWaitObject(thread_id threadID, WaitObject* waitObject,
933 	ThreadWaitObjectGroup** _threadWaitObjectGroup)
934 {
935 	Thread* thread = ThreadByID(threadID);
936 	if (thread == NULL)
937 		return NULL;
938 
939 	return thread->AddThreadWaitObject(waitObject, _threadWaitObjectGroup);
940 }
941 
942 
943 Model::ThreadWaitObjectGroup*
944 Model::ThreadWaitObjectGroupFor(thread_id threadID, uint32 type, addr_t object) const
945 {
946 	Thread* thread = ThreadByID(threadID);
947 	if (thread == NULL)
948 		return NULL;
949 
950 	return thread->ThreadWaitObjectGroupFor(type, object);
951 }
952 
953 
954 int32
955 Model::CountIOSchedulers() const
956 {
957 	return fIOSchedulers.CountItems();
958 }
959 
960 
961 Model::IOScheduler*
962 Model::IOSchedulerAt(int32 index) const
963 {
964 	return fIOSchedulers.ItemAt(index);
965 }
966 
967 
968 Model::IOScheduler*
969 Model::IOSchedulerByID(int32 id) const
970 {
971 	for (int32 i = 0; IOScheduler* scheduler = fIOSchedulers.ItemAt(i); i++) {
972 		if (scheduler->ID() == id)
973 			return scheduler;
974 	}
975 
976 	return NULL;
977 }
978 
979 
980 Model::IOScheduler*
981 Model::AddIOScheduler(system_profiler_io_scheduler_added* event)
982 {
983 	IOScheduler* scheduler = new(std::nothrow) IOScheduler(event,
984 		fIOSchedulers.CountItems());
985 	if (scheduler == NULL || !fIOSchedulers.AddItem(scheduler)) {
986 		delete scheduler;
987 		return NULL;
988 	}
989 
990 	return scheduler;
991 }
992 
993 
994 bool
995 Model::AddSchedulingStateSnapshot(const SchedulingState& state,
996 	off_t eventOffset)
997 {
998 	CompactSchedulingState* compactState = CompactSchedulingState::Create(state,
999 		eventOffset);
1000 	if (compactState == NULL)
1001 		return false;
1002 
1003 	if (!fSchedulingStates.AddItem(compactState)) {
1004 		compactState->Delete();
1005 		return false;
1006 	}
1007 
1008 	return true;
1009 }
1010 
1011 
1012 const Model::CompactSchedulingState*
1013 Model::ClosestSchedulingState(nanotime_t eventTime) const
1014 {
1015 	int32 index = fSchedulingStates.BinarySearchIndexByKey(eventTime,
1016 		&_CompareEventTimeSchedulingState);
1017 	if (index >= 0)
1018 		return fSchedulingStates.ItemAt(index);
1019 
1020 	// no exact match
1021 	index = -index - 1;
1022 	return index > 0 ? fSchedulingStates.ItemAt(index - 1) : NULL;
1023 }
1024 
1025 
1026 /*static*/ int
1027 Model::_CompareEventTimeSchedulingState(const nanotime_t* time,
1028 	const CompactSchedulingState* state)
1029 {
1030 	if (*time < state->LastEventTime())
1031 		return -1;
1032 	return *time == state->LastEventTime() ? 0 : 1;
1033 }
1034 
1035