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 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 693 int32 result = alert->Go(); 694 kill = (result == 1); 695 _NotifyRegistrar(fTeam, false, !kill); 696 } 697 698 return kill; 699 } 700 701 702 void 703 TeamDebugHandler::_LookupSymbolAddress( 704 debug_symbol_lookup_context *lookupContext, const void *address, 705 char *buffer, int32 bufferSize) 706 { 707 // lookup the symbol 708 void *baseAddress; 709 char symbolName[1024]; 710 char imageName[B_PATH_NAME_LENGTH]; 711 bool exactMatch; 712 bool lookupSucceeded = false; 713 if (lookupContext) { 714 status_t error = debug_lookup_symbol_address(lookupContext, address, 715 &baseAddress, symbolName, sizeof(symbolName), imageName, 716 sizeof(imageName), &exactMatch); 717 lookupSucceeded = (error == B_OK); 718 } 719 720 if (lookupSucceeded) { 721 // we were able to look something up 722 if (strlen(symbolName) > 0) { 723 // we even got a symbol 724 snprintf(buffer, bufferSize, "%s + %#lx%s", symbolName, 725 (addr_t)address - (addr_t)baseAddress, 726 (exactMatch ? "" : " (closest symbol)")); 727 728 } else { 729 // no symbol: image relative address 730 snprintf(buffer, bufferSize, "(%s + %#lx)", imageName, 731 (addr_t)address - (addr_t)baseAddress); 732 } 733 734 } else { 735 // lookup failed: find area containing the IP 736 bool useAreaInfo = false; 737 area_info info; 738 int32 cookie = 0; 739 while (get_next_area_info(fTeam, &cookie, &info) == B_OK) { 740 if ((addr_t)info.address <= (addr_t)address 741 && (addr_t)info.address + info.size > (addr_t)address) { 742 useAreaInfo = true; 743 break; 744 } 745 } 746 747 if (useAreaInfo) { 748 snprintf(buffer, bufferSize, "(%s + %#lx)", info.name, 749 (addr_t)address - (addr_t)info.address); 750 } else if (bufferSize > 0) 751 buffer[0] = '\0'; 752 } 753 } 754 755 756 void 757 TeamDebugHandler::_PrintStackTrace(thread_id thread) 758 { 759 // print a stacktrace 760 void *ip = NULL; 761 void *stackFrameAddress = NULL; 762 status_t error = debug_get_instruction_pointer(&fDebugContext, thread, &ip, 763 &stackFrameAddress); 764 765 if (error == B_OK) { 766 // create a symbol lookup context 767 debug_symbol_lookup_context *lookupContext = NULL; 768 error = debug_create_symbol_lookup_context(fTeam, &lookupContext); 769 if (error != B_OK) { 770 debug_printf("debug_server: Failed to create symbol lookup " 771 "context: %s\n", strerror(error)); 772 } 773 774 // lookup the IP 775 char symbolBuffer[2048]; 776 _LookupSymbolAddress(lookupContext, ip, symbolBuffer, 777 sizeof(symbolBuffer) - 1); 778 779 debug_printf("stack trace, current PC %p %s:\n", ip, symbolBuffer); 780 781 for (int32 i = 0; i < 50; i++) { 782 debug_stack_frame_info stackFrameInfo; 783 784 error = debug_get_stack_frame(&fDebugContext, stackFrameAddress, 785 &stackFrameInfo); 786 if (error < B_OK || stackFrameInfo.parent_frame == NULL) 787 break; 788 789 // lookup the return address 790 _LookupSymbolAddress(lookupContext, stackFrameInfo.return_address, 791 symbolBuffer, sizeof(symbolBuffer) - 1); 792 793 debug_printf(" (%p) %p %s\n", stackFrameInfo.frame, 794 stackFrameInfo.return_address, symbolBuffer); 795 796 stackFrameAddress = stackFrameInfo.parent_frame; 797 } 798 799 // delete the symbol lookup context 800 if (lookupContext) 801 debug_delete_symbol_lookup_context(lookupContext); 802 } 803 } 804 805 806 void 807 TeamDebugHandler::_NotifyAppServer(team_id team) 808 { 809 // This will remove any kWindowScreenFeels of the application, so that 810 // the debugger alert is visible on screen 811 BRoster::Private roster; 812 roster.ApplicationCrashed(team); 813 } 814 815 816 void 817 TeamDebugHandler::_NotifyRegistrar(team_id team, bool openAlert, 818 bool stopShutdown) 819 { 820 BMessage notify(BPrivate::B_REG_TEAM_DEBUGGER_ALERT); 821 notify.AddInt32("team", team); 822 notify.AddBool("open", openAlert); 823 notify.AddBool("stop shutdown", stopShutdown); 824 825 BRoster::Private roster; 826 BMessage reply; 827 roster.SendTo(¬ify, &reply, false); 828 } 829 830 831 status_t 832 TeamDebugHandler::_InitGUI() 833 { 834 DebugServer *app = dynamic_cast<DebugServer*>(be_app); 835 BAutolock _(app); 836 return app->InitGUIContext(); 837 } 838 839 840 status_t 841 TeamDebugHandler::_HandlerThreadEntry(void *data) 842 { 843 return ((TeamDebugHandler*)data)->_HandlerThread(); 844 } 845 846 847 status_t 848 TeamDebugHandler::_HandlerThread() 849 { 850 TRACE(("debug_server: TeamDebugHandler::_HandlerThread(): team %ld\n", 851 fTeam)); 852 853 // get initial message 854 TRACE(("debug_server: TeamDebugHandler::_HandlerThread(): team %ld: " 855 "getting message...\n", fTeam)); 856 857 DebugMessage *message; 858 status_t error = _PopMessage(message); 859 bool kill; 860 if (error == B_OK) { 861 // handle the message 862 kill = _HandleMessage(message); 863 delete message; 864 } else { 865 debug_printf("TeamDebugHandler::_HandlerThread(): Failed to pop " 866 "initial message: %s", strerror(error)); 867 kill = true; 868 } 869 870 bool isGuiServer = _IsGUIServer(); 871 872 // kill the team or hand it over to the debugger 873 thread_id debuggerThread = -1; 874 if (kill) { 875 // The team shall be killed. Since that is also the handling in case 876 // an error occurs while handing over the team to the debugger, we do 877 // nothing here. 878 } else if ((debuggerThread = _EnterDebugger()) >= 0) { 879 // wait for the "handed over" or a "team deleted" message 880 bool terminate = false; 881 do { 882 error = _PopMessage(message); 883 if (error != B_OK) { 884 debug_printf("TeamDebugHandler::_HandlerThread(): Failed to " 885 "pop message: %s", strerror(error)); 886 kill = true; 887 break; 888 } 889 890 if (message->Code() == B_DEBUGGER_MESSAGE_HANDED_OVER) { 891 // The team has successfully been handed over to the debugger. 892 // Nothing to do. 893 terminate = true; 894 } else if (message->Code() == B_DEBUGGER_MESSAGE_TEAM_DELETED) { 895 // The team died. Nothing to do. 896 terminate = true; 897 } else { 898 // Some message we can ignore. The debugger will take care of 899 // it. 900 901 // check whether the debugger thread still lives 902 thread_info threadInfo; 903 if (get_thread_info(debuggerThread, &threadInfo) != B_OK) { 904 // the debugger is gone 905 debug_printf("debug_server: The debugger for team %ld " 906 "seems to be gone.", fTeam); 907 908 kill = true; 909 terminate = true; 910 } 911 } 912 913 delete message; 914 } while (!terminate); 915 } else 916 kill = true; 917 918 if (kill) { 919 // kill the team 920 _KillTeam(); 921 } 922 923 // remove this handler from the roster and delete it 924 TeamDebugHandlerRoster::Default()->RemoveHandler(fTeam); 925 926 if (isGuiServer) { 927 // wait till debugging is done 928 status_t dummy; 929 wait_for_thread(debuggerThread, &dummy); 930 931 // find the registrar port 932 port_id rosterPort = find_port(BPrivate::get_roster_port_name()); 933 port_info info; 934 BMessenger messenger; 935 if (rosterPort >= 0 && get_port_info(rosterPort, &info) == B_OK) { 936 BMessenger::Private(messenger).SetTo(info.team, rosterPort, 937 B_PREFERRED_TOKEN); 938 } 939 messenger.SendMessage(kMsgRestartAppServer); 940 } 941 942 delete this; 943 944 return B_OK; 945 } 946 947 948 bool 949 TeamDebugHandler::_ExecutableNameEquals(const char *name) const 950 { 951 return strcmp(_LastPathComponent(fExecutablePath), name) == 0; 952 } 953 954 955 bool 956 TeamDebugHandler::_IsAppServer() const 957 { 958 return _ExecutableNameEquals("app_server"); 959 } 960 961 962 bool 963 TeamDebugHandler::_IsInputServer() const 964 { 965 return _ExecutableNameEquals("input_server"); 966 } 967 968 969 bool 970 TeamDebugHandler::_IsRegistrar() const 971 { 972 return _ExecutableNameEquals("registrar"); 973 } 974 975 976 bool 977 TeamDebugHandler::_IsGUIServer() const 978 { 979 // app or input server 980 return _IsAppServer() || _IsInputServer() || _IsRegistrar(); 981 } 982 983 984 const char * 985 TeamDebugHandler::_LastPathComponent(const char *path) 986 { 987 const char *lastSlash = strrchr(path, '/'); 988 return lastSlash ? lastSlash + 1 : path; 989 } 990 991 992 team_id 993 TeamDebugHandler::_FindTeam(const char *name) 994 { 995 // Iterate through all teams and check their executable name. 996 int32 cookie = 0; 997 team_info teamInfo; 998 while (get_next_team_info(&cookie, &teamInfo) == B_OK) { 999 entry_ref ref; 1000 if (BPrivate::get_app_ref(teamInfo.team, &ref) == B_OK) { 1001 if (strcmp(ref.name, name) == 0) 1002 return teamInfo.team; 1003 } 1004 } 1005 1006 return B_ENTRY_NOT_FOUND; 1007 } 1008 1009 1010 bool 1011 TeamDebugHandler::_AreGUIServersAlive() 1012 { 1013 return _FindTeam("app_server") >= 0 && _FindTeam("input_server") >= 0 1014 && _FindTeam("registrar"); 1015 } 1016 1017 1018 // #pragma mark - 1019 1020 1021 DebugServer::DebugServer(status_t &error) 1022 : 1023 BServer(kSignature, false, &error), 1024 fListenerPort(-1), 1025 fListener(-1), 1026 fTerminating(false) 1027 { 1028 } 1029 1030 1031 status_t 1032 DebugServer::Init() 1033 { 1034 // create listener port 1035 fListenerPort = create_port(10, "kernel listener"); 1036 if (fListenerPort < 0) 1037 return fListenerPort; 1038 1039 // spawn the listener thread 1040 fListener = spawn_thread(_ListenerEntry, "kernel listener", 1041 B_NORMAL_PRIORITY, this); 1042 if (fListener < 0) 1043 return fListener; 1044 1045 // register as default debugger 1046 // TODO: could set default flags 1047 status_t error = install_default_debugger(fListenerPort); 1048 if (error != B_OK) 1049 return error; 1050 1051 // resume the listener 1052 resume_thread(fListener); 1053 1054 return B_OK; 1055 } 1056 1057 1058 bool 1059 DebugServer::QuitRequested() 1060 { 1061 // Never give up, never surrender. ;-) 1062 return false; 1063 } 1064 1065 1066 status_t 1067 DebugServer::_ListenerEntry(void *data) 1068 { 1069 return ((DebugServer*)data)->_Listener(); 1070 } 1071 1072 1073 status_t 1074 DebugServer::_Listener() 1075 { 1076 while (!fTerminating) { 1077 // receive the next debug message 1078 DebugMessage *message = new DebugMessage; 1079 int32 code; 1080 ssize_t bytesRead; 1081 do { 1082 bytesRead = read_port(fListenerPort, &code, &message->Data(), 1083 sizeof(debug_debugger_message_data)); 1084 } while (bytesRead == B_INTERRUPTED); 1085 1086 if (bytesRead < 0) { 1087 debug_printf("debug_server: Failed to read from listener port: " 1088 "%s. Terminating!\n", strerror(bytesRead)); 1089 exit(1); 1090 } 1091 TRACE(("debug_server: Got debug message: team: %ld, code: %ld\n", 1092 message->Data().origin.team, code)); 1093 1094 message->SetCode((debug_debugger_message)code); 1095 1096 // dispatch the message 1097 TeamDebugHandlerRoster::Default()->DispatchMessage(message); 1098 } 1099 1100 return B_OK; 1101 } 1102 1103 1104 // #pragma mark - 1105 1106 1107 int 1108 main() 1109 { 1110 status_t error; 1111 1112 // for the time being let the debug server print to the syslog 1113 int console = open("/dev/dprintf", O_RDONLY); 1114 if (console < 0) { 1115 debug_printf("debug_server: Failed to open console: %s\n", 1116 strerror(errno)); 1117 } 1118 dup2(console, STDOUT_FILENO); 1119 dup2(console, STDERR_FILENO); 1120 close(console); 1121 1122 // create the team debug handler roster 1123 if (!TeamDebugHandlerRoster::CreateDefault()) { 1124 debug_printf("debug_server: Failed to create team debug handler " 1125 "roster.\n"); 1126 exit(1); 1127 } 1128 1129 // create application 1130 DebugServer server(error); 1131 if (error != B_OK) { 1132 debug_printf("debug_server: Failed to create BApplication: %s\n", 1133 strerror(error)); 1134 exit(1); 1135 } 1136 1137 // init application 1138 error = server.Init(); 1139 if (error != B_OK) { 1140 debug_printf("debug_server: Failed to init application: %s\n", 1141 strerror(error)); 1142 exit(1); 1143 } 1144 1145 server.Run(); 1146 1147 return 0; 1148 } 1149