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