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