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