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