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