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