xref: /haiku/src/apps/debuganalyzer/model_loader/ModelLoader.cpp (revision a5a3b2d9a3d95cbae71eaf371708c73a1780ac0d)
1 /*
2  * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "ModelLoader.h"
8 
9 #include <stdio.h>
10 #include <string.h>
11 
12 #include <algorithm>
13 #include <new>
14 
15 #include <AutoDeleter.h>
16 #include <AutoLocker.h>
17 #include <DebugEventStream.h>
18 
19 #include <system_profiler_defs.h>
20 #include <thread_defs.h>
21 
22 #include "DataSource.h"
23 #include "MessageCodes.h"
24 #include "Model.h"
25 
26 
27 // add a scheduling state snapshot every x events
28 static const uint32 kSchedulingSnapshotInterval = 1024;
29 
30 static const uint32 kMaxCPUCount = 1024;
31 
32 
33 struct SimpleWaitObjectInfo : system_profiler_wait_object_info {
34 	SimpleWaitObjectInfo(uint32 type)
35 	{
36 		this->type = type;
37 		object = 0;
38 		referenced_object = 0;
39 		name[0] = '\0';
40 	}
41 };
42 
43 
44 static const SimpleWaitObjectInfo kSnoozeWaitObjectInfo(
45 	THREAD_BLOCK_TYPE_SNOOZE);
46 static const SimpleWaitObjectInfo kSignalWaitObjectInfo(
47 	THREAD_BLOCK_TYPE_SIGNAL);
48 
49 
50 // #pragma mark - CPUInfo
51 
52 
53 struct ModelLoader::CPUInfo {
54 	nanotime_t	idleTime;
55 
56 	CPUInfo()
57 		:
58 		idleTime(0)
59 	{
60 	}
61 };
62 
63 
64 // #pragma mark - IOOperation
65 
66 
67 struct ModelLoader::IOOperation : DoublyLinkedListLinkImpl<IOOperation> {
68 	io_operation_started*	startedEvent;
69 	io_operation_finished*	finishedEvent;
70 
71 	IOOperation(io_operation_started* startedEvent)
72 		:
73 		startedEvent(startedEvent),
74 		finishedEvent(NULL)
75 	{
76 	}
77 };
78 
79 
80 // #pragma mark - IORequest
81 
82 
83 struct ModelLoader::IORequest : DoublyLinkedListLinkImpl<IORequest> {
84 	io_request_scheduled*	scheduledEvent;
85 	io_request_finished*	finishedEvent;
86 	IOOperationList			operations;
87 	size_t					operationCount;
88 	IORequest*				hashNext;
89 
90 	IORequest(io_request_scheduled* scheduledEvent)
91 		:
92 		scheduledEvent(scheduledEvent),
93 		finishedEvent(NULL),
94 		operationCount(0)
95 	{
96 	}
97 
98 	~IORequest()
99 	{
100 		while (IOOperation* operation = operations.RemoveHead())
101 			delete operation;
102 	}
103 
104 	void AddOperation(IOOperation* operation)
105 	{
106 		operations.Add(operation);
107 		operationCount++;
108 	}
109 
110 	IOOperation* FindOperation(void* address) const
111 	{
112 		for (IOOperationList::ConstReverseIterator it
113 				= operations.GetReverseIterator();
114 			IOOperation* operation = it.Next();) {
115 			if (operation->startedEvent->operation == address)
116 				return operation;
117 		}
118 
119 		return NULL;
120 	}
121 
122 	Model::IORequest* CreateModelRequest() const
123 	{
124 		size_t operationCount = operations.Count();
125 
126 		Model::IORequest* modelRequest = Model::IORequest::Create(
127 			scheduledEvent, finishedEvent, operationCount);
128 		if (modelRequest == NULL)
129 			return NULL;
130 
131 		size_t index = 0;
132 		for (IOOperationList::ConstIterator it = operations.GetIterator();
133 				IOOperation* operation = it.Next();) {
134 			Model::IOOperation& modelOperation
135 				= modelRequest->operations[index++];
136 			modelOperation.startedEvent = operation->startedEvent;
137 			modelOperation.finishedEvent = operation->finishedEvent;
138 		}
139 
140 		return modelRequest;
141 	}
142 };
143 
144 
145 // #pragma mark - IORequestHashDefinition
146 
147 
148 struct ModelLoader::IORequestHashDefinition {
149 	typedef void*		KeyType;
150 	typedef	IORequest	ValueType;
151 
152 	size_t HashKey(KeyType key) const
153 		{ return (size_t)key; }
154 
155 	size_t Hash(const IORequest* value) const
156 		{ return HashKey(value->scheduledEvent->request); }
157 
158 	bool Compare(KeyType key, const IORequest* value) const
159 		{ return key == value->scheduledEvent->request; }
160 
161 	IORequest*& GetLink(IORequest* value) const
162 		{ return value->hashNext; }
163 };
164 
165 
166 // #pragma mark - ExtendedThreadSchedulingState
167 
168 
169 struct ModelLoader::ExtendedThreadSchedulingState
170 		: Model::ThreadSchedulingState {
171 
172 	ExtendedThreadSchedulingState(Model::Thread* thread)
173 		:
174 		Model::ThreadSchedulingState(thread),
175 		fEvents(NULL),
176 		fEventIndex(0),
177 		fEventCount(0)
178 	{
179 	}
180 
181 	~ExtendedThreadSchedulingState()
182 	{
183 		delete[] fEvents;
184 
185 		while (IORequest* request = fIORequests.RemoveHead())
186 			delete request;
187 		while (IORequest* request = fPendingIORequests.RemoveHead())
188 			delete request;
189 	}
190 
191 	system_profiler_event_header** Events() const
192 	{
193 		return fEvents;
194 	}
195 
196 	size_t CountEvents() const
197 	{
198 		return fEventCount;
199 	}
200 
201 	system_profiler_event_header** DetachEvents()
202 	{
203 		system_profiler_event_header** events = fEvents;
204 		fEvents = NULL;
205 		return events;
206 	}
207 
208 	void IncrementEventCount()
209 	{
210 		fEventCount++;
211 	}
212 
213 	void AddEvent(system_profiler_event_header* event)
214 	{
215 		fEvents[fEventIndex++] = event;
216 	}
217 
218 	bool AllocateEventArray()
219 	{
220 		if (fEventCount == 0)
221 			return true;
222 
223 		fEvents = new(std::nothrow) system_profiler_event_header*[fEventCount];
224 		if (fEvents == NULL)
225 			return false;
226 
227 		return true;
228 	}
229 
230 	void AddIORequest(IORequest* request)
231 	{
232 		fPendingIORequests.Add(request);
233 	}
234 
235 	void IORequestFinished(IORequest* request)
236 	{
237 		fPendingIORequests.Remove(request);
238 		fIORequests.Add(request);
239 	}
240 
241 	bool PrepareThreadIORequests(Model::IORequest**& _requests,
242 		size_t& _requestCount)
243 	{
244 		fIORequests.MoveFrom(&fPendingIORequests);
245 		size_t requestCount = fIORequests.Count();
246 
247 		if (requestCount == 0) {
248 			_requests = NULL;
249 			_requestCount = 0;
250 			return true;
251 		}
252 
253 		Model::IORequest** requests
254 			= new(std::nothrow) Model::IORequest*[requestCount];
255 		if (requests == NULL)
256 			return false;
257 
258 		size_t index = 0;
259 		while (IORequest* request = fIORequests.RemoveHead()) {
260 			ObjectDeleter<IORequest> requestDeleter(request);
261 
262 			Model::IORequest* modelRequest = request->CreateModelRequest();
263 			if (modelRequest == NULL) {
264 				for (size_t i = 0; i < index; i++)
265 					requests[i]->Delete();
266 				return false;
267 			}
268 
269 			requests[index++] = modelRequest;
270 		}
271 
272 		_requests = requests;
273 		_requestCount = requestCount;
274 		return true;
275 	}
276 
277 private:
278 	system_profiler_event_header**	fEvents;
279 	size_t							fEventIndex;
280 	size_t							fEventCount;
281 	IORequestList					fIORequests;
282 	IORequestList					fPendingIORequests;
283 };
284 
285 
286 // #pragma mark - ExtendedSchedulingState
287 
288 
289 struct ModelLoader::ExtendedSchedulingState : Model::SchedulingState {
290 	inline ExtendedThreadSchedulingState* LookupThread(thread_id threadID) const
291 	{
292 		Model::ThreadSchedulingState* thread
293 			= Model::SchedulingState::LookupThread(threadID);
294 		return thread != NULL
295 			? static_cast<ExtendedThreadSchedulingState*>(thread) : NULL;
296 	}
297 
298 
299 protected:
300 	virtual void DeleteThread(Model::ThreadSchedulingState* thread)
301 	{
302 		delete static_cast<ExtendedThreadSchedulingState*>(thread);
303 	}
304 };
305 
306 
307 // #pragma mark - ModelLoader
308 
309 
310 inline void
311 ModelLoader::_UpdateLastEventTime(nanotime_t time)
312 {
313 	if (fBaseTime < 0) {
314 		fBaseTime = time;
315 		fModel->SetBaseTime(time);
316 	}
317 
318 	fState->SetLastEventTime(time - fBaseTime);
319 }
320 
321 
322 ModelLoader::ModelLoader(DataSource* dataSource,
323 	const BMessenger& target, void* targetCookie)
324 	:
325 	AbstractModelLoader(target, targetCookie),
326 	fModel(NULL),
327 	fDataSource(dataSource),
328 	fCPUInfos(NULL),
329 	fState(NULL),
330 	fIORequests(NULL)
331 {
332 }
333 
334 
335 ModelLoader::~ModelLoader()
336 {
337 	delete[] fCPUInfos;
338 	delete fDataSource;
339 	delete fModel;
340 	delete fState;
341 	delete fIORequests;
342 }
343 
344 
345 Model*
346 ModelLoader::DetachModel()
347 {
348 	AutoLocker<BLocker> locker(fLock);
349 
350 	if (fModel == NULL || fLoading)
351 		return NULL;
352 
353 	Model* model = fModel;
354 	fModel = NULL;
355 
356 	return model;
357 }
358 
359 
360 status_t
361 ModelLoader::PrepareForLoading()
362 {
363 	if (fModel != NULL || fDataSource == NULL)
364 		return B_BAD_VALUE;
365 
366 	// create and init the state
367 	fState = new(std::nothrow) ExtendedSchedulingState;
368 	if (fState == NULL)
369 		return B_NO_MEMORY;
370 
371 	status_t error = fState->Init();
372 	if (error != B_OK)
373 		return error;
374 
375 	// create CPU info array
376 	fCPUInfos = new(std::nothrow) CPUInfo[kMaxCPUCount];
377 	if (fCPUInfos == NULL)
378 		return B_NO_MEMORY;
379 
380 	// create IORequest hash table
381 	fIORequests = new(std::nothrow) IORequestTable;
382 	if (fIORequests == NULL || fIORequests->Init() != B_OK)
383 		return B_NO_MEMORY;
384 
385 	return B_OK;
386 }
387 
388 
389 status_t
390 ModelLoader::Load()
391 {
392 	try {
393 		return _Load();
394 	} catch(...) {
395 		return B_ERROR;
396 	}
397 }
398 
399 
400 void
401 ModelLoader::FinishLoading(bool success)
402 {
403 	delete fState;
404 	fState = NULL;
405 
406 	if (!success) {
407 		delete fModel;
408 		fModel = NULL;
409 	}
410 
411 	delete[] fCPUInfos;
412 	fCPUInfos = NULL;
413 
414 	delete fIORequests;
415 	fIORequests = NULL;
416 }
417 
418 
419 status_t
420 ModelLoader::_Load()
421 {
422 	// read the complete data into memory
423 	void* eventData;
424 	size_t eventDataSize;
425 	status_t error = _ReadDebugEvents(&eventData, &eventDataSize);
426 	if (error != B_OK)
427 		return error;
428 	MemoryDeleter eventDataDeleter(eventData);
429 
430 	// create a debug event array
431 	system_profiler_event_header** events;
432 	size_t eventCount;
433 	error = _CreateDebugEventArray(eventData, eventDataSize, events,
434 		eventCount);
435 	if (error != B_OK)
436 		return error;
437 	ArrayDeleter<system_profiler_event_header*> eventsDeleter(events);
438 
439 	// get the data source name
440 	BString dataSourceName;
441 	fDataSource->GetName(dataSourceName);
442 
443 	// create a model
444 	fModel = new(std::nothrow) Model(dataSourceName.String(), eventData,
445 		eventDataSize, events, eventCount);
446 	if (fModel == NULL)
447 		return B_NO_MEMORY;
448 	eventDataDeleter.Detach();
449 	eventsDeleter.Detach();
450 
451 	// create a debug input stream
452 	BDebugEventInputStream input;
453 	error = input.SetTo(eventData, eventDataSize, false);
454 	if (error != B_OK)
455 		return error;
456 
457 	// add the snooze and signal wait objects to the model
458 	if (fModel->AddWaitObject(&kSnoozeWaitObjectInfo, NULL) == NULL
459 		|| fModel->AddWaitObject(&kSignalWaitObjectInfo, NULL) == NULL) {
460 		return B_NO_MEMORY;
461 	}
462 
463 	// process the events
464 	fMaxCPUIndex = 0;
465 	fState->Clear();
466 	fBaseTime = -1;
467 	uint64 count = 0;
468 
469 	while (true) {
470 		// get next event
471 		uint32 event;
472 		uint32 cpu;
473 		const void* buffer;
474 		off_t offset;
475 		ssize_t bufferSize = input.ReadNextEvent(&event, &cpu, &buffer,
476 			&offset);
477 		if (bufferSize < 0)
478 			return bufferSize;
479 		if (buffer == NULL)
480 			break;
481 
482 		// process the event
483 		status_t error = _ProcessEvent(event, cpu, buffer, bufferSize);
484 		if (error != B_OK)
485 			return error;
486 
487 		if (cpu > fMaxCPUIndex) {
488 			if (cpu + 1 > kMaxCPUCount)
489 				return B_BAD_DATA;
490 			fMaxCPUIndex = cpu;
491 		}
492 
493 		// periodically check whether we're supposed to abort
494 		if (++count % 32 == 0) {
495 			AutoLocker<BLocker> locker(fLock);
496 			if (fAborted)
497 				return B_ERROR;
498 		}
499 
500 		// periodically add scheduling snapshots
501 		if (count % kSchedulingSnapshotInterval == 0)
502 			fModel->AddSchedulingStateSnapshot(*fState, offset);
503 	}
504 
505 	if (!fModel->SetCPUCount(fMaxCPUIndex + 1))
506 		return B_NO_MEMORY;
507 
508 	for (uint32 i = 0; i <= fMaxCPUIndex; i++)
509 		fModel->CPUAt(i)->SetIdleTime(fCPUInfos[i].idleTime);
510 
511 	fModel->SetLastEventTime(fState->LastEventTime());
512 
513 	if (!_SetThreadEvents() || !_SetThreadIORequests())
514 		return B_NO_MEMORY;
515 
516 	fModel->LoadingFinished();
517 
518 	return B_OK;
519 }
520 
521 
522 status_t
523 ModelLoader::_ReadDebugEvents(void** _eventData, size_t* _size)
524 {
525 	// get a BDataIO from the data source
526 	BDataIO* io;
527 	status_t error = fDataSource->CreateDataIO(&io);
528 	if (error != B_OK)
529 		return error;
530 	ObjectDeleter<BDataIO> dataIOtDeleter(io);
531 
532 	// First we need to find out how large a buffer to allocate.
533 	size_t size;
534 
535 	if (BPositionIO* positionIO = dynamic_cast<BPositionIO*>(io)) {
536 		// it's a BPositionIO -- this makes things easier, since we know how
537 		// many bytes to read
538 		off_t currentPos = positionIO->Position();
539 		if (currentPos < 0)
540 			return currentPos;
541 
542 		off_t fileSize;
543 		error = positionIO->GetSize(&fileSize);
544 		if (error != B_OK)
545 			return error;
546 
547 		size = fileSize - currentPos;
548 	} else {
549 		// no BPositionIO -- we need to determine the total size by iteratively
550 		// reading the whole data one time
551 
552 		// allocate a dummy buffer for reading
553 		const size_t kBufferSize = 1024 * 1024;
554 		void* buffer = malloc(kBufferSize);
555 		if (buffer == NULL)
556 			return B_NO_MEMORY;
557 		MemoryDeleter bufferDeleter(buffer);
558 
559 		size = 0;
560 		while (true) {
561 			ssize_t bytesRead = io->Read(buffer, kBufferSize);
562 			if (bytesRead < 0)
563 				return bytesRead;
564 			if (bytesRead == 0)
565 				break;
566 
567 			size += bytesRead;
568 		}
569 
570 		// we've got the size -- recreate the BDataIO
571 		dataIOtDeleter.Delete();
572 		error = fDataSource->CreateDataIO(&io);
573 		if (error != B_OK)
574 			return error;
575 		dataIOtDeleter.SetTo(io);
576 	}
577 
578 	// allocate the data buffer
579 	void* data = malloc(size);
580 	if (data == NULL)
581 		return B_NO_MEMORY;
582 	MemoryDeleter dataDeleter(data);
583 
584 	// read the data
585 	ssize_t bytesRead = io->Read(data, size);
586 	if (bytesRead < 0)
587 		return bytesRead;
588 	if ((size_t)bytesRead != size)
589 		return B_FILE_ERROR;
590 
591 	dataDeleter.Detach();
592 	*_eventData = data;
593 	*_size = size;
594 	return B_OK;
595 }
596 
597 
598 status_t
599 ModelLoader::_CreateDebugEventArray(void* eventData, size_t eventDataSize,
600 	system_profiler_event_header**& _events, size_t& _eventCount)
601 {
602 	// count the events
603 	BDebugEventInputStream input;
604 	status_t error = input.SetTo(eventData, eventDataSize, false);
605 	if (error != B_OK)
606 		return error;
607 
608 	size_t eventCount = 0;
609 	while (true) {
610 		// get next event
611 		uint32 event;
612 		uint32 cpu;
613 		const void* buffer;
614 		ssize_t bufferSize = input.ReadNextEvent(&event, &cpu, &buffer, NULL);
615 		if (bufferSize < 0)
616 			return bufferSize;
617 		if (buffer == NULL)
618 			break;
619 
620 		eventCount++;
621 	}
622 
623 	// create the array
624 	system_profiler_event_header** events = new(std::nothrow)
625 		system_profiler_event_header*[eventCount];
626 	if (events == NULL)
627 		return B_NO_MEMORY;
628 
629 	// populate the array
630 	error = input.SetTo(eventData, eventDataSize, false);
631 	if (error != B_OK) {
632 		delete[] events;
633 		return error;
634 	}
635 
636 	size_t eventIndex = 0;
637 	while (true) {
638 		// get next event
639 		uint32 event;
640 		uint32 cpu;
641 		const void* buffer;
642 		off_t offset;
643 		input.ReadNextEvent(&event, &cpu, &buffer, &offset);
644 		if (buffer == NULL)
645 			break;
646 
647 		events[eventIndex++]
648 			= (system_profiler_event_header*)((uint8*)eventData + offset);
649 	}
650 
651 	_events = events;
652 	_eventCount = eventCount;
653 	return B_OK;
654 }
655 
656 
657 status_t
658 ModelLoader::_ProcessEvent(uint32 event, uint32 cpu, const void* buffer,
659 	size_t size)
660 {
661 	switch (event) {
662 		case B_SYSTEM_PROFILER_TEAM_ADDED:
663 			_HandleTeamAdded((system_profiler_team_added*)buffer);
664 			break;
665 
666 		case B_SYSTEM_PROFILER_TEAM_REMOVED:
667 			_HandleTeamRemoved((system_profiler_team_removed*)buffer);
668 			break;
669 
670 		case B_SYSTEM_PROFILER_TEAM_EXEC:
671 			_HandleTeamExec((system_profiler_team_exec*)buffer);
672 			break;
673 
674 		case B_SYSTEM_PROFILER_THREAD_ADDED:
675 			_HandleThreadAdded((system_profiler_thread_added*)buffer);
676 			break;
677 
678 		case B_SYSTEM_PROFILER_THREAD_REMOVED:
679 			_HandleThreadRemoved((system_profiler_thread_removed*)buffer);
680 			break;
681 
682 		case B_SYSTEM_PROFILER_THREAD_SCHEDULED:
683 			_HandleThreadScheduled(cpu,
684 				(system_profiler_thread_scheduled*)buffer);
685 			break;
686 
687 		case B_SYSTEM_PROFILER_THREAD_ENQUEUED_IN_RUN_QUEUE:
688 			_HandleThreadEnqueuedInRunQueue(
689 				(thread_enqueued_in_run_queue*)buffer);
690 			break;
691 
692 		case B_SYSTEM_PROFILER_THREAD_REMOVED_FROM_RUN_QUEUE:
693 			_HandleThreadRemovedFromRunQueue(cpu,
694 				(thread_removed_from_run_queue*)buffer);
695 			break;
696 
697 		case B_SYSTEM_PROFILER_WAIT_OBJECT_INFO:
698 			_HandleWaitObjectInfo((system_profiler_wait_object_info*)buffer);
699 			break;
700 
701 		case B_SYSTEM_PROFILER_IO_SCHEDULER_ADDED:
702 			_HandleIOSchedulerAdded(
703 				(system_profiler_io_scheduler_added*)buffer);
704 			break;
705 
706 		case B_SYSTEM_PROFILER_IO_SCHEDULER_REMOVED:
707 			// not so interesting
708 			break;
709 
710 		case B_SYSTEM_PROFILER_IO_REQUEST_SCHEDULED:
711 			_HandleIORequestScheduled((io_request_scheduled*)buffer);
712 			break;
713 		case B_SYSTEM_PROFILER_IO_REQUEST_FINISHED:
714 			_HandleIORequestFinished((io_request_finished*)buffer);
715 			break;
716 		case B_SYSTEM_PROFILER_IO_OPERATION_STARTED:
717 			_HandleIOOperationStarted((io_operation_started*)buffer);
718 			break;
719 		case B_SYSTEM_PROFILER_IO_OPERATION_FINISHED:
720 			_HandleIOOperationFinished((io_operation_finished*)buffer);
721 			break;
722 
723 		default:
724 			printf("unsupported event type %" B_PRIu32 ", size: %" B_PRIuSIZE
725 				"\n", event, size);
726 			return B_BAD_DATA;
727 	}
728 
729 	return B_OK;
730 }
731 
732 
733 bool
734 ModelLoader::_SetThreadEvents()
735 {
736 	// allocate the threads' events arrays
737 	for (int32 i = 0; Model::Thread* thread = fModel->ThreadAt(i); i++) {
738 		ExtendedThreadSchedulingState* state
739 			= fState->LookupThread(thread->ID());
740 		if (!state->AllocateEventArray())
741 			return false;
742 	}
743 
744 	// fill the threads' event arrays
745 	system_profiler_event_header** events = fModel->Events();
746 	size_t eventCount = fModel->CountEvents();
747 	for (size_t i = 0; i < eventCount; i++) {
748 		system_profiler_event_header* header = events[i];
749 		void* buffer = header + 1;
750 
751 		switch (header->event) {
752 			case B_SYSTEM_PROFILER_THREAD_ADDED:
753 			{
754 				system_profiler_thread_added* event
755 					= (system_profiler_thread_added*)buffer;
756 				fState->LookupThread(event->thread)->AddEvent(header);
757 				break;
758 			}
759 
760 			case B_SYSTEM_PROFILER_THREAD_REMOVED:
761 			{
762 				system_profiler_thread_removed* event
763 					= (system_profiler_thread_removed*)buffer;
764 				fState->LookupThread(event->thread)->AddEvent(header);
765 				break;
766 			}
767 
768 			case B_SYSTEM_PROFILER_THREAD_SCHEDULED:
769 			{
770 				system_profiler_thread_scheduled* event
771 					= (system_profiler_thread_scheduled*)buffer;
772 				fState->LookupThread(event->thread)->AddEvent(header);
773 
774 				if (event->thread != event->previous_thread) {
775 					fState->LookupThread(event->previous_thread)
776 						->AddEvent(header);
777 				}
778 				break;
779 			}
780 
781 			case B_SYSTEM_PROFILER_THREAD_ENQUEUED_IN_RUN_QUEUE:
782 			{
783 				thread_enqueued_in_run_queue* event
784 					= (thread_enqueued_in_run_queue*)buffer;
785 				fState->LookupThread(event->thread)->AddEvent(header);
786 				break;
787 			}
788 
789 			case B_SYSTEM_PROFILER_THREAD_REMOVED_FROM_RUN_QUEUE:
790 			{
791 				thread_removed_from_run_queue* event
792 					= (thread_removed_from_run_queue*)buffer;
793 				fState->LookupThread(event->thread)->AddEvent(header);
794 				break;
795 			}
796 
797 			default:
798 				break;
799 		}
800 	}
801 
802 	// transfer the events arrays from the scheduling states to the thread
803 	// objects
804 	for (int32 i = 0; Model::Thread* thread = fModel->ThreadAt(i); i++) {
805 		ExtendedThreadSchedulingState* state
806 			= fState->LookupThread(thread->ID());
807 		thread->SetEvents(state->Events(), state->CountEvents());
808 		state->DetachEvents();
809 	}
810 
811 	return true;
812 }
813 
814 
815 bool
816 ModelLoader::_SetThreadIORequests()
817 {
818 	for (int32 i = 0; Model::Thread* thread = fModel->ThreadAt(i); i++) {
819 		ExtendedThreadSchedulingState* state
820 			= fState->LookupThread(thread->ID());
821 		Model::IORequest** requests;
822 		size_t requestCount;
823 		if (!state->PrepareThreadIORequests(requests, requestCount))
824 			return false;
825 		if (requestCount > 0)
826 			_SetThreadIORequests(thread, requests, requestCount);
827 	}
828 
829 	return true;
830 }
831 
832 
833 void
834 ModelLoader::_SetThreadIORequests(Model::Thread* thread,
835 	Model::IORequest** requests, size_t requestCount)
836 {
837 	// compute some totals
838 	int64 ioCount = 0;
839 	nanotime_t ioTime = 0;
840 
841 	// sort requests by scheduler and start time
842 	std::sort(requests, requests + requestCount,
843 		Model::IORequest::SchedulerTimeLess);
844 
845 	nanotime_t endTime = fBaseTime + fModel->LastEventTime();
846 
847 	// compute the summed up I/O times
848 	nanotime_t ioStart = requests[0]->scheduledEvent->time;
849 	nanotime_t previousEnd = requests[0]->finishedEvent != NULL
850 		? requests[0]->finishedEvent->time : endTime;
851 	int32 scheduler = requests[0]->scheduledEvent->scheduler;
852 
853 	for (size_t i = 1; i < requestCount; i++) {
854 		system_profiler_io_request_scheduled* scheduledEvent
855 			= requests[i]->scheduledEvent;
856 		if (scheduledEvent->scheduler != scheduler
857 			|| scheduledEvent->time >= previousEnd) {
858 			ioCount++;
859 			ioTime += previousEnd - ioStart;
860 			ioStart = scheduledEvent->time;
861 		}
862 
863 		previousEnd = requests[i]->finishedEvent != NULL
864 			? requests[i]->finishedEvent->time : endTime;
865 	}
866 
867 	ioCount++;
868 	ioTime += previousEnd - ioStart;
869 
870 	// sort requests by start time
871 	std::sort(requests, requests + requestCount, Model::IORequest::TimeLess);
872 
873 	// set the computed values
874 	thread->SetIORequests(requests, requestCount);
875 	thread->SetIOs(ioCount, ioTime);
876 }
877 
878 
879 void
880 ModelLoader::_HandleTeamAdded(system_profiler_team_added* event)
881 {
882 	if (fModel->AddTeam(event, fState->LastEventTime()) == NULL)
883 		throw std::bad_alloc();
884 }
885 
886 
887 void
888 ModelLoader::_HandleTeamRemoved(system_profiler_team_removed* event)
889 {
890 	if (Model::Team* team = fModel->TeamByID(event->team))
891 		team->SetDeletionTime(fState->LastEventTime());
892 	else {
893 		printf("Warning: Removed event for unknown team: %" B_PRId32 "\n",
894 			event->team);
895 	}
896 }
897 
898 
899 void
900 ModelLoader::_HandleTeamExec(system_profiler_team_exec* event)
901 {
902 	// TODO:...
903 }
904 
905 
906 void
907 ModelLoader::_HandleThreadAdded(system_profiler_thread_added* event)
908 {
909 	_AddThread(event)->IncrementEventCount();
910 }
911 
912 
913 void
914 ModelLoader::_HandleThreadRemoved(system_profiler_thread_removed* event)
915 {
916 	ExtendedThreadSchedulingState* thread = fState->LookupThread(event->thread);
917 	if (thread == NULL) {
918 		printf("Warning: Removed event for unknown thread: %" B_PRId32 "\n",
919 			event->thread);
920 		thread = _AddUnknownThread(event->thread);
921 	}
922 
923 	thread->thread->SetDeletionTime(fState->LastEventTime());
924 	thread->IncrementEventCount();
925 }
926 
927 
928 void
929 ModelLoader::_HandleThreadScheduled(uint32 cpu,
930 	system_profiler_thread_scheduled* event)
931 {
932 	_UpdateLastEventTime(event->time);
933 
934 	ExtendedThreadSchedulingState* thread = fState->LookupThread(event->thread);
935 	if (thread == NULL) {
936 		printf("Warning: Schedule event for unknown thread: %" B_PRId32 "\n",
937 			event->thread);
938 		thread = _AddUnknownThread(event->thread);
939 		return;
940 	}
941 
942 	nanotime_t diffTime = fState->LastEventTime() - thread->lastTime;
943 
944 	if (thread->state == READY) {
945 		// thread scheduled after having been woken up
946 		thread->thread->AddLatency(diffTime);
947 	} else if (thread->state == PREEMPTED) {
948 		// thread scheduled after having been preempted before
949 		thread->thread->AddRerun(diffTime);
950 	}
951 
952 	if (thread->state == STILL_RUNNING) {
953 		// Thread was running and continues to run.
954 		thread->state = RUNNING;
955 	}
956 
957 	if (thread->state != RUNNING) {
958 		thread->lastTime = fState->LastEventTime();
959 		thread->state = RUNNING;
960 	}
961 
962 	thread->IncrementEventCount();
963 
964 	// unscheduled thread
965 
966 	if (event->thread == event->previous_thread)
967 		return;
968 
969 	thread = fState->LookupThread(event->previous_thread);
970 	if (thread == NULL) {
971 		printf("Warning: Schedule event for unknown previous thread: %" B_PRId32
972 			"\n", event->previous_thread);
973 		thread = _AddUnknownThread(event->previous_thread);
974 	}
975 
976 	diffTime = fState->LastEventTime() - thread->lastTime;
977 
978 	if (thread->state == STILL_RUNNING) {
979 		// thread preempted
980 		thread->thread->AddPreemption(diffTime);
981 		thread->thread->AddRun(diffTime);
982 		if (thread->priority == 0)
983 			_AddIdleTime(cpu, diffTime);
984 
985 		thread->lastTime = fState->LastEventTime();
986 		thread->state = PREEMPTED;
987 	} else if (thread->state == RUNNING) {
988 		// thread starts waiting (it hadn't been added to the run
989 		// queue before being unscheduled)
990 		thread->thread->AddRun(diffTime);
991 		if (thread->priority == 0)
992 			_AddIdleTime(cpu, diffTime);
993 
994 		if (event->previous_thread_state == B_THREAD_WAITING) {
995 			addr_t waitObject = event->previous_thread_wait_object;
996 			switch (event->previous_thread_wait_object_type) {
997 				case THREAD_BLOCK_TYPE_SNOOZE:
998 				case THREAD_BLOCK_TYPE_SIGNAL:
999 					waitObject = 0;
1000 					break;
1001 				case THREAD_BLOCK_TYPE_SEMAPHORE:
1002 				case THREAD_BLOCK_TYPE_CONDITION_VARIABLE:
1003 				case THREAD_BLOCK_TYPE_MUTEX:
1004 				case THREAD_BLOCK_TYPE_RW_LOCK:
1005 				case THREAD_BLOCK_TYPE_OTHER:
1006 				default:
1007 					break;
1008 			}
1009 
1010 			_AddThreadWaitObject(thread,
1011 				event->previous_thread_wait_object_type, waitObject);
1012 		}
1013 
1014 		thread->lastTime = fState->LastEventTime();
1015 		thread->state = WAITING;
1016 	} else if (thread->state == UNKNOWN) {
1017 		uint32 threadState = event->previous_thread_state;
1018 		if (threadState == B_THREAD_WAITING
1019 			|| threadState == B_THREAD_SUSPENDED) {
1020 			thread->lastTime = fState->LastEventTime();
1021 			thread->state = WAITING;
1022 		} else if (threadState == B_THREAD_READY) {
1023 			thread->lastTime = fState->LastEventTime();
1024 			thread->state = PREEMPTED;
1025 		}
1026 	}
1027 
1028 	thread->IncrementEventCount();
1029 }
1030 
1031 
1032 void
1033 ModelLoader::_HandleThreadEnqueuedInRunQueue(
1034 	thread_enqueued_in_run_queue* event)
1035 {
1036 	_UpdateLastEventTime(event->time);
1037 
1038 	ExtendedThreadSchedulingState* thread = fState->LookupThread(event->thread);
1039 	if (thread == NULL) {
1040 		printf("Warning: Enqueued in run queue event for unknown thread: %"
1041 			B_PRId32 "\n", event->thread);
1042 		thread = _AddUnknownThread(event->thread);
1043 	}
1044 
1045 	if (thread->state == RUNNING || thread->state == STILL_RUNNING) {
1046 		// Thread was running and is reentered into the run queue. This
1047 		// is done by the scheduler, if the thread remains ready.
1048 		thread->state = STILL_RUNNING;
1049 	} else {
1050 		// Thread was waiting and is ready now.
1051 		nanotime_t diffTime = fState->LastEventTime() - thread->lastTime;
1052 		if (thread->waitObject != NULL) {
1053 			thread->waitObject->AddWait(diffTime);
1054 			thread->waitObject = NULL;
1055 			thread->thread->AddWait(diffTime);
1056 		} else if (thread->state != UNKNOWN)
1057 			thread->thread->AddUnspecifiedWait(diffTime);
1058 
1059 		thread->lastTime = fState->LastEventTime();
1060 		thread->state = READY;
1061 	}
1062 
1063 	thread->priority = event->priority;
1064 
1065 	thread->IncrementEventCount();
1066 }
1067 
1068 
1069 void
1070 ModelLoader::_HandleThreadRemovedFromRunQueue(uint32 cpu,
1071 	thread_removed_from_run_queue* event)
1072 {
1073 	_UpdateLastEventTime(event->time);
1074 
1075 	ExtendedThreadSchedulingState* thread = fState->LookupThread(event->thread);
1076 	if (thread == NULL) {
1077 		printf("Warning: Removed from run queue event for unknown thread: "
1078 			"%" B_PRId32 "\n", event->thread);
1079 		thread = _AddUnknownThread(event->thread);
1080 	}
1081 
1082 	// This really only happens when the thread priority is changed
1083 	// while the thread is ready.
1084 
1085 	nanotime_t diffTime = fState->LastEventTime() - thread->lastTime;
1086 	if (thread->state == RUNNING) {
1087 		// This should never happen.
1088 		thread->thread->AddRun(diffTime);
1089 		if (thread->priority == 0)
1090 			_AddIdleTime(cpu, diffTime);
1091 	} else if (thread->state == READY || thread->state == PREEMPTED) {
1092 		// Not really correct, but the case is rare and we keep it
1093 		// simple.
1094 		thread->thread->AddUnspecifiedWait(diffTime);
1095 	}
1096 
1097 	thread->lastTime = fState->LastEventTime();
1098 	thread->state = WAITING;
1099 
1100 	thread->IncrementEventCount();
1101 }
1102 
1103 
1104 void
1105 ModelLoader::_HandleWaitObjectInfo(system_profiler_wait_object_info* event)
1106 {
1107 	if (fModel->AddWaitObject(event, NULL) == NULL)
1108 		throw std::bad_alloc();
1109 }
1110 
1111 
1112 void
1113 ModelLoader::_HandleIOSchedulerAdded(system_profiler_io_scheduler_added* event)
1114 {
1115 	Model::IOScheduler* scheduler = fModel->IOSchedulerByID(event->scheduler);
1116 	if (scheduler != NULL) {
1117 		printf("Warning: Duplicate added event for I/O scheduler %" B_PRId32
1118 			"\n", event->scheduler);
1119 		return;
1120 	}
1121 
1122 	if (fModel->AddIOScheduler(event) == NULL)
1123 		throw std::bad_alloc();
1124 }
1125 
1126 
1127 void
1128 ModelLoader::_HandleIORequestScheduled(io_request_scheduled* event)
1129 {
1130 	IORequest* request = fIORequests->Lookup(event->request);
1131 	if (request != NULL) {
1132 		printf("Warning: Duplicate schedule event for I/O request %p\n",
1133 			event->request);
1134 		return;
1135 	}
1136 
1137 	ExtendedThreadSchedulingState* thread = fState->LookupThread(event->thread);
1138 	if (thread == NULL) {
1139 		printf("Warning: I/O request for unknown thread %" B_PRId32 "\n",
1140 			event->thread);
1141 		thread = _AddUnknownThread(event->thread);
1142 	}
1143 
1144 	if (fModel->IOSchedulerByID(event->scheduler) == NULL) {
1145 		printf("Warning: I/O requests for unknown scheduler %" B_PRId32 "\n",
1146 			event->scheduler);
1147 		// TODO: Add state for unknown scheduler, as we do for threads.
1148 		return;
1149 	}
1150 
1151 	request = new(std::nothrow) IORequest(event);
1152 	if (request == NULL)
1153 		throw std::bad_alloc();
1154 
1155 	fIORequests->Insert(request);
1156 	thread->AddIORequest(request);
1157 }
1158 
1159 
1160 void
1161 ModelLoader::_HandleIORequestFinished(io_request_finished* event)
1162 {
1163 	IORequest* request = fIORequests->Lookup(event->request);
1164 	if (request == NULL)
1165 		return;
1166 
1167 	request->finishedEvent = event;
1168 
1169 	fIORequests->Remove(request);
1170 	fState->LookupThread(request->scheduledEvent->thread)
1171 		->IORequestFinished(request);
1172 }
1173 
1174 
1175 void
1176 ModelLoader::_HandleIOOperationStarted(io_operation_started* event)
1177 {
1178 	IORequest* request = fIORequests->Lookup(event->request);
1179 	if (request == NULL) {
1180 		printf("Warning: I/O request for operation %p not found\n",
1181 			event->operation);
1182 		return;
1183 	}
1184 
1185 	IOOperation* operation = new(std::nothrow) IOOperation(event);
1186 	if (operation == NULL)
1187 		throw std::bad_alloc();
1188 
1189 	request->AddOperation(operation);
1190 }
1191 
1192 
1193 void
1194 ModelLoader::_HandleIOOperationFinished(io_operation_finished* event)
1195 {
1196 	IORequest* request = fIORequests->Lookup(event->request);
1197 	if (request == NULL) {
1198 		printf("Warning: I/O request for operation %p not found\n",
1199 			event->operation);
1200 		return;
1201 	}
1202 
1203 	IOOperation* operation = request->FindOperation(event->operation);
1204 	if (operation == NULL) {
1205 		printf("Warning: operation %p not found\n", event->operation);
1206 		return;
1207 	}
1208 
1209 	operation->finishedEvent = event;
1210 }
1211 
1212 
1213 ModelLoader::ExtendedThreadSchedulingState*
1214 ModelLoader::_AddThread(system_profiler_thread_added* event)
1215 {
1216 	// do we know the thread already?
1217 	ExtendedThreadSchedulingState* info = fState->LookupThread(event->thread);
1218 	if (info != NULL) {
1219 		printf("Warning: Duplicate thread added event for thread %" B_PRId32
1220 			"\n", event->thread);
1221 		return info;
1222 	}
1223 
1224 	// add the thread to the model
1225 	Model::Thread* thread = fModel->AddThread(event, fState->LastEventTime());
1226 	if (thread == NULL)
1227 		throw std::bad_alloc();
1228 
1229 	// create and add a ThreadSchedulingState
1230 	info = new(std::nothrow) ExtendedThreadSchedulingState(thread);
1231 	if (info == NULL)
1232 		throw std::bad_alloc();
1233 
1234 	// TODO: The priority is missing from the system_profiler_thread_added
1235 	// struct. For now guess at least whether this is an idle thread.
1236 	if (strncmp(event->name, "idle thread", strlen("idle thread")) == 0)
1237 		info->priority = 0;
1238 	else
1239 		info->priority = B_NORMAL_PRIORITY;
1240 
1241 	fState->InsertThread(info);
1242 
1243 	return info;
1244 }
1245 
1246 
1247 ModelLoader::ExtendedThreadSchedulingState*
1248 ModelLoader::_AddUnknownThread(thread_id threadID)
1249 {
1250 	// create a dummy "add thread" event
1251 	system_profiler_thread_added* event = (system_profiler_thread_added*)
1252 		malloc(sizeof(system_profiler_thread_added));
1253 	if (event == NULL)
1254 		throw std::bad_alloc();
1255 
1256 	if (!fModel->AddAssociatedData(event)) {
1257 		free(event);
1258 		throw std::bad_alloc();
1259 	}
1260 
1261 	try {
1262 		event->team = _AddUnknownTeam()->ID();
1263 		event->thread = threadID;
1264 		snprintf(event->name, sizeof(event->name), "unknown thread %" B_PRId32,
1265 			threadID);
1266 
1267 		// add the thread to the model
1268 		ExtendedThreadSchedulingState* state = _AddThread(event);
1269 		return state;
1270 	} catch (...) {
1271 		throw;
1272 	}
1273 }
1274 
1275 Model::Team*
1276 ModelLoader::_AddUnknownTeam()
1277 {
1278 	team_id teamID = 0;
1279 	Model::Team* team = fModel->TeamByID(teamID);
1280 	if (team != NULL)
1281 		return team;
1282 
1283 	// create a dummy "add team" event
1284 	static const char* const kUnknownThreadsTeamName = "unknown threads";
1285 	size_t nameLength = strlen(kUnknownThreadsTeamName);
1286 
1287 	system_profiler_team_added* event = (system_profiler_team_added*)
1288 		malloc(sizeof(system_profiler_team_added) + nameLength);
1289 	if (event == NULL)
1290 		throw std::bad_alloc();
1291 
1292 	event->team = teamID;
1293 	event->args_offset = nameLength;
1294 	strlcpy(event->name, kUnknownThreadsTeamName, nameLength + 1);
1295 
1296 	// add the team to the model
1297 	team = fModel->AddTeam(event, fState->LastEventTime());
1298 	if (team == NULL)
1299 		throw std::bad_alloc();
1300 
1301 	return team;
1302 }
1303 
1304 
1305 void
1306 ModelLoader::_AddThreadWaitObject(ExtendedThreadSchedulingState* thread,
1307 	uint32 type, addr_t object)
1308 {
1309 	Model::WaitObjectGroup* waitObjectGroup
1310 		= fModel->WaitObjectGroupFor(type, object);
1311 	if (waitObjectGroup == NULL) {
1312 		// The algorithm should prevent this case.
1313 		printf("ModelLoader::_AddThreadWaitObject(): Unknown wait object: type:"
1314 			" %" B_PRIu32 ", " "object: %#" B_PRIxADDR "\n", type, object);
1315 		return;
1316 	}
1317 
1318 	Model::WaitObject* waitObject = waitObjectGroup->MostRecentWaitObject();
1319 
1320 	Model::ThreadWaitObjectGroup* threadWaitObjectGroup
1321 		= fModel->ThreadWaitObjectGroupFor(thread->ID(), type, object);
1322 
1323 	if (threadWaitObjectGroup == NULL
1324 		|| threadWaitObjectGroup->MostRecentWaitObject() != waitObject) {
1325 		Model::ThreadWaitObject* threadWaitObject
1326 			= fModel->AddThreadWaitObject(thread->ID(), waitObject,
1327 				&threadWaitObjectGroup);
1328 		if (threadWaitObject == NULL)
1329 			throw std::bad_alloc();
1330 	}
1331 
1332 	thread->waitObject = threadWaitObjectGroup->MostRecentThreadWaitObject();
1333 }
1334 
1335 
1336 void
1337 ModelLoader::_AddIdleTime(uint32 cpu, nanotime_t time)
1338 {
1339 	fCPUInfos[cpu].idleTime += time;
1340 }
1341