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