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