1 /* 2 * Copyright 2005-2006, Ingo Weinhold, bonefish@users.sf.net. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include <map> 7 8 #include <errno.h> 9 #include <fcntl.h> 10 #include <stdio.h> 11 #include <string.h> 12 #include <unistd.h> 13 14 #include <Alert.h> 15 #include <AppMisc.h> 16 #include <AutoDeleter.h> 17 #include <Autolock.h> 18 #include <debug_support.h> 19 #include <Entry.h> 20 #include <Invoker.h> 21 22 #include <RegistrarDefs.h> 23 #include <RosterPrivate.h> 24 #include <Server.h> 25 26 #include <util/DoublyLinkedList.h> 27 28 #define USE_GUI true 29 // define to false if the debug server shouldn't use GUI (i.e. an alert) 30 31 //#define TRACE_DEBUG_SERVER 32 #ifdef TRACE_DEBUG_SERVER 33 # define TRACE(x) debug_printf x 34 #else 35 # define TRACE(x) ; 36 #endif 37 38 using std::map; 39 using std::nothrow; 40 41 static const char *kSignature = "application/x-vnd.Haiku-debug_server"; 42 43 // paths to the apps used for debugging 44 static const char *kConsoledPath = "/bin/consoled"; 45 static const char *kTerminalPath = "/boot/beos/apps/Terminal"; 46 static const char *kGDBPath = "/bin/gdb"; 47 48 49 // KillTeam 50 static void 51 KillTeam(team_id team, const char *appName = NULL) 52 { 53 // get a team info to verify the team still lives 54 team_info info; 55 if (!appName) { 56 status_t error = get_team_info(team, &info); 57 if (error != B_OK) { 58 debug_printf("debug_server: KillTeam(): Error getting info for " 59 "team %ld: %s\n", team, strerror(error)); 60 info.args[0] = '\0'; 61 } 62 63 appName = info.args; 64 } 65 66 debug_printf("debug_server: Killing team %ld (%s)\n", team, appName); 67 68 kill_team(team); 69 } 70 71 72 // #pragma mark - 73 74 // DebugMessage 75 class DebugMessage : public DoublyLinkedListLinkImpl<DebugMessage> { 76 public: 77 DebugMessage() 78 { 79 } 80 81 void SetCode(debug_debugger_message code) { fCode = code; } 82 debug_debugger_message Code() const { return fCode; } 83 84 debug_debugger_message_data &Data() { return fData; } 85 const debug_debugger_message_data &Data() const { return fData; } 86 87 private: 88 debug_debugger_message fCode; 89 debug_debugger_message_data fData; 90 }; 91 92 typedef DoublyLinkedList<DebugMessage> DebugMessageList; 93 94 95 // TeamDebugHandler 96 class TeamDebugHandler : public BLocker { 97 public: 98 TeamDebugHandler(team_id team); 99 ~TeamDebugHandler(); 100 101 status_t Init(port_id nubPort); 102 103 team_id Team() const; 104 105 status_t PushMessage(DebugMessage *message); 106 107 private: 108 status_t _PopMessage(DebugMessage *&message); 109 110 thread_id _EnterDebugger(); 111 void _KillTeam(); 112 113 bool _HandleMessage(DebugMessage *message); 114 115 void _LookupSymbolAddress(debug_symbol_lookup_context *lookupContext, 116 const void *address, char *buffer, int32 bufferSize); 117 void _PrintStackTrace(thread_id thread); 118 void _NotifyAppServer(team_id team); 119 void _NotifyRegistrar(team_id team, bool openAlert, bool stopShutdown); 120 121 status_t _InitGUI(); 122 123 static status_t _HandlerThreadEntry(void *data); 124 status_t _HandlerThread(); 125 126 bool _ExecutableNameEquals(const char *name) const; 127 bool _IsAppServer() const; 128 bool _IsInputServer() const; 129 bool _IsRegistrar() const; 130 bool _IsGUIServer() const; 131 132 static const char *_LastPathComponent(const char *path); 133 static team_id _FindTeam(const char *name); 134 static bool _AreGUIServersAlive(); 135 136 private: 137 DebugMessageList fMessages; 138 sem_id fMessageCountSem; 139 team_id fTeam; 140 team_info fTeamInfo; 141 char fExecutablePath[B_PATH_NAME_LENGTH]; 142 thread_id fHandlerThread; 143 debug_context fDebugContext; 144 }; 145 146 147 // TeamDebugHandlerRoster 148 class TeamDebugHandlerRoster : public BLocker { 149 private: 150 TeamDebugHandlerRoster() 151 : BLocker("team debug handler roster") 152 { 153 } 154 155 public: 156 static TeamDebugHandlerRoster *CreateDefault() 157 { 158 if (!sRoster) 159 sRoster = new(nothrow) TeamDebugHandlerRoster; 160 161 return sRoster; 162 } 163 164 static TeamDebugHandlerRoster *Default() 165 { 166 return sRoster; 167 } 168 169 bool AddHandler(TeamDebugHandler *handler) 170 { 171 if (!handler) 172 return false; 173 174 BAutolock _(this); 175 176 fHandlers[handler->Team()] = handler; 177 178 return true; 179 } 180 181 TeamDebugHandler *RemoveHandler(team_id team) 182 { 183 BAutolock _(this); 184 185 TeamDebugHandler *handler = NULL; 186 187 TeamDebugHandlerMap::iterator it = fHandlers.find(team); 188 if (it != fHandlers.end()) { 189 handler = it->second; 190 fHandlers.erase(it); 191 } 192 193 return handler; 194 } 195 196 TeamDebugHandler *HandlerFor(team_id team) 197 { 198 BAutolock _(this); 199 200 TeamDebugHandler *handler = NULL; 201 202 TeamDebugHandlerMap::iterator it = fHandlers.find(team); 203 if (it != fHandlers.end()) 204 handler = it->second; 205 206 return handler; 207 } 208 209 status_t DispatchMessage(DebugMessage *message) 210 { 211 if (!message) 212 return B_BAD_VALUE; 213 214 ObjectDeleter<DebugMessage> messageDeleter(message); 215 216 team_id team = message->Data().origin.team; 217 218 // get the responsible team debug handler 219 BAutolock _(this); 220 221 TeamDebugHandler *handler = HandlerFor(team); 222 if (!handler) { 223 // no handler yet, we need to create one 224 handler = new(nothrow) TeamDebugHandler(team); 225 if (!handler) { 226 KillTeam(team); 227 return B_NO_MEMORY; 228 } 229 230 status_t error = handler->Init(message->Data().origin.nub_port); 231 if (error != B_OK) { 232 delete handler; 233 KillTeam(team); 234 return error; 235 } 236 237 if (!AddHandler(handler)) { 238 delete handler; 239 KillTeam(team); 240 return B_NO_MEMORY; 241 } 242 } 243 244 // hand over the message to it 245 handler->PushMessage(message); 246 messageDeleter.Detach(); 247 248 return B_OK; 249 } 250 251 private: 252 typedef map<team_id, TeamDebugHandler*> TeamDebugHandlerMap; 253 254 static TeamDebugHandlerRoster *sRoster; 255 256 TeamDebugHandlerMap fHandlers; 257 }; 258 259 TeamDebugHandlerRoster *TeamDebugHandlerRoster::sRoster = NULL; 260 261 262 // DebugServer 263 class DebugServer : public BServer { 264 public: 265 DebugServer(status_t &error); 266 267 status_t Init(); 268 269 virtual bool QuitRequested(); 270 271 private: 272 static status_t _ListenerEntry(void *data); 273 status_t _Listener(); 274 275 void _DeleteTeamDebugHandler(TeamDebugHandler *handler); 276 277 private: 278 typedef map<team_id, TeamDebugHandler*> TeamDebugHandlerMap; 279 280 port_id fListenerPort; 281 thread_id fListener; 282 bool fTerminating; 283 }; 284 285 286 // #pragma mark - 287 288 // constructor 289 TeamDebugHandler::TeamDebugHandler(team_id team) 290 : BLocker("team debug handler"), 291 fMessages(), 292 fMessageCountSem(-1), 293 fTeam(team), 294 fHandlerThread(-1) 295 { 296 fDebugContext.nub_port = -1; 297 fDebugContext.reply_port = -1; 298 299 fExecutablePath[0] = '\0'; 300 } 301 302 // destructor 303 TeamDebugHandler::~TeamDebugHandler() 304 { 305 // delete the message count semaphore and wait for the thread to die 306 if (fMessageCountSem >= 0) 307 delete_sem(fMessageCountSem); 308 309 if (fHandlerThread >= 0 && find_thread(NULL) != fHandlerThread) { 310 status_t result; 311 wait_for_thread(fHandlerThread, &result); 312 } 313 314 // destroy debug context 315 if (fDebugContext.nub_port >= 0) 316 destroy_debug_context(&fDebugContext); 317 318 // delete the remaining messages 319 while (DebugMessage *message = fMessages.Head()) { 320 fMessages.Remove(message); 321 delete message; 322 } 323 } 324 325 // Init 326 status_t 327 TeamDebugHandler::Init(port_id nubPort) 328 { 329 // get the team info for the team 330 status_t error = get_team_info(fTeam, &fTeamInfo); 331 if (error != B_OK) { 332 debug_printf("debug_server: TeamDebugHandler::Init(): Failed to get " 333 "info for team %ld: %s\n", fTeam, strerror(error)); 334 return error; 335 } 336 337 // get the executable path 338 error = BPrivate::get_app_path(fTeam, fExecutablePath); 339 if (error != B_OK) { 340 debug_printf("debug_server: TeamDebugHandler::Init(): Failed to get " 341 "executable path of team %ld: %s\n", fTeam, strerror(error)); 342 343 fExecutablePath[0] = '\0'; 344 } 345 346 // init a debug context for the handler 347 error = init_debug_context(&fDebugContext, fTeam, nubPort); 348 if (error != B_OK) { 349 debug_printf("debug_server: TeamDebugHandler::Init(): Failed to init " 350 "debug context for team %ld, port %ld: %s\n", fTeam, nubPort, 351 strerror(error)); 352 return error; 353 } 354 355 // create the message count semaphore 356 char name[B_OS_NAME_LENGTH]; 357 snprintf(name, sizeof(name), "team %ld message count", fTeam); 358 fMessageCountSem = create_sem(0, name); 359 if (fMessageCountSem < 0) { 360 debug_printf("debug_server: TeamDebugHandler::Init(): Failed to create " 361 "message count semaphore: %s\n", strerror(fMessageCountSem)); 362 return fMessageCountSem; 363 } 364 365 // spawn the handler thread 366 snprintf(name, sizeof(name), "team %ld handler", fTeam); 367 fHandlerThread = spawn_thread(&_HandlerThreadEntry, name, B_NORMAL_PRIORITY, 368 this); 369 if (fHandlerThread < 0) { 370 debug_printf("debug_server: TeamDebugHandler::Init(): Failed to spawn " 371 "handler thread: %s\n", strerror(fHandlerThread)); 372 return fHandlerThread; 373 } 374 375 resume_thread(fHandlerThread); 376 377 return B_OK; 378 } 379 380 // Team 381 team_id 382 TeamDebugHandler::Team() const 383 { 384 return fTeam; 385 } 386 387 // PushMessage 388 status_t 389 TeamDebugHandler::PushMessage(DebugMessage *message) 390 { 391 BAutolock _(this); 392 393 fMessages.Add(message); 394 release_sem(fMessageCountSem); 395 396 return B_OK; 397 } 398 399 // _PopMessage 400 status_t 401 TeamDebugHandler::_PopMessage(DebugMessage *&message) 402 { 403 // acquire the semaphore 404 status_t error; 405 do { 406 error = acquire_sem(fMessageCountSem); 407 } while (error == B_INTERRUPTED); 408 409 if (error != B_OK) 410 return error; 411 412 // get the message 413 BAutolock _(this); 414 415 message = fMessages.Head(); 416 fMessages.Remove(message); 417 418 return B_OK; 419 } 420 421 // _EnterDebugger 422 423 thread_id 424 TeamDebugHandler::_EnterDebugger() 425 { 426 TRACE(("debug_server: TeamDebugHandler::_EnterDebugger(): team %ld\n", 427 fTeam)); 428 429 bool debugInConsoled = _IsGUIServer() || !_AreGUIServersAlive(); 430 431 // prepare a debugger handover 432 TRACE(("debug_server: TeamDebugHandler::_EnterDebugger(): preparing " 433 "debugger handover for team %ld...\n", fTeam)); 434 435 status_t error = send_debug_message(&fDebugContext, 436 B_DEBUG_MESSAGE_PREPARE_HANDOVER, NULL, 0, NULL, 0); 437 if (error != B_OK) { 438 debug_printf("debug_server: Failed to prepare debugger handover: %s\n", 439 strerror(error)); 440 return error; 441 } 442 443 // prepare the argument vector 444 char teamString[32]; 445 snprintf(teamString, sizeof(teamString), "--pid=%ld", fTeam); 446 447 const char *terminal = (debugInConsoled ? kConsoledPath : kTerminalPath); 448 449 const char *argv[16]; 450 int argc = 0; 451 452 argv[argc++] = terminal; 453 454 if (!debugInConsoled) { 455 char windowTitle[64]; 456 snprintf(windowTitle, sizeof(windowTitle), "Debug of Team %ld: %s", 457 fTeam, _LastPathComponent(fExecutablePath)); 458 argv[argc++] = "-t"; 459 argv[argc++] = windowTitle; 460 } 461 462 argv[argc++] = kGDBPath; 463 argv[argc++] = teamString; 464 if (strlen(fExecutablePath) > 0) 465 argv[argc++] = fExecutablePath; 466 argv[argc] = NULL; 467 468 // start the terminal 469 TRACE(("debug_server: TeamDebugHandler::_EnterDebugger(): starting " 470 "terminal (debugger) for team %ld...\n", fTeam)); 471 472 thread_id thread = load_image(argc, argv, (const char**)environ); 473 if (thread < 0) { 474 debug_printf("debug_server: Failed to start consoled + gdb: %s\n", 475 strerror(thread)); 476 return thread; 477 } 478 resume_thread(thread); 479 480 TRACE(("debug_server: TeamDebugHandler::_EnterDebugger(): debugger started " 481 "for team %ld: thread: %ld\n", fTeam, thread)); 482 483 return thread; 484 } 485 486 // _KillTeam 487 void 488 TeamDebugHandler::_KillTeam() 489 { 490 KillTeam(fTeam, fTeamInfo.args); 491 } 492 493 // _HandleMessage 494 bool 495 TeamDebugHandler::_HandleMessage(DebugMessage *message) 496 { 497 // This method is called only for the first message the debugger gets for 498 // a team. That means only a few messages are actually possible, while 499 // others wouldn't trigger the debugger in the first place. So we deal with 500 // all of them the same way, by popping up an alert. 501 TRACE(("debug_server: TeamDebugHandler::_HandleMessage(): team %ld, code: " 502 "%ld\n", fTeam, (int32)message->Code())); 503 504 thread_id thread = message->Data().origin.thread; 505 506 // get some user-readable message 507 char buffer[512]; 508 switch (message->Code()) { 509 case B_DEBUGGER_MESSAGE_TEAM_DELETED: 510 // This shouldn't happen. 511 debug_printf("debug_server: Got a spurious " 512 "B_DEBUGGER_MESSAGE_TEAM_DELETED message for team %ld\n", 513 fTeam); 514 return true; 515 516 case B_DEBUGGER_MESSAGE_EXCEPTION_OCCURRED: 517 get_debug_exception_string( 518 message->Data().exception_occurred.exception, buffer, 519 sizeof(buffer)); 520 break; 521 522 case B_DEBUGGER_MESSAGE_DEBUGGER_CALL: 523 { 524 // get the debugger() message 525 void *messageAddress = message->Data().debugger_call.message; 526 char messageBuffer[128]; 527 status_t error = B_OK; 528 ssize_t bytesRead = debug_read_string(&fDebugContext, 529 messageAddress, messageBuffer, sizeof(messageBuffer)); 530 if (bytesRead < 0) 531 error = bytesRead; 532 533 if (error == B_OK) { 534 sprintf(buffer, "Debugger call: `%s'", messageBuffer); 535 } else { 536 snprintf(buffer, sizeof(buffer), "Debugger call: %p " 537 "(Failed to read message: %s)", messageAddress, 538 strerror(error)); 539 } 540 break; 541 } 542 543 default: 544 get_debug_message_string(message->Code(), buffer, sizeof(buffer)); 545 break; 546 } 547 548 debug_printf("debug_server: Thread %ld entered the debugger: %s\n", thread, 549 buffer); 550 551 _PrintStackTrace(thread); 552 553 bool kill = true; 554 555 // ask the user whether to debug or kill the team 556 if (_IsGUIServer()) { 557 // App server, input server, or registrar. We always debug those. 558 kill = false; 559 } else if (USE_GUI && _AreGUIServersAlive() && _InitGUI() == B_OK) { 560 // normal app -- tell the user 561 _NotifyAppServer(fTeam); 562 _NotifyRegistrar(fTeam, true, false); 563 564 char buffer[1024]; 565 snprintf(buffer, sizeof(buffer), "The application:\n\n %s\n\n" 566 "has encountered an error which prevents it from continuing. Haiku " 567 "will terminate the application and clean up.", fTeamInfo.args); 568 569 // TODO: It would be nice if the alert would go away automatically 570 // if someone else kills our teams. 571 BAlert *alert = new BAlert(NULL, buffer, "Debug", "OK", NULL, 572 B_WIDTH_AS_USUAL, B_WARNING_ALERT); 573 int32 result = alert->Go(); 574 kill = (result == 1); 575 _NotifyRegistrar(fTeam, false, !kill); 576 } 577 578 return kill; 579 } 580 581 // _LookupSymbolAddress 582 void 583 TeamDebugHandler::_LookupSymbolAddress( 584 debug_symbol_lookup_context *lookupContext, const void *address, 585 char *buffer, int32 bufferSize) 586 { 587 // lookup the symbol 588 void *baseAddress; 589 char symbolName[1024]; 590 char imageName[B_PATH_NAME_LENGTH]; 591 bool exactMatch; 592 bool lookupSucceeded = false; 593 if (lookupContext) { 594 status_t error = debug_lookup_symbol_address(lookupContext, address, 595 &baseAddress, symbolName, sizeof(symbolName), imageName, 596 sizeof(imageName), &exactMatch); 597 lookupSucceeded = (error == B_OK); 598 } 599 600 if (lookupSucceeded) { 601 // we were able to look something up 602 if (strlen(symbolName) > 0) { 603 // we even got a symbol 604 snprintf(buffer, bufferSize, "%s + %#lx%s", symbolName, 605 (addr_t)address - (addr_t)baseAddress, 606 (exactMatch ? "" : " (closest symbol)")); 607 608 } else { 609 // no symbol: image relative address 610 snprintf(buffer, bufferSize, "(%s + %#lx)", imageName, 611 (addr_t)address - (addr_t)baseAddress); 612 } 613 614 } else { 615 // lookup failed: find area containing the IP 616 bool useAreaInfo = false; 617 area_info info; 618 int32 cookie = 0; 619 while (get_next_area_info(fTeam, &cookie, &info) == B_OK) { 620 if ((addr_t)info.address <= (addr_t)address 621 && (addr_t)info.address + info.size > (addr_t)address) { 622 useAreaInfo = true; 623 break; 624 } 625 } 626 627 if (useAreaInfo) { 628 snprintf(buffer, bufferSize, "(%s + %#lx)", info.name, 629 (addr_t)address - (addr_t)info.address); 630 } else if (bufferSize > 0) 631 buffer[0] = '\0'; 632 } 633 } 634 635 // _PrintStackTrace 636 void 637 TeamDebugHandler::_PrintStackTrace(thread_id thread) 638 { 639 // print a stacktrace 640 void *ip = NULL; 641 void *stackFrameAddress = NULL; 642 status_t error = debug_get_instruction_pointer(&fDebugContext, thread, &ip, 643 &stackFrameAddress); 644 645 if (error == B_OK) { 646 // create a symbol lookup context 647 debug_symbol_lookup_context *lookupContext = NULL; 648 error = debug_create_symbol_lookup_context(&fDebugContext, 649 &lookupContext); 650 if (error != B_OK) { 651 debug_printf("debug_server: Failed to create symbol lookup " 652 "context: %s\n", strerror(error)); 653 } 654 655 // lookup the IP 656 char symbolBuffer[2048]; 657 _LookupSymbolAddress(lookupContext, ip, symbolBuffer, 658 sizeof(symbolBuffer) - 1); 659 660 debug_printf("stack trace, current PC %p %s:\n", ip, symbolBuffer); 661 662 for (int32 i = 0; i < 50; i++) { 663 debug_stack_frame_info stackFrameInfo; 664 665 error = debug_get_stack_frame(&fDebugContext, stackFrameAddress, 666 &stackFrameInfo); 667 if (error < B_OK || stackFrameInfo.parent_frame == NULL) 668 break; 669 670 // lookup the return address 671 _LookupSymbolAddress(lookupContext, stackFrameInfo.return_address, 672 symbolBuffer, sizeof(symbolBuffer) - 1); 673 674 debug_printf(" (%p) %p %s\n", stackFrameInfo.frame, 675 stackFrameInfo.return_address, symbolBuffer); 676 677 stackFrameAddress = stackFrameInfo.parent_frame; 678 } 679 680 // delete the symbol lookup context 681 if (lookupContext) 682 debug_delete_symbol_lookup_context(lookupContext); 683 } 684 } 685 686 687 void 688 TeamDebugHandler::_NotifyAppServer(team_id team) 689 { 690 // This will remove any kWindowScreenFeels of the application, so that 691 // the debugger alert is visible on screen 692 BRoster::Private roster; 693 roster.ApplicationCrashed(team); 694 } 695 696 697 void 698 TeamDebugHandler::_NotifyRegistrar(team_id team, bool openAlert, 699 bool stopShutdown) 700 { 701 BMessage notify(BPrivate::B_REG_TEAM_DEBUGGER_ALERT); 702 notify.AddInt32("team", team); 703 notify.AddBool("open", openAlert); 704 notify.AddBool("stop shutdown", stopShutdown); 705 706 BRoster::Private roster; 707 BMessage reply; 708 roster.SendTo(¬ify, &reply, false); 709 } 710 711 712 // _InitGUI 713 status_t 714 TeamDebugHandler::_InitGUI() 715 { 716 DebugServer *app = dynamic_cast<DebugServer*>(be_app); 717 BAutolock _(app); 718 return app->InitGUIContext(); 719 } 720 721 // _HandlerThreadEntry 722 status_t 723 TeamDebugHandler::_HandlerThreadEntry(void *data) 724 { 725 return ((TeamDebugHandler*)data)->_HandlerThread(); 726 } 727 728 // _HandlerThread 729 status_t 730 TeamDebugHandler::_HandlerThread() 731 { 732 TRACE(("debug_server: TeamDebugHandler::_HandlerThread(): team %ld\n", 733 fTeam)); 734 735 // get initial message 736 TRACE(("debug_server: TeamDebugHandler::_HandlerThread(): team %ld: " 737 "getting message...\n", fTeam)); 738 739 DebugMessage *message; 740 status_t error = _PopMessage(message); 741 bool kill; 742 if (error == B_OK) { 743 // handle the message 744 kill = _HandleMessage(message); 745 delete message; 746 } else { 747 debug_printf("TeamDebugHandler::_HandlerThread(): Failed to pop " 748 "initial message: %s", strerror(error)); 749 kill = true; 750 } 751 752 // kill the team or hand it over to the debugger 753 thread_id debuggerThread; 754 if (kill) { 755 // The team shall be killed. Since that is also the handling in case 756 // an error occurs while handing over the team to the debugger, we do 757 // nothing here. 758 } else if ((debuggerThread = _EnterDebugger()) >= 0) { 759 // wait for the "handed over" or a "team deleted" message 760 bool terminate = false; 761 do { 762 error = _PopMessage(message); 763 if (error != B_OK) { 764 debug_printf("TeamDebugHandler::_HandlerThread(): Failed to " 765 "pop message: %s", strerror(error)); 766 kill = true; 767 break; 768 } 769 770 if (message->Code() == B_DEBUGGER_MESSAGE_HANDED_OVER) { 771 // The team has successfully been handed over to the debugger. 772 // Nothing to do. 773 terminate = true; 774 } else if (message->Code() == B_DEBUGGER_MESSAGE_TEAM_DELETED) { 775 // The team died. Nothing to do. 776 terminate = true; 777 } else { 778 // Some message we can ignore. The debugger will take care of 779 // it. 780 781 // check whether the debugger thread still lives 782 thread_info threadInfo; 783 if (get_thread_info(debuggerThread, &threadInfo) != B_OK) { 784 // the debugger is gone 785 debug_printf("debug_server: The debugger for team %ld " 786 "seems to be gone.", fTeam); 787 788 kill = true; 789 terminate = true; 790 } 791 } 792 793 delete message; 794 } while (!terminate); 795 } else 796 kill = true; 797 798 if (kill) { 799 // kill the team 800 _KillTeam(); 801 802 // remove this handler from the roster and delete it 803 TeamDebugHandlerRoster::Default()->RemoveHandler(fTeam); 804 805 delete this; 806 } 807 808 return B_OK; 809 } 810 811 // _ExecutableNameEquals 812 bool 813 TeamDebugHandler::_ExecutableNameEquals(const char *name) const 814 { 815 return strcmp(_LastPathComponent(fExecutablePath), name) == 0; 816 } 817 818 // _IsAppServer 819 bool 820 TeamDebugHandler::_IsAppServer() const 821 { 822 return _ExecutableNameEquals("app_server"); 823 } 824 825 // _IsInputServer 826 bool 827 TeamDebugHandler::_IsInputServer() const 828 { 829 return _ExecutableNameEquals("input_server"); 830 } 831 832 // _IsRegistrar 833 bool 834 TeamDebugHandler::_IsRegistrar() const 835 { 836 return _ExecutableNameEquals("registrar"); 837 } 838 839 // _IsGUIServer 840 bool 841 TeamDebugHandler::_IsGUIServer() const 842 { 843 // app or input server 844 return _IsAppServer() || _IsInputServer() || _IsRegistrar(); 845 } 846 847 // _LastPathComponent 848 const char * 849 TeamDebugHandler::_LastPathComponent(const char *path) 850 { 851 const char *lastSlash = strrchr(path, '/'); 852 return lastSlash ? lastSlash + 1 : path; 853 } 854 855 // _FindTeam 856 team_id 857 TeamDebugHandler::_FindTeam(const char *name) 858 { 859 // Iterate through all teams and check their executable name. 860 int32 cookie = 0; 861 team_info teamInfo; 862 while (get_next_team_info(&cookie, &teamInfo) == B_OK) { 863 entry_ref ref; 864 if (BPrivate::get_app_ref(teamInfo.team, &ref) == B_OK) { 865 if (strcmp(ref.name, name) == 0) 866 return teamInfo.team; 867 } 868 } 869 870 return B_ENTRY_NOT_FOUND; 871 } 872 873 // _AreGUIServersAlive 874 bool 875 TeamDebugHandler::_AreGUIServersAlive() 876 { 877 return _FindTeam("app_server") >= 0 && _FindTeam("input_server") >= 0 878 && _FindTeam("registrar"); 879 } 880 881 882 // #pragma mark - 883 884 // constructor 885 DebugServer::DebugServer(status_t &error) 886 : BServer(kSignature, false, &error), 887 fListenerPort(-1), 888 fListener(-1), 889 fTerminating(false) 890 { 891 } 892 893 // Init 894 status_t 895 DebugServer::Init() 896 { 897 // create listener port 898 fListenerPort = create_port(10, "kernel listener"); 899 if (fListenerPort < 0) 900 return fListenerPort; 901 902 // spawn the listener thread 903 fListener = spawn_thread(_ListenerEntry, "kernel listener", 904 B_NORMAL_PRIORITY, this); 905 if (fListener < 0) 906 return fListener; 907 908 // register as default debugger 909 status_t error = install_default_debugger(fListenerPort); 910 if (error != B_OK) 911 return error; 912 913 // resume the listener 914 resume_thread(fListener); 915 916 return B_OK; 917 } 918 919 920 bool 921 DebugServer::QuitRequested() 922 { 923 // Never give up, never surrender. ;-) 924 return false; 925 } 926 927 // _ListenerEntry 928 status_t 929 DebugServer::_ListenerEntry(void *data) 930 { 931 return ((DebugServer*)data)->_Listener(); 932 } 933 934 // _Listener 935 status_t 936 DebugServer::_Listener() 937 { 938 while (!fTerminating) { 939 // receive the next debug message 940 DebugMessage *message = new DebugMessage; 941 int32 code; 942 ssize_t bytesRead; 943 do { 944 bytesRead = read_port(fListenerPort, &code, &message->Data(), 945 sizeof(debug_debugger_message_data)); 946 } while (bytesRead == B_INTERRUPTED); 947 948 if (bytesRead < 0) { 949 debug_printf("debug_server: Failed to read from listener port: " 950 "%s. Terminating!\n", strerror(bytesRead)); 951 exit(1); 952 } 953 TRACE(("debug_server: Got debug message: team: %ld, code: %ld\n", 954 message->Data().origin.team, code)); 955 956 message->SetCode((debug_debugger_message)code); 957 958 // dispatch the message 959 TeamDebugHandlerRoster::Default()->DispatchMessage(message); 960 } 961 962 return B_OK; 963 } 964 965 966 // #pragma mark - 967 968 // main 969 int 970 main() 971 { 972 status_t error; 973 974 // for the time being let the debug server print to the syslog 975 int console = open("/dev/dprintf", O_RDONLY); 976 if (console < 0) { 977 debug_printf("debug_server: Failed to open console: %s\n", 978 strerror(errno)); 979 } 980 dup2(console, STDOUT_FILENO); 981 dup2(console, STDERR_FILENO); 982 close(console); 983 984 // create the team debug handler roster 985 if (!TeamDebugHandlerRoster::CreateDefault()) { 986 debug_printf("debug_server: Failed to create team debug handler " 987 "roster.\n"); 988 exit(1); 989 } 990 991 // create application 992 DebugServer server(error); 993 if (error != B_OK) { 994 debug_printf("debug_server: Failed to create BApplication: %s\n", 995 strerror(error)); 996 exit(1); 997 } 998 999 // init application 1000 error = server.Init(); 1001 if (error != B_OK) { 1002 debug_printf("debug_server: Failed to init application: %s\n", 1003 strerror(error)); 1004 exit(1); 1005 } 1006 1007 server.Run(); 1008 1009 return 0; 1010 } 1011