xref: /haiku/src/apps/debuganalyzer/model_loader/ModelLoader.cpp (revision b671e9bbdbd10268a042b4f4cc4317ccd03d105e)
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 <new>
13 
14 #include <AutoDeleter.h>
15 #include <AutoLocker.h>
16 #include <DebugEventStream.h>
17 
18 #include <system_profiler_defs.h>
19 #include <thread_defs.h>
20 
21 #include "DataSource.h"
22 #include "MessageCodes.h"
23 #include "Model.h"
24 
25 
26 // add a scheduling state snapshot every x events
27 static const uint32 kSchedulingSnapshotInterval = 1024;
28 
29 
30 struct SimpleWaitObjectInfo : system_profiler_wait_object_info {
31 	SimpleWaitObjectInfo(uint32 type)
32 	{
33 		this->type = type;
34 		object = 0;
35 		referenced_object = 0;
36 		name[0] = '\0';
37 	}
38 };
39 
40 
41 static const SimpleWaitObjectInfo kSnoozeWaitObjectInfo(
42 	THREAD_BLOCK_TYPE_SNOOZE);
43 static const SimpleWaitObjectInfo kSignalWaitObjectInfo(
44 	THREAD_BLOCK_TYPE_SIGNAL);
45 
46 
47 // #pragma mark -
48 
49 
50 inline void
51 ModelLoader::_UpdateLastEventTime(bigtime_t time)
52 {
53 	if (fBaseTime < 0) {
54 		fBaseTime = time;
55 		fModel->SetBaseTime(time);
56 	}
57 
58 	fState.SetLastEventTime(time - fBaseTime);
59 }
60 
61 
62 ModelLoader::ModelLoader(DataSource* dataSource,
63 	const BMessenger& target, void* targetCookie)
64 	:
65 	AbstractModelLoader(target, targetCookie),
66 	fModel(NULL),
67 	fDataSource(dataSource)
68 {
69 }
70 
71 
72 ModelLoader::~ModelLoader()
73 {
74 	delete fDataSource;
75 	delete fModel;
76 }
77 
78 
79 Model*
80 ModelLoader::DetachModel()
81 {
82 	AutoLocker<BLocker> locker(fLock);
83 
84 	if (fModel == NULL || fLoading)
85 		return NULL;
86 
87 	Model* model = fModel;
88 	fModel = NULL;
89 
90 	return model;
91 }
92 
93 
94 status_t
95 ModelLoader::PrepareForLoading()
96 {
97 	if (fModel != NULL || fDataSource == NULL)
98 		return B_BAD_VALUE;
99 
100 	// init the state
101 	status_t error = fState.Init();
102 	if (error != B_OK)
103 		return error;
104 
105 	return B_OK;
106 }
107 
108 
109 status_t
110 ModelLoader::Load()
111 {
112 	try {
113 		return _Load();
114 	} catch(...) {
115 		return B_ERROR;
116 	}
117 }
118 
119 
120 void
121 ModelLoader::FinishLoading(bool success)
122 {
123 	fState.Clear();
124 
125 	if (!success) {
126 		delete fModel;
127 		fModel = NULL;
128 	}
129 }
130 
131 
132 status_t
133 ModelLoader::_Load()
134 {
135 	// read the complete data into memory
136 	void* eventData;
137 	size_t eventDataSize;
138 	status_t error = _ReadDebugEvents(&eventData, &eventDataSize);
139 	if (error != B_OK)
140 		return error;
141 
142 	// get the data source name
143 	BString dataSourceName;
144 	fDataSource->GetName(dataSourceName);
145 
146 	// create a model
147 	fModel = new(std::nothrow) Model(dataSourceName.String(), eventData,
148 		eventDataSize);
149 	if (fModel == NULL) {
150 		free(eventData);
151 		return B_NO_MEMORY;
152 	}
153 
154 	// create a debug input stream
155 	BDebugEventInputStream* input = new(std::nothrow) BDebugEventInputStream;
156 	if (input == NULL)
157 		return B_NO_MEMORY;
158 	ObjectDeleter<BDebugEventInputStream> inputDeleter(input);
159 
160 	error = input->SetTo(eventData, eventDataSize, false);
161 	if (error != B_OK)
162 		return error;
163 
164 	// add the snooze and signal wait objects to the model
165 	if (fModel->AddWaitObject(&kSnoozeWaitObjectInfo, NULL) == NULL
166 		|| fModel->AddWaitObject(&kSignalWaitObjectInfo, NULL) == NULL) {
167 		return B_NO_MEMORY;
168 	}
169 
170 	// process the events
171 	fState.Clear();
172 	fBaseTime = -1;
173 	uint64 count = 0;
174 
175 	while (true) {
176 		// get next event
177 		uint32 event;
178 		uint32 cpu;
179 		const void* buffer;
180 		off_t offset;
181 		ssize_t bufferSize = input->ReadNextEvent(&event, &cpu, &buffer,
182 			&offset);
183 		if (bufferSize < 0)
184 			return bufferSize;
185 		if (buffer == NULL)
186 			break;
187 
188 		// process the event
189 		status_t error = _ProcessEvent(event, cpu, buffer, bufferSize);
190 		if (error != B_OK)
191 			return error;
192 
193 		// periodically check whether we're supposed to abort
194 		if (++count % 32 == 0) {
195 			AutoLocker<BLocker> locker(fLock);
196 			if (fAborted)
197 				return B_ERROR;
198 		}
199 
200 		// periodically add scheduling snapshots
201 		if (count % kSchedulingSnapshotInterval == 0)
202 			fModel->AddSchedulingStateSnapshot(fState, offset);
203 	}
204 
205 	fModel->SetLastEventTime(fState.LastEventTime());
206 	fModel->LoadingFinished();
207 
208 	return B_OK;
209 }
210 
211 
212 status_t
213 ModelLoader::_ReadDebugEvents(void** _eventData, size_t* _size)
214 {
215 	// get a BDataIO from the data source
216 	BDataIO* io;
217 	status_t error = fDataSource->CreateDataIO(&io);
218 	if (error != B_OK)
219 		return error;
220 	ObjectDeleter<BDataIO> dataIOtDeleter(io);
221 
222 	// First we need to find out how large a buffer to allocate.
223 	size_t size;
224 
225 	if (BPositionIO* positionIO = dynamic_cast<BPositionIO*>(io)) {
226 		// it's a BPositionIO -- this makes things easier, since we know how
227 		// many bytes to read
228 		off_t currentPos = positionIO->Position();
229 		if (currentPos < 0)
230 			return currentPos;
231 
232 		off_t fileSize;
233 		error = positionIO->GetSize(&fileSize);
234 		if (error != B_OK)
235 			return error;
236 
237 		size = fileSize - currentPos;
238 	} else {
239 		// no BPositionIO -- we need to determine the total size by iteratively
240 		// reading the whole data one time
241 
242 		// allocate a dummy buffer for reading
243 		const size_t kBufferSize = 1024 * 1024;
244 		void* buffer = malloc(kBufferSize);
245 		if (buffer == NULL)
246 			return B_NO_MEMORY;
247 		MemoryDeleter bufferDeleter(buffer);
248 
249 		size = 0;
250 		while (true) {
251 			ssize_t bytesRead = io->Read(buffer, kBufferSize);
252 			if (bytesRead < 0)
253 				return bytesRead;
254 			if (bytesRead == 0)
255 				break;
256 
257 			size += bytesRead;
258 		}
259 
260 		// we've got the size -- recreate the BDataIO
261 		dataIOtDeleter.Delete();
262 		error = fDataSource->CreateDataIO(&io);
263 		if (error != B_OK)
264 			return error;
265 		dataIOtDeleter.SetTo(io);
266 	}
267 
268 	// allocate the data buffer
269 	void* data = malloc(size);
270 	if (data == NULL)
271 		return B_NO_MEMORY;
272 	MemoryDeleter dataDeleter(data);
273 
274 	// read the data
275 	ssize_t bytesRead = io->Read(data, size);
276 	if (bytesRead < 0)
277 		return bytesRead;
278 	if ((size_t)bytesRead != size)
279 		return B_FILE_ERROR;
280 
281 	dataDeleter.Detach();
282 	*_eventData = data;
283 	*_size = size;
284 	return B_OK;
285 }
286 
287 
288 status_t
289 ModelLoader::_ProcessEvent(uint32 event, uint32 cpu, const void* buffer,
290 	size_t size)
291 {
292 	switch (event) {
293 		case B_SYSTEM_PROFILER_TEAM_ADDED:
294 			_HandleTeamAdded((system_profiler_team_added*)buffer);
295 			break;
296 
297 		case B_SYSTEM_PROFILER_TEAM_REMOVED:
298 			_HandleTeamRemoved((system_profiler_team_removed*)buffer);
299 			break;
300 
301 		case B_SYSTEM_PROFILER_TEAM_EXEC:
302 			_HandleTeamExec((system_profiler_team_exec*)buffer);
303 			break;
304 
305 		case B_SYSTEM_PROFILER_THREAD_ADDED:
306 			_HandleThreadAdded((system_profiler_thread_added*)buffer);
307 			break;
308 
309 		case B_SYSTEM_PROFILER_THREAD_REMOVED:
310 			_HandleThreadRemoved((system_profiler_thread_removed*)buffer);
311 			break;
312 
313 		case B_SYSTEM_PROFILER_THREAD_SCHEDULED:
314 			_HandleThreadScheduled((system_profiler_thread_scheduled*)buffer);
315 			break;
316 
317 		case B_SYSTEM_PROFILER_THREAD_ENQUEUED_IN_RUN_QUEUE:
318 			_HandleThreadEnqueuedInRunQueue(
319 				(thread_enqueued_in_run_queue*)buffer);
320 			break;
321 
322 		case B_SYSTEM_PROFILER_THREAD_REMOVED_FROM_RUN_QUEUE:
323 			_HandleThreadRemovedFromRunQueue(
324 				(thread_removed_from_run_queue*)buffer);
325 			break;
326 
327 		case B_SYSTEM_PROFILER_WAIT_OBJECT_INFO:
328 			_HandleWaitObjectInfo((system_profiler_wait_object_info*)buffer);
329 			break;
330 
331 		default:
332 printf("unsupported event type %lu, size: %lu\n", event, size);
333 return B_BAD_DATA;
334 			break;
335 	}
336 
337 	return B_OK;
338 }
339 
340 
341 void
342 ModelLoader::_HandleTeamAdded(system_profiler_team_added* event)
343 {
344 	if (fModel->AddTeam(event, fState.LastEventTime()) == NULL)
345 		throw std::bad_alloc();
346 }
347 
348 
349 void
350 ModelLoader::_HandleTeamRemoved(system_profiler_team_removed* event)
351 {
352 	if (Model::Team* team = fModel->TeamByID(event->team))
353 		team->SetDeletionTime(fState.LastEventTime());
354 	else
355 		printf("Removed event for unknown team: %ld\n", event->team);
356 }
357 
358 
359 void
360 ModelLoader::_HandleTeamExec(system_profiler_team_exec* event)
361 {
362 	// TODO:...
363 }
364 
365 
366 void
367 ModelLoader::_HandleThreadAdded(system_profiler_thread_added* event)
368 {
369 	if (_AddThread(event) == NULL)
370 		throw std::bad_alloc();
371 }
372 
373 
374 void
375 ModelLoader::_HandleThreadRemoved(system_profiler_thread_removed* event)
376 {
377 	if (Model::Thread* thread = fModel->ThreadByID(event->thread))
378 		thread->SetDeletionTime(fState.LastEventTime());
379 	else
380 		printf("Removed event for unknown team: %ld\n", event->thread);
381 }
382 
383 
384 void
385 ModelLoader::_HandleThreadScheduled(system_profiler_thread_scheduled* event)
386 {
387 	_UpdateLastEventTime(event->time);
388 
389 	Model::ThreadSchedulingState* thread = fState.LookupThread(event->thread);
390 	if (thread == NULL) {
391 		printf("Schedule event for unknown thread: %ld\n", event->thread);
392 		return;
393 	}
394 
395 	bigtime_t diffTime = fState.LastEventTime() - thread->lastTime;
396 
397 	if (thread->state == READY) {
398 		// thread scheduled after having been woken up
399 		thread->thread->AddLatency(diffTime);
400 	} else if (thread->state == PREEMPTED) {
401 		// thread scheduled after having been preempted before
402 		thread->thread->AddRerun(diffTime);
403 	}
404 
405 	if (thread->state == STILL_RUNNING) {
406 		// Thread was running and continues to run.
407 		thread->state = RUNNING;
408 	}
409 
410 	if (thread->state != RUNNING) {
411 		thread->lastTime = fState.LastEventTime();
412 		thread->state = RUNNING;
413 	}
414 
415 	// unscheduled thread
416 
417 	if (event->thread == event->previous_thread)
418 		return;
419 
420 	thread = fState.LookupThread(event->previous_thread);
421 	if (thread == NULL) {
422 		printf("Schedule event for unknown previous thread: %ld\n",
423 			event->previous_thread);
424 		return;
425 	}
426 
427 	diffTime = fState.LastEventTime() - thread->lastTime;
428 
429 	if (thread->state == STILL_RUNNING) {
430 		// thread preempted
431 		thread->thread->AddPreemption(diffTime);
432 		thread->thread->AddRun(diffTime);
433 
434 		thread->lastTime = fState.LastEventTime();
435 		thread->state = PREEMPTED;
436 	} else if (thread->state == RUNNING) {
437 		// thread starts waiting (it hadn't been added to the run
438 		// queue before being unscheduled)
439 		thread->thread->AddRun(diffTime);
440 
441 		if (event->previous_thread_state == B_THREAD_WAITING) {
442 			addr_t waitObject = event->previous_thread_wait_object;
443 			switch (event->previous_thread_wait_object_type) {
444 				case THREAD_BLOCK_TYPE_SNOOZE:
445 				case THREAD_BLOCK_TYPE_SIGNAL:
446 					waitObject = 0;
447 					break;
448 				case THREAD_BLOCK_TYPE_SEMAPHORE:
449 				case THREAD_BLOCK_TYPE_CONDITION_VARIABLE:
450 				case THREAD_BLOCK_TYPE_MUTEX:
451 				case THREAD_BLOCK_TYPE_RW_LOCK:
452 				case THREAD_BLOCK_TYPE_OTHER:
453 				default:
454 					break;
455 			}
456 
457 			_AddThreadWaitObject(thread,
458 				event->previous_thread_wait_object_type, waitObject);
459 		}
460 
461 		thread->lastTime = fState.LastEventTime();
462 		thread->state = WAITING;
463 	} else if (thread->state == UNKNOWN) {
464 		uint32 threadState = event->previous_thread_state;
465 		if (threadState == B_THREAD_WAITING
466 			|| threadState == B_THREAD_SUSPENDED) {
467 			thread->lastTime = fState.LastEventTime();
468 			thread->state = WAITING;
469 		} else if (threadState == B_THREAD_READY) {
470 			thread->lastTime = fState.LastEventTime();
471 			thread->state = PREEMPTED;
472 		}
473 	}
474 }
475 
476 
477 void
478 ModelLoader::_HandleThreadEnqueuedInRunQueue(
479 	thread_enqueued_in_run_queue* event)
480 {
481 	_UpdateLastEventTime(event->time);
482 
483 	Model::ThreadSchedulingState* thread = fState.LookupThread(event->thread);
484 	if (thread == NULL) {
485 		printf("Enqueued in run queue event for unknown thread: %ld\n",
486 			event->thread);
487 		return;
488 	}
489 
490 	if (thread->state == RUNNING || thread->state == STILL_RUNNING) {
491 		// Thread was running and is reentered into the run queue. This
492 		// is done by the scheduler, if the thread remains ready.
493 		thread->state = STILL_RUNNING;
494 	} else {
495 		// Thread was waiting and is ready now.
496 		bigtime_t diffTime = fState.LastEventTime() - thread->lastTime;
497 		if (thread->waitObject != NULL) {
498 			thread->waitObject->AddWait(diffTime);
499 			thread->waitObject = NULL;
500 			thread->thread->AddWait(diffTime);
501 		} else if (thread->state != UNKNOWN)
502 			thread->thread->AddUnspecifiedWait(diffTime);
503 
504 		thread->lastTime = fState.LastEventTime();
505 		thread->state = READY;
506 	}
507 }
508 
509 
510 void
511 ModelLoader::_HandleThreadRemovedFromRunQueue(
512 	thread_removed_from_run_queue* event)
513 {
514 	_UpdateLastEventTime(event->time);
515 
516 	Model::ThreadSchedulingState* thread = fState.LookupThread(event->thread);
517 	if (thread == NULL) {
518 		printf("Removed from run queue event for unknown thread: %ld\n",
519 			event->thread);
520 		return;
521 	}
522 
523 	// This really only happens when the thread priority is changed
524 	// while the thread is ready.
525 
526 	bigtime_t diffTime = fState.LastEventTime() - thread->lastTime;
527 	if (thread->state == RUNNING) {
528 		// This should never happen.
529 		thread->thread->AddRun(diffTime);
530 	} else if (thread->state == READY || thread->state == PREEMPTED) {
531 		// Not really correct, but the case is rare and we keep it
532 		// simple.
533 		thread->thread->AddUnspecifiedWait(diffTime);
534 	}
535 
536 	thread->lastTime = fState.LastEventTime();
537 	thread->state = WAITING;
538 }
539 
540 
541 void
542 ModelLoader::_HandleWaitObjectInfo(system_profiler_wait_object_info* event)
543 {
544 	if (fModel->AddWaitObject(event, NULL) == NULL)
545 		throw std::bad_alloc();
546 }
547 
548 
549 Model::ThreadSchedulingState*
550 ModelLoader::_AddThread(system_profiler_thread_added* event)
551 {
552 	// do we know the thread already?
553 	Model::ThreadSchedulingState* info = fState.LookupThread(event->thread);
554 	if (info != NULL) {
555 		// TODO: ?
556 		return info;
557 	}
558 
559 	// add the thread to the model
560 	Model::Thread* thread = fModel->AddThread(event, fState.LastEventTime());
561 	if (thread == NULL)
562 		return NULL;
563 
564 	// create and add a ThreadSchedulingState
565 	info = new(std::nothrow) Model::ThreadSchedulingState(thread);
566 	if (info == NULL)
567 		return NULL;
568 
569 	fState.InsertThread(info);
570 
571 	return info;
572 }
573 
574 
575 void
576 ModelLoader::_AddThreadWaitObject(Model::ThreadSchedulingState* thread,
577 	uint32 type, addr_t object)
578 {
579 	Model::WaitObjectGroup* waitObjectGroup
580 		= fModel->WaitObjectGroupFor(type, object);
581 	if (waitObjectGroup == NULL) {
582 		// The algorithm should prevent this case.
583 printf("ModelLoader::_AddThreadWaitObject(): Unknown wait object: type: %lu, "
584 "object: %#lx\n", type, object);
585 		return;
586 	}
587 
588 	Model::WaitObject* waitObject = waitObjectGroup->MostRecentWaitObject();
589 
590 	Model::ThreadWaitObjectGroup* threadWaitObjectGroup
591 		= fModel->ThreadWaitObjectGroupFor(thread->ID(), type, object);
592 
593 	if (threadWaitObjectGroup == NULL
594 		|| threadWaitObjectGroup->MostRecentWaitObject() != waitObject) {
595 		Model::ThreadWaitObject* threadWaitObject
596 			= fModel->AddThreadWaitObject(thread->ID(), waitObject,
597 				&threadWaitObjectGroup);
598 		if (threadWaitObject == NULL)
599 			throw std::bad_alloc();
600 	}
601 
602 	thread->waitObject = threadWaitObjectGroup->MostRecentThreadWaitObject();
603 }
604