xref: /haiku/src/apps/debugger/Debugger.cpp (revision be9a70562e3c6552efb0caa53bd26965e7e1bed7)
1 /*
2  * Copyright 2009-2016, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Copyright 2011-2016, 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 <Entry.h>
17 #include <Message.h>
18 #include <ObjectList.h>
19 #include <Path.h>
20 
21 #include <ArgumentVector.h>
22 #include <AutoDeleter.h>
23 #include <AutoLocker.h>
24 
25 #include "AppMessageCodes.h"
26 #include "CommandLineUserInterface.h"
27 #include "ConnectionConfigHandlerRoster.h"
28 #include "ConnectionConfigWindow.h"
29 #include "DebuggerGlobals.h"
30 #include "DebuggerSettingsManager.h"
31 #include "DebuggerUiSettingsFactory.h"
32 #include "ElfFile.h"
33 #include "GraphicalUserInterface.h"
34 #include "MessageCodes.h"
35 #include "ReportUserInterface.h"
36 #include "SignalSet.h"
37 #include "StartTeamWindow.h"
38 #include "TargetHostInterface.h"
39 #include "TargetHostInterfaceRoster.h"
40 #include "TeamDebugger.h"
41 #include "TeamsWindow.h"
42 #include "ValueHandlerRoster.h"
43 
44 
45 extern const char* __progname;
46 const char* kProgramName = __progname;
47 
48 static const char* const kDebuggerSignature
49 	= "application/x-vnd.Haiku-Debugger";
50 
51 
52 static const char* const kUsage =
53 	"Usage: %s [ <options> ]\n"
54 	"       %s [ <options> ] <command line>\n"
55 	"       %s [ <options> ] --team <team>\n"
56 	"       %s [ <options> ] --thread <thread>\n"
57 	"       %s [ <options> ] --core <file>\n"
58 	"\n"
59 	"The first form starts the debugger displaying a requester to choose a\n"
60 	"running team to debug respectively to specify the program to run and\n"
61 	"debug.\n"
62 	"\n"
63 	"The second form runs the given command line and attaches the debugger to\n"
64 	"the new team. Unless specified otherwise the program will be stopped at\n"
65 	"the beginning of its main() function.\n"
66 	"\n"
67 	"The third and fourth forms attach the debugger to a running team. The\n"
68 	"fourth form additionally stops the specified thread.\n"
69 	"\n"
70 	"The fifth form loads a core file.\n"
71 	"\n"
72 	"Options:\n"
73 	"  -h, --help        - Print this usage info and exit.\n"
74 	"  -c, --cli         - Use command line user interface\n"
75 	"  -s, --save-report - Save crash report for the targetted team and exit.\n"
76 	"                      Implies --cli.\n"
77 ;
78 
79 
80 static void
81 print_usage_and_exit(bool error)
82 {
83     fprintf(error ? stderr : stdout, kUsage, kProgramName, kProgramName,
84     	kProgramName, kProgramName, kProgramName);
85     exit(error ? 1 : 0);
86 }
87 
88 
89 struct Options {
90 	int					commandLineArgc;
91 	const char* const*	commandLineArgv;
92 	team_id				team;
93 	thread_id			thread;
94 	bool				useCLI;
95 	bool				saveReport;
96 	const char*			reportPath;
97 	const char*			coreFilePath;
98 
99 	Options()
100 		:
101 		commandLineArgc(0),
102 		commandLineArgv(NULL),
103 		team(-1),
104 		thread(-1),
105 		useCLI(false),
106 		saveReport(false),
107 		reportPath(NULL),
108 		coreFilePath(NULL)
109 	{
110 	}
111 };
112 
113 
114 static void
115 set_debugger_options_from_options(TeamDebuggerOptions& _debuggerOptions,
116 	const Options& options)
117 {
118 	_debuggerOptions.commandLineArgc = options.commandLineArgc;
119 	_debuggerOptions.commandLineArgv = options.commandLineArgv;
120 	_debuggerOptions.team = options.team;
121 	_debuggerOptions.thread = options.thread;
122 	_debuggerOptions.coreFilePath = options.coreFilePath;
123 
124 	if (options.coreFilePath != NULL)
125 		_debuggerOptions.requestType = TEAM_DEBUGGER_REQUEST_LOAD_CORE;
126 	else if (options.commandLineArgc != 0)
127 		_debuggerOptions.requestType = TEAM_DEBUGGER_REQUEST_CREATE;
128 	else
129 		_debuggerOptions.requestType = TEAM_DEBUGGER_REQUEST_ATTACH;
130 }
131 
132 
133 
134 static bool
135 parse_arguments(int argc, const char* const* argv, bool noOutput,
136 	Options& options)
137 {
138 	optind = 1;
139 
140 	while (true) {
141 		static struct option sLongOptions[] = {
142 			{ "help", no_argument, 0, 'h' },
143 			{ "cli", no_argument, 0, 'c' },
144 			{ "save-report", optional_argument, 0, 's' },
145 			{ "team", required_argument, 0, 't' },
146 			{ "thread", required_argument, 0, 'T' },
147 			{ "core", required_argument, 0, 'C' },
148 			{ 0, 0, 0, 0 }
149 		};
150 
151 		opterr = 0; // don't print errors
152 
153 		int c = getopt_long(argc, (char**)argv, "+chs", sLongOptions, NULL);
154 		if (c == -1)
155 			break;
156 
157 		switch (c) {
158 			case 'c':
159 				options.useCLI = true;
160 				break;
161 
162 			case 'C':
163 				options.coreFilePath = optarg;
164 				break;
165 
166 			case 'h':
167 				if (noOutput)
168 					return false;
169 				print_usage_and_exit(false);
170 				break;
171 
172 			case 's':
173 			{
174 				options.saveReport = true;
175 				options.reportPath = optarg;
176 				break;
177 			}
178 
179 			case 't':
180 			{
181 				options.team = strtol(optarg, NULL, 0);
182 				if (options.team <= 0) {
183 					if (noOutput)
184 						return false;
185 					print_usage_and_exit(true);
186 				}
187 				break;
188 			}
189 
190 			case 'T':
191 			{
192 				options.thread = strtol(optarg, NULL, 0);
193 				if (options.thread <= 0) {
194 					if (noOutput)
195 						return false;
196 					print_usage_and_exit(true);
197 				}
198 				break;
199 			}
200 
201 			default:
202 				if (noOutput)
203 					return false;
204 				print_usage_and_exit(true);
205 				break;
206 		}
207 	}
208 
209 	if (optind < argc) {
210 		options.commandLineArgc = argc - optind;
211 		options.commandLineArgv = argv + optind;
212 	}
213 
214 	int exclusiveParams = 0;
215 	if (options.team > 0)
216 		exclusiveParams++;
217 	if (options.thread > 0)
218 		exclusiveParams++;
219 	if (options.commandLineArgc > 0)
220 		exclusiveParams++;
221 
222 	if (exclusiveParams == 0) {
223 		return true;
224 	} else if (exclusiveParams != 1) {
225 		if (noOutput)
226 			return false;
227 		print_usage_and_exit(true);
228 	}
229 
230 	return true;
231 }
232 
233 
234 // #pragma mark - Debugger application class
235 
236 
237 class Debugger : public BApplication,
238 	private TargetHostInterfaceRoster::Listener {
239 public:
240 								Debugger();
241 								~Debugger();
242 
243 			status_t			Init();
244 	virtual void 				MessageReceived(BMessage* message);
245 	virtual void 				ReadyToRun();
246 	virtual void 				ArgvReceived(int32 argc, char** argv);
247 	virtual	void				RefsReceived(BMessage* message);
248 
249 private:
250 	virtual bool 				QuitRequested();
251 	virtual void 				Quit();
252 
253 	// TargetHostInterfaceRoster::Listener
254 	virtual	void				TeamDebuggerCountChanged(int32 count);
255 
256 private:
257 			status_t			_StartNewTeam(TargetHostInterface* interface,
258 									const char* teamPath, const char* args);
259 			status_t			_HandleOptions(const Options& options);
260 
261 private:
262 			DebuggerSettingsManager fSettingsManager;
263 			ConnectionConfigWindow* fConnectionWindow;
264 			TeamsWindow*		fTeamsWindow;
265 			StartTeamWindow*	fStartTeamWindow;
266 };
267 
268 
269 // #pragma mark - CliDebugger
270 
271 
272 class CliDebugger : private TargetHostInterfaceRoster::Listener {
273 public:
274 								CliDebugger();
275 								~CliDebugger();
276 
277 			bool				Run(const Options& options);
278 };
279 
280 
281 class ReportDebugger : private TargetHostInterfaceRoster::Listener  {
282 public:
283 								ReportDebugger();
284 								~ReportDebugger();
285 			bool				Run(const Options& options);
286 };
287 
288 
289 // #pragma mark - Debugger application class
290 
291 
292 Debugger::Debugger()
293 	:
294 	BApplication(kDebuggerSignature),
295 	TargetHostInterfaceRoster::Listener(),
296 	fConnectionWindow(NULL),
297 	fTeamsWindow(NULL),
298 	fStartTeamWindow(NULL)
299 {
300 }
301 
302 
303 Debugger::~Debugger()
304 {
305 	DebuggerUiSettingsFactory::DeleteDefault();
306 	ValueHandlerRoster::DeleteDefault();
307 	ConnectionConfigHandlerRoster::DeleteDefault();
308 
309 	debugger_global_uninit();
310 }
311 
312 
313 status_t
314 Debugger::Init()
315 {
316 	status_t error = debugger_global_init(this);
317 	if (error != B_OK)
318 		return error;
319 
320 	error = DebuggerUiSettingsFactory::CreateDefault();
321 	if (error != B_OK)
322 		return error;
323 
324 	error = ValueHandlerRoster::CreateDefault();
325 	if (error != B_OK)
326 		return error;
327 
328 	error = ConnectionConfigHandlerRoster::CreateDefault();
329 	if (error != B_OK)
330 		return error;
331 
332 	return fSettingsManager.Init(DebuggerUiSettingsFactory::Default());
333 }
334 
335 
336 void
337 Debugger::MessageReceived(BMessage* message)
338 {
339 	switch (message->what) {
340 		case MSG_SHOW_TEAMS_WINDOW:
341 		{
342 			if (fTeamsWindow) {
343 				fTeamsWindow->Activate(true);
344 				break;
345 			}
346 
347 			try {
348 				fTeamsWindow = TeamsWindow::Create(&fSettingsManager);
349 				if (fTeamsWindow != NULL)
350 					fTeamsWindow->Show();
351 			} catch (...) {
352 				// TODO: Notify the user!
353 				fprintf(stderr, "Error: Failed to create Teams window\n");
354 			}
355 			break;
356 		}
357 		case MSG_TEAMS_WINDOW_CLOSED:
358 		{
359 			fTeamsWindow = NULL;
360 			Quit();
361 			break;
362 		}
363 		case MSG_SHOW_START_TEAM_WINDOW:
364 		{
365 			TargetHostInterface* hostInterface;
366 			if (message->FindPointer("interface",
367 					reinterpret_cast<void**>(&hostInterface)) != B_OK) {
368 				// if an interface isn't explicitly supplied, fall back to
369 				// the default local interface.
370 				hostInterface = TargetHostInterfaceRoster::Default()
371 					->ActiveInterfaceAt(0);
372 			}
373 
374 			BMessenger messenger(fStartTeamWindow);
375 			if (!messenger.IsValid()) {
376 				fStartTeamWindow = StartTeamWindow::Create(hostInterface);
377 				if (fStartTeamWindow == NULL)
378 					break;
379 				fStartTeamWindow->Show();
380 			} else
381 				fStartTeamWindow->Activate();
382 			break;
383 		}
384 		case MSG_START_TEAM_WINDOW_CLOSED:
385 		{
386 			fStartTeamWindow = NULL;
387 			break;
388 		}
389 		case MSG_SHOW_CONNECTION_CONFIG_WINDOW:
390 		{
391 			if (fConnectionWindow != NULL) {
392 				fConnectionWindow->Activate(true);
393 				break;
394 			}
395 
396 			try {
397 				fConnectionWindow = ConnectionConfigWindow::Create();
398 				if (fConnectionWindow != NULL)
399 					fConnectionWindow->Show();
400 			} catch (...) {
401 				// TODO: Notify the user!
402 				fprintf(stderr, "Error: Failed to create Teams window\n");
403 			}
404 			break;
405 		}
406 		case MSG_CONNECTION_CONFIG_WINDOW_CLOSED:
407 		{
408 			fConnectionWindow = NULL;
409 			break;
410 		}
411 		case MSG_DEBUG_THIS_TEAM:
412 		{
413 			team_id teamID;
414 			if (message->FindInt32("team", &teamID) != B_OK)
415 				break;
416 
417 			TargetHostInterface* interface;
418 			if (message->FindPointer("interface", reinterpret_cast<void**>(
419 					&interface)) != B_OK) {
420 				break;
421 			}
422 
423 			TeamDebuggerOptions options;
424 			options.requestType = TEAM_DEBUGGER_REQUEST_ATTACH;
425 			options.settingsManager = &fSettingsManager;
426 			options.team = teamID;
427 			options.userInterface = new(std::nothrow) GraphicalUserInterface;
428 			if (options.userInterface == NULL) {
429 				// TODO: notify user.
430 				break;
431 			}
432 			BReference<UserInterface> uiReference(options.userInterface, true);
433 			status_t error = interface->StartTeamDebugger(options);
434 			if (error != B_OK) {
435 				// TODO: notify user.
436 			} else
437 				uiReference.Detach();
438 			break;
439 		}
440 		case MSG_START_NEW_TEAM:
441 		{
442 			TargetHostInterface* interface;
443 			if (message->FindPointer("interface", reinterpret_cast<void**>(
444 					&interface)) != B_OK) {
445 				break;
446 			}
447 
448 			const char* teamPath = NULL;
449 			const char* args = NULL;
450 
451 			message->FindString("path", &teamPath);
452 			message->FindString("arguments", &args);
453 
454 			status_t result = _StartNewTeam(interface, teamPath, args);
455 			BMessage reply;
456 			reply.AddInt32("status", result);
457 			message->SendReply(&reply);
458 			break;
459 		}
460 		case MSG_LOAD_CORE_TEAM:
461 		{
462 			TargetHostInterface* interface;
463 			if (message->FindPointer("interface", reinterpret_cast<void**>(
464 					&interface)) != B_OK) {
465 				break;
466 			}
467 
468 			entry_ref ref;
469 			if (message->FindRef("core", &ref) != B_OK)
470 				break;
471 
472 			BPath path(&ref);
473 			if (path.InitCheck() != B_OK)
474 				break;
475 
476 			Options options;
477 			options.coreFilePath = path.Path();
478 			_HandleOptions(options);
479 			break;
480 		}
481 		default:
482 			BApplication::MessageReceived(message);
483 			break;
484 	}
485 }
486 
487 
488 void
489 Debugger::ReadyToRun()
490 {
491 	TargetHostInterfaceRoster* roster = TargetHostInterfaceRoster::Default();
492 	AutoLocker<TargetHostInterfaceRoster> lock(roster);
493 	if (roster->CountRunningTeamDebuggers() == 0)
494 		PostMessage(MSG_SHOW_TEAMS_WINDOW);
495 }
496 
497 
498 void
499 Debugger::ArgvReceived(int32 argc, char** argv)
500 {
501 	Options options;
502 	if (!parse_arguments(argc, argv, true, options)) {
503 		printf("Debugger::ArgvReceived(): parsing args failed!\n");
504 		return;
505 	}
506 
507 	_HandleOptions(options);
508 }
509 
510 
511 void
512 Debugger::RefsReceived(BMessage* message)
513 {
514 	// iterate through the refs and handle the files we can handle
515 	entry_ref ref;
516 	for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) {
517 		BPath path;
518 		if (path.SetTo(&ref) != B_OK)
519 			continue;
520 
521 		// check, whether this is a core file
522 		{
523 			ElfFile elfFile;
524 			if (elfFile.Init(path.Path()) != B_OK || elfFile.Type() != ET_CORE)
525 				continue;
526 		}
527 
528 		// handle the core file
529 		Options options;
530 		options.coreFilePath = path.Path();
531 		_HandleOptions(options);
532 	}
533 }
534 
535 
536 bool
537 Debugger::QuitRequested()
538 {
539 	// NOTE: The default implementation will just ask all windows'
540 	// QuitRequested() hooks. This in turn will ask the TeamWindows.
541 	// For now, this is what we want. If we have more windows later,
542 	// like the global TeamsWindow, then we want to just ask the
543 	// TeamDebuggers, the TeamsWindow should of course not go away already
544 	// if one or more TeamDebuggers want to stay later. There are multiple
545 	// ways how to do this. For example, TeamDebugger could get a
546 	// QuitRequested() hook or the TeamsWindow and other global windows
547 	// could always return false in their QuitRequested().
548 	return BApplication::QuitRequested();
549 		// TODO: This is ugly. The team debuggers own the windows, not the
550 		// other way around.
551 }
552 
553 void
554 Debugger::Quit()
555 {
556 	TargetHostInterfaceRoster* roster = TargetHostInterfaceRoster::Default();
557 	AutoLocker<TargetHostInterfaceRoster> lock(roster);
558 	// don't quit before all team debuggers have been quit
559 	if (roster->CountRunningTeamDebuggers() == 0 && fTeamsWindow == NULL)
560 		BApplication::Quit();
561 }
562 
563 
564 void
565 Debugger::TeamDebuggerCountChanged(int32 count)
566 {
567 	if (count == 0) {
568 		AutoLocker<Debugger> lock(this);
569 		Quit();
570 	}
571 }
572 
573 
574 status_t
575 Debugger::_StartNewTeam(TargetHostInterface* interface, const char* path,
576 	const char* args)
577 {
578 	if (path == NULL)
579 		return B_BAD_VALUE;
580 
581 	BString data;
582 	data.SetToFormat("\"%s\" %s", path, args);
583 	if (data.Length() == 0)
584 		return B_NO_MEMORY;
585 
586 	ArgumentVector argVector;
587 	argVector.Parse(data.String());
588 
589 	TeamDebuggerOptions options;
590 	options.requestType = TEAM_DEBUGGER_REQUEST_CREATE;
591 	options.settingsManager = &fSettingsManager;
592 	options.userInterface = new(std::nothrow) GraphicalUserInterface;
593 	if (options.userInterface == NULL)
594 		return B_NO_MEMORY;
595 	BReference<UserInterface> uiReference(options.userInterface, true);
596 	options.commandLineArgc = argVector.ArgumentCount();
597 	if (options.commandLineArgc <= 0)
598 		return B_BAD_VALUE;
599 
600 	char** argv = argVector.DetachArguments();
601 
602 	options.commandLineArgv = argv;
603 	MemoryDeleter deleter(argv);
604 
605 	status_t error = interface->StartTeamDebugger(options);
606 	if (error == B_OK) {
607 		deleter.Detach();
608 		uiReference.Detach();
609 	}
610 
611 	return error;
612 }
613 
614 
615 status_t
616 Debugger::_HandleOptions(const Options& options)
617 {
618 	TeamDebuggerOptions debuggerOptions;
619 	set_debugger_options_from_options(debuggerOptions, options);
620 	debuggerOptions.settingsManager = &fSettingsManager;
621 	debuggerOptions.userInterface = new(std::nothrow) GraphicalUserInterface;
622 	if (debuggerOptions.userInterface == NULL)
623 		return B_NO_MEMORY;
624 	BReference<UserInterface> uiReference(debuggerOptions.userInterface, true);
625 	TargetHostInterface* hostInterface
626 		= TargetHostInterfaceRoster::Default()->ActiveInterfaceAt(0);
627 	status_t error = hostInterface->StartTeamDebugger(debuggerOptions);
628 	if (error == B_OK)
629 		uiReference.Detach();
630 
631 	return error;
632 }
633 
634 
635 // #pragma mark - CliDebugger
636 
637 
638 CliDebugger::CliDebugger()
639 {
640 }
641 
642 
643 CliDebugger::~CliDebugger()
644 {
645 	DebuggerUiSettingsFactory::DeleteDefault();
646 	debugger_global_uninit();
647 }
648 
649 
650 bool
651 CliDebugger::Run(const Options& options)
652 {
653 	// Block SIGINT, in this thread so all threads created by it inherit the
654 	// a block mask with the signal blocked. In the input loop the signal will
655 	// be unblocked again.
656 	SignalSet(SIGINT).BlockInCurrentThread();
657 
658 	// initialize global objects and settings manager
659 	status_t error = debugger_global_init(this);
660 	if (error != B_OK) {
661 		fprintf(stderr, "Error: Global initialization failed: %s\n",
662 			strerror(error));
663 		return false;
664 	}
665 
666 	error = DebuggerUiSettingsFactory::CreateDefault();
667 	if (error != B_OK) {
668 		fprintf(stderr, "Error: Failed to create default settings factory: "
669 			"%s\n",	strerror(error));
670 		return false;
671 	}
672 
673 
674 	DebuggerSettingsManager settingsManager;
675 	error = settingsManager.Init(DebuggerUiSettingsFactory::Default());
676 	if (error != B_OK) {
677 		fprintf(stderr, "Error: Settings manager initialization failed: "
678 			"%s\n", strerror(error));
679 		return false;
680 	}
681 
682 	// create the command line UI
683 	CommandLineUserInterface* userInterface
684 		= new(std::nothrow) CommandLineUserInterface();
685 	if (userInterface == NULL) {
686 		fprintf(stderr, "Error: Out of memory!\n");
687 		return false;
688 	}
689 	BReference<UserInterface> userInterfaceReference(userInterface, true);
690 
691 	// TODO: once we support specifying a remote interface via command line
692 	// args, this needs to be adjusted. For now, always assume actions
693 	// are being taken via the local interface.
694 	TargetHostInterface* hostInterface
695 		= TargetHostInterfaceRoster::Default()->ActiveInterfaceAt(0);
696 
697 	TeamDebuggerOptions debuggerOptions;
698 	set_debugger_options_from_options(debuggerOptions, options);
699 	debuggerOptions.userInterface = userInterface;
700 	debuggerOptions.settingsManager = &settingsManager;
701 	error = hostInterface->StartTeamDebugger(debuggerOptions);
702 	if (error != B_OK)
703 		return false;
704 
705 	TeamDebugger* teamDebugger = hostInterface->TeamDebuggerAt(0);
706 	thread_id teamDebuggerThread = teamDebugger->Thread();
707 
708 	// run the input loop
709 	userInterface->Run();
710 
711 	// wait for the team debugger thread to terminate
712 	wait_for_thread(teamDebuggerThread, NULL);
713 
714 	return true;
715 }
716 
717 
718 // #pragma mark - ReportDebugger
719 
720 
721 ReportDebugger::ReportDebugger()
722 {
723 }
724 
725 
726 ReportDebugger::~ReportDebugger()
727 {
728 	debugger_global_uninit();
729 }
730 
731 
732 bool
733 ReportDebugger::Run(const Options& options)
734 {
735 	// initialize global objects and settings manager
736 	status_t error = debugger_global_init(this);
737 	if (error != B_OK) {
738 		fprintf(stderr, "Error: Global initialization failed: %s\n",
739 			strerror(error));
740 		return false;
741 	}
742 
743 	// create the report UI
744 	ReportUserInterface* userInterface
745 		= new(std::nothrow) ReportUserInterface(options.thread, options.reportPath);
746 	if (userInterface == NULL) {
747 		fprintf(stderr, "Error: Out of memory!\n");
748 		return false;
749 	}
750 	BReference<UserInterface> userInterfaceReference(userInterface, true);
751 
752 	TargetHostInterface* hostInterface
753 		= TargetHostInterfaceRoster::Default()->ActiveInterfaceAt(0);
754 
755 	TeamDebuggerOptions debuggerOptions;
756 	set_debugger_options_from_options(debuggerOptions, options);
757 	debuggerOptions.userInterface = userInterface;
758 	error = hostInterface->StartTeamDebugger(debuggerOptions);
759 	if (error != B_OK)
760 		return false;
761 
762 	TeamDebugger* teamDebugger = hostInterface->TeamDebuggerAt(0);
763 	thread_id teamDebuggerThread = teamDebugger->Thread();
764 
765 	// run the input loop
766 	userInterface->Run();
767 
768 	// wait for the team debugger thread to terminate
769 	wait_for_thread(teamDebuggerThread, NULL);
770 
771 	return true;
772 }
773 
774 
775 // #pragma mark -
776 
777 
778 int
779 main(int argc, const char* const* argv)
780 {
781 	// We test-parse the arguments here, so that, when we're started from the
782 	// terminal and there's an instance already running, we can print an error
783 	// message to the terminal, if something's wrong with the arguments.
784 	// Otherwise, the arguments are reparsed in the actual application,
785 	// unless the option to use the command line interface was chosen.
786 
787 	Options options;
788 	parse_arguments(argc, argv, false, options);
789 
790 	if (options.useCLI) {
791 		CliDebugger debugger;
792 		return debugger.Run(options) ? 0 : 1;
793 	} else if (options.saveReport) {
794 		ReportDebugger debugger;
795 		return debugger.Run(options) ? 0 : 1;
796 	}
797 
798 	Debugger app;
799 	status_t error = app.Init();
800 	if (error != B_OK) {
801 		fprintf(stderr, "Error: Failed to init application: %s\n",
802 			strerror(error));
803 		return 1;
804 	}
805 
806 	app.Run();
807 
808 	return 0;
809 }
810