xref: /haiku/src/apps/debugger/Debugger.cpp (revision 21258e2674226d6aa732321b6f8494841895af5f)
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 			}
437 			break;
438 		}
439 		case MSG_START_NEW_TEAM:
440 		{
441 			TargetHostInterface* interface;
442 			if (message->FindPointer("interface", reinterpret_cast<void**>(
443 					&interface)) != B_OK) {
444 				break;
445 			}
446 
447 			const char* teamPath = NULL;
448 			const char* args = NULL;
449 
450 			message->FindString("path", &teamPath);
451 			message->FindString("arguments", &args);
452 
453 			status_t result = _StartNewTeam(interface, teamPath, args);
454 			BMessage reply;
455 			reply.AddInt32("status", result);
456 			message->SendReply(&reply);
457 			break;
458 		}
459 		case MSG_LOAD_CORE_TEAM:
460 		{
461 			TargetHostInterface* interface;
462 			if (message->FindPointer("interface", reinterpret_cast<void**>(
463 					&interface)) != B_OK) {
464 				break;
465 			}
466 
467 			entry_ref ref;
468 			if (message->FindRef("core", &ref) != B_OK)
469 				break;
470 
471 			BPath path(&ref);
472 			if (path.InitCheck() != B_OK)
473 				break;
474 
475 			Options options;
476 			options.coreFilePath = path.Path();
477 			_HandleOptions(options);
478 			break;
479 		}
480 		default:
481 			BApplication::MessageReceived(message);
482 			break;
483 	}
484 }
485 
486 
487 void
488 Debugger::ReadyToRun()
489 {
490 	TargetHostInterfaceRoster* roster = TargetHostInterfaceRoster::Default();
491 	AutoLocker<TargetHostInterfaceRoster> lock(roster);
492 	if (roster->CountRunningTeamDebuggers() == 0)
493 		PostMessage(MSG_SHOW_TEAMS_WINDOW);
494 }
495 
496 
497 void
498 Debugger::ArgvReceived(int32 argc, char** argv)
499 {
500 	Options options;
501 	if (!parse_arguments(argc, argv, true, options)) {
502 		printf("Debugger::ArgvReceived(): parsing args failed!\n");
503 		return;
504 	}
505 
506 	_HandleOptions(options);
507 }
508 
509 
510 void
511 Debugger::RefsReceived(BMessage* message)
512 {
513 	// iterate through the refs and handle the files we can handle
514 	entry_ref ref;
515 	for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) {
516 		BPath path;
517 		if (path.SetTo(&ref) != B_OK)
518 			continue;
519 
520 		// check, whether this is a core file
521 		{
522 			ElfFile elfFile;
523 			if (elfFile.Init(path.Path()) != B_OK || elfFile.Type() != ET_CORE)
524 				continue;
525 		}
526 
527 		// handle the core file
528 		Options options;
529 		options.coreFilePath = path.Path();
530 		_HandleOptions(options);
531 	}
532 }
533 
534 
535 bool
536 Debugger::QuitRequested()
537 {
538 	// NOTE: The default implementation will just ask all windows'
539 	// QuitRequested() hooks. This in turn will ask the TeamWindows.
540 	// For now, this is what we want. If we have more windows later,
541 	// like the global TeamsWindow, then we want to just ask the
542 	// TeamDebuggers, the TeamsWindow should of course not go away already
543 	// if one or more TeamDebuggers want to stay later. There are multiple
544 	// ways how to do this. For example, TeamDebugger could get a
545 	// QuitRequested() hook or the TeamsWindow and other global windows
546 	// could always return false in their QuitRequested().
547 	return BApplication::QuitRequested();
548 		// TODO: This is ugly. The team debuggers own the windows, not the
549 		// other way around.
550 }
551 
552 void
553 Debugger::Quit()
554 {
555 	TargetHostInterfaceRoster* roster = TargetHostInterfaceRoster::Default();
556 	AutoLocker<TargetHostInterfaceRoster> lock(roster);
557 	// don't quit before all team debuggers have been quit
558 	if (roster->CountRunningTeamDebuggers() == 0 && fTeamsWindow == NULL)
559 		BApplication::Quit();
560 }
561 
562 
563 void
564 Debugger::TeamDebuggerCountChanged(int32 count)
565 {
566 	if (count == 0) {
567 		AutoLocker<Debugger> lock(this);
568 		Quit();
569 	}
570 }
571 
572 
573 status_t
574 Debugger::_StartNewTeam(TargetHostInterface* interface, const char* path,
575 	const char* args)
576 {
577 	if (path == NULL)
578 		return B_BAD_VALUE;
579 
580 	BString data;
581 	data.SetToFormat("\"%s\" %s", path, args);
582 	if (data.Length() == 0)
583 		return B_NO_MEMORY;
584 
585 	ArgumentVector argVector;
586 	argVector.Parse(data.String());
587 
588 	TeamDebuggerOptions options;
589 	options.requestType = TEAM_DEBUGGER_REQUEST_CREATE;
590 	options.settingsManager = &fSettingsManager;
591 	options.userInterface = new(std::nothrow) GraphicalUserInterface;
592 	if (options.userInterface == NULL)
593 		return B_NO_MEMORY;
594 	BReference<UserInterface> uiReference(options.userInterface, true);
595 	options.commandLineArgc = argVector.ArgumentCount();
596 	if (options.commandLineArgc <= 0)
597 		return B_BAD_VALUE;
598 
599 	char** argv = argVector.DetachArguments();
600 
601 	options.commandLineArgv = argv;
602 	MemoryDeleter deleter(argv);
603 
604 	status_t error = interface->StartTeamDebugger(options);
605 	if (error == B_OK) {
606 		deleter.Detach();
607 	}
608 
609 	return error;
610 }
611 
612 
613 status_t
614 Debugger::_HandleOptions(const Options& options)
615 {
616 	TeamDebuggerOptions debuggerOptions;
617 	set_debugger_options_from_options(debuggerOptions, options);
618 	debuggerOptions.settingsManager = &fSettingsManager;
619 	debuggerOptions.userInterface = new(std::nothrow) GraphicalUserInterface;
620 	if (debuggerOptions.userInterface == NULL)
621 		return B_NO_MEMORY;
622 	BReference<UserInterface> uiReference(debuggerOptions.userInterface, true);
623 	TargetHostInterface* hostInterface
624 		= TargetHostInterfaceRoster::Default()->ActiveInterfaceAt(0);
625 	return hostInterface->StartTeamDebugger(debuggerOptions);
626 }
627 
628 
629 // #pragma mark - CliDebugger
630 
631 
632 CliDebugger::CliDebugger()
633 {
634 }
635 
636 
637 CliDebugger::~CliDebugger()
638 {
639 	DebuggerUiSettingsFactory::DeleteDefault();
640 	debugger_global_uninit();
641 }
642 
643 
644 bool
645 CliDebugger::Run(const Options& options)
646 {
647 	// Block SIGINT, in this thread so all threads created by it inherit the
648 	// a block mask with the signal blocked. In the input loop the signal will
649 	// be unblocked again.
650 	SignalSet(SIGINT).BlockInCurrentThread();
651 
652 	// initialize global objects and settings manager
653 	status_t error = debugger_global_init(this);
654 	if (error != B_OK) {
655 		fprintf(stderr, "Error: Global initialization failed: %s\n",
656 			strerror(error));
657 		return false;
658 	}
659 
660 	error = DebuggerUiSettingsFactory::CreateDefault();
661 	if (error != B_OK) {
662 		fprintf(stderr, "Error: Failed to create default settings factory: "
663 			"%s\n",	strerror(error));
664 		return false;
665 	}
666 
667 
668 	DebuggerSettingsManager settingsManager;
669 	error = settingsManager.Init(DebuggerUiSettingsFactory::Default());
670 	if (error != B_OK) {
671 		fprintf(stderr, "Error: Settings manager initialization failed: "
672 			"%s\n", strerror(error));
673 		return false;
674 	}
675 
676 	// create the command line UI
677 	CommandLineUserInterface* userInterface
678 		= new(std::nothrow) CommandLineUserInterface();
679 	if (userInterface == NULL) {
680 		fprintf(stderr, "Error: Out of memory!\n");
681 		return false;
682 	}
683 	BReference<UserInterface> userInterfaceReference(userInterface, true);
684 
685 	// TODO: once we support specifying a remote interface via command line
686 	// args, this needs to be adjusted. For now, always assume actions
687 	// are being taken via the local interface.
688 	TargetHostInterface* hostInterface
689 		= TargetHostInterfaceRoster::Default()->ActiveInterfaceAt(0);
690 
691 	TeamDebuggerOptions debuggerOptions;
692 	set_debugger_options_from_options(debuggerOptions, options);
693 	debuggerOptions.userInterface = userInterface;
694 	debuggerOptions.settingsManager = &settingsManager;
695 	error = hostInterface->StartTeamDebugger(debuggerOptions);
696 	if (error != B_OK)
697 		return false;
698 
699 	TeamDebugger* teamDebugger = hostInterface->TeamDebuggerAt(0);
700 	thread_id teamDebuggerThread = teamDebugger->Thread();
701 
702 	// run the input loop
703 	userInterface->Run();
704 
705 	// wait for the team debugger thread to terminate
706 	wait_for_thread(teamDebuggerThread, NULL);
707 
708 	return true;
709 }
710 
711 
712 // #pragma mark - ReportDebugger
713 
714 
715 ReportDebugger::ReportDebugger()
716 {
717 }
718 
719 
720 ReportDebugger::~ReportDebugger()
721 {
722 	debugger_global_uninit();
723 }
724 
725 
726 bool
727 ReportDebugger::Run(const Options& options)
728 {
729 	// initialize global objects and settings manager
730 	status_t error = debugger_global_init(this);
731 	if (error != B_OK) {
732 		fprintf(stderr, "Error: Global initialization failed: %s\n",
733 			strerror(error));
734 		return false;
735 	}
736 
737 	// create the report UI
738 	ReportUserInterface* userInterface
739 		= new(std::nothrow) ReportUserInterface(options.thread, options.reportPath);
740 	if (userInterface == NULL) {
741 		fprintf(stderr, "Error: Out of memory!\n");
742 		return false;
743 	}
744 	BReference<UserInterface> userInterfaceReference(userInterface, true);
745 
746 	TargetHostInterface* hostInterface
747 		= TargetHostInterfaceRoster::Default()->ActiveInterfaceAt(0);
748 
749 	TeamDebuggerOptions debuggerOptions;
750 	set_debugger_options_from_options(debuggerOptions, options);
751 	debuggerOptions.userInterface = userInterface;
752 	error = hostInterface->StartTeamDebugger(debuggerOptions);
753 	if (error != B_OK)
754 		return false;
755 
756 	TeamDebugger* teamDebugger = hostInterface->TeamDebuggerAt(0);
757 	thread_id teamDebuggerThread = teamDebugger->Thread();
758 
759 	// run the input loop
760 	userInterface->Run();
761 
762 	// wait for the team debugger thread to terminate
763 	wait_for_thread(teamDebuggerThread, NULL);
764 
765 	return true;
766 }
767 
768 
769 // #pragma mark -
770 
771 
772 int
773 main(int argc, const char* const* argv)
774 {
775 	// We test-parse the arguments here, so that, when we're started from the
776 	// terminal and there's an instance already running, we can print an error
777 	// message to the terminal, if something's wrong with the arguments.
778 	// Otherwise, the arguments are reparsed in the actual application,
779 	// unless the option to use the command line interface was chosen.
780 
781 	Options options;
782 	parse_arguments(argc, argv, false, options);
783 
784 	if (options.useCLI) {
785 		CliDebugger debugger;
786 		return debugger.Run(options) ? 0 : 1;
787 	} else if (options.saveReport) {
788 		ReportDebugger debugger;
789 		return debugger.Run(options) ? 0 : 1;
790 	}
791 
792 	Debugger app;
793 	status_t error = app.Init();
794 	if (error != B_OK) {
795 		fprintf(stderr, "Error: Failed to init application: %s\n",
796 			strerror(error));
797 		return 1;
798 	}
799 
800 	app.Run();
801 
802 	return 0;
803 }
804