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