xref: /haiku/src/system/kernel/debug/system_profiler.cpp (revision a6e73cb9e8addfe832c064bfcb68067f1c2fa3eb)
1 /*
2  * Copyright 2009-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <system_profiler.h>
8 
9 #include <AutoDeleter.h>
10 #include <Referenceable.h>
11 
12 #include <util/AutoLock.h>
13 
14 #include <system_profiler_defs.h>
15 
16 #include <cpu.h>
17 #include <kernel.h>
18 #include <kimage.h>
19 #include <kscheduler.h>
20 #include <listeners.h>
21 #include <Notifications.h>
22 #include <sem.h>
23 #include <team.h>
24 #include <thread.h>
25 #include <user_debugger.h>
26 #include <vm/vm.h>
27 
28 #include <arch/debug.h>
29 
30 #include "IOSchedulerRoster.h"
31 
32 
33 // This is the kernel-side implementation of the system profiling support.
34 // A userland team can register as system profiler, providing an area as buffer
35 // for events. Those events are team, thread, and image changes (added/removed),
36 // periodic sampling of the return address stack for each CPU, as well as
37 // scheduling and I/O scheduling events.
38 
39 
40 class SystemProfiler;
41 
42 
43 // minimum/maximum size of the table used for wait object caching
44 #define MIN_WAIT_OBJECT_COUNT	128
45 #define MAX_WAIT_OBJECT_COUNT	1024
46 
47 
48 static spinlock sProfilerLock = B_SPINLOCK_INITIALIZER;
49 static SystemProfiler* sProfiler = NULL;
50 static struct system_profiler_parameters* sRecordedParameters = NULL;
51 
52 
53 class SystemProfiler : public BReferenceable, private NotificationListener,
54 	private SchedulerListener, private WaitObjectListener {
55 public:
56 								SystemProfiler(team_id team,
57 									const area_info& userAreaInfo,
58 									const system_profiler_parameters&
59 										parameters);
60 								~SystemProfiler();
61 
62 			team_id				TeamID() const	{ return fTeam; }
63 
64 			status_t			Init();
65 			status_t			NextBuffer(size_t bytesRead,
66 									uint64* _droppedEvents);
67 
68 private:
69     virtual	void				EventOccurred(NotificationService& service,
70 									const KMessage* event);
71 
72 	virtual	void				ThreadEnqueuedInRunQueue(Thread* thread);
73 	virtual	void				ThreadRemovedFromRunQueue(Thread* thread);
74 	virtual	void				ThreadScheduled(Thread* oldThread,
75 									Thread* newThread);
76 
77 	virtual	void				SemaphoreCreated(sem_id id,
78 									const char* name);
79 	virtual	void				ConditionVariableInitialized(
80 									ConditionVariable* variable);
81 	virtual	void				MutexInitialized(mutex* lock);
82 	virtual	void				RWLockInitialized(rw_lock* lock);
83 
84 			bool				_TeamAdded(Team* team);
85 			bool				_TeamRemoved(Team* team);
86 			bool				_TeamExec(Team* team);
87 
88 			bool				_ThreadAdded(Thread* thread);
89 			bool				_ThreadRemoved(Thread* thread);
90 
91 			bool				_ImageAdded(struct image* image);
92 			bool				_ImageRemoved(struct image* image);
93 
94 			bool				_IOSchedulerAdded(IOScheduler* scheduler);
95 			bool				_IOSchedulerRemoved(IOScheduler* scheduler);
96 			bool				_IORequestScheduled(IOScheduler* scheduler,
97 									IORequest* request);
98 			bool				_IORequestFinished(IOScheduler* scheduler,
99 									IORequest* request);
100 			bool				_IOOperationStarted(IOScheduler* scheduler,
101 									IORequest* request, IOOperation* operation);
102 			bool				_IOOperationFinished(IOScheduler* scheduler,
103 									IORequest* request, IOOperation* operation);
104 
105 			void				_WaitObjectCreated(addr_t object, uint32 type);
106 			void				_WaitObjectUsed(addr_t object, uint32 type);
107 
108 	inline	void				_MaybeNotifyProfilerThreadLocked();
109 	inline	void				_MaybeNotifyProfilerThread();
110 
111 	static	bool				_InitialImageIterator(struct image* image,
112 									void* cookie);
113 
114 			void*				_AllocateBuffer(size_t size, int event, int cpu,
115 									int count);
116 
117 	static	void				_InitTimers(void* cookie, int cpu);
118 	static	void				_UninitTimers(void* cookie, int cpu);
119 			void				_ScheduleTimer(int cpu);
120 
121 			void				_DoSample();
122 
123 	static	int32				_ProfilingEvent(struct timer* timer);
124 
125 private:
126 			struct CPUProfileData {
127 				struct timer	timer;
128 				bigtime_t		timerEnd;
129 				bool			timerScheduled;
130 				addr_t			buffer[B_DEBUG_STACK_TRACE_DEPTH];
131 			};
132 
133 			struct WaitObjectKey {
134 				addr_t	object;
135 				uint32	type;
136 			};
137 
138 			struct WaitObject : DoublyLinkedListLinkImpl<WaitObject>,
139 					WaitObjectKey {
140 				struct WaitObject* hash_link;
141 			};
142 
143 			struct WaitObjectTableDefinition {
144 				typedef WaitObjectKey	KeyType;
145 				typedef	WaitObject		ValueType;
146 
147 				size_t HashKey(const WaitObjectKey& key) const
148 				{
149 					return (size_t)key.object ^ (size_t)key.type;
150 				}
151 
152 				size_t Hash(const WaitObject* value) const
153 				{
154 					return HashKey(*value);
155 				}
156 
157 				bool Compare(const WaitObjectKey& key,
158 					const WaitObject* value) const
159 				{
160 					return value->type == key.type
161 						&& value->object == key.object;
162 				}
163 
164 				WaitObject*& GetLink(WaitObject* value) const
165 				{
166 					return value->hash_link;
167 				}
168 			};
169 
170 			typedef DoublyLinkedList<WaitObject> WaitObjectList;
171 			typedef BOpenHashTable<WaitObjectTableDefinition> WaitObjectTable;
172 
173 private:
174 			spinlock			fLock;
175 			team_id				fTeam;
176 			area_id				fUserArea;
177 			area_id				fKernelArea;
178 			size_t				fAreaSize;
179 			uint32				fFlags;
180 			uint32				fStackDepth;
181 			bigtime_t			fInterval;
182 			system_profiler_buffer_header* fHeader;
183 			uint8*				fBufferBase;
184 			size_t				fBufferCapacity;
185 			size_t				fBufferStart;
186 			size_t				fBufferSize;
187 			uint64				fDroppedEvents;
188 			int64				fLastTeamAddedSerialNumber;
189 			int64				fLastThreadAddedSerialNumber;
190 			bool				fTeamNotificationsRequested;
191 			bool				fTeamNotificationsEnabled;
192 			bool				fThreadNotificationsRequested;
193 			bool				fThreadNotificationsEnabled;
194 			bool				fImageNotificationsRequested;
195 			bool				fImageNotificationsEnabled;
196 			bool				fIONotificationsRequested;
197 			bool				fIONotificationsEnabled;
198 			bool				fSchedulerNotificationsRequested;
199 			bool				fWaitObjectNotificationsRequested;
200 			Thread* volatile	fWaitingProfilerThread;
201 			bool				fProfilingActive;
202 			bool				fReentered[B_MAX_CPU_COUNT];
203 			CPUProfileData		fCPUData[B_MAX_CPU_COUNT];
204 			WaitObject*			fWaitObjectBuffer;
205 			int32				fWaitObjectCount;
206 			WaitObjectList		fUsedWaitObjects;
207 			WaitObjectList		fFreeWaitObjects;
208 			WaitObjectTable		fWaitObjectTable;
209 };
210 
211 
212 /*!	Notifies the profiler thread when the profiling buffer is full enough.
213 	The caller must hold the scheduler lock and fLock.
214 */
215 inline void
216 SystemProfiler::_MaybeNotifyProfilerThreadLocked()
217 {
218 	// If the buffer is full enough, notify the profiler.
219 	if (fWaitingProfilerThread != NULL && fBufferSize > fBufferCapacity / 2) {
220 		int cpu = smp_get_current_cpu();
221 		fReentered[cpu] = true;
222 
223 		thread_unblock_locked(fWaitingProfilerThread, B_OK);
224 
225 		fWaitingProfilerThread = NULL;
226 		fReentered[cpu] = false;
227 	}
228 }
229 
230 
231 inline void
232 SystemProfiler::_MaybeNotifyProfilerThread()
233 {
234 	if (fWaitingProfilerThread == NULL)
235 		return;
236 
237 	InterruptsSpinLocker schedulerLocker(gSchedulerLock);
238 	SpinLocker locker(fLock);
239 
240 	_MaybeNotifyProfilerThreadLocked();
241 }
242 
243 
244 SystemProfiler::SystemProfiler(team_id team, const area_info& userAreaInfo,
245 	const system_profiler_parameters& parameters)
246 	:
247 	fTeam(team),
248 	fUserArea(userAreaInfo.area),
249 	fKernelArea(-1),
250 	fAreaSize(userAreaInfo.size),
251 	fFlags(parameters.flags),
252 	fStackDepth(parameters.stack_depth),
253 	fInterval(parameters.interval),
254 	fHeader(NULL),
255 	fBufferBase(NULL),
256 	fBufferCapacity(0),
257 	fBufferStart(0),
258 	fBufferSize(0),
259 	fDroppedEvents(0),
260 	fLastTeamAddedSerialNumber(0),
261 	fLastThreadAddedSerialNumber(0),
262 	fTeamNotificationsRequested(false),
263 	fTeamNotificationsEnabled(false),
264 	fThreadNotificationsRequested(false),
265 	fThreadNotificationsEnabled(false),
266 	fImageNotificationsRequested(false),
267 	fImageNotificationsEnabled(false),
268 	fIONotificationsRequested(false),
269 	fIONotificationsEnabled(false),
270 	fSchedulerNotificationsRequested(false),
271 	fWaitObjectNotificationsRequested(false),
272 	fWaitingProfilerThread(NULL),
273 	fWaitObjectBuffer(NULL),
274 	fWaitObjectCount(0),
275 	fUsedWaitObjects(),
276 	fFreeWaitObjects(),
277 	fWaitObjectTable()
278 {
279 	B_INITIALIZE_SPINLOCK(&fLock);
280 
281 	memset(fReentered, 0, sizeof(fReentered));
282 
283 	// compute the number wait objects we want to cache
284 	if ((fFlags & B_SYSTEM_PROFILER_SCHEDULING_EVENTS) != 0) {
285 		fWaitObjectCount = parameters.locking_lookup_size
286 			/ (sizeof(WaitObject) + (sizeof(void*) * 3 / 2));
287 		if (fWaitObjectCount < MIN_WAIT_OBJECT_COUNT)
288 			fWaitObjectCount = MIN_WAIT_OBJECT_COUNT;
289 		if (fWaitObjectCount > MAX_WAIT_OBJECT_COUNT)
290 			fWaitObjectCount = MAX_WAIT_OBJECT_COUNT;
291 	}
292 }
293 
294 
295 SystemProfiler::~SystemProfiler()
296 {
297 	// Wake up the user thread, if it is waiting, and mark profiling
298 	// inactive.
299 	InterruptsSpinLocker locker(fLock);
300 	if (fWaitingProfilerThread != NULL) {
301 		InterruptsSpinLocker schedulerLocker(gSchedulerLock);
302 		thread_unblock_locked(fWaitingProfilerThread, B_OK);
303 		fWaitingProfilerThread = NULL;
304 	}
305 	fProfilingActive = false;
306 	locker.Unlock();
307 
308 	// stop scheduler listening
309 	if (fSchedulerNotificationsRequested) {
310 		InterruptsSpinLocker schedulerLocker(gSchedulerLock);
311 		scheduler_remove_listener(this);
312 	}
313 
314 	// stop wait object listening
315 	if (fWaitObjectNotificationsRequested) {
316 		InterruptsSpinLocker locker(gWaitObjectListenerLock);
317 		remove_wait_object_listener(this);
318 	}
319 
320 	// deactivate the profiling timers on all CPUs
321 	if ((fFlags & B_SYSTEM_PROFILER_SAMPLING_EVENTS) != 0)
322 		call_all_cpus(_UninitTimers, this);
323 
324 	// cancel notifications
325 	NotificationManager& notificationManager
326 		= NotificationManager::Manager();
327 
328 	// images
329 	if (fImageNotificationsRequested) {
330 		fImageNotificationsRequested = false;
331 		notificationManager.RemoveListener("images", NULL, *this);
332 	}
333 
334 	// threads
335 	if (fThreadNotificationsRequested) {
336 		fThreadNotificationsRequested = false;
337 		notificationManager.RemoveListener("threads", NULL, *this);
338 	}
339 
340 	// teams
341 	if (fTeamNotificationsRequested) {
342 		fTeamNotificationsRequested = false;
343 		notificationManager.RemoveListener("teams", NULL, *this);
344 	}
345 
346 	// I/O
347 	if (fIONotificationsRequested) {
348 		fIONotificationsRequested = false;
349 		notificationManager.RemoveListener("I/O", NULL, *this);
350 	}
351 
352 	// delete wait object related allocations
353 	fWaitObjectTable.Clear();
354 	delete[] fWaitObjectBuffer;
355 
356 	// unlock the memory and delete the area
357 	if (fKernelArea >= 0) {
358 		unlock_memory(fHeader, fAreaSize, B_READ_DEVICE);
359 		delete_area(fKernelArea);
360 		fKernelArea = -1;
361 	}
362 }
363 
364 
365 status_t
366 SystemProfiler::Init()
367 {
368 	// clone the user area
369 	void* areaBase;
370 	fKernelArea = clone_area("profiling samples", &areaBase,
371 		B_ANY_KERNEL_ADDRESS, B_READ_AREA | B_WRITE_AREA,
372 		fUserArea);
373 	if (fKernelArea < 0)
374 		return fKernelArea;
375 
376 	// we need the memory locked
377 	status_t error = lock_memory(areaBase, fAreaSize, B_READ_DEVICE);
378 	if (error != B_OK) {
379 		delete_area(fKernelArea);
380 		fKernelArea = -1;
381 		return error;
382 	}
383 
384 	// the buffer is ready for use
385 	fHeader = (system_profiler_buffer_header*)areaBase;
386 	fBufferBase = (uint8*)(fHeader + 1);
387 	fBufferCapacity = fAreaSize - (fBufferBase - (uint8*)areaBase);
388 	fHeader->start = 0;
389 	fHeader->size = 0;
390 
391 	// allocate the wait object buffer and init the hash table
392 	if (fWaitObjectCount > 0) {
393 		fWaitObjectBuffer = new(std::nothrow) WaitObject[fWaitObjectCount];
394 		if (fWaitObjectBuffer == NULL)
395 			return B_NO_MEMORY;
396 
397 		for (int32 i = 0; i < fWaitObjectCount; i++)
398 			fFreeWaitObjects.Add(fWaitObjectBuffer + i);
399 
400 		error = fWaitObjectTable.Init(fWaitObjectCount * 3 / 2);
401 		if (error != B_OK)
402 			return error;
403 	}
404 
405 	// start listening for notifications
406 
407 	// teams
408 	NotificationManager& notificationManager
409 		= NotificationManager::Manager();
410 	if ((fFlags & B_SYSTEM_PROFILER_TEAM_EVENTS) != 0) {
411 		error = notificationManager.AddListener("teams",
412 			TEAM_ADDED | TEAM_REMOVED | TEAM_EXEC, *this);
413 		if (error != B_OK)
414 			return error;
415 		fTeamNotificationsRequested = true;
416 	}
417 
418 	// threads
419 	if ((fFlags & B_SYSTEM_PROFILER_THREAD_EVENTS) != 0) {
420 		error = notificationManager.AddListener("threads",
421 			THREAD_ADDED | THREAD_REMOVED, *this);
422 		if (error != B_OK)
423 			return error;
424 		fThreadNotificationsRequested = true;
425 	}
426 
427 	// images
428 	if ((fFlags & B_SYSTEM_PROFILER_IMAGE_EVENTS) != 0) {
429 		error = notificationManager.AddListener("images",
430 			IMAGE_ADDED | IMAGE_REMOVED, *this);
431 		if (error != B_OK)
432 			return error;
433 		fImageNotificationsRequested = true;
434 	}
435 
436 	// I/O events
437 	if ((fFlags & B_SYSTEM_PROFILER_IO_SCHEDULING_EVENTS) != 0) {
438 		error = notificationManager.AddListener("I/O",
439 			IO_SCHEDULER_ADDED | IO_SCHEDULER_REMOVED
440 				| IO_SCHEDULER_REQUEST_SCHEDULED | IO_SCHEDULER_REQUEST_FINISHED
441 				| IO_SCHEDULER_OPERATION_STARTED
442 				| IO_SCHEDULER_OPERATION_FINISHED,
443 			*this);
444 		if (error != B_OK)
445 			return error;
446 		fIONotificationsRequested = true;
447 	}
448 
449 	// We need to fill the buffer with the initial state of teams, threads,
450 	// and images.
451 
452 	// teams
453 	if ((fFlags & B_SYSTEM_PROFILER_TEAM_EVENTS) != 0) {
454 		InterruptsSpinLocker locker(fLock);
455 
456 		TeamListIterator iterator;
457 		while (Team* team = iterator.Next()) {
458 			locker.Unlock();
459 
460 			bool added = _TeamAdded(team);
461 
462 			// release the reference returned by the iterator
463 			team->ReleaseReference();
464 
465 			if (!added)
466 				return B_BUFFER_OVERFLOW;
467 
468 			locker.Lock();
469 		}
470 
471 		fTeamNotificationsEnabled = true;
472 	}
473 
474 	// images
475 	if ((fFlags & B_SYSTEM_PROFILER_IMAGE_EVENTS) != 0) {
476 		if (image_iterate_through_images(&_InitialImageIterator, this) != NULL)
477 			return B_BUFFER_OVERFLOW;
478 	}
479 
480 	// threads
481 	if ((fFlags & B_SYSTEM_PROFILER_THREAD_EVENTS) != 0) {
482 		InterruptsSpinLocker locker(fLock);
483 
484 		ThreadListIterator iterator;
485 		while (Thread* thread = iterator.Next()) {
486 			locker.Unlock();
487 
488 			bool added = _ThreadAdded(thread);
489 
490 			// release the reference returned by the iterator
491 			thread->ReleaseReference();
492 
493 			if (!added)
494 				return B_BUFFER_OVERFLOW;
495 
496 			locker.Lock();
497 		}
498 
499 		fThreadNotificationsEnabled = true;
500 	}
501 
502 	InterruptsSpinLocker schedulerLocker(gSchedulerLock);
503 
504 	fProfilingActive = true;
505 
506 	// start scheduler and wait object listening
507 	if ((fFlags & B_SYSTEM_PROFILER_SCHEDULING_EVENTS) != 0) {
508 		scheduler_add_listener(this);
509 		fSchedulerNotificationsRequested = true;
510 
511 		SpinLocker waitObjectLocker(gWaitObjectListenerLock);
512 		add_wait_object_listener(this);
513 		fWaitObjectNotificationsRequested = true;
514 		waitObjectLocker.Unlock();
515 
516 		// fake schedule events for the initially running threads
517 		int32 cpuCount = smp_get_num_cpus();
518 		for (int32 i = 0; i < cpuCount; i++) {
519 			Thread* thread = gCPU[i].running_thread;
520 			if (thread != NULL)
521 				ThreadScheduled(thread, thread);
522 		}
523 	}
524 
525 	schedulerLocker.Unlock();
526 
527 	// I/O scheduling
528 	if ((fFlags & B_SYSTEM_PROFILER_IO_SCHEDULING_EVENTS) != 0) {
529 		IOSchedulerRoster* roster = IOSchedulerRoster::Default();
530 		AutoLocker<IOSchedulerRoster> rosterLocker(roster);
531 
532 		for (IOSchedulerList::ConstIterator it
533 				= roster->SchedulerList().GetIterator();
534 			IOScheduler* scheduler = it.Next();) {
535 			_IOSchedulerAdded(scheduler);
536 		}
537 
538 		fIONotificationsEnabled = true;
539 	}
540 
541 	// activate the profiling timers on all CPUs
542 	if ((fFlags & B_SYSTEM_PROFILER_SAMPLING_EVENTS) != 0)
543 		call_all_cpus(_InitTimers, this);
544 
545 	return B_OK;
546 }
547 
548 
549 status_t
550 SystemProfiler::NextBuffer(size_t bytesRead, uint64* _droppedEvents)
551 {
552 	InterruptsSpinLocker locker(fLock);
553 
554 	if (fWaitingProfilerThread != NULL || !fProfilingActive
555 		|| bytesRead > fBufferSize) {
556 		return B_BAD_VALUE;
557 	}
558 
559 	fBufferSize -= bytesRead;
560 	fBufferStart += bytesRead;
561 	if (fBufferStart > fBufferCapacity)
562 		fBufferStart -= fBufferCapacity;
563 	fHeader->size = fBufferSize;
564 	fHeader->start = fBufferStart;
565 
566 	// already enough data in the buffer to return?
567 	if (fBufferSize > fBufferCapacity / 2)
568 		return B_OK;
569 
570 	// Wait until the buffer gets too full or an error or a timeout occurs.
571 	while (true) {
572 		Thread* thread = thread_get_current_thread();
573 		fWaitingProfilerThread = thread;
574 
575 		InterruptsSpinLocker schedulerLocker(gSchedulerLock);
576 
577 		thread_prepare_to_block(thread, B_CAN_INTERRUPT,
578 			THREAD_BLOCK_TYPE_OTHER, "system profiler buffer");
579 
580 		schedulerLocker.Unlock();
581 		locker.Unlock();
582 
583 		status_t error = thread_block_with_timeout(B_RELATIVE_TIMEOUT, 1000000);
584 
585 		locker.Lock();
586 
587 		if (error == B_OK) {
588 			// the caller has unset fWaitingProfilerThread for us
589 			break;
590 		}
591 
592 		fWaitingProfilerThread = NULL;
593 
594 		if (error != B_TIMED_OUT)
595 			return error;
596 
597 		// just the timeout -- return, if the buffer is not empty
598 		if (fBufferSize > 0)
599 			break;
600 	}
601 
602 	if (_droppedEvents != NULL) {
603 		*_droppedEvents = fDroppedEvents;
604 		fDroppedEvents = 0;
605 	}
606 
607 	return B_OK;
608 }
609 
610 
611 void
612 SystemProfiler::EventOccurred(NotificationService& service,
613 	const KMessage* event)
614 {
615 	int32 eventCode;
616 	if (event->FindInt32("event", &eventCode) != B_OK)
617 		return;
618 
619 	if (strcmp(service.Name(), "teams") == 0) {
620 		Team* team = (Team*)event->GetPointer("teamStruct", NULL);
621 		if (team == NULL)
622 			return;
623 
624 		switch (eventCode) {
625 			case TEAM_ADDED:
626 				if (fTeamNotificationsEnabled)
627 					_TeamAdded(team);
628 				break;
629 
630 			case TEAM_REMOVED:
631 				if (team->id == fTeam) {
632 					// The profiling team is gone -- uninstall the profiler!
633 					InterruptsSpinLocker locker(sProfilerLock);
634 					if (sProfiler != this)
635 						return;
636 
637 					sProfiler = NULL;
638 					locker.Unlock();
639 
640 					ReleaseReference();
641 					return;
642 				}
643 
644 				// When we're still doing the initial team list scan, we are
645 				// also interested in removals that happened to teams we have
646 				// already seen.
647 				if (fTeamNotificationsEnabled
648 					|| team->serial_number <= fLastTeamAddedSerialNumber) {
649 					_TeamRemoved(team);
650 				}
651 				break;
652 
653 			case TEAM_EXEC:
654 				if (fTeamNotificationsEnabled)
655 					_TeamExec(team);
656 				break;
657 		}
658 	} else if (strcmp(service.Name(), "threads") == 0) {
659 		Thread* thread = (Thread*)event->GetPointer("threadStruct", NULL);
660 		if (thread == NULL)
661 			return;
662 
663 		switch (eventCode) {
664 			case THREAD_ADDED:
665 				if (fThreadNotificationsEnabled)
666 					_ThreadAdded(thread);
667 				break;
668 
669 			case THREAD_REMOVED:
670 				// When we're still doing the initial thread list scan, we are
671 				// also interested in removals that happened to threads we have
672 				// already seen.
673 				if (fThreadNotificationsEnabled
674 					|| thread->serial_number <= fLastThreadAddedSerialNumber) {
675 					_ThreadRemoved(thread);
676 				}
677 				break;
678 		}
679 	} else if (strcmp(service.Name(), "images") == 0) {
680 		if (!fImageNotificationsEnabled)
681 			return;
682 
683 		struct image* image = (struct image*)event->GetPointer(
684 			"imageStruct", NULL);
685 		if (image == NULL)
686 			return;
687 
688 		switch (eventCode) {
689 			case IMAGE_ADDED:
690 				_ImageAdded(image);
691 				break;
692 
693 			case IMAGE_REMOVED:
694 				_ImageRemoved(image);
695 				break;
696 		}
697 	} else if (strcmp(service.Name(), "I/O") == 0) {
698 		if (!fIONotificationsEnabled)
699 			return;
700 
701 		IOScheduler* scheduler = (IOScheduler*)event->GetPointer("scheduler",
702 			NULL);
703 		if (scheduler == NULL)
704 			return;
705 
706 		IORequest* request = (IORequest*)event->GetPointer("request", NULL);
707 		IOOperation* operation = (IOOperation*)event->GetPointer("operation",
708 			NULL);
709 
710 		switch (eventCode) {
711 			case IO_SCHEDULER_ADDED:
712 				_IOSchedulerAdded(scheduler);
713 				break;
714 
715 			case IO_SCHEDULER_REMOVED:
716 				_IOSchedulerRemoved(scheduler);
717 				break;
718 
719 			case IO_SCHEDULER_REQUEST_SCHEDULED:
720 				_IORequestScheduled(scheduler, request);
721 				break;
722 
723 			case IO_SCHEDULER_REQUEST_FINISHED:
724 				_IORequestFinished(scheduler, request);
725 				break;
726 
727 			case IO_SCHEDULER_OPERATION_STARTED:
728 				_IOOperationStarted(scheduler, request, operation);
729 				break;
730 
731 			case IO_SCHEDULER_OPERATION_FINISHED:
732 				_IOOperationFinished(scheduler, request, operation);
733 				break;
734 		}
735 	}
736 
737 	_MaybeNotifyProfilerThread();
738 }
739 
740 
741 void
742 SystemProfiler::ThreadEnqueuedInRunQueue(Thread* thread)
743 {
744 	int cpu = smp_get_current_cpu();
745 
746 	SpinLocker locker(fLock, false, !fReentered[cpu]);
747 		// When re-entering, we already hold the lock.
748 
749 	system_profiler_thread_enqueued_in_run_queue* event
750 		= (system_profiler_thread_enqueued_in_run_queue*)
751 			_AllocateBuffer(
752 				sizeof(system_profiler_thread_enqueued_in_run_queue),
753 				B_SYSTEM_PROFILER_THREAD_ENQUEUED_IN_RUN_QUEUE, cpu, 0);
754 	if (event == NULL)
755 		return;
756 
757 	event->time = system_time_nsecs();
758 	event->thread = thread->id;
759 	event->priority = thread->priority;
760 
761 	fHeader->size = fBufferSize;
762 
763 	// Unblock the profiler thread, if necessary, but don't unblock the thread,
764 	// if it had been waiting on a condition variable, since then we'd likely
765 	// deadlock in ConditionVariable::NotifyOne(), as it acquires a static
766 	// spinlock.
767 	if (thread->wait.type != THREAD_BLOCK_TYPE_CONDITION_VARIABLE)
768 		_MaybeNotifyProfilerThreadLocked();
769 }
770 
771 
772 void
773 SystemProfiler::ThreadRemovedFromRunQueue(Thread* thread)
774 {
775 	int cpu = smp_get_current_cpu();
776 
777 	SpinLocker locker(fLock, false, !fReentered[cpu]);
778 		// When re-entering, we already hold the lock.
779 
780 	system_profiler_thread_removed_from_run_queue* event
781 		= (system_profiler_thread_removed_from_run_queue*)
782 			_AllocateBuffer(
783 				sizeof(system_profiler_thread_removed_from_run_queue),
784 				B_SYSTEM_PROFILER_THREAD_REMOVED_FROM_RUN_QUEUE, cpu, 0);
785 	if (event == NULL)
786 		return;
787 
788 	event->time = system_time_nsecs();
789 	event->thread = thread->id;
790 
791 	fHeader->size = fBufferSize;
792 
793 	// unblock the profiler thread, if necessary
794 	_MaybeNotifyProfilerThreadLocked();
795 }
796 
797 
798 void
799 SystemProfiler::ThreadScheduled(Thread* oldThread, Thread* newThread)
800 {
801 	int cpu = smp_get_current_cpu();
802 
803 	SpinLocker locker(fLock, false, !fReentered[cpu]);
804 		// When re-entering, we already hold the lock.
805 
806 	// If the old thread starts waiting, handle the wait object.
807 	if (oldThread->state == B_THREAD_WAITING)
808 		_WaitObjectUsed((addr_t)oldThread->wait.object, oldThread->wait.type);
809 
810 	system_profiler_thread_scheduled* event
811 		= (system_profiler_thread_scheduled*)
812 			_AllocateBuffer(sizeof(system_profiler_thread_scheduled),
813 				B_SYSTEM_PROFILER_THREAD_SCHEDULED, cpu, 0);
814 	if (event == NULL)
815 		return;
816 
817 	event->time = system_time_nsecs();
818 	event->thread = newThread->id;
819 	event->previous_thread = oldThread->id;
820 	event->previous_thread_state = oldThread->state;
821 	event->previous_thread_wait_object_type = oldThread->wait.type;
822 	event->previous_thread_wait_object = (addr_t)oldThread->wait.object;
823 
824 	fHeader->size = fBufferSize;
825 
826 	// unblock the profiler thread, if necessary
827 	_MaybeNotifyProfilerThreadLocked();
828 }
829 
830 
831 void
832 SystemProfiler::SemaphoreCreated(sem_id id, const char* name)
833 {
834 	_WaitObjectCreated((addr_t)id, THREAD_BLOCK_TYPE_SEMAPHORE);
835 }
836 
837 
838 void
839 SystemProfiler::ConditionVariableInitialized(ConditionVariable* variable)
840 {
841 	_WaitObjectCreated((addr_t)variable, THREAD_BLOCK_TYPE_CONDITION_VARIABLE);
842 }
843 
844 
845 void
846 SystemProfiler::MutexInitialized(mutex* lock)
847 {
848 	_WaitObjectCreated((addr_t)lock, THREAD_BLOCK_TYPE_MUTEX);
849 }
850 
851 
852 void
853 SystemProfiler::RWLockInitialized(rw_lock* lock)
854 {
855 	_WaitObjectCreated((addr_t)lock, THREAD_BLOCK_TYPE_RW_LOCK);
856 }
857 
858 
859 bool
860 SystemProfiler::_TeamAdded(Team* team)
861 {
862 	TeamLocker teamLocker(team);
863 
864 	size_t nameLen = strlen(team->Name());
865 	size_t argsLen = strlen(team->Args());
866 
867 	InterruptsSpinLocker locker(fLock);
868 
869 	// During the initial scan check whether the team is already gone again.
870 	// Later this cannot happen, since the team creator notifies us before
871 	// actually starting the team.
872 	if (!fTeamNotificationsEnabled && team->state >= TEAM_STATE_DEATH)
873 		return true;
874 
875 	if (team->serial_number > fLastTeamAddedSerialNumber)
876 		fLastTeamAddedSerialNumber = team->serial_number;
877 
878 	system_profiler_team_added* event = (system_profiler_team_added*)
879 		_AllocateBuffer(
880 			sizeof(system_profiler_team_added) + nameLen + 1 + argsLen,
881 			B_SYSTEM_PROFILER_TEAM_ADDED, 0, 0);
882 	if (event == NULL)
883 		return false;
884 
885 	event->team = team->id;
886 	strcpy(event->name, team->Name());
887 	event->args_offset = nameLen + 1;
888 	strcpy(event->name + nameLen + 1, team->Args());
889 
890 	fHeader->size = fBufferSize;
891 
892 	return true;
893 }
894 
895 
896 bool
897 SystemProfiler::_TeamRemoved(Team* team)
898 {
899 	// TODO: It is possible that we get remove notifications for teams that
900 	// had already been removed from the global team list when we did the
901 	// initial scan, but were still in the process of dying. ATM it is not
902 	// really possible to identify such a case.
903 
904 	TeamLocker teamLocker(team);
905 	InterruptsSpinLocker locker(fLock);
906 
907 	system_profiler_team_removed* event = (system_profiler_team_removed*)
908 		_AllocateBuffer(sizeof(system_profiler_team_removed),
909 			B_SYSTEM_PROFILER_TEAM_REMOVED, 0, 0);
910 	if (event == NULL)
911 		return false;
912 
913 	event->team = team->id;
914 
915 	fHeader->size = fBufferSize;
916 
917 	return true;
918 }
919 
920 
921 bool
922 SystemProfiler::_TeamExec(Team* team)
923 {
924 	TeamLocker teamLocker(team);
925 
926 	size_t argsLen = strlen(team->Args());
927 
928 	InterruptsSpinLocker locker(fLock);
929 
930 	system_profiler_team_exec* event = (system_profiler_team_exec*)
931 		_AllocateBuffer(sizeof(system_profiler_team_exec) + argsLen,
932 			B_SYSTEM_PROFILER_TEAM_EXEC, 0, 0);
933 	if (event == NULL)
934 		return false;
935 
936 	event->team = team->id;
937 	strlcpy(event->thread_name, team->main_thread->name,
938 		sizeof(event->thread_name));
939 	strcpy(event->args, team->Args());
940 
941 	fHeader->size = fBufferSize;
942 
943 	return true;
944 }
945 
946 
947 bool
948 SystemProfiler::_ThreadAdded(Thread* thread)
949 {
950 	ThreadLocker threadLocker(thread);
951 	InterruptsSpinLocker locker(fLock);
952 
953 	// During the initial scan check whether the team is already gone again.
954 	// Later this cannot happen, since the team creator notifies us before
955 	// actually starting the thread.
956 	if (!fThreadNotificationsEnabled && !thread->IsAlive())
957 		return true;
958 
959 	if (thread->serial_number > fLastThreadAddedSerialNumber)
960 		fLastThreadAddedSerialNumber = thread->serial_number;
961 
962 	system_profiler_thread_added* event = (system_profiler_thread_added*)
963 		_AllocateBuffer(sizeof(system_profiler_thread_added),
964 			B_SYSTEM_PROFILER_THREAD_ADDED, 0, 0);
965 	if (event == NULL)
966 		return false;
967 
968 	event->team = thread->team->id;
969 	event->thread = thread->id;
970 	strlcpy(event->name, thread->name, sizeof(event->name));
971 
972 	fHeader->size = fBufferSize;
973 
974 	return true;
975 }
976 
977 
978 bool
979 SystemProfiler::_ThreadRemoved(Thread* thread)
980 {
981 	// TODO: It is possible that we get remove notifications for threads that
982 	// had already been removed from the global thread list when we did the
983 	// initial scan, but were still in the process of dying. ATM it is not
984 	// really possible to identify such a case.
985 
986 	ThreadLocker threadLocker(thread);
987 	InterruptsSpinLocker locker(fLock);
988 
989 	system_profiler_thread_removed* event
990 		= (system_profiler_thread_removed*)
991 			_AllocateBuffer(sizeof(system_profiler_thread_removed),
992 				B_SYSTEM_PROFILER_THREAD_REMOVED, 0, 0);
993 	if (event == NULL)
994 		return false;
995 
996 	event->team = thread->team->id;
997 	event->thread = thread->id;
998 
999 	fHeader->size = fBufferSize;
1000 
1001 	return true;
1002 }
1003 
1004 
1005 bool
1006 SystemProfiler::_ImageAdded(struct image* image)
1007 {
1008 	InterruptsSpinLocker locker(fLock);
1009 
1010 	system_profiler_image_added* event = (system_profiler_image_added*)
1011 		_AllocateBuffer(sizeof(system_profiler_image_added),
1012 			B_SYSTEM_PROFILER_IMAGE_ADDED, 0, 0);
1013 	if (event == NULL)
1014 		return false;
1015 
1016 	event->team = image->team;
1017 	event->info = image->info;
1018 
1019 	fHeader->size = fBufferSize;
1020 
1021 	return true;
1022 }
1023 
1024 
1025 bool
1026 SystemProfiler::_ImageRemoved(struct image* image)
1027 {
1028 	InterruptsSpinLocker locker(fLock);
1029 
1030 	system_profiler_image_removed* event = (system_profiler_image_removed*)
1031 		_AllocateBuffer(sizeof(system_profiler_image_removed),
1032 			B_SYSTEM_PROFILER_IMAGE_REMOVED, 0, 0);
1033 	if (event == NULL)
1034 		return false;
1035 
1036 	event->team = image->team;
1037 	event->image = image->info.id;
1038 
1039 	fHeader->size = fBufferSize;
1040 
1041 	return true;
1042 }
1043 
1044 
1045 bool
1046 SystemProfiler::_IOSchedulerAdded(IOScheduler* scheduler)
1047 {
1048 	size_t nameLen = strlen(scheduler->Name());
1049 
1050 	InterruptsSpinLocker locker(fLock);
1051 
1052 	system_profiler_io_scheduler_added* event
1053 		= (system_profiler_io_scheduler_added*)_AllocateBuffer(
1054 			sizeof(system_profiler_io_scheduler_added) + nameLen,
1055 			B_SYSTEM_PROFILER_IO_SCHEDULER_ADDED, 0, 0);
1056 	if (event == NULL)
1057 		return false;
1058 
1059 	event->scheduler = scheduler->ID();
1060 	strcpy(event->name, scheduler->Name());
1061 
1062 	fHeader->size = fBufferSize;
1063 
1064 	return true;
1065 }
1066 
1067 
1068 bool
1069 SystemProfiler::_IOSchedulerRemoved(IOScheduler* scheduler)
1070 {
1071 	InterruptsSpinLocker locker(fLock);
1072 
1073 	system_profiler_io_scheduler_removed* event
1074 		= (system_profiler_io_scheduler_removed*)_AllocateBuffer(
1075 			sizeof(system_profiler_io_scheduler_removed),
1076 			B_SYSTEM_PROFILER_IO_SCHEDULER_REMOVED, 0, 0);
1077 	if (event == NULL)
1078 		return false;
1079 
1080 	event->scheduler = scheduler->ID();
1081 
1082 	fHeader->size = fBufferSize;
1083 
1084 	return true;
1085 }
1086 
1087 
1088 bool
1089 SystemProfiler::_IORequestScheduled(IOScheduler* scheduler, IORequest* request)
1090 {
1091 	InterruptsSpinLocker locker(fLock);
1092 
1093 	system_profiler_io_request_scheduled* event
1094 		= (system_profiler_io_request_scheduled*)_AllocateBuffer(
1095 			sizeof(system_profiler_io_request_scheduled),
1096 			B_SYSTEM_PROFILER_IO_REQUEST_SCHEDULED, 0, 0);
1097 	if (event == NULL)
1098 		return false;
1099 
1100 	IORequestOwner* owner = request->Owner();
1101 
1102 	event->time = system_time_nsecs();
1103 	event->scheduler = scheduler->ID();
1104 	event->team = owner->team;
1105 	event->thread = owner->thread;
1106 	event->request = request;
1107 	event->offset = request->Offset();
1108 	event->length = request->Length();
1109 	event->write = request->IsWrite();
1110 	event->priority = owner->priority;
1111 
1112 	fHeader->size = fBufferSize;
1113 
1114 	return true;
1115 }
1116 
1117 
1118 bool
1119 SystemProfiler::_IORequestFinished(IOScheduler* scheduler, IORequest* request)
1120 {
1121 	InterruptsSpinLocker locker(fLock);
1122 
1123 	system_profiler_io_request_finished* event
1124 		= (system_profiler_io_request_finished*)_AllocateBuffer(
1125 			sizeof(system_profiler_io_request_finished),
1126 			B_SYSTEM_PROFILER_IO_REQUEST_FINISHED, 0, 0);
1127 	if (event == NULL)
1128 		return false;
1129 
1130 	event->time = system_time_nsecs();
1131 	event->scheduler = scheduler->ID();
1132 	event->request = request;
1133 	event->status = request->Status();
1134 	event->transferred = request->TransferredBytes();
1135 
1136 	fHeader->size = fBufferSize;
1137 
1138 	return true;
1139 }
1140 
1141 
1142 bool
1143 SystemProfiler::_IOOperationStarted(IOScheduler* scheduler, IORequest* request,
1144 	IOOperation* operation)
1145 {
1146 	InterruptsSpinLocker locker(fLock);
1147 
1148 	system_profiler_io_operation_started* event
1149 		= (system_profiler_io_operation_started*)_AllocateBuffer(
1150 			sizeof(system_profiler_io_operation_started),
1151 			B_SYSTEM_PROFILER_IO_OPERATION_STARTED, 0, 0);
1152 	if (event == NULL)
1153 		return false;
1154 
1155 	event->time = system_time_nsecs();
1156 	event->scheduler = scheduler->ID();
1157 	event->request = request;
1158 	event->operation = operation;
1159 	event->offset = request->Offset();
1160 	event->length = request->Length();
1161 	event->write = request->IsWrite();
1162 
1163 	fHeader->size = fBufferSize;
1164 
1165 	return true;
1166 }
1167 
1168 
1169 bool
1170 SystemProfiler::_IOOperationFinished(IOScheduler* scheduler, IORequest* request,
1171 	IOOperation* operation)
1172 {
1173 	InterruptsSpinLocker locker(fLock);
1174 
1175 	system_profiler_io_operation_finished* event
1176 		= (system_profiler_io_operation_finished*)_AllocateBuffer(
1177 			sizeof(system_profiler_io_operation_finished),
1178 			B_SYSTEM_PROFILER_IO_OPERATION_FINISHED, 0, 0);
1179 	if (event == NULL)
1180 		return false;
1181 
1182 	event->time = system_time_nsecs();
1183 	event->scheduler = scheduler->ID();
1184 	event->request = request;
1185 	event->operation = operation;
1186 	event->status = request->Status();
1187 	event->transferred = request->TransferredBytes();
1188 
1189 	fHeader->size = fBufferSize;
1190 
1191 	return true;
1192 }
1193 
1194 
1195 void
1196 SystemProfiler::_WaitObjectCreated(addr_t object, uint32 type)
1197 {
1198 	SpinLocker locker(fLock);
1199 
1200 	// look up the object
1201 	WaitObjectKey key;
1202 	key.object = object;
1203 	key.type = type;
1204 	WaitObject* waitObject = fWaitObjectTable.Lookup(key);
1205 
1206 	// If found, remove it and add it to the free list. This might sound weird,
1207 	// but it makes sense, since we lazily track *used* wait objects only.
1208 	// I.e. the object in the table is now guaranteedly obsolete.
1209 	if (waitObject) {
1210 		fWaitObjectTable.RemoveUnchecked(waitObject);
1211 		fUsedWaitObjects.Remove(waitObject);
1212 		fFreeWaitObjects.Add(waitObject, false);
1213 	}
1214 }
1215 
1216 void
1217 SystemProfiler::_WaitObjectUsed(addr_t object, uint32 type)
1218 {
1219 	// look up the object
1220 	WaitObjectKey key;
1221 	key.object = object;
1222 	key.type = type;
1223 	WaitObject* waitObject = fWaitObjectTable.Lookup(key);
1224 
1225 	// If already known, re-queue it as most recently used and be done.
1226 	if (waitObject != NULL) {
1227 		fUsedWaitObjects.Remove(waitObject);
1228 		fUsedWaitObjects.Add(waitObject);
1229 		return;
1230 	}
1231 
1232 	// not known yet -- get the info
1233 	const char* name = NULL;
1234 	const void* referencedObject = NULL;
1235 
1236 	switch (type) {
1237 		case THREAD_BLOCK_TYPE_SEMAPHORE:
1238 		{
1239 			name = sem_get_name_unsafe((sem_id)object);
1240 			break;
1241 		}
1242 
1243 		case THREAD_BLOCK_TYPE_CONDITION_VARIABLE:
1244 		{
1245 			ConditionVariable* variable = (ConditionVariable*)object;
1246 			name = variable->ObjectType();
1247 			referencedObject = variable->Object();
1248 			break;
1249 		}
1250 
1251 		case THREAD_BLOCK_TYPE_MUTEX:
1252 		{
1253 			mutex* lock = (mutex*)object;
1254 			name = lock->name;
1255 			break;
1256 		}
1257 
1258 		case THREAD_BLOCK_TYPE_RW_LOCK:
1259 		{
1260 			rw_lock* lock = (rw_lock*)object;
1261 			name = lock->name;
1262 			break;
1263 		}
1264 
1265 		case THREAD_BLOCK_TYPE_OTHER:
1266 		{
1267 			name = (const char*)(void*)object;
1268 			break;
1269 		}
1270 
1271 		case THREAD_BLOCK_TYPE_SNOOZE:
1272 		case THREAD_BLOCK_TYPE_SIGNAL:
1273 		default:
1274 			return;
1275 	}
1276 
1277 	// add the event
1278 	size_t nameLen = name != NULL ? strlen(name) : 0;
1279 
1280 	system_profiler_wait_object_info* event
1281 		= (system_profiler_wait_object_info*)
1282 			_AllocateBuffer(sizeof(system_profiler_wait_object_info) + nameLen,
1283 				B_SYSTEM_PROFILER_WAIT_OBJECT_INFO, 0, 0);
1284 	if (event == NULL)
1285 		return;
1286 
1287 	event->type = type;
1288 	event->object = object;
1289 	event->referenced_object = (addr_t)referencedObject;
1290 	if (name != NULL)
1291 		strcpy(event->name, name);
1292 	else
1293 		event->name[0] = '\0';
1294 
1295 	fHeader->size = fBufferSize;
1296 
1297 	// add the wait object
1298 
1299 	// get a free one or steal the least recently used one
1300 	waitObject = fFreeWaitObjects.RemoveHead();
1301 	if (waitObject == NULL) {
1302 		waitObject = fUsedWaitObjects.RemoveHead();
1303 		fWaitObjectTable.RemoveUnchecked(waitObject);
1304 	}
1305 
1306 	waitObject->object = object;
1307 	waitObject->type = type;
1308 	fWaitObjectTable.InsertUnchecked(waitObject);
1309 	fUsedWaitObjects.Add(waitObject);
1310 }
1311 
1312 
1313 /*static*/ bool
1314 SystemProfiler::_InitialImageIterator(struct image* image, void* cookie)
1315 {
1316 	SystemProfiler* self = (SystemProfiler*)cookie;
1317 	self->fImageNotificationsEnabled = true;
1318 		// Set that here, since the image lock is being held now.
1319 	return !self->_ImageAdded(image);
1320 }
1321 
1322 
1323 void*
1324 SystemProfiler::_AllocateBuffer(size_t size, int event, int cpu, int count)
1325 {
1326 	size = (size + 3) / 4 * 4;
1327 	size += sizeof(system_profiler_event_header);
1328 
1329 	size_t end = fBufferStart + fBufferSize;
1330 	if (end + size > fBufferCapacity) {
1331 		// Buffer is wrapped or needs wrapping.
1332 		if (end < fBufferCapacity) {
1333 			// not wrapped yet, but needed
1334 			system_profiler_event_header* header
1335 				= (system_profiler_event_header*)(fBufferBase + end);
1336 			header->event = B_SYSTEM_PROFILER_BUFFER_END;
1337 			fBufferSize = fBufferCapacity - fBufferStart;
1338 			end = 0;
1339 		} else
1340 			end -= fBufferCapacity;
1341 
1342 		if (end + size > fBufferStart) {
1343 			fDroppedEvents++;
1344 			return NULL;
1345 		}
1346 	}
1347 
1348 	system_profiler_event_header* header
1349 		= (system_profiler_event_header*)(fBufferBase + end);
1350 	header->event = event;
1351 	header->cpu = cpu;
1352 	header->size = size - sizeof(system_profiler_event_header);
1353 
1354 	fBufferSize += size;
1355 
1356 	return header + 1;
1357 }
1358 
1359 
1360 /*static*/ void
1361 SystemProfiler::_InitTimers(void* cookie, int cpu)
1362 {
1363 	SystemProfiler* self = (SystemProfiler*)cookie;
1364 	self->_ScheduleTimer(cpu);
1365 }
1366 
1367 
1368 /*static*/ void
1369 SystemProfiler::_UninitTimers(void* cookie, int cpu)
1370 {
1371 	SystemProfiler* self = (SystemProfiler*)cookie;
1372 
1373 	CPUProfileData& cpuData = self->fCPUData[cpu];
1374 	cancel_timer(&cpuData.timer);
1375 	cpuData.timerScheduled = false;
1376 }
1377 
1378 
1379 void
1380 SystemProfiler::_ScheduleTimer(int cpu)
1381 {
1382 	CPUProfileData& cpuData = fCPUData[cpu];
1383 	cpuData.timerEnd = system_time() + fInterval;
1384 	cpuData.timer.user_data = this;
1385 	add_timer(&cpuData.timer, &_ProfilingEvent, fInterval,
1386 		B_ONE_SHOT_RELATIVE_TIMER);
1387 	cpuData.timerScheduled = true;
1388 }
1389 
1390 
1391 void
1392 SystemProfiler::_DoSample()
1393 {
1394 	Thread* thread = thread_get_current_thread();
1395 	int cpu = thread->cpu->cpu_num;
1396 	CPUProfileData& cpuData = fCPUData[cpu];
1397 
1398 	// get the samples
1399 	int32 count = arch_debug_get_stack_trace(cpuData.buffer, fStackDepth, 1,
1400 		0, STACK_TRACE_KERNEL | STACK_TRACE_USER);
1401 
1402 	InterruptsSpinLocker locker(fLock);
1403 
1404 	system_profiler_samples* event = (system_profiler_samples*)
1405 		_AllocateBuffer(sizeof(system_profiler_samples)
1406 				+ count * sizeof(addr_t),
1407 			B_SYSTEM_PROFILER_SAMPLES, cpu, count);
1408 	if (event == NULL)
1409 		return;
1410 
1411 	event->thread = thread->id;
1412 	memcpy(event->samples, cpuData.buffer, count * sizeof(addr_t));
1413 
1414 	fHeader->size = fBufferSize;
1415 }
1416 
1417 
1418 /*static*/ int32
1419 SystemProfiler::_ProfilingEvent(struct timer* timer)
1420 {
1421 	SystemProfiler* self = (SystemProfiler*)timer->user_data;
1422 
1423 	self->_DoSample();
1424 	self->_ScheduleTimer(timer->cpu);
1425 
1426 	return B_HANDLED_INTERRUPT;
1427 }
1428 
1429 
1430 // #pragma mark - private kernel API
1431 
1432 
1433 status_t
1434 start_system_profiler(size_t areaSize, uint32 stackDepth, bigtime_t interval)
1435 {
1436 	struct ParameterDeleter {
1437 		ParameterDeleter(area_id area)
1438 			:
1439 			fArea(area),
1440 			fDetached(false)
1441 		{
1442 		}
1443 
1444 		~ParameterDeleter()
1445 		{
1446 			if (!fDetached) {
1447 				delete_area(fArea);
1448 				delete sRecordedParameters;
1449 				sRecordedParameters = NULL;
1450 			}
1451 		}
1452 
1453 		void Detach()
1454 		{
1455 			fDetached = true;
1456 		}
1457 
1458 	private:
1459 		area_id	fArea;
1460 		bool	fDetached;
1461 	};
1462 
1463 	void* address;
1464 	area_id area = create_area("kernel profile data", &address,
1465 		B_ANY_KERNEL_ADDRESS, areaSize, B_FULL_LOCK,
1466 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
1467 	if (area < 0)
1468 		return area;
1469 
1470 	ParameterDeleter parameterDeleter(area);
1471 
1472 	sRecordedParameters = new(std::nothrow) system_profiler_parameters;
1473 	if (sRecordedParameters == NULL)
1474 		return B_NO_MEMORY;
1475 
1476 	sRecordedParameters->buffer_area = area;
1477 	sRecordedParameters->flags = B_SYSTEM_PROFILER_TEAM_EVENTS
1478 		| B_SYSTEM_PROFILER_THREAD_EVENTS | B_SYSTEM_PROFILER_IMAGE_EVENTS
1479 		| B_SYSTEM_PROFILER_IO_SCHEDULING_EVENTS
1480 		| B_SYSTEM_PROFILER_SAMPLING_EVENTS;
1481 	sRecordedParameters->locking_lookup_size = 4096;
1482 	sRecordedParameters->interval = interval;
1483 	sRecordedParameters->stack_depth = stackDepth;
1484 
1485 	area_info areaInfo;
1486 	get_area_info(area, &areaInfo);
1487 
1488 	// initialize the profiler
1489 	SystemProfiler* profiler = new(std::nothrow) SystemProfiler(B_SYSTEM_TEAM,
1490 		areaInfo, *sRecordedParameters);
1491 	if (profiler == NULL)
1492 		return B_NO_MEMORY;
1493 
1494 	ObjectDeleter<SystemProfiler> profilerDeleter(profiler);
1495 
1496 	status_t error = profiler->Init();
1497 	if (error != B_OK)
1498 		return error;
1499 
1500 	// set the new profiler
1501 	InterruptsSpinLocker locker(sProfilerLock);
1502 	if (sProfiler != NULL)
1503 		return B_BUSY;
1504 
1505 	parameterDeleter.Detach();
1506 	profilerDeleter.Detach();
1507 	sProfiler = profiler;
1508 	locker.Unlock();
1509 
1510 	return B_OK;
1511 }
1512 
1513 
1514 void
1515 stop_system_profiler()
1516 {
1517 	InterruptsSpinLocker locker(sProfilerLock);
1518 	if (sProfiler == NULL)
1519 		return;
1520 
1521 	SystemProfiler* profiler = sProfiler;
1522 	sProfiler = NULL;
1523 	locker.Unlock();
1524 
1525 	profiler->ReleaseReference();
1526 }
1527 
1528 
1529 // #pragma mark - syscalls
1530 
1531 
1532 status_t
1533 _user_system_profiler_start(struct system_profiler_parameters* userParameters)
1534 {
1535 	// copy params to the kernel
1536 	struct system_profiler_parameters parameters;
1537 	if (userParameters == NULL || !IS_USER_ADDRESS(userParameters)
1538 		|| user_memcpy(&parameters, userParameters, sizeof(parameters))
1539 			!= B_OK) {
1540 		return B_BAD_ADDRESS;
1541 	}
1542 
1543 	// check the parameters
1544 	team_id team = thread_get_current_thread()->team->id;
1545 
1546 	area_info areaInfo;
1547 	status_t error = get_area_info(parameters.buffer_area, &areaInfo);
1548 	if (error != B_OK)
1549 		return error;
1550 
1551 	if (areaInfo.team != team)
1552 		return B_BAD_VALUE;
1553 
1554 	if ((parameters.flags & B_SYSTEM_PROFILER_SAMPLING_EVENTS) != 0) {
1555 		if (parameters.stack_depth < 1)
1556 			return B_BAD_VALUE;
1557 
1558 		if (parameters.interval < B_DEBUG_MIN_PROFILE_INTERVAL)
1559 			parameters.interval = B_DEBUG_MIN_PROFILE_INTERVAL;
1560 
1561 		if (parameters.stack_depth > B_DEBUG_STACK_TRACE_DEPTH)
1562 			parameters.stack_depth = B_DEBUG_STACK_TRACE_DEPTH;
1563 	}
1564 
1565 	// quick check to see whether we do already have a profiler installed
1566 	InterruptsSpinLocker locker(sProfilerLock);
1567 	if (sProfiler != NULL)
1568 		return B_BUSY;
1569 	locker.Unlock();
1570 
1571 	// initialize the profiler
1572 	SystemProfiler* profiler = new(std::nothrow) SystemProfiler(team, areaInfo,
1573 		parameters);
1574 	if (profiler == NULL)
1575 		return B_NO_MEMORY;
1576 	ObjectDeleter<SystemProfiler> profilerDeleter(profiler);
1577 
1578 	error = profiler->Init();
1579 	if (error != B_OK)
1580 		return error;
1581 
1582 	// set the new profiler
1583 	locker.Lock();
1584 	if (sProfiler != NULL)
1585 		return B_BUSY;
1586 
1587 	profilerDeleter.Detach();
1588 	sProfiler = profiler;
1589 	locker.Unlock();
1590 
1591 	return B_OK;
1592 }
1593 
1594 
1595 status_t
1596 _user_system_profiler_next_buffer(size_t bytesRead, uint64* _droppedEvents)
1597 {
1598 	if (_droppedEvents != NULL && !IS_USER_ADDRESS(_droppedEvents))
1599 		return B_BAD_ADDRESS;
1600 
1601 	team_id team = thread_get_current_thread()->team->id;
1602 
1603 	InterruptsSpinLocker locker(sProfilerLock);
1604 	if (sProfiler == NULL || sProfiler->TeamID() != team)
1605 		return B_BAD_VALUE;
1606 
1607 	// get a reference to the profiler
1608 	SystemProfiler* profiler = sProfiler;
1609 	BReference<SystemProfiler> reference(profiler);
1610 	locker.Unlock();
1611 
1612 	uint64 droppedEvents;
1613 	status_t error = profiler->NextBuffer(bytesRead,
1614 		_droppedEvents != NULL ? &droppedEvents : NULL);
1615 	if (error == B_OK && _droppedEvents != NULL)
1616 		user_memcpy(_droppedEvents, &droppedEvents, sizeof(droppedEvents));
1617 
1618 	return error;
1619 }
1620 
1621 
1622 status_t
1623 _user_system_profiler_stop()
1624 {
1625 	team_id team = thread_get_current_thread()->team->id;
1626 
1627 	InterruptsSpinLocker locker(sProfilerLock);
1628 	if (sProfiler == NULL || sProfiler->TeamID() != team)
1629 		return B_BAD_VALUE;
1630 
1631 	SystemProfiler* profiler = sProfiler;
1632 	sProfiler = NULL;
1633 	locker.Unlock();
1634 
1635 	profiler->ReleaseReference();
1636 
1637 	return B_OK;
1638 }
1639 
1640 
1641 status_t
1642 _user_system_profiler_recorded(struct system_profiler_parameters* userParameters)
1643 {
1644 	if (userParameters == NULL || !IS_USER_ADDRESS(userParameters))
1645 		return B_BAD_ADDRESS;
1646 	if (sRecordedParameters == NULL)
1647 		return B_ERROR;
1648 
1649 	// Transfer the area to the userland process
1650 
1651 	void* address;
1652 	area_id newArea = transfer_area(sRecordedParameters->buffer_area, &address,
1653 		B_ANY_ADDRESS, team_get_current_team_id(), true);
1654 	if (newArea < 0)
1655 		return newArea;
1656 
1657 	status_t status = set_area_protection(newArea, B_READ_AREA);
1658 	if (status == B_OK) {
1659 		sRecordedParameters->buffer_area = newArea;
1660 
1661 		status = user_memcpy(userParameters, sRecordedParameters,
1662 			sizeof(system_profiler_parameters));
1663 	}
1664 	if (status != B_OK)
1665 		delete_area(newArea);
1666 
1667 	delete sRecordedParameters;
1668 	sRecordedParameters = NULL;
1669 
1670 	return status;
1671 }
1672