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