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.
15 #include <Application.h>
18 #include <ObjectList.h>
21 #include <ArgumentVector.h>
22 #include <AutoDeleter.h>
23 #include <AutoLocker.h>
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"
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"
45 extern const char* __progname
;
46 const char* kProgramName
= __progname
;
48 static const char* const kDebuggerSignature
49 = "application/x-vnd.Haiku-Debugger";
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"
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"
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"
67 "The third and fourth forms attach the debugger to a running team. The\n"
68 "fourth form additionally stops the specified thread.\n"
70 "The fifth form loads a core file.\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"
81 print_usage_and_exit(bool error
)
83 fprintf(error
? stderr
: stdout
, kUsage
, kProgramName
, kProgramName
,
84 kProgramName
, kProgramName
, kProgramName
);
91 const char* const* commandLineArgv
;
96 const char* reportPath
;
97 const char* coreFilePath
;
102 commandLineArgv(NULL
),
115 set_debugger_options_from_options(TeamDebuggerOptions
& _debuggerOptions
,
116 const Options
& options
)
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
;
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
;
129 _debuggerOptions
.requestType
= TEAM_DEBUGGER_REQUEST_ATTACH
;
135 parse_arguments(int argc
, const char* const* argv
, bool noOutput
,
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' },
151 opterr
= 0; // don't print errors
153 int c
= getopt_long(argc
, (char**)argv
, "+chs", sLongOptions
, NULL
);
159 options
.useCLI
= true;
163 options
.coreFilePath
= optarg
;
169 print_usage_and_exit(false);
174 options
.saveReport
= true;
175 options
.reportPath
= optarg
;
181 options
.team
= strtol(optarg
, NULL
, 0);
182 if (options
.team
<= 0) {
185 print_usage_and_exit(true);
192 options
.thread
= strtol(optarg
, NULL
, 0);
193 if (options
.thread
<= 0) {
196 print_usage_and_exit(true);
204 print_usage_and_exit(true);
210 options
.commandLineArgc
= argc
- optind
;
211 options
.commandLineArgv
= argv
+ optind
;
214 int exclusiveParams
= 0;
215 if (options
.team
> 0)
217 if (options
.thread
> 0)
219 if (options
.commandLineArgc
> 0)
222 if (exclusiveParams
== 0) {
224 } else if (exclusiveParams
!= 1) {
227 print_usage_and_exit(true);
234 // #pragma mark - Debugger application class
237 class Debugger
: public BApplication
,
238 private TargetHostInterfaceRoster::Listener
{
244 virtual void MessageReceived(BMessage
* message
);
245 virtual void ReadyToRun();
246 virtual void ArgvReceived(int32 argc
, char** argv
);
247 virtual void RefsReceived(BMessage
* message
);
250 virtual bool QuitRequested();
253 // TargetHostInterfaceRoster::Listener
254 virtual void TeamDebuggerCountChanged(int32 count
);
257 status_t
_StartNewTeam(TargetHostInterface
* interface
,
258 const char* teamPath
, const char* args
);
259 status_t
_HandleOptions(const Options
& options
);
262 DebuggerSettingsManager fSettingsManager
;
263 ConnectionConfigWindow
* fConnectionWindow
;
264 TeamsWindow
* fTeamsWindow
;
265 StartTeamWindow
* fStartTeamWindow
;
269 // #pragma mark - CliDebugger
272 class CliDebugger
: private TargetHostInterfaceRoster::Listener
{
277 bool Run(const Options
& options
);
281 class ReportDebugger
: private TargetHostInterfaceRoster::Listener
{
285 bool Run(const Options
& options
);
289 // #pragma mark - Debugger application class
294 BApplication(kDebuggerSignature
),
295 TargetHostInterfaceRoster::Listener(),
296 fConnectionWindow(NULL
),
298 fStartTeamWindow(NULL
)
303 Debugger::~Debugger()
305 DebuggerUiSettingsFactory::DeleteDefault();
306 ValueHandlerRoster::DeleteDefault();
307 ConnectionConfigHandlerRoster::DeleteDefault();
309 debugger_global_uninit();
316 status_t error
= debugger_global_init(this);
320 error
= DebuggerUiSettingsFactory::CreateDefault();
324 error
= ValueHandlerRoster::CreateDefault();
328 error
= ConnectionConfigHandlerRoster::CreateDefault();
332 return fSettingsManager
.Init(DebuggerUiSettingsFactory::Default());
337 Debugger::MessageReceived(BMessage
* message
)
339 switch (message
->what
) {
340 case MSG_SHOW_TEAMS_WINDOW
:
343 fTeamsWindow
->Activate(true);
348 fTeamsWindow
= TeamsWindow::Create(&fSettingsManager
);
349 if (fTeamsWindow
!= NULL
)
350 fTeamsWindow
->Show();
352 // TODO: Notify the user!
353 fprintf(stderr
, "Error: Failed to create Teams window\n");
357 case MSG_TEAMS_WINDOW_CLOSED
:
363 case MSG_SHOW_START_TEAM_WINDOW
:
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);
374 BMessenger
messenger(fStartTeamWindow
);
375 if (!messenger
.IsValid()) {
376 fStartTeamWindow
= StartTeamWindow::Create(hostInterface
);
377 if (fStartTeamWindow
== NULL
)
379 fStartTeamWindow
->Show();
381 fStartTeamWindow
->Activate();
384 case MSG_START_TEAM_WINDOW_CLOSED
:
386 fStartTeamWindow
= NULL
;
389 case MSG_SHOW_CONNECTION_CONFIG_WINDOW
:
391 if (fConnectionWindow
!= NULL
) {
392 fConnectionWindow
->Activate(true);
397 fConnectionWindow
= ConnectionConfigWindow::Create();
398 if (fConnectionWindow
!= NULL
)
399 fConnectionWindow
->Show();
401 // TODO: Notify the user!
402 fprintf(stderr
, "Error: Failed to create Teams window\n");
406 case MSG_CONNECTION_CONFIG_WINDOW_CLOSED
:
408 fConnectionWindow
= NULL
;
411 case MSG_DEBUG_THIS_TEAM
:
414 if (message
->FindInt32("team", &teamID
) != B_OK
)
417 TargetHostInterface
* interface
;
418 if (message
->FindPointer("interface", reinterpret_cast<void**>(
419 &interface
)) != B_OK
) {
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.
432 BReference
<UserInterface
> uiReference(options
.userInterface
, true);
433 status_t error
= interface
->StartTeamDebugger(options
);
435 // TODO: notify user.
437 uiReference
.Detach();
440 case MSG_START_NEW_TEAM
:
442 TargetHostInterface
* interface
;
443 if (message
->FindPointer("interface", reinterpret_cast<void**>(
444 &interface
)) != B_OK
) {
448 const char* teamPath
= NULL
;
449 const char* args
= NULL
;
451 message
->FindString("path", &teamPath
);
452 message
->FindString("arguments", &args
);
454 status_t result
= _StartNewTeam(interface
, teamPath
, args
);
456 reply
.AddInt32("status", result
);
457 message
->SendReply(&reply
);
460 case MSG_LOAD_CORE_TEAM
:
462 TargetHostInterface
* interface
;
463 if (message
->FindPointer("interface", reinterpret_cast<void**>(
464 &interface
)) != B_OK
) {
469 if (message
->FindRef("core", &ref
) != B_OK
)
473 if (path
.InitCheck() != B_OK
)
477 options
.coreFilePath
= path
.Path();
478 _HandleOptions(options
);
482 BApplication::MessageReceived(message
);
489 Debugger::ReadyToRun()
491 TargetHostInterfaceRoster
* roster
= TargetHostInterfaceRoster::Default();
492 AutoLocker
<TargetHostInterfaceRoster
> lock(roster
);
493 if (roster
->CountRunningTeamDebuggers() == 0)
494 PostMessage(MSG_SHOW_TEAMS_WINDOW
);
499 Debugger::ArgvReceived(int32 argc
, char** argv
)
502 if (!parse_arguments(argc
, argv
, true, options
)) {
503 printf("Debugger::ArgvReceived(): parsing args failed!\n");
507 _HandleOptions(options
);
512 Debugger::RefsReceived(BMessage
* message
)
514 // iterate through the refs and handle the files we can handle
516 for (int32 i
= 0; message
->FindRef("refs", i
, &ref
) == B_OK
; i
++) {
518 if (path
.SetTo(&ref
) != B_OK
)
521 // check, whether this is a core file
524 if (elfFile
.Init(path
.Path()) != B_OK
|| elfFile
.Type() != ET_CORE
)
528 // handle the core file
530 options
.coreFilePath
= path
.Path();
531 _HandleOptions(options
);
537 Debugger::QuitRequested()
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
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();
565 Debugger::TeamDebuggerCountChanged(int32 count
)
568 AutoLocker
<Debugger
> lock(this);
575 Debugger::_StartNewTeam(TargetHostInterface
* interface
, const char* path
,
582 data
.SetToFormat("\"%s\" %s", path
, args
);
583 if (data
.Length() == 0)
586 ArgumentVector argVector
;
587 argVector
.Parse(data
.String());
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
)
595 BReference
<UserInterface
> uiReference(options
.userInterface
, true);
596 options
.commandLineArgc
= argVector
.ArgumentCount();
597 if (options
.commandLineArgc
<= 0)
600 char** argv
= argVector
.DetachArguments();
602 options
.commandLineArgv
= argv
;
603 MemoryDeleter
deleter(argv
);
605 status_t error
= interface
->StartTeamDebugger(options
);
608 uiReference
.Detach();
616 Debugger::_HandleOptions(const Options
& options
)
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
)
624 BReference
<UserInterface
> uiReference(debuggerOptions
.userInterface
, true);
625 TargetHostInterface
* hostInterface
626 = TargetHostInterfaceRoster::Default()->ActiveInterfaceAt(0);
627 status_t error
= hostInterface
->StartTeamDebugger(debuggerOptions
);
629 uiReference
.Detach();
635 // #pragma mark - CliDebugger
638 CliDebugger::CliDebugger()
643 CliDebugger::~CliDebugger()
645 DebuggerUiSettingsFactory::DeleteDefault();
646 debugger_global_uninit();
651 CliDebugger::Run(const Options
& options
)
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();
658 // initialize global objects and settings manager
659 status_t error
= debugger_global_init(this);
661 fprintf(stderr
, "Error: Global initialization failed: %s\n",
666 error
= DebuggerUiSettingsFactory::CreateDefault();
668 fprintf(stderr
, "Error: Failed to create default settings factory: "
669 "%s\n", strerror(error
));
674 DebuggerSettingsManager settingsManager
;
675 error
= settingsManager
.Init(DebuggerUiSettingsFactory::Default());
677 fprintf(stderr
, "Error: Settings manager initialization failed: "
678 "%s\n", strerror(error
));
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");
689 BReference
<UserInterface
> userInterfaceReference(userInterface
, true);
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);
697 TeamDebuggerOptions debuggerOptions
;
698 set_debugger_options_from_options(debuggerOptions
, options
);
699 debuggerOptions
.userInterface
= userInterface
;
700 debuggerOptions
.settingsManager
= &settingsManager
;
701 error
= hostInterface
->StartTeamDebugger(debuggerOptions
);
705 TeamDebugger
* teamDebugger
= hostInterface
->TeamDebuggerAt(0);
706 thread_id teamDebuggerThread
= teamDebugger
->Thread();
708 // run the input loop
709 userInterface
->Run();
711 // wait for the team debugger thread to terminate
712 wait_for_thread(teamDebuggerThread
, NULL
);
718 // #pragma mark - ReportDebugger
721 ReportDebugger::ReportDebugger()
726 ReportDebugger::~ReportDebugger()
728 debugger_global_uninit();
733 ReportDebugger::Run(const Options
& options
)
735 // initialize global objects and settings manager
736 status_t error
= debugger_global_init(this);
738 fprintf(stderr
, "Error: Global initialization failed: %s\n",
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");
750 BReference
<UserInterface
> userInterfaceReference(userInterface
, true);
752 TargetHostInterface
* hostInterface
753 = TargetHostInterfaceRoster::Default()->ActiveInterfaceAt(0);
755 TeamDebuggerOptions debuggerOptions
;
756 set_debugger_options_from_options(debuggerOptions
, options
);
757 debuggerOptions
.userInterface
= userInterface
;
758 error
= hostInterface
->StartTeamDebugger(debuggerOptions
);
762 TeamDebugger
* teamDebugger
= hostInterface
->TeamDebuggerAt(0);
763 thread_id teamDebuggerThread
= teamDebugger
->Thread();
765 // run the input loop
766 userInterface
->Run();
768 // wait for the team debugger thread to terminate
769 wait_for_thread(teamDebuggerThread
, NULL
);
779 main(int argc
, const char* const* argv
)
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.
788 parse_arguments(argc
, argv
, false, options
);
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;
799 status_t error
= app
.Init();
801 fprintf(stderr
, "Error: Failed to init application: %s\n",