xref: /haiku/src/bin/debug/profile/profile.cpp (revision efafab643ce980e3f3c916795ed302599f6b4f66)
1 /*
2  * Copyright 2008-2010, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Copyright 2013, Rene Gollent, rene@gollent.com.
4  * Distributed under the terms of the MIT License.
5  */
6 
7 
8 #include <ctype.h>
9 #include <errno.h>
10 #include <getopt.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 
15 #include <algorithm>
16 #include <map>
17 #include <new>
18 #include <string>
19 
20 #include <debugger.h>
21 #include <FindDirectory.h>
22 #include <OS.h>
23 #include <Path.h>
24 #include <String.h>
25 
26 #include <syscalls.h>
27 #include <system_profiler_defs.h>
28 
29 #include <AutoDeleter.h>
30 #include <debug_support.h>
31 #include <ObjectList.h>
32 #include <Referenceable.h>
33 
34 #include <util/DoublyLinkedList.h>
35 
36 #include "BasicProfileResult.h"
37 #include "CallgrindProfileResult.h"
38 #include "debug_utils.h"
39 #include "Image.h"
40 #include "Options.h"
41 #include "SummaryProfileResult.h"
42 #include "Team.h"
43 
44 
45 // size of the sample buffer area for system profiling
46 #define PROFILE_ALL_SAMPLE_AREA_SIZE	(4 * 1024 * 1024)
47 
48 
49 extern const char* __progname;
50 const char* kCommandName = __progname;
51 
52 
53 class Image;
54 class Team;
55 class Thread;
56 
57 
58 static const char* kUsage =
59 	"Usage: %s [ <options> ] [ <command line> ]\n"
60 	"Profiles threads by periodically sampling the program counter. There are\n"
61 	"two different modes: One profiles the complete system. The other starts\n"
62 	"a program and profiles that and (optionally) its children. When a thread\n"
63 	"terminates, a list of the functions where the thread was encountered is\n"
64 	"printed.\n"
65 	"\n"
66 	"Options:\n"
67 	"  -a, --all      - Profile all teams.\n"
68 	"  -c             - Don't profile child threads. Default is to\n"
69 	"                   recursively profile all threads created by a profiled\n"
70 	"                   thread.\n"
71 	"  -C             - Don't profile child teams. Default is to recursively\n"
72 	"                   profile all teams created by a profiled team.\n"
73 	"  -f             - Always analyze the full caller stack. The hit count\n"
74 	"                   for every encountered function will be incremented.\n"
75 	"                   This increases the default for the caller stack depth\n"
76 	"                   (\"-s\") to 64.\n"
77 	"  -h, --help     - Print this usage info.\n"
78 	"  -i <interval>  - Use a tick interval of <interval> microseconds.\n"
79 	"                   Default is 1000 (1 ms). On a fast machine, a shorter\n"
80 	"                   interval might lead to better results, while it might\n"
81 	"                   make them worse on slow machines.\n"
82 	"  -k             - Don't check kernel images for hits.\n"
83 	"  -l             - Also profile loading the executable.\n"
84 	"  -o <output>    - Print the results to file <output>.\n"
85 	"  -r, --recorded - Don't profile, but evaluate a recorded kernel profile\n"
86 	"                   data.\n"
87 	"  -s <depth>     - Number of return address samples to take from the\n"
88 	"                   caller stack per tick. If the topmost address doesn't\n"
89 	"                   hit a known image, the next address will be matched\n"
90 	"                   (and so on).\n"
91 	"  -S             - Don't output results for individual threads, but\n"
92 	"                   produce a combined output at the end.\n"
93 	"  -v <directory> - Create valgrind/callgrind output. <directory> is the\n"
94 	"                   directory where to put the output files.\n"
95 ;
96 
97 
98 Options gOptions;
99 
100 static bool sCaughtDeadlySignal = false;
101 
102 
103 class ThreadManager : private ProfiledEntity {
104 public:
105 	ThreadManager(port_id debuggerPort)
106 		:
107 		fTeams(20),
108 		fThreads(20, true),
109 		fKernelTeam(NULL),
110 		fDebuggerPort(debuggerPort),
111 		fSummaryProfileResult(NULL)
112 	{
113 	}
114 
115 	virtual ~ThreadManager()
116 	{
117 		// release image references
118 		for (ImageMap::iterator it = fImages.begin(); it != fImages.end(); ++it)
119 			it->second->ReleaseReference();
120 
121 		if (fSummaryProfileResult != NULL)
122 			fSummaryProfileResult->ReleaseReference();
123 
124 		for (int32 i = 0; Team* team = fTeams.ItemAt(i); i++)
125 			team->ReleaseReference();
126 	}
127 
128 	status_t Init()
129 	{
130 		if (!gOptions.summary_result)
131 			return B_OK;
132 
133 		ProfileResult* profileResult;
134 		status_t error = _CreateProfileResult(this, profileResult);
135 		if (error != B_OK)
136 			return error;
137 
138 		BReference<ProfileResult> profileResultReference(profileResult, true);
139 
140 		fSummaryProfileResult = new(std::nothrow) SummaryProfileResult(
141 			profileResult);
142 		if (fSummaryProfileResult == NULL)
143 			return B_NO_MEMORY;
144 
145 		return fSummaryProfileResult->Init(profileResult->Entity());
146 	}
147 
148 	status_t AddTeam(team_id teamID, Team** _team = NULL)
149 	{
150 		return _AddTeam(teamID, NULL, _team);
151 	}
152 
153 	status_t AddTeam(system_profiler_team_added* addedInfo, Team** _team = NULL)
154 	{
155 		return _AddTeam(addedInfo->team, addedInfo, _team);
156 	}
157 
158 	status_t AddThread(thread_id threadID)
159 	{
160 		thread_info threadInfo;
161 		status_t error = get_thread_info(threadID, &threadInfo);
162 		if (error != B_OK)
163 			return error;
164 
165 		return AddThread(threadInfo.team, threadID, threadInfo.name);
166 	}
167 
168 	status_t AddThread(team_id teamID, thread_id threadID, const char* name)
169 	{
170 		if (FindThread(threadID) != NULL)
171 			return B_BAD_VALUE;
172 
173 		Team* team = FindTeam(teamID);
174 		if (team == NULL)
175 			return B_BAD_TEAM_ID;
176 
177 		Thread* thread = new(std::nothrow) Thread(threadID, name, team);
178 		if (thread == NULL)
179 			return B_NO_MEMORY;
180 
181 		status_t error = _CreateThreadProfileResult(thread);
182 		if (error != B_OK) {
183 			delete thread;
184 			return error;
185 		}
186 
187 		error = team->InitThread(thread);
188 		if (error != B_OK) {
189 			delete thread;
190 			return error;
191 		}
192 
193 		fThreads.AddItem(thread);
194 		return B_OK;
195 	}
196 
197 	void RemoveTeam(team_id teamID)
198 	{
199 		if (Team* team = FindTeam(teamID)) {
200 			if (team == fKernelTeam)
201 				fKernelTeam = NULL;
202 			fTeams.RemoveItem(team);
203 			team->ReleaseReference();
204 		}
205 	}
206 
207 	void RemoveThread(thread_id threadID)
208 	{
209 		if (Thread* thread = FindThread(threadID)) {
210 			thread->GetTeam()->RemoveThread(thread);
211 			fThreads.RemoveItem(thread, true);
212 		}
213 	}
214 
215 	Team* FindTeam(team_id teamID) const
216 	{
217 		for (int32 i = 0; Team* team = fTeams.ItemAt(i); i++) {
218 			if (team->ID() == teamID)
219 				return team;
220 		}
221 		return NULL;
222 	}
223 
224 	Thread* FindThread(thread_id threadID) const
225 	{
226 		for (int32 i = 0; Thread* thread = fThreads.ItemAt(i); i++) {
227 			if (thread->ID() == threadID)
228 				return thread;
229 		}
230 		return NULL;
231 	}
232 
233 	int32 CountThreads() const
234 	{
235 		return fThreads.CountItems();
236 	}
237 
238 	Thread* ThreadAt(int32 index) const
239 	{
240 		return fThreads.ItemAt(index);
241 	}
242 
243 	status_t AddImage(team_id teamID, const image_info& imageInfo, int32 event)
244 	{
245 		// get a shared image
246 		SharedImage* sharedImage = NULL;
247 		status_t error = _GetSharedImage(teamID, imageInfo, &sharedImage);
248 		if (error != B_OK)
249 			return error;
250 
251 		if (teamID == B_SYSTEM_TEAM) {
252 			// a kernel image -- add it to all teams
253 			int32 count = fTeams.CountItems();
254 			for (int32 i = 0; i < count; i++) {
255 				fTeams.ItemAt(i)->AddImage(sharedImage, imageInfo, teamID,
256 					event);
257 			}
258 		}
259 
260 		// a userland team image -- add it to that image
261 		if (Team* team = FindTeam(teamID))
262 			return team->AddImage(sharedImage, imageInfo, teamID, event);
263 
264 		return B_BAD_TEAM_ID;
265 	}
266 
267 	void RemoveImage(team_id teamID, image_id imageID, int32 event)
268 	{
269 		if (teamID == B_SYSTEM_TEAM) {
270 			// a kernel image -- remove it from all teams
271 			int32 count = fTeams.CountItems();
272 			for (int32 i = 0; i < count; i++)
273 				fTeams.ItemAt(i)->RemoveImage(imageID, event);
274 		} else {
275 			// a userland team image -- add it to that image
276 			if (Team* team = FindTeam(teamID))
277 				team->RemoveImage(imageID, event);
278 		}
279 	}
280 
281 	void PrintSummaryResults()
282 	{
283 		if (fSummaryProfileResult != NULL)
284 			fSummaryProfileResult->PrintSummaryResults();
285 	}
286 
287 private:
288 	virtual int32 EntityID() const
289 	{
290 		return 1;
291 	}
292 
293 	virtual const char* EntityName() const
294 	{
295 		return "all";
296 	}
297 
298 	virtual const char* EntityType() const
299 	{
300 		return "summary";
301 	}
302 
303 private:
304 	status_t _AddTeam(team_id teamID, system_profiler_team_added* addedInfo,
305 		Team** _team = NULL)
306 	{
307 		if (FindTeam(teamID) != NULL)
308 			return B_BAD_VALUE;
309 
310 		Team* team = new(std::nothrow) Team;
311 		if (team == NULL)
312 			return B_NO_MEMORY;
313 
314 		status_t error = addedInfo != NULL
315 			? _InitUndebuggedTeam(team, addedInfo)
316 			: _InitDebuggedTeam(team, teamID);
317 		if (error != B_OK) {
318 			team->ReleaseReference();
319 			return error;
320 		}
321 
322 		fTeams.AddItem(team);
323 
324 		if (teamID == B_SYSTEM_TEAM)
325 			fKernelTeam = team;
326 
327 		if (_team != NULL)
328 			*_team = team;
329 
330 		return B_OK;
331 	}
332 
333 	status_t _InitDebuggedTeam(Team* team, team_id teamID)
334 	{
335 		// init the team
336 		status_t error = team->Init(teamID, fDebuggerPort);
337 		if (error != B_OK)
338 			return error;
339 
340 		// add the team's images
341 		error = _LoadTeamImages(team, teamID);
342 		if (error != B_OK)
343 			return error;
344 
345 		// add the kernel images
346 		return _LoadTeamImages(team, B_SYSTEM_TEAM);
347 	}
348 
349 	status_t _InitUndebuggedTeam(Team* team,
350 		system_profiler_team_added* addedInfo)
351 	{
352 		// init the team
353 		status_t error = team->Init(addedInfo);
354 		if (error != B_OK)
355 			return error;
356 
357 		// in case of a user team, add the kernel images
358 		if (team->ID() == B_SYSTEM_TEAM || fKernelTeam == NULL)
359 			return B_OK;
360 
361 		const BObjectList<Image>& kernelImages = fKernelTeam->Images();
362 		int32 count = kernelImages.CountItems();
363 		for (int32 i = 0; i < count; i++) {
364 			SharedImage* sharedImage = kernelImages.ItemAt(i)->GetSharedImage();
365 			team->AddImage(sharedImage, sharedImage->Info(), B_SYSTEM_TEAM, 0);
366 		}
367 
368 		return B_OK;
369 	}
370 
371 	status_t _LoadTeamImages(Team* team, team_id teamID)
372 	{
373 		// iterate through the team's images and collect the symbols
374 		image_info imageInfo;
375 		int32 cookie = 0;
376 		while (get_next_image_info(teamID, &cookie, &imageInfo) == B_OK) {
377 			// get a shared image
378 			SharedImage* sharedImage;
379 			status_t error = _GetSharedImage(teamID, imageInfo, &sharedImage);
380 			if (error != B_OK)
381 				return error;
382 
383 			// add the image to the team
384 			error = team->AddImage(sharedImage, imageInfo, teamID, 0);
385 			if (error != B_OK)
386 				return error;
387 		}
388 
389 		return B_OK;
390 	}
391 
392 	status_t _CreateThreadProfileResult(Thread* thread)
393 	{
394 		if (fSummaryProfileResult != NULL) {
395 			thread->SetProfileResult(fSummaryProfileResult);
396 			return B_OK;
397 		}
398 
399 		ProfileResult* profileResult;
400 		status_t error = _CreateProfileResult(thread, profileResult);
401 		if (error != B_OK)
402 			return error;
403 
404 		thread->SetProfileResult(profileResult);
405 
406 		return B_OK;
407 	}
408 
409 	status_t _CreateProfileResult(ProfiledEntity* profiledEntity,
410 		ProfileResult*& _profileResult)
411 	{
412 		ProfileResult* profileResult;
413 
414 		if (gOptions.callgrind_directory != NULL)
415 			profileResult = new(std::nothrow) CallgrindProfileResult;
416 		else if (gOptions.analyze_full_stack)
417 			profileResult = new(std::nothrow) InclusiveProfileResult;
418 		else
419 			profileResult = new(std::nothrow) ExclusiveProfileResult;
420 
421 		if (profileResult == NULL)
422 			return B_NO_MEMORY;
423 
424 		BReference<ProfileResult> profileResultReference(profileResult, true);
425 
426 		status_t error = profileResult->Init(profiledEntity);
427 		if (error != B_OK)
428 			return error;
429 
430 		_profileResult = profileResultReference.Detach();
431 		return B_OK;
432 	}
433 
434 	status_t _GetSharedImage(team_id teamID, const image_info& imageInfo,
435 		SharedImage** _sharedImage)
436 	{
437 		// check whether the image has already been loaded
438 		ImageMap::iterator it = fImages.find(imageInfo.name);
439 		if (it != fImages.end()) {
440 			*_sharedImage = it->second;
441 			return B_OK;
442 		}
443 
444 		// create the shared image
445 		SharedImage* sharedImage = new(std::nothrow) SharedImage;
446 		if (sharedImage == NULL)
447 			return B_NO_MEMORY;
448 		ObjectDeleter<SharedImage> imageDeleter(sharedImage);
449 
450 		// load the symbols
451 		status_t error;
452 		if (teamID == B_SYSTEM_TEAM) {
453 			error = sharedImage->Init(teamID, imageInfo.id);
454 			if (error != B_OK) {
455 				// The image has obviously been unloaded already, try to get
456 				// it by path.
457 				BString name = imageInfo.name;
458 				if (name.FindFirst('/') == -1) {
459 					// modules without a path are likely to be boot modules
460 					BPath bootAddonPath;
461 					if (find_directory(B_SYSTEM_ADDONS_DIRECTORY,
462 							&bootAddonPath) == B_OK
463 						&& bootAddonPath.Append("kernel") == B_OK
464 						&& bootAddonPath.Append("boot") == B_OK) {
465 						name = BString(bootAddonPath.Path()) << "/" << name;
466 				}
467 				}
468 
469 				error = sharedImage->Init(name.String());
470 			}
471 		} else if (strcmp(imageInfo.name, "commpage") == 0)
472 			error = sharedImage->Init(teamID, imageInfo.id);
473 		else
474 			error = sharedImage->Init(imageInfo.name);
475 		if (error != B_OK)
476 			return error;
477 
478 		try {
479 			fImages[sharedImage->Name()] = sharedImage;
480 		} catch (std::bad_alloc&) {
481 			return B_NO_MEMORY;
482 		}
483 
484 		imageDeleter.Detach();
485 		*_sharedImage = sharedImage;
486 		return B_OK;
487 	}
488 
489 private:
490 	typedef std::map<std::string, SharedImage*> ImageMap;
491 
492 private:
493 	BObjectList<Team>				fTeams;
494 	BObjectList<Thread>				fThreads;
495 	ImageMap						fImages;
496 	Team*							fKernelTeam;
497 	port_id							fDebuggerPort;
498 	SummaryProfileResult*			fSummaryProfileResult;
499 };
500 
501 
502 static void
503 print_usage_and_exit(bool error)
504 {
505     fprintf(error ? stderr : stdout, kUsage, __progname);
506     exit(error ? 1 : 0);
507 }
508 
509 
510 /*
511 // get_id
512 static bool
513 get_id(const char *str, int32 &id)
514 {
515 	int32 len = strlen(str);
516 	for (int32 i = 0; i < len; i++) {
517 		if (!isdigit(str[i]))
518 			return false;
519 	}
520 
521 	id = atol(str);
522 	return true;
523 }
524 */
525 
526 
527 static bool
528 process_event_buffer(ThreadManager& threadManager, uint8* buffer,
529 	size_t bufferSize, team_id mainTeam)
530 {
531 //printf("process_event_buffer(%p, %lu)\n", buffer, bufferSize);
532 	const uint8* bufferEnd = buffer + bufferSize;
533 
534 	while (buffer < bufferEnd) {
535 		system_profiler_event_header* header
536 			= (system_profiler_event_header*)buffer;
537 
538 		buffer += sizeof(system_profiler_event_header);
539 
540 		switch (header->event) {
541 			case B_SYSTEM_PROFILER_TEAM_ADDED:
542 			{
543 				system_profiler_team_added* event
544 					= (system_profiler_team_added*)buffer;
545 
546 				if (threadManager.AddTeam(event) != B_OK)
547 					exit(1);
548 				break;
549 			}
550 
551 			case B_SYSTEM_PROFILER_TEAM_REMOVED:
552 			{
553 				system_profiler_team_removed* event
554 					= (system_profiler_team_removed*)buffer;
555 
556 				threadManager.RemoveTeam(event->team);
557 
558 				// quit, if the main team we're interested in is gone
559 				if (mainTeam >= 0 && event->team == mainTeam)
560 					return true;
561 
562 				break;
563 			}
564 
565 			case B_SYSTEM_PROFILER_TEAM_EXEC:
566 			{
567 				system_profiler_team_exec* event
568 					= (system_profiler_team_exec*)buffer;
569 
570 				if (Team* team = threadManager.FindTeam(event->team))
571 					team->Exec(0, event->args, event->thread_name);
572 				break;
573 			}
574 
575 			case B_SYSTEM_PROFILER_THREAD_ADDED:
576 			{
577 				system_profiler_thread_added* event
578 					= (system_profiler_thread_added*)buffer;
579 
580 				if (threadManager.AddThread(event->team, event->thread,
581 						event->name) != B_OK) {
582 					exit(1);
583 				}
584 				break;
585 			}
586 
587 			case B_SYSTEM_PROFILER_THREAD_REMOVED:
588 			{
589 				system_profiler_thread_removed* event
590 					= (system_profiler_thread_removed*)buffer;
591 
592 				if (Thread* thread = threadManager.FindThread(event->thread)) {
593 					thread->PrintResults();
594 					threadManager.RemoveThread(event->thread);
595 				}
596 				break;
597 			}
598 
599 			case B_SYSTEM_PROFILER_IMAGE_ADDED:
600 			{
601 				system_profiler_image_added* event
602 					= (system_profiler_image_added*)buffer;
603 
604 				threadManager.AddImage(event->team, event->info, 0);
605 				break;
606 			}
607 
608 			case B_SYSTEM_PROFILER_IMAGE_REMOVED:
609 			{
610 				system_profiler_image_removed* event
611 					= (system_profiler_image_removed*)buffer;
612 
613  				threadManager.RemoveImage(event->team, event->image, 0);
614 				break;
615 			}
616 
617 			case B_SYSTEM_PROFILER_SAMPLES:
618 			{
619 				system_profiler_samples* event
620 					= (system_profiler_samples*)buffer;
621 
622 				Thread* thread = threadManager.FindThread(event->thread);
623 				if (thread != NULL) {
624 					thread->AddSamples(event->samples,
625 						(addr_t*)(buffer + header->size) - event->samples);
626 				}
627 
628 				break;
629 			}
630 
631 			case B_SYSTEM_PROFILER_BUFFER_END:
632 			{
633 				// Marks the end of the ring buffer -- we need to ignore the
634 				// remaining bytes.
635 				return false;
636 			}
637 		}
638 
639 		buffer += header->size;
640 	}
641 
642 	return false;
643 }
644 
645 
646 static void
647 signal_handler(int signal, void* data)
648 {
649 	sCaughtDeadlySignal = true;
650 }
651 
652 
653 static void
654 profile_all(const char* const* programArgs, int programArgCount)
655 {
656 	// Load the executable, if we have to.
657 	thread_id threadID = -1;
658 	if (programArgCount >= 1) {
659 		threadID = load_program(programArgs, programArgCount,
660 			gOptions.profile_loading);
661 		if (threadID < 0) {
662 			fprintf(stderr, "%s: Failed to start `%s': %s\n", kCommandName,
663 				programArgs[0], strerror(threadID));
664 			exit(1);
665 		}
666 	}
667 
668 	// install signal handlers so we can exit gracefully
669     struct sigaction action;
670     action.sa_handler = (__sighandler_t)signal_handler;
671     sigemptyset(&action.sa_mask);
672     action.sa_userdata = NULL;
673     if (sigaction(SIGHUP, &action, NULL) < 0
674 		|| sigaction(SIGINT, &action, NULL) < 0
675 		|| sigaction(SIGQUIT, &action, NULL) < 0) {
676 		fprintf(stderr, "%s: Failed to install signal handlers: %s\n",
677 			kCommandName, strerror(errno));
678 		exit(1);
679     }
680 
681 	// create an area for the sample buffer
682 	system_profiler_buffer_header* bufferHeader;
683 	area_id area = create_area("profiling buffer", (void**)&bufferHeader,
684 		B_ANY_ADDRESS, PROFILE_ALL_SAMPLE_AREA_SIZE, B_NO_LOCK,
685 		B_READ_AREA | B_WRITE_AREA);
686 	if (area < 0) {
687 		fprintf(stderr, "%s: Failed to create sample area: %s\n", kCommandName,
688 			strerror(area));
689 		exit(1);
690 	}
691 
692 	uint8* bufferBase = (uint8*)(bufferHeader + 1);
693 	size_t totalBufferSize = PROFILE_ALL_SAMPLE_AREA_SIZE
694 		- (bufferBase - (uint8*)bufferHeader);
695 
696 	// create a thread manager
697 	ThreadManager threadManager(-1);	// TODO: We don't need a debugger port!
698 	status_t error = threadManager.Init();
699 	if (error != B_OK) {
700 		fprintf(stderr, "%s: Failed to init thread manager: %s\n", kCommandName,
701 			strerror(error));
702 		exit(1);
703 	}
704 
705 	// start profiling
706 	system_profiler_parameters profilerParameters;
707 	profilerParameters.buffer_area = area;
708 	profilerParameters.flags = B_SYSTEM_PROFILER_TEAM_EVENTS
709 		| B_SYSTEM_PROFILER_THREAD_EVENTS | B_SYSTEM_PROFILER_IMAGE_EVENTS
710 		| B_SYSTEM_PROFILER_SAMPLING_EVENTS;
711 	profilerParameters.interval = gOptions.interval;
712 	profilerParameters.stack_depth = gOptions.stack_depth;
713 
714 	error = _kern_system_profiler_start(&profilerParameters);
715 	if (error != B_OK) {
716 		fprintf(stderr, "%s: Failed to start profiling: %s\n", kCommandName,
717 			strerror(error));
718 		exit(1);
719 	}
720 
721 	// resume the loaded team, if we have one
722 	if (threadID >= 0)
723 		resume_thread(threadID);
724 
725 	// main event loop
726 	while (true) {
727 		// get the current buffer
728 		size_t bufferStart = bufferHeader->start;
729 		size_t bufferSize = bufferHeader->size;
730 		uint8* buffer = bufferBase + bufferStart;
731 //printf("processing buffer of size %lu bytes\n", bufferSize);
732 
733 		bool quit;
734 		if (bufferStart + bufferSize <= totalBufferSize) {
735 			quit = process_event_buffer(threadManager, buffer, bufferSize,
736 				threadID);
737 		} else {
738 			size_t remainingSize = bufferStart + bufferSize - totalBufferSize;
739 			quit = process_event_buffer(threadManager, buffer,
740 					bufferSize - remainingSize, threadID)
741 				|| process_event_buffer(threadManager, bufferBase,
742 					remainingSize, threadID);
743 		}
744 
745 		if (quit)
746 			break;
747 
748 		// get next buffer
749 		uint64 droppedEvents = 0;
750 		error = _kern_system_profiler_next_buffer(bufferSize, &droppedEvents);
751 
752 		if (error != B_OK) {
753 			if (error == B_INTERRUPTED) {
754 				if (sCaughtDeadlySignal)
755 					break;
756 				continue;
757 			}
758 
759 			fprintf(stderr, "%s: Failed to get next sample buffer: %s\n",
760 				kCommandName, strerror(error));
761 			break;
762 		}
763 	}
764 
765 	// stop profiling
766 	_kern_system_profiler_stop();
767 
768 	// print results
769 	int32 threadCount = threadManager.CountThreads();
770 	for (int32 i = 0; i < threadCount; i++) {
771 		Thread* thread = threadManager.ThreadAt(i);
772 		thread->PrintResults();
773 	}
774 
775 	threadManager.PrintSummaryResults();
776 }
777 
778 
779 static void
780 dump_recorded()
781 {
782 	// retrieve recorded samples and parameters
783 	system_profiler_parameters profilerParameters;
784 	status_t error = _kern_system_profiler_recorded(&profilerParameters);
785 	if (error != B_OK) {
786 		fprintf(stderr, "%s: Failed to get recorded profiling buffer: %s\n",
787 			kCommandName, strerror(error));
788 		exit(1);
789 	}
790 
791 	// set global options to those of the profiler parameters
792 	gOptions.interval = profilerParameters.interval;
793 	gOptions.stack_depth = profilerParameters.stack_depth;
794 
795 	// create an area for the sample buffer
796 	area_info info;
797 	error = get_area_info(profilerParameters.buffer_area, &info);
798 	if (error != B_OK) {
799 		fprintf(stderr, "%s: Recorded profiling buffer invalid: %s\n",
800 			kCommandName, strerror(error));
801 		exit(1);
802 	}
803 
804 	system_profiler_buffer_header* bufferHeader
805 		= (system_profiler_buffer_header*)info.address;
806 
807 	uint8* bufferBase = (uint8*)(bufferHeader + 1);
808 	size_t totalBufferSize = info.size - (bufferBase - (uint8*)bufferHeader);
809 
810 	// create a thread manager
811 	ThreadManager threadManager(-1);	// TODO: We don't need a debugger port!
812 	error = threadManager.Init();
813 	if (error != B_OK) {
814 		fprintf(stderr, "%s: Failed to init thread manager: %s\n", kCommandName,
815 			strerror(error));
816 		exit(1);
817 	}
818 
819 	// get the current buffer
820 	size_t bufferStart = bufferHeader->start;
821 	size_t bufferSize = bufferHeader->size;
822 	uint8* buffer = bufferBase + bufferStart;
823 
824 	if (bufferStart + bufferSize <= totalBufferSize) {
825 		process_event_buffer(threadManager, buffer, bufferSize, -1);
826 	} else {
827 		size_t remainingSize = bufferStart + bufferSize - totalBufferSize;
828 		if (!process_event_buffer(threadManager, buffer,
829 				bufferSize - remainingSize, -1)) {
830 			process_event_buffer(threadManager, bufferBase, remainingSize, -1);
831 		}
832 	}
833 
834 	// print results
835 	int32 threadCount = threadManager.CountThreads();
836 	for (int32 i = 0; i < threadCount; i++) {
837 		Thread* thread = threadManager.ThreadAt(i);
838 		thread->PrintResults();
839 	}
840 
841 	threadManager.PrintSummaryResults();
842 }
843 
844 
845 static void
846 profile_single(const char* const* programArgs, int programArgCount)
847 {
848 	// get thread/team to be debugged
849 	thread_id threadID = load_program(programArgs, programArgCount,
850 		gOptions.profile_loading);
851 	if (threadID < 0) {
852 		fprintf(stderr, "%s: Failed to start `%s': %s\n", kCommandName,
853 			programArgs[0], strerror(threadID));
854 		exit(1);
855 	}
856 
857 	// get the team ID
858 	thread_info threadInfo;
859 	status_t error = get_thread_info(threadID, &threadInfo);
860 	if (error != B_OK) {
861 		fprintf(stderr, "%s: Failed to get info for thread %ld: %s\n",
862 			kCommandName, threadID, strerror(error));
863 		exit(1);
864 	}
865 	team_id teamID = threadInfo.team;
866 
867 	// create a debugger port
868 	port_id debuggerPort = create_port(10, "debugger port");
869 	if (debuggerPort < 0) {
870 		fprintf(stderr, "%s: Failed to create debugger port: %s\n",
871 			kCommandName, strerror(debuggerPort));
872 		exit(1);
873 	}
874 
875 	// add team and thread to the thread manager
876 	ThreadManager threadManager(debuggerPort);
877 	error = threadManager.Init();
878 	if (error != B_OK) {
879 		fprintf(stderr, "%s: Failed to init thread manager: %s\n", kCommandName,
880 			strerror(error));
881 		exit(1);
882 	}
883 
884 	if (threadManager.AddTeam(teamID) != B_OK
885 		|| threadManager.AddThread(threadID) != B_OK) {
886 		exit(1);
887 	}
888 
889 	// debug loop
890 	while (true) {
891 		debug_debugger_message_data message;
892 		bool quitLoop = false;
893 		int32 code;
894 		ssize_t messageSize = read_port(debuggerPort, &code, &message,
895 			sizeof(message));
896 
897 		if (messageSize < 0) {
898 			if (messageSize == B_INTERRUPTED)
899 				continue;
900 
901 			fprintf(stderr, "%s: Reading from debugger port failed: %s\n",
902 				kCommandName, strerror(messageSize));
903 			exit(1);
904 		}
905 
906 		switch (code) {
907 			case B_DEBUGGER_MESSAGE_PROFILER_UPDATE:
908 			{
909 				Thread* thread = threadManager.FindThread(
910 					message.profiler_update.origin.thread);
911 				if (thread == NULL)
912 					break;
913 
914 				thread->AddSamples(message.profiler_update.sample_count,
915 					message.profiler_update.dropped_ticks,
916 					message.profiler_update.stack_depth,
917 					message.profiler_update.variable_stack_depth,
918 					message.profiler_update.image_event);
919 
920 				if (message.profiler_update.stopped) {
921 					thread->PrintResults();
922 					threadManager.RemoveThread(thread->ID());
923 				}
924 				break;
925 			}
926 
927 			case B_DEBUGGER_MESSAGE_TEAM_CREATED:
928 				if (!gOptions.profile_teams)
929 					break;
930 
931 				if (threadManager.AddTeam(message.team_created.new_team)
932 						== B_OK) {
933 					threadManager.AddThread(message.team_created.new_team);
934 				}
935 				break;
936 			case B_DEBUGGER_MESSAGE_TEAM_DELETED:
937 				// a debugged team is gone -- quit, if it is our team
938 				threadManager.RemoveTeam(message.origin.team);
939 				quitLoop = message.origin.team == teamID;
940 				break;
941 			case B_DEBUGGER_MESSAGE_TEAM_EXEC:
942 				if (Team* team = threadManager.FindTeam(message.origin.team)) {
943 					team_info teamInfo;
944 					thread_info threadInfo;
945 					if (get_team_info(message.origin.team, &teamInfo) == B_OK
946 						&& get_thread_info(message.origin.team, &threadInfo)
947 							== B_OK) {
948 						team->Exec(message.team_exec.image_event, teamInfo.args,
949 							threadInfo.name);
950 					}
951 				}
952 				break;
953 
954 			case B_DEBUGGER_MESSAGE_THREAD_CREATED:
955 				if (!gOptions.profile_threads)
956 					break;
957 
958 				threadManager.AddThread(message.thread_created.new_thread);
959 				break;
960 			case B_DEBUGGER_MESSAGE_THREAD_DELETED:
961 				threadManager.RemoveThread(message.origin.thread);
962 				break;
963 
964 			case B_DEBUGGER_MESSAGE_IMAGE_CREATED:
965 				threadManager.AddImage(message.origin.team,
966 					message.image_created.info,
967 					message.image_created.image_event);
968 				break;
969 			case B_DEBUGGER_MESSAGE_IMAGE_DELETED:
970 				threadManager.RemoveImage(message.origin.team,
971 					message.image_deleted.info.id,
972 					message.image_deleted.image_event);
973 				break;
974 
975 			case B_DEBUGGER_MESSAGE_POST_SYSCALL:
976 			case B_DEBUGGER_MESSAGE_SIGNAL_RECEIVED:
977 			case B_DEBUGGER_MESSAGE_THREAD_DEBUGGED:
978 			case B_DEBUGGER_MESSAGE_DEBUGGER_CALL:
979 			case B_DEBUGGER_MESSAGE_BREAKPOINT_HIT:
980 			case B_DEBUGGER_MESSAGE_WATCHPOINT_HIT:
981 			case B_DEBUGGER_MESSAGE_SINGLE_STEP:
982 			case B_DEBUGGER_MESSAGE_PRE_SYSCALL:
983 			case B_DEBUGGER_MESSAGE_EXCEPTION_OCCURRED:
984 				break;
985 		}
986 
987 		if (quitLoop)
988 			break;
989 
990 		// tell the thread to continue (only when there is a thread and the
991 		// message was synchronous)
992 		if (message.origin.thread >= 0 && message.origin.nub_port >= 0)
993 			continue_thread(message.origin.nub_port, message.origin.thread);
994 	}
995 
996 	// prints summary results
997 	threadManager.PrintSummaryResults();
998 }
999 
1000 
1001 int
1002 main(int argc, const char* const* argv)
1003 {
1004 	int32 stackDepth = 0;
1005 	bool dumpRecorded = false;
1006 	const char* outputFile = NULL;
1007 
1008 	while (true) {
1009 		static struct option sLongOptions[] = {
1010 			{ "all", no_argument, 0, 'a' },
1011 			{ "help", no_argument, 0, 'h' },
1012 			{ "recorded", no_argument, 0, 'r' },
1013 			{ 0, 0, 0, 0 }
1014 		};
1015 
1016 		opterr = 0; // don't print errors
1017 		int c = getopt_long(argc, (char**)argv, "+acCfhi:klo:rs:Sv:",
1018 			sLongOptions, NULL);
1019 		if (c == -1)
1020 			break;
1021 
1022 		switch (c) {
1023 			case 'a':
1024 				gOptions.profile_all = true;
1025 				break;
1026 			case 'c':
1027 				gOptions.profile_threads = false;
1028 				break;
1029 			case 'C':
1030 				gOptions.profile_teams = false;
1031 				break;
1032 			case 'f':
1033 				gOptions.stack_depth = 64;
1034 				gOptions.analyze_full_stack = true;
1035 				break;
1036 			case 'h':
1037 				print_usage_and_exit(false);
1038 				break;
1039 			case 'i':
1040 				gOptions.interval = atol(optarg);
1041 				break;
1042 			case 'k':
1043 				gOptions.profile_kernel = false;
1044 				break;
1045 			case 'l':
1046 				gOptions.profile_loading = true;
1047 				break;
1048 			case 'o':
1049 				outputFile = optarg;
1050 				break;
1051 			case 'r':
1052 				dumpRecorded = true;
1053 				break;
1054 			case 's':
1055 				stackDepth = atol(optarg);
1056 				break;
1057 			case 'S':
1058 				gOptions.summary_result = true;
1059 				break;
1060 			case 'v':
1061 				gOptions.callgrind_directory = optarg;
1062 				gOptions.analyze_full_stack = true;
1063 				gOptions.stack_depth = 64;
1064 				break;
1065 			default:
1066 				print_usage_and_exit(true);
1067 				break;
1068 		}
1069 	}
1070 
1071 	if ((!gOptions.profile_all && !dumpRecorded && optind >= argc)
1072 		|| (dumpRecorded && optind != argc))
1073 		print_usage_and_exit(true);
1074 
1075 	if (stackDepth != 0)
1076 		gOptions.stack_depth = stackDepth;
1077 
1078 	if (outputFile != NULL) {
1079 		gOptions.output = fopen(outputFile, "w+");
1080 		if (gOptions.output == NULL) {
1081 			fprintf(stderr, "%s: Failed to open output file \"%s\": %s\n",
1082 				kCommandName, outputFile, strerror(errno));
1083 			exit(1);
1084 		}
1085 	} else
1086 		gOptions.output = stdout;
1087 
1088 	if (dumpRecorded) {
1089 		dump_recorded();
1090 		return 0;
1091 	}
1092 
1093 	const char* const* programArgs = argv + optind;
1094 	int programArgCount = argc - optind;
1095 
1096 	if (gOptions.profile_all) {
1097 		profile_all(programArgs, programArgCount);
1098 		return 0;
1099 	}
1100 
1101 	profile_single(programArgs, programArgCount);
1102 	return 0;
1103 }
1104