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