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