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