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