xref: /haiku/src/apps/debuganalyzer/model_loader/ModelLoader.cpp (revision 25a7b01d15612846f332751841da3579db313082)
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 %lu, size: %lu\n", event, size);
725 return B_BAD_DATA;
726 			break;
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: %ld\n", event->team);
894 }
895 
896 
897 void
898 ModelLoader::_HandleTeamExec(system_profiler_team_exec* event)
899 {
900 	// TODO:...
901 }
902 
903 
904 void
905 ModelLoader::_HandleThreadAdded(system_profiler_thread_added* event)
906 {
907 	_AddThread(event)->IncrementEventCount();
908 }
909 
910 
911 void
912 ModelLoader::_HandleThreadRemoved(system_profiler_thread_removed* event)
913 {
914 	ExtendedThreadSchedulingState* thread = fState->LookupThread(event->thread);
915 	if (thread == NULL) {
916 		printf("Warning: Removed event for unknown thread: %ld\n",
917 			event->thread);
918 		thread = _AddUnknownThread(event->thread);
919 	}
920 
921 	thread->thread->SetDeletionTime(fState->LastEventTime());
922 	thread->IncrementEventCount();
923 }
924 
925 
926 void
927 ModelLoader::_HandleThreadScheduled(uint32 cpu,
928 	system_profiler_thread_scheduled* event)
929 {
930 	_UpdateLastEventTime(event->time);
931 
932 	ExtendedThreadSchedulingState* thread = fState->LookupThread(event->thread);
933 	if (thread == NULL) {
934 		printf("Warning: Schedule event for unknown thread: %ld\n",
935 			event->thread);
936 		thread = _AddUnknownThread(event->thread);
937 		return;
938 	}
939 
940 	nanotime_t diffTime = fState->LastEventTime() - thread->lastTime;
941 
942 	if (thread->state == READY) {
943 		// thread scheduled after having been woken up
944 		thread->thread->AddLatency(diffTime);
945 	} else if (thread->state == PREEMPTED) {
946 		// thread scheduled after having been preempted before
947 		thread->thread->AddRerun(diffTime);
948 	}
949 
950 	if (thread->state == STILL_RUNNING) {
951 		// Thread was running and continues to run.
952 		thread->state = RUNNING;
953 	}
954 
955 	if (thread->state != RUNNING) {
956 		thread->lastTime = fState->LastEventTime();
957 		thread->state = RUNNING;
958 	}
959 
960 	thread->IncrementEventCount();
961 
962 	// unscheduled thread
963 
964 	if (event->thread == event->previous_thread)
965 		return;
966 
967 	thread = fState->LookupThread(event->previous_thread);
968 	if (thread == NULL) {
969 		printf("Warning: Schedule event for unknown previous thread: %ld\n",
970 			event->previous_thread);
971 		thread = _AddUnknownThread(event->previous_thread);
972 	}
973 
974 	diffTime = fState->LastEventTime() - thread->lastTime;
975 
976 	if (thread->state == STILL_RUNNING) {
977 		// thread preempted
978 		thread->thread->AddPreemption(diffTime);
979 		thread->thread->AddRun(diffTime);
980 		if (thread->priority == 0)
981 			_AddIdleTime(cpu, diffTime);
982 
983 		thread->lastTime = fState->LastEventTime();
984 		thread->state = PREEMPTED;
985 	} else if (thread->state == RUNNING) {
986 		// thread starts waiting (it hadn't been added to the run
987 		// queue before being unscheduled)
988 		thread->thread->AddRun(diffTime);
989 		if (thread->priority == 0)
990 			_AddIdleTime(cpu, diffTime);
991 
992 		if (event->previous_thread_state == B_THREAD_WAITING) {
993 			addr_t waitObject = event->previous_thread_wait_object;
994 			switch (event->previous_thread_wait_object_type) {
995 				case THREAD_BLOCK_TYPE_SNOOZE:
996 				case THREAD_BLOCK_TYPE_SIGNAL:
997 					waitObject = 0;
998 					break;
999 				case THREAD_BLOCK_TYPE_SEMAPHORE:
1000 				case THREAD_BLOCK_TYPE_CONDITION_VARIABLE:
1001 				case THREAD_BLOCK_TYPE_MUTEX:
1002 				case THREAD_BLOCK_TYPE_RW_LOCK:
1003 				case THREAD_BLOCK_TYPE_OTHER:
1004 				default:
1005 					break;
1006 			}
1007 
1008 			_AddThreadWaitObject(thread,
1009 				event->previous_thread_wait_object_type, waitObject);
1010 		}
1011 
1012 		thread->lastTime = fState->LastEventTime();
1013 		thread->state = WAITING;
1014 	} else if (thread->state == UNKNOWN) {
1015 		uint32 threadState = event->previous_thread_state;
1016 		if (threadState == B_THREAD_WAITING
1017 			|| threadState == B_THREAD_SUSPENDED) {
1018 			thread->lastTime = fState->LastEventTime();
1019 			thread->state = WAITING;
1020 		} else if (threadState == B_THREAD_READY) {
1021 			thread->lastTime = fState->LastEventTime();
1022 			thread->state = PREEMPTED;
1023 		}
1024 	}
1025 
1026 	thread->IncrementEventCount();
1027 }
1028 
1029 
1030 void
1031 ModelLoader::_HandleThreadEnqueuedInRunQueue(
1032 	thread_enqueued_in_run_queue* event)
1033 {
1034 	_UpdateLastEventTime(event->time);
1035 
1036 	ExtendedThreadSchedulingState* thread = fState->LookupThread(event->thread);
1037 	if (thread == NULL) {
1038 		printf("Warning: Enqueued in run queue event for unknown thread: %ld\n",
1039 			event->thread);
1040 		thread = _AddUnknownThread(event->thread);
1041 	}
1042 
1043 	if (thread->state == RUNNING || thread->state == STILL_RUNNING) {
1044 		// Thread was running and is reentered into the run queue. This
1045 		// is done by the scheduler, if the thread remains ready.
1046 		thread->state = STILL_RUNNING;
1047 	} else {
1048 		// Thread was waiting and is ready now.
1049 		nanotime_t diffTime = fState->LastEventTime() - thread->lastTime;
1050 		if (thread->waitObject != NULL) {
1051 			thread->waitObject->AddWait(diffTime);
1052 			thread->waitObject = NULL;
1053 			thread->thread->AddWait(diffTime);
1054 		} else if (thread->state != UNKNOWN)
1055 			thread->thread->AddUnspecifiedWait(diffTime);
1056 
1057 		thread->lastTime = fState->LastEventTime();
1058 		thread->state = READY;
1059 	}
1060 
1061 	thread->priority = event->priority;
1062 
1063 	thread->IncrementEventCount();
1064 }
1065 
1066 
1067 void
1068 ModelLoader::_HandleThreadRemovedFromRunQueue(uint32 cpu,
1069 	thread_removed_from_run_queue* event)
1070 {
1071 	_UpdateLastEventTime(event->time);
1072 
1073 	ExtendedThreadSchedulingState* thread = fState->LookupThread(event->thread);
1074 	if (thread == NULL) {
1075 		printf("Warning: Removed from run queue event for unknown thread: "
1076 			"%ld\n", event->thread);
1077 		thread = _AddUnknownThread(event->thread);
1078 	}
1079 
1080 	// This really only happens when the thread priority is changed
1081 	// while the thread is ready.
1082 
1083 	nanotime_t diffTime = fState->LastEventTime() - thread->lastTime;
1084 	if (thread->state == RUNNING) {
1085 		// This should never happen.
1086 		thread->thread->AddRun(diffTime);
1087 		if (thread->priority == 0)
1088 			_AddIdleTime(cpu, diffTime);
1089 	} else if (thread->state == READY || thread->state == PREEMPTED) {
1090 		// Not really correct, but the case is rare and we keep it
1091 		// simple.
1092 		thread->thread->AddUnspecifiedWait(diffTime);
1093 	}
1094 
1095 	thread->lastTime = fState->LastEventTime();
1096 	thread->state = WAITING;
1097 
1098 	thread->IncrementEventCount();
1099 }
1100 
1101 
1102 void
1103 ModelLoader::_HandleWaitObjectInfo(system_profiler_wait_object_info* event)
1104 {
1105 	if (fModel->AddWaitObject(event, NULL) == NULL)
1106 		throw std::bad_alloc();
1107 }
1108 
1109 
1110 void
1111 ModelLoader::_HandleIOSchedulerAdded(system_profiler_io_scheduler_added* event)
1112 {
1113 	Model::IOScheduler* scheduler = fModel->IOSchedulerByID(event->scheduler);
1114 	if (scheduler != NULL) {
1115 		printf("Warning: Duplicate added event for I/O scheduler %ld\n",
1116 			event->scheduler);
1117 		return;
1118 	}
1119 
1120 	if (fModel->AddIOScheduler(event) == NULL)
1121 		throw std::bad_alloc();
1122 }
1123 
1124 
1125 void
1126 ModelLoader::_HandleIORequestScheduled(io_request_scheduled* event)
1127 {
1128 	IORequest* request = fIORequests->Lookup(event->request);
1129 	if (request != NULL) {
1130 		printf("Warning: Duplicate schedule event for I/O request %p\n",
1131 			event->request);
1132 		return;
1133 	}
1134 
1135 	ExtendedThreadSchedulingState* thread = fState->LookupThread(event->thread);
1136 	if (thread == NULL) {
1137 		printf("Warning: I/O request for unknown thread %ld\n", event->thread);
1138 		thread = _AddUnknownThread(event->thread);
1139 	}
1140 
1141 	if (fModel->IOSchedulerByID(event->scheduler) == NULL) {
1142 		printf("Warning: I/O requests for unknown scheduler %ld\n",
1143 			event->scheduler);
1144 		// TODO: Add state for unknown scheduler, as we do for threads.
1145 		return;
1146 	}
1147 
1148 	request = new(std::nothrow) IORequest(event);
1149 	if (request == NULL)
1150 		throw std::bad_alloc();
1151 
1152 	fIORequests->Insert(request);
1153 	thread->AddIORequest(request);
1154 }
1155 
1156 
1157 void
1158 ModelLoader::_HandleIORequestFinished(io_request_finished* event)
1159 {
1160 	IORequest* request = fIORequests->Lookup(event->request);
1161 	if (request == NULL)
1162 		return;
1163 
1164 	request->finishedEvent = event;
1165 
1166 	fIORequests->Remove(request);
1167 	fState->LookupThread(request->scheduledEvent->thread)
1168 		->IORequestFinished(request);
1169 }
1170 
1171 
1172 void
1173 ModelLoader::_HandleIOOperationStarted(io_operation_started* event)
1174 {
1175 	IORequest* request = fIORequests->Lookup(event->request);
1176 	if (request == NULL) {
1177 		printf("Warning: I/O request for operation %p not found\n",
1178 			event->operation);
1179 		return;
1180 	}
1181 
1182 	IOOperation* operation = new(std::nothrow) IOOperation(event);
1183 	if (operation == NULL)
1184 		throw std::bad_alloc();
1185 
1186 	request->AddOperation(operation);
1187 }
1188 
1189 
1190 void
1191 ModelLoader::_HandleIOOperationFinished(io_operation_finished* event)
1192 {
1193 	IORequest* request = fIORequests->Lookup(event->request);
1194 	if (request == NULL) {
1195 		printf("Warning: I/O request for operation %p not found\n",
1196 			event->operation);
1197 		return;
1198 	}
1199 
1200 	IOOperation* operation = request->FindOperation(event->operation);
1201 	if (operation == NULL) {
1202 		printf("Warning: operation %p not found\n", event->operation);
1203 		return;
1204 	}
1205 
1206 	operation->finishedEvent = event;
1207 }
1208 
1209 
1210 ModelLoader::ExtendedThreadSchedulingState*
1211 ModelLoader::_AddThread(system_profiler_thread_added* event)
1212 {
1213 	// do we know the thread already?
1214 	ExtendedThreadSchedulingState* info = fState->LookupThread(event->thread);
1215 	if (info != NULL) {
1216 		printf("Warning: Duplicate thread added event for thread %" B_PRId32
1217 			"\n", event->thread);
1218 		return info;
1219 	}
1220 
1221 	// add the thread to the model
1222 	Model::Thread* thread = fModel->AddThread(event, fState->LastEventTime());
1223 	if (thread == NULL)
1224 		throw std::bad_alloc();
1225 
1226 	// create and add a ThreadSchedulingState
1227 	info = new(std::nothrow) ExtendedThreadSchedulingState(thread);
1228 	if (info == NULL)
1229 		throw std::bad_alloc();
1230 
1231 	// TODO: The priority is missing from the system_profiler_thread_added
1232 	// struct. For now guess at least whether this is an idle thread.
1233 	if (strncmp(event->name, "idle thread", strlen("idle thread")) == 0)
1234 		info->priority = 0;
1235 	else
1236 		info->priority = B_NORMAL_PRIORITY;
1237 
1238 	fState->InsertThread(info);
1239 
1240 	return info;
1241 }
1242 
1243 
1244 ModelLoader::ExtendedThreadSchedulingState*
1245 ModelLoader::_AddUnknownThread(thread_id threadID)
1246 {
1247 	// create a dummy "add thread" event
1248 	system_profiler_thread_added* event = (system_profiler_thread_added*)
1249 		malloc(sizeof(system_profiler_thread_added));
1250 	if (event == NULL)
1251 		throw std::bad_alloc();
1252 
1253 	if (!fModel->AddAssociatedData(event)) {
1254 		free(event);
1255 		throw std::bad_alloc();
1256 	}
1257 
1258 	try {
1259 		event->team = _AddUnknownTeam()->ID();
1260 		event->thread = threadID;
1261 		snprintf(event->name, sizeof(event->name), "unknown thread %" B_PRId32,
1262 			threadID);
1263 
1264 		// add the thread to the model
1265 		ExtendedThreadSchedulingState* state = _AddThread(event);
1266 		return state;
1267 	} catch (...) {
1268 		throw;
1269 	}
1270 }
1271 
1272 Model::Team*
1273 ModelLoader::_AddUnknownTeam()
1274 {
1275 	team_id teamID = 0;
1276 	Model::Team* team = fModel->TeamByID(teamID);
1277 	if (team != NULL)
1278 		return team;
1279 
1280 	// create a dummy "add team" event
1281 	static const char* const kUnknownThreadsTeamName = "unknown threads";
1282 	size_t nameLength = strlen(kUnknownThreadsTeamName);
1283 
1284 	system_profiler_team_added* event = (system_profiler_team_added*)
1285 		malloc(sizeof(system_profiler_team_added) + nameLength);
1286 	if (event == NULL)
1287 		throw std::bad_alloc();
1288 
1289 	event->team = teamID;
1290 	event->args_offset = nameLength;
1291 	strlcpy(event->name, kUnknownThreadsTeamName, nameLength + 1);
1292 
1293 	// add the team to the model
1294 	team = fModel->AddTeam(event, fState->LastEventTime());
1295 	if (team == NULL)
1296 		throw std::bad_alloc();
1297 
1298 	return team;
1299 }
1300 
1301 
1302 void
1303 ModelLoader::_AddThreadWaitObject(ExtendedThreadSchedulingState* thread,
1304 	uint32 type, addr_t object)
1305 {
1306 	Model::WaitObjectGroup* waitObjectGroup
1307 		= fModel->WaitObjectGroupFor(type, object);
1308 	if (waitObjectGroup == NULL) {
1309 		// The algorithm should prevent this case.
1310 printf("ModelLoader::_AddThreadWaitObject(): Unknown wait object: type: %lu, "
1311 "object: %#lx\n", type, object);
1312 		return;
1313 	}
1314 
1315 	Model::WaitObject* waitObject = waitObjectGroup->MostRecentWaitObject();
1316 
1317 	Model::ThreadWaitObjectGroup* threadWaitObjectGroup
1318 		= fModel->ThreadWaitObjectGroupFor(thread->ID(), type, object);
1319 
1320 	if (threadWaitObjectGroup == NULL
1321 		|| threadWaitObjectGroup->MostRecentWaitObject() != waitObject) {
1322 		Model::ThreadWaitObject* threadWaitObject
1323 			= fModel->AddThreadWaitObject(thread->ID(), waitObject,
1324 				&threadWaitObjectGroup);
1325 		if (threadWaitObject == NULL)
1326 			throw std::bad_alloc();
1327 	}
1328 
1329 	thread->waitObject = threadWaitObjectGroup->MostRecentThreadWaitObject();
1330 }
1331 
1332 
1333 void
1334 ModelLoader::_AddIdleTime(uint32 cpu, nanotime_t time)
1335 {
1336 	fCPUInfos[cpu].idleTime += time;
1337 }
1338