xref: /haiku/src/apps/debugger/Debugger.cpp (revision 445d4fd926c569e7b9ae28017da86280aaecbae2)
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			_ShowStartTeamWindow(TargetHostInterface* interface);
258 			status_t			_StartNewTeam(TargetHostInterface* interface,
259 									const char* teamPath, const char* args);
260 			status_t			_HandleOptions(const Options& options);
261 
262 private:
263 			DebuggerSettingsManager fSettingsManager;
264 			ConnectionConfigWindow* fConnectionWindow;
265 			TeamsWindow*		fTeamsWindow;
266 			StartTeamWindow*	fStartTeamWindow;
267 };
268 
269 
270 // #pragma mark - CliDebugger
271 
272 
273 class CliDebugger : private TargetHostInterfaceRoster::Listener {
274 public:
275 								CliDebugger();
276 								~CliDebugger();
277 
278 			bool				Run(const Options& options);
279 };
280 
281 
282 class ReportDebugger : private TargetHostInterfaceRoster::Listener  {
283 public:
284 								ReportDebugger();
285 								~ReportDebugger();
286 			bool				Run(const Options& options);
287 };
288 
289 
290 // #pragma mark - Debugger application class
291 
292 
293 Debugger::Debugger()
294 	:
295 	BApplication(kDebuggerSignature),
296 	TargetHostInterfaceRoster::Listener(),
297 	fConnectionWindow(NULL),
298 	fTeamsWindow(NULL),
299 	fStartTeamWindow(NULL)
300 {
301 }
302 
303 
304 Debugger::~Debugger()
305 {
306 	DebuggerUiSettingsFactory::DeleteDefault();
307 	ValueHandlerRoster::DeleteDefault();
308 	ConnectionConfigHandlerRoster::DeleteDefault();
309 
310 	debugger_global_uninit();
311 }
312 
313 
314 status_t
315 Debugger::Init()
316 {
317 	status_t error = debugger_global_init(this);
318 	if (error != B_OK)
319 		return error;
320 
321 	error = DebuggerUiSettingsFactory::CreateDefault();
322 	if (error != B_OK)
323 		return error;
324 
325 	error = ValueHandlerRoster::CreateDefault();
326 	if (error != B_OK)
327 		return error;
328 
329 	error = ConnectionConfigHandlerRoster::CreateDefault();
330 	if (error != B_OK)
331 		return error;
332 
333 	return fSettingsManager.Init(DebuggerUiSettingsFactory::Default());
334 }
335 
336 
337 void
338 Debugger::MessageReceived(BMessage* message)
339 {
340 	switch (message->what) {
341 		case MSG_SHOW_TEAMS_WINDOW:
342 		{
343 			if (fTeamsWindow) {
344 				fTeamsWindow->Activate(true);
345 				break;
346 			}
347 
348 			try {
349 				fTeamsWindow = TeamsWindow::Create(&fSettingsManager);
350 				if (fTeamsWindow != NULL)
351 					fTeamsWindow->Show();
352 			} catch (...) {
353 				// TODO: Notify the user!
354 				fprintf(stderr, "Error: Failed to create Teams window\n");
355 			}
356 			break;
357 		}
358 		case MSG_TEAMS_WINDOW_CLOSED:
359 		{
360 			fTeamsWindow = NULL;
361 			Quit();
362 			break;
363 		}
364 		case MSG_SHOW_START_TEAM_WINDOW:
365 		{
366 			TargetHostInterface* hostInterface;
367 			if (message->FindPointer("interface",
368 					reinterpret_cast<void**>(&hostInterface)) != B_OK) {
369 				// if an interface isn't explicitly supplied, fall back to
370 				// the default local interface.
371 				hostInterface = TargetHostInterfaceRoster::Default()
372 					->ActiveInterfaceAt(0);
373 			}
374 
375 			_ShowStartTeamWindow(hostInterface);
376 			break;
377 		}
378 		case MSG_START_TEAM_WINDOW_CLOSED:
379 		{
380 			fStartTeamWindow = NULL;
381 			Quit();
382 			break;
383 		}
384 		case MSG_SHOW_CONNECTION_CONFIG_WINDOW:
385 		{
386 			if (fConnectionWindow != NULL) {
387 				fConnectionWindow->Activate(true);
388 				break;
389 			}
390 
391 			try {
392 				fConnectionWindow = ConnectionConfigWindow::Create();
393 				if (fConnectionWindow != NULL)
394 					fConnectionWindow->Show();
395 			} catch (...) {
396 				// TODO: Notify the user!
397 				fprintf(stderr, "Error: Failed to create Teams window\n");
398 			}
399 			break;
400 		}
401 		case MSG_CONNECTION_CONFIG_WINDOW_CLOSED:
402 		{
403 			fConnectionWindow = NULL;
404 			break;
405 		}
406 		case MSG_DEBUG_THIS_TEAM:
407 		{
408 			team_id teamID;
409 			if (message->FindInt32("team", &teamID) != B_OK)
410 				break;
411 
412 			TargetHostInterface* interface;
413 			if (message->FindPointer("interface", reinterpret_cast<void**>(
414 					&interface)) != B_OK) {
415 				break;
416 			}
417 
418 			TeamDebuggerOptions options;
419 			options.requestType = TEAM_DEBUGGER_REQUEST_ATTACH;
420 			options.settingsManager = &fSettingsManager;
421 			options.team = teamID;
422 			options.userInterface = new(std::nothrow) GraphicalUserInterface;
423 			if (options.userInterface == NULL) {
424 				// TODO: notify user.
425 				break;
426 			}
427 			BReference<UserInterface> uiReference(options.userInterface, true);
428 			status_t error = interface->StartTeamDebugger(options);
429 			if (error != B_OK) {
430 				// TODO: notify user.
431 			}
432 			break;
433 		}
434 		case MSG_START_NEW_TEAM:
435 		{
436 			TargetHostInterface* interface;
437 			if (message->FindPointer("interface", reinterpret_cast<void**>(
438 					&interface)) != B_OK) {
439 				break;
440 			}
441 
442 			const char* teamPath = NULL;
443 			const char* args = NULL;
444 
445 			message->FindString("path", &teamPath);
446 			message->FindString("arguments", &args);
447 
448 			status_t result = _StartNewTeam(interface, teamPath, args);
449 			BMessage reply;
450 			reply.AddInt32("status", result);
451 			message->SendReply(&reply);
452 			break;
453 		}
454 		case MSG_LOAD_CORE_TEAM:
455 		{
456 			TargetHostInterface* interface;
457 			if (message->FindPointer("interface", reinterpret_cast<void**>(
458 					&interface)) != B_OK) {
459 				break;
460 			}
461 
462 			entry_ref ref;
463 			if (message->FindRef("core", &ref) != B_OK)
464 				break;
465 
466 			BPath path(&ref);
467 			if (path.InitCheck() != B_OK)
468 				break;
469 
470 			Options options;
471 			options.coreFilePath = path.Path();
472 			_HandleOptions(options);
473 			break;
474 		}
475 		default:
476 			BApplication::MessageReceived(message);
477 			break;
478 	}
479 }
480 
481 
482 void
483 Debugger::ReadyToRun()
484 {
485 	TargetHostInterfaceRoster* roster = TargetHostInterfaceRoster::Default();
486 	AutoLocker<TargetHostInterfaceRoster> lock(roster);
487 	if (roster->CountRunningTeamDebuggers() == 0 && fStartTeamWindow == NULL)
488 		PostMessage(MSG_SHOW_TEAMS_WINDOW);
489 }
490 
491 
492 void
493 Debugger::ArgvReceived(int32 argc, char** argv)
494 {
495 	Options options;
496 	if (!parse_arguments(argc, argv, true, options)) {
497 		printf("Debugger::ArgvReceived(): parsing args failed!\n");
498 		return;
499 	}
500 
501 	_HandleOptions(options);
502 }
503 
504 
505 void
506 Debugger::RefsReceived(BMessage* message)
507 {
508 	// iterate through the refs and handle the files we can handle
509 	entry_ref ref;
510 	for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) {
511 		BPath path;
512 		if (path.SetTo(&ref) != B_OK)
513 			continue;
514 
515 		ElfFile elfFile;
516 		if (elfFile.Init(path.Path()) != B_OK)
517 			continue;
518 
519 		switch (elfFile.Type()) {
520 			case ET_CORE:
521 			{
522 				// open the core file
523 				Options options;
524 				options.coreFilePath = path.Path();
525 				_HandleOptions(options);
526 				break;
527 			}
528 			case ET_EXEC:
529 			case ET_DYN:
530 			{
531 				// ask the user for arguments to pass to the executable
532 				TargetHostInterface* hostInterface = TargetHostInterfaceRoster::Default()
533 						->ActiveInterfaceAt(0);
534 				status_t error = _ShowStartTeamWindow(hostInterface);
535 				if (error != B_OK)
536 					continue;
537 
538 				BMessage message(MSG_SET_TEAM_PATH);
539 				message.AddRef("refs", &ref);
540 				fStartTeamWindow->PostMessage(&message);
541 				break;
542 			}
543 		}
544 	}
545 }
546 
547 
548 bool
549 Debugger::QuitRequested()
550 {
551 	// NOTE: The default implementation will just ask all windows'
552 	// QuitRequested() hooks. This in turn will ask the TeamWindows.
553 	// For now, this is what we want. If we have more windows later,
554 	// like the global TeamsWindow, then we want to just ask the
555 	// TeamDebuggers, the TeamsWindow should of course not go away already
556 	// if one or more TeamDebuggers want to stay later. There are multiple
557 	// ways how to do this. For example, TeamDebugger could get a
558 	// QuitRequested() hook or the TeamsWindow and other global windows
559 	// could always return false in their QuitRequested().
560 	return BApplication::QuitRequested();
561 		// TODO: This is ugly. The team debuggers own the windows, not the
562 		// other way around.
563 }
564 
565 void
566 Debugger::Quit()
567 {
568 	TargetHostInterfaceRoster* roster = TargetHostInterfaceRoster::Default();
569 	AutoLocker<TargetHostInterfaceRoster> lock(roster);
570 	// don't quit before all team debuggers have been quit
571 	if (roster->CountRunningTeamDebuggers() == 0 && fTeamsWindow == NULL)
572 		BApplication::Quit();
573 }
574 
575 
576 void
577 Debugger::TeamDebuggerCountChanged(int32 count)
578 {
579 	if (count == 0) {
580 		AutoLocker<Debugger> lock(this);
581 		Quit();
582 	}
583 }
584 
585 
586 status_t
587 Debugger::_ShowStartTeamWindow(TargetHostInterface* interface)
588 {
589 	if (fStartTeamWindow == NULL) {
590 		TargetHostInterface* hostInterface = TargetHostInterfaceRoster::Default()
591 			->ActiveInterfaceAt(0);
592 		fStartTeamWindow = StartTeamWindow::Create(hostInterface);
593 		if (fStartTeamWindow == NULL)
594 			return B_NO_MEMORY;
595 		fStartTeamWindow->Show();
596 	} else
597 		fStartTeamWindow->Activate();
598 
599 	return B_OK;
600 }
601 
602 
603 status_t
604 Debugger::_StartNewTeam(TargetHostInterface* interface, const char* path,
605 	const char* args)
606 {
607 	if (path == NULL)
608 		return B_BAD_VALUE;
609 
610 	BString data;
611 	data.SetToFormat("\"%s\" %s", path, args);
612 	if (data.Length() == 0)
613 		return B_NO_MEMORY;
614 
615 	ArgumentVector argVector;
616 	argVector.Parse(data.String());
617 
618 	TeamDebuggerOptions options;
619 	options.requestType = TEAM_DEBUGGER_REQUEST_CREATE;
620 	options.settingsManager = &fSettingsManager;
621 	options.userInterface = new(std::nothrow) GraphicalUserInterface;
622 	if (options.userInterface == NULL)
623 		return B_NO_MEMORY;
624 	BReference<UserInterface> uiReference(options.userInterface, true);
625 	options.commandLineArgc = argVector.ArgumentCount();
626 	if (options.commandLineArgc <= 0)
627 		return B_BAD_VALUE;
628 
629 	char** argv = argVector.DetachArguments();
630 
631 	options.commandLineArgv = argv;
632 	MemoryDeleter deleter(argv);
633 
634 	status_t error = interface->StartTeamDebugger(options);
635 	if (error == B_OK) {
636 		deleter.Detach();
637 	}
638 
639 	return error;
640 }
641 
642 
643 status_t
644 Debugger::_HandleOptions(const Options& options)
645 {
646 	TeamDebuggerOptions debuggerOptions;
647 	set_debugger_options_from_options(debuggerOptions, options);
648 	debuggerOptions.settingsManager = &fSettingsManager;
649 	debuggerOptions.userInterface = new(std::nothrow) GraphicalUserInterface;
650 	if (debuggerOptions.userInterface == NULL)
651 		return B_NO_MEMORY;
652 	BReference<UserInterface> uiReference(debuggerOptions.userInterface, true);
653 	TargetHostInterface* hostInterface
654 		= TargetHostInterfaceRoster::Default()->ActiveInterfaceAt(0);
655 	return hostInterface->StartTeamDebugger(debuggerOptions);
656 }
657 
658 
659 // #pragma mark - CliDebugger
660 
661 
662 CliDebugger::CliDebugger()
663 {
664 }
665 
666 
667 CliDebugger::~CliDebugger()
668 {
669 	DebuggerUiSettingsFactory::DeleteDefault();
670 	debugger_global_uninit();
671 }
672 
673 
674 bool
675 CliDebugger::Run(const Options& options)
676 {
677 	if (options.commandLineArgc == 0
678 		&& options.team < 0
679 		&& options.thread < 0
680 		&& options.coreFilePath == NULL) {
681 		fprintf(stderr, "No target specified to debug\n");
682 		return false;
683 	}
684 
685 	// Block SIGINT, in this thread so all threads created by it inherit the
686 	// a block mask with the signal blocked. In the input loop the signal will
687 	// be unblocked again.
688 	SignalSet(SIGINT).BlockInCurrentThread();
689 
690 	// initialize global objects and settings manager
691 	status_t error = debugger_global_init(this);
692 	if (error != B_OK) {
693 		fprintf(stderr, "Error: Global initialization failed: %s\n",
694 			strerror(error));
695 		return false;
696 	}
697 
698 	error = DebuggerUiSettingsFactory::CreateDefault();
699 	if (error != B_OK) {
700 		fprintf(stderr, "Error: Failed to create default settings factory: "
701 			"%s\n",	strerror(error));
702 		return false;
703 	}
704 
705 
706 	DebuggerSettingsManager settingsManager;
707 	error = settingsManager.Init(DebuggerUiSettingsFactory::Default());
708 	if (error != B_OK) {
709 		fprintf(stderr, "Error: Settings manager initialization failed: "
710 			"%s\n", strerror(error));
711 		return false;
712 	}
713 
714 	// create the command line UI
715 	CommandLineUserInterface* userInterface
716 		= new(std::nothrow) CommandLineUserInterface();
717 	if (userInterface == NULL) {
718 		fprintf(stderr, "Error: Out of memory!\n");
719 		return false;
720 	}
721 	BReference<UserInterface> userInterfaceReference(userInterface, true);
722 
723 	// TODO: once we support specifying a remote interface via command line
724 	// args, this needs to be adjusted. For now, always assume actions
725 	// are being taken via the local interface.
726 	TargetHostInterface* hostInterface
727 		= TargetHostInterfaceRoster::Default()->ActiveInterfaceAt(0);
728 
729 	TeamDebuggerOptions debuggerOptions;
730 	set_debugger_options_from_options(debuggerOptions, options);
731 	debuggerOptions.userInterface = userInterface;
732 	debuggerOptions.settingsManager = &settingsManager;
733 	error = hostInterface->StartTeamDebugger(debuggerOptions);
734 	if (error != B_OK)
735 		return false;
736 
737 	TeamDebugger* teamDebugger = hostInterface->TeamDebuggerAt(0);
738 	thread_id teamDebuggerThread = teamDebugger->Thread();
739 
740 	// run the input loop
741 	userInterface->Run();
742 
743 	// wait for the team debugger thread to terminate
744 	wait_for_thread(teamDebuggerThread, NULL);
745 
746 	return true;
747 }
748 
749 
750 // #pragma mark - ReportDebugger
751 
752 
753 ReportDebugger::ReportDebugger()
754 {
755 }
756 
757 
758 ReportDebugger::~ReportDebugger()
759 {
760 	debugger_global_uninit();
761 }
762 
763 
764 bool
765 ReportDebugger::Run(const Options& options)
766 {
767 	// initialize global objects and settings manager
768 	status_t error = debugger_global_init(this);
769 	if (error != B_OK) {
770 		fprintf(stderr, "Error: Global initialization failed: %s\n",
771 			strerror(error));
772 		return false;
773 	}
774 
775 	// create the report UI
776 	ReportUserInterface* userInterface
777 		= new(std::nothrow) ReportUserInterface(options.thread, options.reportPath);
778 	if (userInterface == NULL) {
779 		fprintf(stderr, "Error: Out of memory!\n");
780 		return false;
781 	}
782 	BReference<UserInterface> userInterfaceReference(userInterface, true);
783 
784 	TargetHostInterface* hostInterface
785 		= TargetHostInterfaceRoster::Default()->ActiveInterfaceAt(0);
786 
787 	TeamDebuggerOptions debuggerOptions;
788 	set_debugger_options_from_options(debuggerOptions, options);
789 	debuggerOptions.userInterface = userInterface;
790 	error = hostInterface->StartTeamDebugger(debuggerOptions);
791 	if (error != B_OK)
792 		return false;
793 
794 	TeamDebugger* teamDebugger = hostInterface->TeamDebuggerAt(0);
795 	thread_id teamDebuggerThread = teamDebugger->Thread();
796 
797 	// run the input loop
798 	userInterface->Run();
799 
800 	// wait for the team debugger thread to terminate
801 	wait_for_thread(teamDebuggerThread, NULL);
802 
803 	return true;
804 }
805 
806 
807 // #pragma mark -
808 
809 
810 int
811 main(int argc, const char* const* argv)
812 {
813 	// We test-parse the arguments here, so that, when we're started from the
814 	// terminal and there's an instance already running, we can print an error
815 	// message to the terminal, if something's wrong with the arguments.
816 	// Otherwise, the arguments are reparsed in the actual application,
817 	// unless the option to use the command line interface was chosen.
818 
819 	Options options;
820 	parse_arguments(argc, argv, false, options);
821 
822 	if (options.useCLI) {
823 		CliDebugger debugger;
824 		return debugger.Run(options) ? 0 : 1;
825 	} else if (options.saveReport) {
826 		ReportDebugger debugger;
827 		return debugger.Run(options) ? 0 : 1;
828 	}
829 
830 	Debugger app;
831 	status_t error = app.Init();
832 	if (error != B_OK) {
833 		fprintf(stderr, "Error: Failed to init application: %s\n",
834 			strerror(error));
835 		return 1;
836 	}
837 
838 	app.Run();
839 
840 	return 0;
841 }
842