1 /* 2 * Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2011-2015, 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 <Message.h> 17 18 #include <ArgumentVector.h> 19 #include <AutoDeleter.h> 20 #include <AutoLocker.h> 21 #include <ObjectList.h> 22 23 #include "debug_utils.h" 24 25 #include "CommandLineUserInterface.h" 26 #include "GraphicalUserInterface.h" 27 #include "ImageDebugLoadingStateHandlerRoster.h" 28 #include "MessageCodes.h" 29 #include "ReportUserInterface.h" 30 #include "SettingsManager.h" 31 #include "SignalSet.h" 32 #include "StartTeamWindow.h" 33 #include "TeamDebugger.h" 34 #include "TeamsWindow.h" 35 #include "TypeHandlerRoster.h" 36 #include "ValueHandlerRoster.h" 37 38 39 extern const char* __progname; 40 const char* kProgramName = __progname; 41 42 static const char* const kDebuggerSignature 43 = "application/x-vnd.Haiku-Debugger"; 44 45 46 static const char* kUsage = 47 "Usage: %s [ <options> ]\n" 48 " %s [ <options> ] <command line>\n" 49 " %s [ <options> ] --team <team>\n" 50 " %s [ <options> ] --thread <thread>\n" 51 "\n" 52 "The first form starts the debugger displaying a requester to choose a\n" 53 "running team to debug respectively to specify the program to run and\n" 54 "debug.\n" 55 "\n" 56 "The second form runs the given command line and attaches the debugger to\n" 57 "the new team. Unless specified otherwise the program will be stopped at\n" 58 "the beginning of its main() function.\n" 59 "\n" 60 "The third and fourth forms attach the debugger to a running team. The\n" 61 "fourth form additionally stops the specified thread.\n" 62 "\n" 63 "Options:\n" 64 " -h, --help - Print this usage info and exit.\n" 65 " -c, --cli - Use command line user interface\n" 66 " -s, --save-report - Save crash report for the targetted team and exit.\n" 67 " Implies --cli.\n" 68 ; 69 70 71 static void 72 print_usage_and_exit(bool error) 73 { 74 fprintf(error ? stderr : stdout, kUsage, kProgramName, kProgramName, 75 kProgramName, kProgramName); 76 exit(error ? 1 : 0); 77 } 78 79 80 struct Options { 81 int commandLineArgc; 82 const char* const* commandLineArgv; 83 team_id team; 84 thread_id thread; 85 bool useCLI; 86 bool saveReport; 87 const char* reportPath; 88 89 Options() 90 : 91 commandLineArgc(0), 92 commandLineArgv(NULL), 93 team(-1), 94 thread(-1), 95 useCLI(false), 96 saveReport(false), 97 reportPath(NULL) 98 { 99 } 100 }; 101 102 103 struct DebuggedProgramInfo { 104 team_id team; 105 thread_id thread; 106 int commandLineArgc; 107 const char* const* commandLineArgv; 108 bool stopInMain; 109 }; 110 111 112 static bool 113 parse_arguments(int argc, const char* const* argv, bool noOutput, 114 Options& options) 115 { 116 optind = 1; 117 118 while (true) { 119 static struct option sLongOptions[] = { 120 { "help", no_argument, 0, 'h' }, 121 { "cli", no_argument, 0, 'c' }, 122 { "save-report", optional_argument, 0, 's' }, 123 { "team", required_argument, 0, 't' }, 124 { "thread", required_argument, 0, 'T' }, 125 { 0, 0, 0, 0 } 126 }; 127 128 opterr = 0; // don't print errors 129 130 int c = getopt_long(argc, (char**)argv, "+chs", sLongOptions, NULL); 131 if (c == -1) 132 break; 133 134 switch (c) { 135 case 'c': 136 options.useCLI = true; 137 break; 138 139 case 'h': 140 if (noOutput) 141 return false; 142 print_usage_and_exit(false); 143 break; 144 145 case 's': 146 { 147 options.saveReport = true; 148 options.reportPath = optarg; 149 break; 150 } 151 152 case 't': 153 { 154 options.team = strtol(optarg, NULL, 0); 155 if (options.team <= 0) { 156 if (noOutput) 157 return false; 158 print_usage_and_exit(true); 159 } 160 break; 161 } 162 163 case 'T': 164 { 165 options.thread = strtol(optarg, NULL, 0); 166 if (options.thread <= 0) { 167 if (noOutput) 168 return false; 169 print_usage_and_exit(true); 170 } 171 break; 172 } 173 174 default: 175 if (noOutput) 176 return false; 177 print_usage_and_exit(true); 178 break; 179 } 180 } 181 182 if (optind < argc) { 183 options.commandLineArgc = argc - optind; 184 options.commandLineArgv = argv + optind; 185 } 186 187 int exclusiveParams = 0; 188 if (options.team > 0) 189 exclusiveParams++; 190 if (options.thread > 0) 191 exclusiveParams++; 192 if (options.commandLineArgc > 0) 193 exclusiveParams++; 194 195 if (exclusiveParams == 0) { 196 return true; 197 } else if (exclusiveParams != 1) { 198 if (noOutput) 199 return false; 200 print_usage_and_exit(true); 201 } 202 203 return true; 204 } 205 206 static status_t 207 global_init() 208 { 209 status_t error = TypeHandlerRoster::CreateDefault(); 210 if (error != B_OK) 211 return error; 212 213 error = ValueHandlerRoster::CreateDefault(); 214 if (error != B_OK) 215 return error; 216 217 error = ImageDebugLoadingStateHandlerRoster::CreateDefault(); 218 if (error != B_OK) 219 return error; 220 221 return B_OK; 222 } 223 224 225 /** 226 * Finds or runs the program to debug, depending on the command line options. 227 * @param options The parsed command line options. 228 * @param _info The info for the program to fill in. Will only be filled in 229 * if successful. 230 * @return \c true, if the program has been found or ran. 231 */ 232 static bool 233 get_debugged_program(const Options& options, DebuggedProgramInfo& _info) 234 { 235 team_id team = options.team; 236 thread_id thread = options.thread; 237 bool stopInMain = false; 238 239 // If command line arguments were given, start the program. 240 if (options.commandLineArgc > 0) { 241 printf("loading program: \"%s\" ...\n", options.commandLineArgv[0]); 242 // TODO: What about the CWD? 243 thread = load_program(options.commandLineArgv, 244 options.commandLineArgc, false); 245 if (thread < 0) { 246 // TODO: Notify the user! 247 fprintf(stderr, "Error: Failed to load program \"%s\": %s\n", 248 options.commandLineArgv[0], strerror(thread)); 249 return false; 250 } 251 252 team = thread; 253 // main thread ID == team ID 254 stopInMain = true; 255 } 256 257 // no parameters given, prompt the user to attach to a team 258 if (team < 0 && thread < 0) 259 return false; 260 261 // no team, but a thread -- get team 262 if (team < 0) { 263 printf("no team yet, getting thread info...\n"); 264 thread_info threadInfo; 265 status_t error = get_thread_info(thread, &threadInfo); 266 if (error != B_OK) { 267 // TODO: Notify the user! 268 fprintf(stderr, "Error: Failed to get info for thread \"%" B_PRId32 269 "\": %s\n", thread, strerror(error)); 270 return false; 271 } 272 273 team = threadInfo.team; 274 } 275 printf("team: %" B_PRId32 ", thread: %" B_PRId32 "\n", team, thread); 276 277 _info.commandLineArgc = options.commandLineArgc; 278 _info.commandLineArgv = options.commandLineArgv; 279 _info.team = team; 280 _info.thread = thread; 281 _info.stopInMain = stopInMain; 282 return true; 283 } 284 285 286 /** 287 * Creates a TeamDebugger for the given team. If userInterface is given, 288 * that user interface is used (the caller retains its reference), otherwise 289 * a graphical user interface is created. 290 */ 291 static TeamDebugger* 292 start_team_debugger(team_id teamID, SettingsManager* settingsManager, 293 TeamDebugger::Listener* listener, thread_id threadID = -1, 294 int commandLineArgc = 0, const char* const* commandLineArgv = NULL, 295 bool stopInMain = false, UserInterface* userInterface = NULL, 296 status_t* _result = NULL) 297 { 298 if (teamID < 0) 299 return NULL; 300 301 BReference<UserInterface> userInterfaceReference; 302 if (userInterface == NULL) { 303 userInterface = new(std::nothrow) GraphicalUserInterface; 304 if (userInterface == NULL) { 305 // TODO: Notify the user! 306 fprintf(stderr, "Error: Out of memory!\n"); 307 return NULL; 308 } 309 310 userInterfaceReference.SetTo(userInterface, true); 311 } 312 313 status_t error = B_NO_MEMORY; 314 315 TeamDebugger* debugger = new(std::nothrow) TeamDebugger(listener, 316 userInterface, settingsManager); 317 if (debugger) { 318 error = debugger->Init(teamID, threadID, commandLineArgc, 319 commandLineArgv, stopInMain); 320 } 321 322 if (error != B_OK) { 323 printf("Error: debugger for team %" B_PRId32 " failed to init: %s!\n", 324 teamID, strerror(error)); 325 delete debugger; 326 debugger = NULL; 327 } else 328 printf("debugger for team %" B_PRId32 " created and initialized " 329 "successfully!\n", teamID); 330 331 if (_result != NULL) 332 *_result = error; 333 return debugger; 334 } 335 336 337 // #pragma mark - Debugger application class 338 339 340 class Debugger : public BApplication, private TeamDebugger::Listener { 341 public: 342 Debugger(); 343 ~Debugger(); 344 345 status_t Init(); 346 virtual void MessageReceived(BMessage* message); 347 virtual void ReadyToRun(); 348 virtual void ArgvReceived(int32 argc, char** argv); 349 350 private: 351 typedef BObjectList<TeamDebugger> TeamDebuggerList; 352 353 private: 354 // TeamDebugger::Listener 355 virtual void TeamDebuggerStarted(TeamDebugger* debugger); 356 virtual void TeamDebuggerRestartRequested( 357 TeamDebugger* debugger); 358 virtual void TeamDebuggerQuit(TeamDebugger* debugger); 359 360 virtual bool QuitRequested(); 361 virtual void Quit(); 362 363 TeamDebugger* _FindTeamDebugger(team_id teamID) const; 364 365 status_t _StartNewTeam(const char* path, const char* args); 366 status_t _StartOrFindTeam(Options& options); 367 368 private: 369 SettingsManager fSettingsManager; 370 TeamDebuggerList fTeamDebuggers; 371 int32 fRunningTeamDebuggers; 372 TeamsWindow* fTeamsWindow; 373 StartTeamWindow* fStartTeamWindow; 374 }; 375 376 377 // #pragma mark - CliDebugger 378 379 380 class CliDebugger : private TeamDebugger::Listener { 381 public: 382 CliDebugger(); 383 ~CliDebugger(); 384 385 bool Run(const Options& options); 386 387 private: 388 // TeamDebugger::Listener 389 virtual void TeamDebuggerStarted(TeamDebugger* debugger); 390 virtual void TeamDebuggerRestartRequested( 391 TeamDebugger* debugger); 392 virtual void TeamDebuggerQuit(TeamDebugger* debugger); 393 }; 394 395 396 class ReportDebugger : private TeamDebugger::Listener { 397 public: 398 ReportDebugger(); 399 ~ReportDebugger(); 400 bool Run(const Options& options); 401 402 private: 403 // TeamDebugger::Listener 404 virtual void TeamDebuggerStarted(TeamDebugger* debugger); 405 virtual void TeamDebuggerRestartRequested( 406 TeamDebugger* debugger); 407 virtual void TeamDebuggerQuit(TeamDebugger* debugger); 408 }; 409 410 411 // #pragma mark - Debugger application class 412 413 414 Debugger::Debugger() 415 : 416 BApplication(kDebuggerSignature), 417 fRunningTeamDebuggers(0), 418 fTeamsWindow(NULL), 419 fStartTeamWindow(NULL) 420 { 421 } 422 423 424 Debugger::~Debugger() 425 { 426 ValueHandlerRoster::DeleteDefault(); 427 TypeHandlerRoster::DeleteDefault(); 428 ImageDebugLoadingStateHandlerRoster::DeleteDefault(); 429 } 430 431 432 status_t 433 Debugger::Init() 434 { 435 status_t error = global_init(); 436 if (error != B_OK) 437 return error; 438 439 return fSettingsManager.Init(); 440 } 441 442 443 void 444 Debugger::MessageReceived(BMessage* message) 445 { 446 switch (message->what) { 447 case MSG_SHOW_TEAMS_WINDOW: 448 { 449 if (fTeamsWindow) { 450 fTeamsWindow->Activate(true); 451 break; 452 } 453 454 try { 455 fTeamsWindow = TeamsWindow::Create(&fSettingsManager); 456 if (fTeamsWindow != NULL) 457 fTeamsWindow->Show(); 458 } catch (...) { 459 // TODO: Notify the user! 460 fprintf(stderr, "Error: Failed to create Teams window\n"); 461 } 462 break; 463 } 464 case MSG_TEAMS_WINDOW_CLOSED: 465 { 466 fTeamsWindow = NULL; 467 Quit(); 468 break; 469 } 470 case MSG_SHOW_START_TEAM_WINDOW: 471 { 472 BMessenger messenger(fStartTeamWindow); 473 if (!messenger.IsValid()) { 474 fStartTeamWindow = StartTeamWindow::Create(); 475 if (fStartTeamWindow == NULL) 476 break; 477 fStartTeamWindow->Show(); 478 } else 479 fStartTeamWindow->Activate(); 480 break; 481 } 482 case MSG_START_TEAM_WINDOW_CLOSED: 483 { 484 fStartTeamWindow = NULL; 485 break; 486 } 487 case MSG_DEBUG_THIS_TEAM: 488 { 489 int32 teamID; 490 if (message->FindInt32("team", &teamID) != B_OK) 491 break; 492 493 start_team_debugger(teamID, &fSettingsManager, this); 494 break; 495 } 496 case MSG_START_NEW_TEAM: 497 { 498 const char* teamPath = NULL; 499 const char* args = NULL; 500 501 message->FindString("path", &teamPath); 502 message->FindString("arguments", &args); 503 504 status_t result = _StartNewTeam(teamPath, args); 505 BMessage reply; 506 reply.AddInt32("status", result); 507 message->SendReply(&reply); 508 break; 509 } 510 case MSG_TEAM_RESTART_REQUESTED: 511 { 512 int32 teamID; 513 if (message->FindInt32("team", &teamID) != B_OK) 514 break; 515 TeamDebugger* debugger = _FindTeamDebugger(teamID); 516 if (debugger == NULL) 517 break; 518 519 Options options; 520 options.commandLineArgc = debugger->ArgumentCount(); 521 options.commandLineArgv = debugger->Arguments(); 522 523 status_t result = _StartOrFindTeam(options); 524 if (result == B_OK) 525 debugger->PostMessage(B_QUIT_REQUESTED); 526 527 break; 528 } 529 case MSG_TEAM_DEBUGGER_QUIT: 530 { 531 int32 threadID; 532 if (message->FindInt32("thread", &threadID) == B_OK) 533 wait_for_thread(threadID, NULL); 534 535 --fRunningTeamDebuggers; 536 Quit(); 537 break; 538 } 539 default: 540 BApplication::MessageReceived(message); 541 break; 542 } 543 } 544 545 546 void 547 Debugger::ReadyToRun() 548 { 549 if (fRunningTeamDebuggers == 0) 550 PostMessage(MSG_SHOW_TEAMS_WINDOW); 551 } 552 553 554 void 555 Debugger::ArgvReceived(int32 argc, char** argv) 556 { 557 Options options; 558 if (!parse_arguments(argc, argv, true, options)) { 559 printf("Debugger::ArgvReceived(): parsing args failed!\n"); 560 return; 561 } 562 563 _StartOrFindTeam(options); 564 565 } 566 567 568 void 569 Debugger::TeamDebuggerStarted(TeamDebugger* debugger) 570 { 571 printf("debugger for team %" B_PRId32 " started...\n", debugger->TeamID()); 572 573 // Note: see TeamDebuggerQuit() note about locking 574 AutoLocker<Debugger> locker(this); 575 fTeamDebuggers.AddItem(debugger); 576 fRunningTeamDebuggers++; 577 locker.Unlock(); 578 } 579 580 581 void 582 Debugger::TeamDebuggerQuit(TeamDebugger* debugger) 583 { 584 // Note: Locking here only works, since we're never locking the other 585 // way around. If we even need to do that, we'll have to introduce a 586 // separate lock to protect the list. 587 588 printf("debugger for team %" B_PRId32 " quit.\n", debugger->TeamID()); 589 590 AutoLocker<Debugger> locker(this); 591 fTeamDebuggers.RemoveItem(debugger); 592 locker.Unlock(); 593 594 if (debugger->Thread() >= 0) { 595 BMessage message(MSG_TEAM_DEBUGGER_QUIT); 596 message.AddInt32("thread", debugger->Thread()); 597 PostMessage(&message); 598 } 599 } 600 601 602 void 603 Debugger::TeamDebuggerRestartRequested(TeamDebugger* debugger) 604 { 605 BMessage message(MSG_TEAM_RESTART_REQUESTED); 606 message.AddInt32("team", debugger->TeamID()); 607 PostMessage(&message); 608 } 609 610 611 bool 612 Debugger::QuitRequested() 613 { 614 // NOTE: The default implementation will just ask all windows' 615 // QuitRequested() hooks. This in turn will ask the TeamWindows. 616 // For now, this is what we want. If we have more windows later, 617 // like the global TeamsWindow, then we want to just ask the 618 // TeamDebuggers, the TeamsWindow should of course not go away already 619 // if one or more TeamDebuggers want to stay later. There are multiple 620 // ways how to do this. For example, TeamDebugger could get a 621 // QuitRequested() hook or the TeamsWindow and other global windows 622 // could always return false in their QuitRequested(). 623 return BApplication::QuitRequested(); 624 // TODO: This is ugly. The team debuggers own the windows, not the 625 // other way around. 626 } 627 628 void 629 Debugger::Quit() 630 { 631 // don't quit before all team debuggers have been quit 632 if (fRunningTeamDebuggers <= 0 && fTeamsWindow == NULL) 633 BApplication::Quit(); 634 } 635 636 637 TeamDebugger* 638 Debugger::_FindTeamDebugger(team_id teamID) const 639 { 640 for (int32 i = 0; TeamDebugger* debugger = fTeamDebuggers.ItemAt(i); 641 i++) { 642 if (debugger->TeamID() == teamID) 643 return debugger; 644 } 645 646 return NULL; 647 } 648 649 650 status_t 651 Debugger::_StartNewTeam(const char* path, const char* args) 652 { 653 if (path == NULL) 654 return B_BAD_VALUE; 655 656 BString data; 657 data.SetToFormat("\"%s\" %s", path, args); 658 if (data.Length() == 0) 659 return B_NO_MEMORY; 660 661 ArgumentVector argVector; 662 argVector.Parse(data.String()); 663 664 Options options; 665 options.commandLineArgc = argVector.ArgumentCount(); 666 if (options.commandLineArgc <= 0) 667 return B_BAD_VALUE; 668 669 char** argv = argVector.DetachArguments(); 670 671 options.commandLineArgv = argv; 672 MemoryDeleter deleter(argv); 673 674 return _StartOrFindTeam(options); 675 } 676 677 678 status_t 679 Debugger::_StartOrFindTeam(Options& options) 680 { 681 DebuggedProgramInfo programInfo; 682 if (!get_debugged_program(options, programInfo)) 683 return B_BAD_VALUE; 684 685 TeamDebugger* debugger = _FindTeamDebugger(programInfo.team); 686 if (debugger != NULL) { 687 printf("There's already a debugger for team: %" B_PRId32 "\n", 688 programInfo.team); 689 debugger->Activate(); 690 return B_OK; 691 } 692 693 status_t result; 694 start_team_debugger(programInfo.team, &fSettingsManager, this, 695 programInfo.thread, programInfo.commandLineArgc, 696 programInfo.commandLineArgv, programInfo.stopInMain, NULL, &result); 697 698 return result; 699 } 700 701 702 // #pragma mark - CliDebugger 703 704 705 CliDebugger::CliDebugger() 706 { 707 } 708 709 710 CliDebugger::~CliDebugger() 711 { 712 } 713 714 715 bool 716 CliDebugger::Run(const Options& options) 717 { 718 // Block SIGINT, in this thread so all threads created by it inherit the 719 // a block mask with the signal blocked. In the input loop the signal will 720 // be unblocked again. 721 SignalSet(SIGINT).BlockInCurrentThread(); 722 723 // initialize global objects and settings manager 724 status_t error = global_init(); 725 if (error != B_OK) { 726 fprintf(stderr, "Error: Global initialization failed: %s\n", 727 strerror(error)); 728 return false; 729 } 730 731 SettingsManager settingsManager; 732 error = settingsManager.Init(); 733 if (error != B_OK) { 734 fprintf(stderr, "Error: Settings manager initialization failed: " 735 "%s\n", strerror(error)); 736 return false; 737 } 738 739 // create the command line UI 740 CommandLineUserInterface* userInterface 741 = new(std::nothrow) CommandLineUserInterface(); 742 if (userInterface == NULL) { 743 fprintf(stderr, "Error: Out of memory!\n"); 744 return false; 745 } 746 BReference<UserInterface> userInterfaceReference(userInterface, true); 747 748 // get/run the program to be debugged and start the team debugger 749 DebuggedProgramInfo programInfo; 750 if (!get_debugged_program(options, programInfo)) 751 return false; 752 753 TeamDebugger* teamDebugger = start_team_debugger(programInfo.team, 754 &settingsManager, this, programInfo.thread, 755 programInfo.commandLineArgc, programInfo.commandLineArgv, 756 programInfo.stopInMain, userInterface); 757 if (teamDebugger == NULL) 758 return false; 759 760 thread_id teamDebuggerThread = teamDebugger->Thread(); 761 762 // run the input loop 763 userInterface->Run(); 764 765 // wait for the team debugger thread to terminate 766 wait_for_thread(teamDebuggerThread, NULL); 767 768 return true; 769 } 770 771 772 void 773 CliDebugger::TeamDebuggerStarted(TeamDebugger* debugger) 774 { 775 } 776 777 778 void 779 CliDebugger::TeamDebuggerRestartRequested(TeamDebugger* debugger) 780 { 781 // TODO: implement 782 } 783 784 785 void 786 CliDebugger::TeamDebuggerQuit(TeamDebugger* debugger) 787 { 788 } 789 790 791 // #pragma mark - ReportDebugger 792 793 794 ReportDebugger::ReportDebugger() 795 { 796 } 797 798 799 ReportDebugger::~ReportDebugger() 800 { 801 } 802 803 804 bool 805 ReportDebugger::Run(const Options& options) 806 { 807 // initialize global objects and settings manager 808 status_t error = global_init(); 809 if (error != B_OK) { 810 fprintf(stderr, "Error: Global initialization failed: %s\n", 811 strerror(error)); 812 return false; 813 } 814 815 SettingsManager settingsManager; 816 error = settingsManager.Init(); 817 if (error != B_OK) { 818 fprintf(stderr, "Error: Settings manager initialization failed: " 819 "%s\n", strerror(error)); 820 return false; 821 } 822 823 // create the report UI 824 ReportUserInterface* userInterface 825 = new(std::nothrow) ReportUserInterface(options.thread, options.reportPath); 826 if (userInterface == NULL) { 827 fprintf(stderr, "Error: Out of memory!\n"); 828 return false; 829 } 830 BReference<UserInterface> userInterfaceReference(userInterface, true); 831 832 // get/run the program to be debugged and start the team debugger 833 DebuggedProgramInfo programInfo; 834 if (!get_debugged_program(options, programInfo)) 835 return false; 836 837 TeamDebugger* teamDebugger = start_team_debugger(programInfo.team, 838 &settingsManager, this, programInfo.thread, 839 programInfo.commandLineArgc, programInfo.commandLineArgv, 840 programInfo.stopInMain, userInterface); 841 if (teamDebugger == NULL) 842 return false; 843 844 thread_id teamDebuggerThread = teamDebugger->Thread(); 845 846 // run the input loop 847 userInterface->Run(); 848 849 // wait for the team debugger thread to terminate 850 wait_for_thread(teamDebuggerThread, NULL); 851 852 return true; 853 } 854 855 856 void 857 ReportDebugger::TeamDebuggerStarted(TeamDebugger* debugger) 858 { 859 } 860 861 862 void 863 ReportDebugger::TeamDebuggerRestartRequested(TeamDebugger* debugger) 864 { 865 } 866 867 868 void 869 ReportDebugger::TeamDebuggerQuit(TeamDebugger* debugger) 870 { 871 } 872 873 874 // #pragma mark - 875 876 877 int 878 main(int argc, const char* const* argv) 879 { 880 // We test-parse the arguments here, so that, when we're started from the 881 // terminal and there's an instance already running, we can print an error 882 // message to the terminal, if something's wrong with the arguments. 883 // Otherwise, the arguments are reparsed in the actual application, 884 // unless the option to use the command line interface was chosen. 885 886 Options options; 887 parse_arguments(argc, argv, false, options); 888 889 if (options.useCLI) { 890 CliDebugger debugger; 891 return debugger.Run(options) ? 0 : 1; 892 } else if (options.saveReport) { 893 ReportDebugger debugger; 894 return debugger.Run(options) ? 0 : 1; 895 } 896 897 Debugger app; 898 status_t error = app.Init(); 899 if (error != B_OK) { 900 fprintf(stderr, "Error: Failed to init application: %s\n", 901 strerror(error)); 902 return 1; 903 } 904 905 app.Run(); 906 907 return 0; 908 } 909