xref: /haiku/src/apps/debugger/Debugger.cpp (revision d0ac609964842f8cdb6d54b3c539c6c15293e172)
1 /*
2  * Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Copyright 2011-2015, Rene Gollent, rene@gollent.com.
4  * Distributed under the terms of the MIT License.
5  */
6 
7 
8 #include <getopt.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 
13 #include <new>
14 
15 #include <Application.h>
16 #include <Message.h>
17 
18 #include <ArgumentVector.h>
19 #include <AutoDeleter.h>
20 #include <AutoLocker.h>
21 #include <ObjectList.h>
22 
23 #include "debug_utils.h"
24 
25 #include "CommandLineUserInterface.h"
26 #include "GraphicalUserInterface.h"
27 #include "ImageDebugLoadingStateHandlerRoster.h"
28 #include "MessageCodes.h"
29 #include "ReportUserInterface.h"
30 #include "SettingsManager.h"
31 #include "SignalSet.h"
32 #include "StartTeamWindow.h"
33 #include "TeamDebugger.h"
34 #include "TeamsWindow.h"
35 #include "TypeHandlerRoster.h"
36 #include "ValueHandlerRoster.h"
37 
38 
39 extern const char* __progname;
40 const char* kProgramName = __progname;
41 
42 static const char* const kDebuggerSignature
43 	= "application/x-vnd.Haiku-Debugger";
44 
45 
46 static const char* kUsage =
47 	"Usage: %s [ <options> ]\n"
48 	"       %s [ <options> ] <command line>\n"
49 	"       %s [ <options> ] --team <team>\n"
50 	"       %s [ <options> ] --thread <thread>\n"
51 	"\n"
52 	"The first form starts the debugger displaying a requester to choose a\n"
53 	"running team to debug respectively to specify the program to run and\n"
54 	"debug.\n"
55 	"\n"
56 	"The second form runs the given command line and attaches the debugger to\n"
57 	"the new team. Unless specified otherwise the program will be stopped at\n"
58 	"the beginning of its main() function.\n"
59 	"\n"
60 	"The third and fourth forms attach the debugger to a running team. The\n"
61 	"fourth form additionally stops the specified thread.\n"
62 	"\n"
63 	"Options:\n"
64 	"  -h, --help        - Print this usage info and exit.\n"
65 	"  -c, --cli         - Use command line user interface\n"
66 	"  -s, --save-report - Save crash report for the targetted team and exit.\n"
67 	"                      Implies --cli.\n"
68 ;
69 
70 
71 static void
72 print_usage_and_exit(bool error)
73 {
74     fprintf(error ? stderr : stdout, kUsage, kProgramName, kProgramName,
75     	kProgramName, kProgramName);
76     exit(error ? 1 : 0);
77 }
78 
79 
80 struct Options {
81 	int					commandLineArgc;
82 	const char* const*	commandLineArgv;
83 	team_id				team;
84 	thread_id			thread;
85 	bool				useCLI;
86 	bool				saveReport;
87 	const char*			reportPath;
88 
89 	Options()
90 		:
91 		commandLineArgc(0),
92 		commandLineArgv(NULL),
93 		team(-1),
94 		thread(-1),
95 		useCLI(false),
96 		saveReport(false),
97 		reportPath(NULL)
98 	{
99 	}
100 };
101 
102 
103 struct DebuggedProgramInfo {
104 	team_id		team;
105 	thread_id	thread;
106 	int			commandLineArgc;
107 	const char* const* commandLineArgv;
108 	bool		stopInMain;
109 };
110 
111 
112 static bool
113 parse_arguments(int argc, const char* const* argv, bool noOutput,
114 	Options& options)
115 {
116 	optind = 1;
117 
118 	while (true) {
119 		static struct option sLongOptions[] = {
120 			{ "help", no_argument, 0, 'h' },
121 			{ "cli", no_argument, 0, 'c' },
122 			{ "save-report", optional_argument, 0, 's' },
123 			{ "team", required_argument, 0, 't' },
124 			{ "thread", required_argument, 0, 'T' },
125 			{ 0, 0, 0, 0 }
126 		};
127 
128 		opterr = 0; // don't print errors
129 
130 		int c = getopt_long(argc, (char**)argv, "+chs", sLongOptions, NULL);
131 		if (c == -1)
132 			break;
133 
134 		switch (c) {
135 			case 'c':
136 				options.useCLI = true;
137 				break;
138 
139 			case 'h':
140 				if (noOutput)
141 					return false;
142 				print_usage_and_exit(false);
143 				break;
144 
145 			case 's':
146 			{
147 				options.saveReport = true;
148 				options.reportPath = optarg;
149 				break;
150 			}
151 
152 			case 't':
153 			{
154 				options.team = strtol(optarg, NULL, 0);
155 				if (options.team <= 0) {
156 					if (noOutput)
157 						return false;
158 					print_usage_and_exit(true);
159 				}
160 				break;
161 			}
162 
163 			case 'T':
164 			{
165 				options.thread = strtol(optarg, NULL, 0);
166 				if (options.thread <= 0) {
167 					if (noOutput)
168 						return false;
169 					print_usage_and_exit(true);
170 				}
171 				break;
172 			}
173 
174 			default:
175 				if (noOutput)
176 					return false;
177 				print_usage_and_exit(true);
178 				break;
179 		}
180 	}
181 
182 	if (optind < argc) {
183 		options.commandLineArgc = argc - optind;
184 		options.commandLineArgv = argv + optind;
185 	}
186 
187 	int exclusiveParams = 0;
188 	if (options.team > 0)
189 		exclusiveParams++;
190 	if (options.thread > 0)
191 		exclusiveParams++;
192 	if (options.commandLineArgc > 0)
193 		exclusiveParams++;
194 
195 	if (exclusiveParams == 0) {
196 		return true;
197 	} else if (exclusiveParams != 1) {
198 		if (noOutput)
199 			return false;
200 		print_usage_and_exit(true);
201 	}
202 
203 	return true;
204 }
205 
206 static status_t
207 global_init()
208 {
209 	status_t error = TypeHandlerRoster::CreateDefault();
210 	if (error != B_OK)
211 		return error;
212 
213 	error = ValueHandlerRoster::CreateDefault();
214 	if (error != B_OK)
215 		return error;
216 
217 	error = ImageDebugLoadingStateHandlerRoster::CreateDefault();
218 	if (error != B_OK)
219 		return error;
220 
221 	return B_OK;
222 }
223 
224 
225 /**
226  * Finds or runs the program to debug, depending on the command line options.
227  * @param options The parsed command line options.
228  * @param _info The info for the program to fill in. Will only be filled in
229  *		  if successful.
230  * @return \c true, if the program has been found or ran.
231  */
232 static bool
233 get_debugged_program(const Options& options, DebuggedProgramInfo& _info)
234 {
235 	team_id team = options.team;
236 	thread_id thread = options.thread;
237 	bool stopInMain = false;
238 
239 	// If command line arguments were given, start the program.
240 	if (options.commandLineArgc > 0) {
241 		printf("loading program: \"%s\" ...\n", options.commandLineArgv[0]);
242 		// TODO: What about the CWD?
243 		thread = load_program(options.commandLineArgv,
244 			options.commandLineArgc, false);
245 		if (thread < 0) {
246 			// TODO: Notify the user!
247 			fprintf(stderr, "Error: Failed to load program \"%s\": %s\n",
248 				options.commandLineArgv[0], strerror(thread));
249 			return false;
250 		}
251 
252 		team = thread;
253 			// main thread ID == team ID
254 		stopInMain = true;
255 	}
256 
257 	// no parameters given, prompt the user to attach to a team
258 	if (team < 0 && thread < 0)
259 		return false;
260 
261 	// no team, but a thread -- get team
262 	if (team < 0) {
263 		printf("no team yet, getting thread info...\n");
264 		thread_info threadInfo;
265 		status_t error = get_thread_info(thread, &threadInfo);
266 		if (error != B_OK) {
267 			// TODO: Notify the user!
268 			fprintf(stderr, "Error: Failed to get info for thread \"%" B_PRId32
269 				"\": %s\n", thread, strerror(error));
270 			return false;
271 		}
272 
273 		team = threadInfo.team;
274 	}
275 	printf("team: %" B_PRId32 ", thread: %" B_PRId32 "\n", team, thread);
276 
277 	_info.commandLineArgc = options.commandLineArgc;
278 	_info.commandLineArgv = options.commandLineArgv;
279 	_info.team = team;
280 	_info.thread = thread;
281 	_info.stopInMain = stopInMain;
282 	return true;
283 }
284 
285 
286 /**
287  * Creates a TeamDebugger for the given team. If userInterface is given,
288  * that user interface is used (the caller retains its reference), otherwise
289  * a graphical user interface is created.
290  */
291 static TeamDebugger*
292 start_team_debugger(team_id teamID, SettingsManager* settingsManager,
293 	TeamDebugger::Listener* listener, thread_id threadID = -1,
294 	int commandLineArgc = 0, const char* const* commandLineArgv = NULL,
295 	bool stopInMain = false, UserInterface* userInterface = NULL,
296 	status_t* _result = NULL)
297 {
298 	if (teamID < 0)
299 		return NULL;
300 
301 	BReference<UserInterface> userInterfaceReference;
302 	if (userInterface == NULL) {
303 		userInterface = new(std::nothrow) GraphicalUserInterface;
304 		if (userInterface == NULL) {
305 			// TODO: Notify the user!
306 			fprintf(stderr, "Error: Out of memory!\n");
307 			return NULL;
308 		}
309 
310 		userInterfaceReference.SetTo(userInterface, true);
311 	}
312 
313 	status_t error = B_NO_MEMORY;
314 
315 	TeamDebugger* debugger = new(std::nothrow) TeamDebugger(listener,
316 		userInterface, settingsManager);
317 	if (debugger) {
318 		error = debugger->Init(teamID, threadID, commandLineArgc,
319 			commandLineArgv, stopInMain);
320 	}
321 
322 	if (error != B_OK) {
323 		printf("Error: debugger for team %" B_PRId32 " failed to init: %s!\n",
324 			teamID, strerror(error));
325 		delete debugger;
326 		debugger = NULL;
327 	} else
328 		printf("debugger for team %" B_PRId32 " created and initialized "
329 			"successfully!\n", teamID);
330 
331 	if (_result != NULL)
332 		*_result = error;
333 	return debugger;
334 }
335 
336 
337 // #pragma mark - Debugger application class
338 
339 
340 class Debugger : public BApplication, private TeamDebugger::Listener {
341 public:
342 								Debugger();
343 								~Debugger();
344 
345 			status_t			Init();
346 	virtual void 				MessageReceived(BMessage* message);
347 	virtual void 				ReadyToRun();
348 	virtual void 				ArgvReceived(int32 argc, char** argv);
349 
350 private:
351 			typedef BObjectList<TeamDebugger>	TeamDebuggerList;
352 
353 private:
354 	// TeamDebugger::Listener
355 	virtual void 				TeamDebuggerStarted(TeamDebugger* debugger);
356 	virtual	void				TeamDebuggerRestartRequested(
357 									TeamDebugger* debugger);
358 	virtual void 				TeamDebuggerQuit(TeamDebugger* debugger);
359 
360 	virtual bool 				QuitRequested();
361 	virtual void 				Quit();
362 
363 			TeamDebugger* 		_FindTeamDebugger(team_id teamID) const;
364 
365 			status_t			_StartNewTeam(const char* path, const char* args);
366 			status_t			_StartOrFindTeam(Options& options);
367 
368 private:
369 			SettingsManager		fSettingsManager;
370 			TeamDebuggerList	fTeamDebuggers;
371 			int32				fRunningTeamDebuggers;
372 			TeamsWindow*		fTeamsWindow;
373 			StartTeamWindow*	fStartTeamWindow;
374 };
375 
376 
377 // #pragma mark - CliDebugger
378 
379 
380 class CliDebugger : private TeamDebugger::Listener {
381 public:
382 								CliDebugger();
383 								~CliDebugger();
384 
385 			bool				Run(const Options& options);
386 
387 private:
388 	// TeamDebugger::Listener
389 	virtual void 				TeamDebuggerStarted(TeamDebugger* debugger);
390 	virtual	void				TeamDebuggerRestartRequested(
391 									TeamDebugger* debugger);
392 	virtual void 				TeamDebuggerQuit(TeamDebugger* debugger);
393 };
394 
395 
396 class ReportDebugger : private TeamDebugger::Listener  {
397 public:
398 								ReportDebugger();
399 								~ReportDebugger();
400 			bool				Run(const Options& options);
401 
402 private:
403 	// TeamDebugger::Listener
404 	virtual void 				TeamDebuggerStarted(TeamDebugger* debugger);
405 	virtual	void				TeamDebuggerRestartRequested(
406 									TeamDebugger* debugger);
407 	virtual void 				TeamDebuggerQuit(TeamDebugger* debugger);
408 };
409 
410 
411 // #pragma mark - Debugger application class
412 
413 
414 Debugger::Debugger()
415 	:
416 	BApplication(kDebuggerSignature),
417 	fRunningTeamDebuggers(0),
418 	fTeamsWindow(NULL),
419 	fStartTeamWindow(NULL)
420 {
421 }
422 
423 
424 Debugger::~Debugger()
425 {
426 	ValueHandlerRoster::DeleteDefault();
427 	TypeHandlerRoster::DeleteDefault();
428 	ImageDebugLoadingStateHandlerRoster::DeleteDefault();
429 }
430 
431 
432 status_t
433 Debugger::Init()
434 {
435 	status_t error = global_init();
436 	if (error != B_OK)
437 		return error;
438 
439 	return fSettingsManager.Init();
440 }
441 
442 
443 void
444 Debugger::MessageReceived(BMessage* message)
445 {
446 	switch (message->what) {
447 		case MSG_SHOW_TEAMS_WINDOW:
448 		{
449             if (fTeamsWindow) {
450                	fTeamsWindow->Activate(true);
451                	break;
452             }
453 
454            	try {
455 				fTeamsWindow = TeamsWindow::Create(&fSettingsManager);
456 				if (fTeamsWindow != NULL)
457 					fTeamsWindow->Show();
458            	} catch (...) {
459 				// TODO: Notify the user!
460 				fprintf(stderr, "Error: Failed to create Teams window\n");
461            	}
462 			break;
463 		}
464 		case MSG_TEAMS_WINDOW_CLOSED:
465 		{
466 			fTeamsWindow = NULL;
467 			Quit();
468 			break;
469 		}
470 		case MSG_SHOW_START_TEAM_WINDOW:
471 		{
472 			BMessenger messenger(fStartTeamWindow);
473 			if (!messenger.IsValid()) {
474 				fStartTeamWindow = StartTeamWindow::Create();
475 				if (fStartTeamWindow == NULL)
476 					break;
477 				fStartTeamWindow->Show();
478 			} else
479 				fStartTeamWindow->Activate();
480 			break;
481 		}
482 		case MSG_START_TEAM_WINDOW_CLOSED:
483 		{
484 			fStartTeamWindow = NULL;
485 			break;
486 		}
487 		case MSG_DEBUG_THIS_TEAM:
488 		{
489 			int32 teamID;
490 			if (message->FindInt32("team", &teamID) != B_OK)
491 				break;
492 
493 			start_team_debugger(teamID, &fSettingsManager, this);
494 			break;
495 		}
496 		case MSG_START_NEW_TEAM:
497 		{
498 			const char* teamPath = NULL;
499 			const char* args = NULL;
500 
501 			message->FindString("path", &teamPath);
502 			message->FindString("arguments", &args);
503 
504 			status_t result = _StartNewTeam(teamPath, args);
505 			BMessage reply;
506 			reply.AddInt32("status", result);
507 			message->SendReply(&reply);
508 			break;
509 		}
510 		case MSG_TEAM_RESTART_REQUESTED:
511 		{
512 			int32 teamID;
513 			if (message->FindInt32("team", &teamID) != B_OK)
514 				break;
515 			TeamDebugger* debugger = _FindTeamDebugger(teamID);
516 			if (debugger == NULL)
517 				break;
518 
519 			Options options;
520 			options.commandLineArgc = debugger->ArgumentCount();
521 			options.commandLineArgv = debugger->Arguments();
522 
523 			status_t result = _StartOrFindTeam(options);
524 			if (result == B_OK)
525 				debugger->PostMessage(B_QUIT_REQUESTED);
526 
527 			break;
528 		}
529 		case MSG_TEAM_DEBUGGER_QUIT:
530 		{
531 			int32 threadID;
532 			if (message->FindInt32("thread", &threadID) == B_OK)
533 				wait_for_thread(threadID, NULL);
534 
535 			--fRunningTeamDebuggers;
536 			Quit();
537 			break;
538 		}
539 		default:
540 			BApplication::MessageReceived(message);
541 			break;
542 	}
543 }
544 
545 
546 void
547 Debugger::ReadyToRun()
548 {
549 	if (fRunningTeamDebuggers == 0)
550 	   PostMessage(MSG_SHOW_TEAMS_WINDOW);
551 }
552 
553 
554 void
555 Debugger::ArgvReceived(int32 argc, char** argv)
556 {
557 	Options options;
558 	if (!parse_arguments(argc, argv, true, options)) {
559 		printf("Debugger::ArgvReceived(): parsing args failed!\n");
560 		return;
561 	}
562 
563 	_StartOrFindTeam(options);
564 
565 }
566 
567 
568 void
569 Debugger::TeamDebuggerStarted(TeamDebugger* debugger)
570 {
571 	printf("debugger for team %" B_PRId32 " started...\n", debugger->TeamID());
572 
573  	// Note: see TeamDebuggerQuit() note about locking
574 	AutoLocker<Debugger> locker(this);
575 	fTeamDebuggers.AddItem(debugger);
576 	fRunningTeamDebuggers++;
577 	locker.Unlock();
578 }
579 
580 
581 void
582 Debugger::TeamDebuggerQuit(TeamDebugger* debugger)
583 {
584 	// Note: Locking here only works, since we're never locking the other
585 	// way around. If we even need to do that, we'll have to introduce a
586 	// separate lock to protect the list.
587 
588 	printf("debugger for team %" B_PRId32 " quit.\n", debugger->TeamID());
589 
590 	AutoLocker<Debugger> locker(this);
591 	fTeamDebuggers.RemoveItem(debugger);
592 	locker.Unlock();
593 
594 	if (debugger->Thread() >= 0) {
595 		BMessage message(MSG_TEAM_DEBUGGER_QUIT);
596 		message.AddInt32("thread", debugger->Thread());
597 		PostMessage(&message);
598 	}
599 }
600 
601 
602 void
603 Debugger::TeamDebuggerRestartRequested(TeamDebugger* debugger)
604 {
605 	BMessage message(MSG_TEAM_RESTART_REQUESTED);
606 	message.AddInt32("team", debugger->TeamID());
607 	PostMessage(&message);
608 }
609 
610 
611 bool
612 Debugger::QuitRequested()
613 {
614 	// NOTE: The default implementation will just ask all windows'
615 	// QuitRequested() hooks. This in turn will ask the TeamWindows.
616 	// For now, this is what we want. If we have more windows later,
617 	// like the global TeamsWindow, then we want to just ask the
618 	// TeamDebuggers, the TeamsWindow should of course not go away already
619 	// if one or more TeamDebuggers want to stay later. There are multiple
620 	// ways how to do this. For example, TeamDebugger could get a
621 	// QuitRequested() hook or the TeamsWindow and other global windows
622 	// could always return false in their QuitRequested().
623 	return BApplication::QuitRequested();
624 		// TODO: This is ugly. The team debuggers own the windows, not the
625 		// other way around.
626 }
627 
628 void
629 Debugger::Quit()
630 {
631 	// don't quit before all team debuggers have been quit
632 	if (fRunningTeamDebuggers <= 0 && fTeamsWindow == NULL)
633 		BApplication::Quit();
634 }
635 
636 
637 TeamDebugger*
638 Debugger::_FindTeamDebugger(team_id teamID) const
639 {
640 	for (int32 i = 0; TeamDebugger* debugger = fTeamDebuggers.ItemAt(i);
641 			i++) {
642 		if (debugger->TeamID() == teamID)
643 			return debugger;
644 	}
645 
646 	return NULL;
647 }
648 
649 
650 status_t
651 Debugger::_StartNewTeam(const char* path, const char* args)
652 {
653 	if (path == NULL)
654 		return B_BAD_VALUE;
655 
656 	BString data;
657 	data.SetToFormat("\"%s\" %s", path, args);
658 	if (data.Length() == 0)
659 		return B_NO_MEMORY;
660 
661 	ArgumentVector argVector;
662 	argVector.Parse(data.String());
663 
664 	Options options;
665 	options.commandLineArgc = argVector.ArgumentCount();
666 	if (options.commandLineArgc <= 0)
667 		return B_BAD_VALUE;
668 
669 	char** argv = argVector.DetachArguments();
670 
671 	options.commandLineArgv = argv;
672 	MemoryDeleter deleter(argv);
673 
674 	return _StartOrFindTeam(options);
675 }
676 
677 
678 status_t
679 Debugger::_StartOrFindTeam(Options& options)
680 {
681 	DebuggedProgramInfo programInfo;
682 	if (!get_debugged_program(options, programInfo))
683 		return B_BAD_VALUE;
684 
685 	TeamDebugger* debugger = _FindTeamDebugger(programInfo.team);
686 	if (debugger != NULL) {
687 		printf("There's already a debugger for team: %" B_PRId32 "\n",
688 			programInfo.team);
689 		debugger->Activate();
690 		return B_OK;
691 	}
692 
693 	status_t result;
694 	start_team_debugger(programInfo.team, &fSettingsManager, this,
695 		programInfo.thread, programInfo.commandLineArgc,
696 		programInfo.commandLineArgv, programInfo.stopInMain, NULL, &result);
697 
698 	return result;
699 }
700 
701 
702 // #pragma mark - CliDebugger
703 
704 
705 CliDebugger::CliDebugger()
706 {
707 }
708 
709 
710 CliDebugger::~CliDebugger()
711 {
712 }
713 
714 
715 bool
716 CliDebugger::Run(const Options& options)
717 {
718 	// Block SIGINT, in this thread so all threads created by it inherit the
719 	// a block mask with the signal blocked. In the input loop the signal will
720 	// be unblocked again.
721 	SignalSet(SIGINT).BlockInCurrentThread();
722 
723 	// initialize global objects and settings manager
724 	status_t error = global_init();
725 	if (error != B_OK) {
726 		fprintf(stderr, "Error: Global initialization failed: %s\n",
727 			strerror(error));
728 		return false;
729 	}
730 
731 	SettingsManager settingsManager;
732 	error = settingsManager.Init();
733 	if (error != B_OK) {
734 		fprintf(stderr, "Error: Settings manager initialization failed: "
735 			"%s\n", strerror(error));
736 		return false;
737 	}
738 
739 	// create the command line UI
740 	CommandLineUserInterface* userInterface
741 		= new(std::nothrow) CommandLineUserInterface();
742 	if (userInterface == NULL) {
743 		fprintf(stderr, "Error: Out of memory!\n");
744 		return false;
745 	}
746 	BReference<UserInterface> userInterfaceReference(userInterface, true);
747 
748 	// get/run the program to be debugged and start the team debugger
749 	DebuggedProgramInfo programInfo;
750 	if (!get_debugged_program(options, programInfo))
751 		return false;
752 
753 	TeamDebugger* teamDebugger = start_team_debugger(programInfo.team,
754 		&settingsManager, this, programInfo.thread,
755 		programInfo.commandLineArgc, programInfo.commandLineArgv,
756 		programInfo.stopInMain, userInterface);
757 	if (teamDebugger == NULL)
758 		return false;
759 
760 	thread_id teamDebuggerThread = teamDebugger->Thread();
761 
762 	// run the input loop
763 	userInterface->Run();
764 
765 	// wait for the team debugger thread to terminate
766 	wait_for_thread(teamDebuggerThread, NULL);
767 
768 	return true;
769 }
770 
771 
772 void
773 CliDebugger::TeamDebuggerStarted(TeamDebugger* debugger)
774 {
775 }
776 
777 
778 void
779 CliDebugger::TeamDebuggerRestartRequested(TeamDebugger* debugger)
780 {
781 	// TODO: implement
782 }
783 
784 
785 void
786 CliDebugger::TeamDebuggerQuit(TeamDebugger* debugger)
787 {
788 }
789 
790 
791 // #pragma mark - ReportDebugger
792 
793 
794 ReportDebugger::ReportDebugger()
795 {
796 }
797 
798 
799 ReportDebugger::~ReportDebugger()
800 {
801 }
802 
803 
804 bool
805 ReportDebugger::Run(const Options& options)
806 {
807 	// initialize global objects and settings manager
808 	status_t error = global_init();
809 	if (error != B_OK) {
810 		fprintf(stderr, "Error: Global initialization failed: %s\n",
811 			strerror(error));
812 		return false;
813 	}
814 
815 	SettingsManager settingsManager;
816 	error = settingsManager.Init();
817 	if (error != B_OK) {
818 		fprintf(stderr, "Error: Settings manager initialization failed: "
819 			"%s\n", strerror(error));
820 		return false;
821 	}
822 
823 	// create the report UI
824 	ReportUserInterface* userInterface
825 		= new(std::nothrow) ReportUserInterface(options.thread, options.reportPath);
826 	if (userInterface == NULL) {
827 		fprintf(stderr, "Error: Out of memory!\n");
828 		return false;
829 	}
830 	BReference<UserInterface> userInterfaceReference(userInterface, true);
831 
832 	// get/run the program to be debugged and start the team debugger
833 	DebuggedProgramInfo programInfo;
834 	if (!get_debugged_program(options, programInfo))
835 		return false;
836 
837 	TeamDebugger* teamDebugger = start_team_debugger(programInfo.team,
838 		&settingsManager, this, programInfo.thread,
839 		programInfo.commandLineArgc, programInfo.commandLineArgv,
840 		programInfo.stopInMain, userInterface);
841 	if (teamDebugger == NULL)
842 		return false;
843 
844 	thread_id teamDebuggerThread = teamDebugger->Thread();
845 
846 	// run the input loop
847 	userInterface->Run();
848 
849 	// wait for the team debugger thread to terminate
850 	wait_for_thread(teamDebuggerThread, NULL);
851 
852 	return true;
853 }
854 
855 
856 void
857 ReportDebugger::TeamDebuggerStarted(TeamDebugger* debugger)
858 {
859 }
860 
861 
862 void
863 ReportDebugger::TeamDebuggerRestartRequested(TeamDebugger* debugger)
864 {
865 }
866 
867 
868 void
869 ReportDebugger::TeamDebuggerQuit(TeamDebugger* debugger)
870 {
871 }
872 
873 
874 // #pragma mark -
875 
876 
877 int
878 main(int argc, const char* const* argv)
879 {
880 	// We test-parse the arguments here, so that, when we're started from the
881 	// terminal and there's an instance already running, we can print an error
882 	// message to the terminal, if something's wrong with the arguments.
883 	// Otherwise, the arguments are reparsed in the actual application,
884 	// unless the option to use the command line interface was chosen.
885 
886 	Options options;
887 	parse_arguments(argc, argv, false, options);
888 
889 	if (options.useCLI) {
890 		CliDebugger debugger;
891 		return debugger.Run(options) ? 0 : 1;
892 	} else if (options.saveReport) {
893 		ReportDebugger debugger;
894 		return debugger.Run(options) ? 0 : 1;
895 	}
896 
897 	Debugger app;
898 	status_t error = app.Init();
899 	if (error != B_OK) {
900 		fprintf(stderr, "Error: Failed to init application: %s\n",
901 			strerror(error));
902 		return 1;
903 	}
904 
905 	app.Run();
906 
907 	return 0;
908 }
909