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