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