1 /* 2 * Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2010-2012, Rene Gollent, rene@gollent.com. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8 #include "TeamWindow.h" 9 10 #include <stdio.h> 11 12 #include <Alert.h> 13 #include <Button.h> 14 #include <FilePanel.h> 15 #include <FindDirectory.h> 16 #include <LayoutBuilder.h> 17 #include <Menu.h> 18 #include <MenuBar.h> 19 #include <MenuItem.h> 20 #include <Message.h> 21 #include <MessageFilter.h> 22 #include <Path.h> 23 #include <StringView.h> 24 #include <TabView.h> 25 #include <ScrollView.h> 26 #include <SplitView.h> 27 #include <TextView.h> 28 29 #include <AutoLocker.h> 30 31 #include "Breakpoint.h" 32 #include "CpuState.h" 33 #include "DisassembledCode.h" 34 #include "FileSourceCode.h" 35 #include "GuiSettingsUtils.h" 36 #include "GuiTeamUiSettings.h" 37 #include "Image.h" 38 #include "ImageDebugInfo.h" 39 #include "InspectorWindow.h" 40 #include "LocatableFile.h" 41 #include "MessageCodes.h" 42 #include "RegistersView.h" 43 #include "StackTrace.h" 44 #include "StackTraceView.h" 45 #include "Tracing.h" 46 #include "TypeComponentPath.h" 47 #include "UiUtils.h" 48 #include "UserInterface.h" 49 #include "Variable.h" 50 #include "WatchPromptWindow.h" 51 52 53 enum { 54 MAIN_TAB_INDEX_THREADS = 0, 55 MAIN_TAB_INDEX_IMAGES = 1 56 }; 57 58 59 enum { 60 MSG_CHOOSE_DEBUG_REPORT_LOCATION = 'ccrl', 61 MSG_DEBUG_REPORT_SAVED = 'drsa', 62 MSG_LOCATE_SOURCE_IF_NEEDED = 'lsin' 63 }; 64 65 66 class PathViewMessageFilter : public BMessageFilter { 67 public: 68 PathViewMessageFilter(BMessenger teamWindow) 69 : 70 BMessageFilter(B_MOUSE_UP), 71 fTeamWindowMessenger(teamWindow) 72 { 73 } 74 75 virtual filter_result Filter(BMessage*, BHandler**) 76 { 77 fTeamWindowMessenger.SendMessage(MSG_LOCATE_SOURCE_IF_NEEDED); 78 79 return B_DISPATCH_MESSAGE; 80 } 81 82 private: 83 BMessenger fTeamWindowMessenger; 84 }; 85 86 87 // #pragma mark - TeamWindow 88 89 90 TeamWindow::TeamWindow(::Team* team, UserInterfaceListener* listener) 91 : 92 BWindow(BRect(100, 100, 899, 699), "Team", B_TITLED_WINDOW, 93 B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS), 94 fTeam(team), 95 fActiveThread(NULL), 96 fActiveImage(NULL), 97 fActiveStackTrace(NULL), 98 fActiveStackFrame(NULL), 99 fActiveBreakpoint(NULL), 100 fActiveFunction(NULL), 101 fActiveSourceCode(NULL), 102 fActiveSourceObject(ACTIVE_SOURCE_NONE), 103 fListener(listener), 104 fTabView(NULL), 105 fLocalsTabView(NULL), 106 fThreadListView(NULL), 107 fImageListView(NULL), 108 fImageFunctionsView(NULL), 109 fBreakpointsView(NULL), 110 fVariablesView(NULL), 111 fRegistersView(NULL), 112 fStackTraceView(NULL), 113 fSourceView(NULL), 114 fRunButton(NULL), 115 fStepOverButton(NULL), 116 fStepIntoButton(NULL), 117 fStepOutButton(NULL), 118 fInspectorWindow(NULL), 119 fFilePanel(NULL) 120 { 121 fTeam->Lock(); 122 BString name = fTeam->Name(); 123 fTeam->Unlock(); 124 if (fTeam->ID() >= 0) 125 name << " (" << fTeam->ID() << ")"; 126 SetTitle(name.String()); 127 128 fTeam->AddListener(this); 129 } 130 131 132 TeamWindow::~TeamWindow() 133 { 134 if (fThreadListView != NULL) 135 fThreadListView->UnsetListener(); 136 if (fStackTraceView != NULL) 137 fStackTraceView->UnsetListener(); 138 if (fSourceView != NULL) 139 fSourceView->UnsetListener(); 140 141 fTeam->RemoveListener(this); 142 143 _SetActiveSourceCode(NULL); 144 _SetActiveFunction(NULL); 145 _SetActiveBreakpoint(NULL); 146 _SetActiveStackFrame(NULL); 147 _SetActiveStackTrace(NULL); 148 _SetActiveImage(NULL); 149 _SetActiveThread(NULL); 150 151 delete fFilePanel; 152 } 153 154 155 /*static*/ TeamWindow* 156 TeamWindow::Create(::Team* team, UserInterfaceListener* listener) 157 { 158 TeamWindow* self = new TeamWindow(team, listener); 159 160 try { 161 self->_Init(); 162 } catch (...) { 163 delete self; 164 throw; 165 } 166 167 return self; 168 } 169 170 171 void 172 TeamWindow::DispatchMessage(BMessage* message, BHandler* handler) 173 { 174 // Handle function key shortcuts for stepping 175 switch (message->what) { 176 case B_KEY_DOWN: 177 if (fActiveThread != NULL) { 178 int32 key; 179 uint32 modifiers; 180 if (message->FindInt32("key", &key) == B_OK 181 && message->FindInt32("modifiers", (int32*)&modifiers) 182 == B_OK) { 183 switch (key) { 184 case B_F5_KEY: 185 fListener->ThreadActionRequested( 186 fActiveThread->ID(), MSG_THREAD_RUN); 187 break; 188 case B_F10_KEY: 189 fListener->ThreadActionRequested( 190 fActiveThread->ID(), MSG_THREAD_STEP_OVER); 191 break; 192 case B_F11_KEY: 193 if ((modifiers & B_SHIFT_KEY) != 0) { 194 fListener->ThreadActionRequested( 195 fActiveThread->ID(), MSG_THREAD_STEP_OUT); 196 } else { 197 fListener->ThreadActionRequested( 198 fActiveThread->ID(), MSG_THREAD_STEP_INTO); 199 } 200 break; 201 default: 202 break; 203 } 204 } 205 } 206 break; 207 208 case B_COPY: 209 case B_SELECT_ALL: 210 BView* focusView = CurrentFocus(); 211 if (focusView != NULL) 212 focusView->MessageReceived(message); 213 break; 214 } 215 BWindow::DispatchMessage(message, handler); 216 } 217 218 219 void 220 TeamWindow::MessageReceived(BMessage* message) 221 { 222 switch (message->what) { 223 case MSG_CHOOSE_DEBUG_REPORT_LOCATION: 224 { 225 try { 226 char filename[B_FILE_NAME_LENGTH]; 227 UiUtils::ReportNameForTeam(fTeam, filename, sizeof(filename)); 228 BMessenger msgr(this); 229 fFilePanel = new BFilePanel(B_SAVE_PANEL, &msgr, 230 NULL, 0, false, new BMessage(MSG_GENERATE_DEBUG_REPORT)); 231 fFilePanel->SetSaveText(filename); 232 fFilePanel->Show(); 233 } catch (...) { 234 delete fFilePanel; 235 fFilePanel = NULL; 236 } 237 break; 238 } 239 case MSG_GENERATE_DEBUG_REPORT: 240 { 241 delete fFilePanel; 242 fFilePanel = NULL; 243 244 BPath path; 245 entry_ref ref; 246 if (message->FindRef("directory", &ref) == B_OK 247 && message->HasString("name")) { 248 path.SetTo(&ref); 249 path.Append(message->FindString("name")); 250 if (get_ref_for_path(path.Path(), &ref) == B_OK) 251 fListener->DebugReportRequested(&ref); 252 } 253 break; 254 } 255 case MSG_DEBUG_REPORT_SAVED: 256 { 257 BString data; 258 data.SetToFormat("Debug report successfully saved to '%s'", 259 message->FindString("path")); 260 BAlert *alert = new BAlert("Report saved", data.String(), 261 "OK"); 262 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 263 alert->Go(); 264 break; 265 } 266 case MSG_SHOW_INSPECTOR_WINDOW: 267 { 268 if (fInspectorWindow) { 269 fInspectorWindow->Activate(true); 270 } else { 271 try { 272 fInspectorWindow = InspectorWindow::Create(fTeam, 273 fListener, this); 274 if (fInspectorWindow != NULL) { 275 BMessage settings; 276 fInspectorWindow->LoadSettings(fUiSettings); 277 fInspectorWindow->Show(); 278 } 279 } catch (...) { 280 // TODO: notify user 281 } 282 } 283 284 target_addr_t address; 285 if (message->FindUInt64("address", &address) == B_OK) { 286 BMessage addressMessage(MSG_INSPECT_ADDRESS); 287 addressMessage.AddUInt64("address", address); 288 fInspectorWindow->PostMessage(&addressMessage); 289 } 290 break; 291 } 292 case MSG_INSPECTOR_WINDOW_CLOSED: 293 { 294 _SaveInspectorSettings(CurrentMessage()); 295 fInspectorWindow = NULL; 296 break; 297 298 } 299 case MSG_SHOW_WATCH_VARIABLE_PROMPT: 300 { 301 target_addr_t address; 302 uint32 type; 303 int32 length; 304 305 if (message->FindUInt64("address", &address) != B_OK 306 || message->FindUInt32("type", &type) != B_OK 307 || message->FindInt32("length", &length) != B_OK) { 308 break; 309 } 310 311 try { 312 WatchPromptWindow* window = WatchPromptWindow::Create( 313 fTeam->GetArchitecture(), address, type, length, 314 fListener); 315 window->Show(); 316 } catch (...) { 317 // TODO: notify user 318 } 319 break; 320 } 321 case B_REFS_RECEIVED: 322 { 323 entry_ref locatedPath; 324 message->FindRef("refs", &locatedPath); 325 _HandleResolveMissingSourceFile(locatedPath); 326 break; 327 } 328 case MSG_LOCATE_SOURCE_IF_NEEDED: 329 { 330 if (fActiveFunction != NULL 331 && fActiveFunction->GetFunctionDebugInfo() 332 ->SourceFile() != NULL && fActiveSourceCode != NULL 333 && fActiveSourceCode->GetSourceFile() == NULL) { 334 try { 335 if (fFilePanel == NULL) { 336 fFilePanel = new BFilePanel(B_OPEN_PANEL, 337 new BMessenger(this)); 338 } 339 fFilePanel->Show(); 340 } catch (...) { 341 delete fFilePanel; 342 fFilePanel = NULL; 343 } 344 } 345 break; 346 } 347 case MSG_THREAD_RUN: 348 case MSG_THREAD_STOP: 349 case MSG_THREAD_STEP_OVER: 350 case MSG_THREAD_STEP_INTO: 351 case MSG_THREAD_STEP_OUT: 352 if (fActiveThread != NULL) { 353 fListener->ThreadActionRequested(fActiveThread->ID(), 354 message->what); 355 } 356 break; 357 358 case MSG_THREAD_STATE_CHANGED: 359 { 360 int32 threadID; 361 if (message->FindInt32("thread", &threadID) != B_OK) 362 break; 363 364 _HandleThreadStateChanged(threadID); 365 break; 366 } 367 case MSG_THREAD_CPU_STATE_CHANGED: 368 { 369 int32 threadID; 370 if (message->FindInt32("thread", &threadID) != B_OK) 371 break; 372 373 _HandleCpuStateChanged(threadID); 374 break; 375 } 376 377 case MSG_THREAD_STACK_TRACE_CHANGED: 378 { 379 int32 threadID; 380 if (message->FindInt32("thread", &threadID) != B_OK) 381 break; 382 383 _HandleStackTraceChanged(threadID); 384 break; 385 } 386 387 case MSG_IMAGE_DEBUG_INFO_CHANGED: 388 { 389 int32 imageID; 390 if (message->FindInt32("image", &imageID) != B_OK) 391 break; 392 393 _HandleImageDebugInfoChanged(imageID); 394 break; 395 } 396 397 case MSG_USER_BREAKPOINT_CHANGED: 398 { 399 UserBreakpoint* breakpoint; 400 if (message->FindPointer("breakpoint", (void**)&breakpoint) != B_OK) 401 break; 402 BReference<UserBreakpoint> breakpointReference(breakpoint, true); 403 404 _HandleUserBreakpointChanged(breakpoint); 405 break; 406 } 407 408 case MSG_WATCHPOINT_CHANGED: 409 { 410 Watchpoint* watchpoint; 411 if (message->FindPointer("watchpoint", (void**)&watchpoint) != B_OK) 412 break; 413 BReference<Watchpoint> watchpointReference(watchpoint, true); 414 415 _HandleWatchpointChanged(watchpoint); 416 break; 417 418 } 419 420 case MSG_FUNCTION_SOURCE_CODE_CHANGED: 421 { 422 _HandleSourceCodeChanged(); 423 break; 424 } 425 426 default: 427 BWindow::MessageReceived(message); 428 break; 429 } 430 } 431 432 433 bool 434 TeamWindow::QuitRequested() 435 { 436 fListener->UserInterfaceQuitRequested(); 437 438 return false; 439 } 440 441 442 status_t 443 TeamWindow::LoadSettings(const GuiTeamUiSettings* settings) 444 { 445 AutoLocker<BWindow> lock(this); 446 if (!lock.IsLocked()) 447 return B_ERROR; 448 449 BMessage teamWindowSettings; 450 // no settings stored yet 451 if (settings->Settings("teamWindow", teamWindowSettings) != B_OK) 452 return B_OK; 453 454 BRect frame; 455 if (teamWindowSettings.FindRect("frame", &frame) == B_OK) { 456 ResizeTo(frame.Width(), frame.Height()); 457 MoveTo(frame.left, frame.top); 458 } 459 460 BMessage archive; 461 if (teamWindowSettings.FindMessage("sourceSplit", &archive) == B_OK) 462 GuiSettingsUtils::UnarchiveSplitView(archive, fSourceSplitView); 463 464 if (teamWindowSettings.FindMessage("functionSplit", &archive) == B_OK) 465 GuiSettingsUtils::UnarchiveSplitView(archive, fFunctionSplitView); 466 467 if (teamWindowSettings.FindMessage("imageSplit", &archive) == B_OK) 468 GuiSettingsUtils::UnarchiveSplitView(archive, fImageSplitView); 469 470 if (teamWindowSettings.FindMessage("threadSplit", &archive) == B_OK) 471 GuiSettingsUtils::UnarchiveSplitView(archive, fThreadSplitView); 472 473 if (teamWindowSettings.FindMessage("imageListView", &archive) == B_OK) 474 fImageListView->LoadSettings(archive); 475 476 if (teamWindowSettings.FindMessage("imageFunctionsView", &archive) == B_OK) 477 fImageFunctionsView->LoadSettings(archive); 478 479 if (teamWindowSettings.FindMessage("threadListView", &archive) == B_OK) 480 fThreadListView->LoadSettings(archive); 481 482 if (teamWindowSettings.FindMessage("variablesView", &archive) == B_OK) 483 fVariablesView->LoadSettings(archive); 484 485 if (teamWindowSettings.FindMessage("registersView", &archive) == B_OK) 486 fRegistersView->LoadSettings(archive); 487 488 if (teamWindowSettings.FindMessage("stackTraceView", &archive) == B_OK) 489 fStackTraceView->LoadSettings(archive); 490 491 if (teamWindowSettings.FindMessage("breakpointsView", &archive) == B_OK) 492 fBreakpointsView->LoadSettings(archive); 493 494 fUiSettings = *settings; 495 496 return B_OK; 497 } 498 499 500 status_t 501 TeamWindow::SaveSettings(GuiTeamUiSettings* settings) 502 { 503 AutoLocker<BWindow> lock(this); 504 if (!lock.IsLocked()) 505 return B_ERROR; 506 507 BMessage inspectorSettings; 508 if (fUiSettings.Settings("inspectorWindow", inspectorSettings) == B_OK) { 509 if (!settings->AddSettings("inspectorWindow", inspectorSettings)) 510 return B_NO_MEMORY; 511 } 512 513 BMessage archive; 514 BMessage teamWindowSettings; 515 if (teamWindowSettings.AddRect("frame", Frame()) != B_OK) 516 return B_NO_MEMORY; 517 518 if (GuiSettingsUtils::ArchiveSplitView(archive, fSourceSplitView) != B_OK) 519 return B_NO_MEMORY; 520 if (teamWindowSettings.AddMessage("sourceSplit", &archive) != B_OK) 521 return B_NO_MEMORY; 522 523 if (GuiSettingsUtils::ArchiveSplitView(archive, fFunctionSplitView) != B_OK) 524 return B_NO_MEMORY; 525 if (teamWindowSettings.AddMessage("functionSplit", &archive) != B_OK) 526 return B_NO_MEMORY; 527 528 if (GuiSettingsUtils::ArchiveSplitView(archive, fImageSplitView) != B_OK) 529 return B_NO_MEMORY; 530 if (teamWindowSettings.AddMessage("imageSplit", &archive)) 531 return B_NO_MEMORY; 532 533 if (GuiSettingsUtils::ArchiveSplitView(archive, fThreadSplitView) != B_OK) 534 return B_NO_MEMORY; 535 if (teamWindowSettings.AddMessage("threadSplit", &archive)) 536 return B_NO_MEMORY; 537 538 if (fImageListView->SaveSettings(archive) != B_OK) 539 return B_NO_MEMORY; 540 if (teamWindowSettings.AddMessage("imageListView", &archive)) 541 return B_NO_MEMORY; 542 543 if (fImageFunctionsView->SaveSettings(archive) != B_OK) 544 return B_NO_MEMORY; 545 if (teamWindowSettings.AddMessage("imageFunctionsView", &archive)) 546 return B_NO_MEMORY; 547 548 if (fThreadListView->SaveSettings(archive) != B_OK) 549 return B_NO_MEMORY; 550 if (teamWindowSettings.AddMessage("threadListView", &archive)) 551 return B_NO_MEMORY; 552 553 if (fVariablesView->SaveSettings(archive) != B_OK) 554 return B_NO_MEMORY; 555 if (teamWindowSettings.AddMessage("variablesView", &archive)) 556 return B_NO_MEMORY; 557 558 if (fRegistersView->SaveSettings(archive) != B_OK) 559 return B_NO_MEMORY; 560 if (teamWindowSettings.AddMessage("registersView", &archive)) 561 return B_NO_MEMORY; 562 563 if (fStackTraceView->SaveSettings(archive) != B_OK) 564 return B_NO_MEMORY; 565 if (teamWindowSettings.AddMessage("stackTraceView", &archive)) 566 return B_NO_MEMORY; 567 568 if (fBreakpointsView->SaveSettings(archive) != B_OK) 569 return B_NO_MEMORY; 570 if (teamWindowSettings.AddMessage("breakpointsView", &archive)) 571 return B_NO_MEMORY; 572 573 if (!settings->AddSettings("teamWindow", teamWindowSettings)) 574 return B_NO_MEMORY; 575 576 return B_OK; 577 } 578 579 580 void 581 TeamWindow::ThreadSelectionChanged(::Thread* thread) 582 { 583 _SetActiveThread(thread); 584 } 585 586 587 void 588 TeamWindow::ImageSelectionChanged(Image* image) 589 { 590 _SetActiveImage(image); 591 } 592 593 594 void 595 TeamWindow::StackFrameSelectionChanged(StackFrame* frame) 596 { 597 _SetActiveStackFrame(frame); 598 } 599 600 601 void 602 TeamWindow::FunctionSelectionChanged(FunctionInstance* function) 603 { 604 // If the function wasn't already active, it was just selected by the user. 605 if (function != NULL && function != fActiveFunction) 606 fActiveSourceObject = ACTIVE_SOURCE_FUNCTION; 607 608 _SetActiveFunction(function); 609 } 610 611 612 void 613 TeamWindow::BreakpointSelectionChanged(UserBreakpoint* breakpoint) 614 { 615 _SetActiveBreakpoint(breakpoint); 616 } 617 618 619 void 620 TeamWindow::SetBreakpointEnabledRequested(UserBreakpoint* breakpoint, 621 bool enabled) 622 { 623 fListener->SetBreakpointEnabledRequested(breakpoint, enabled); 624 } 625 626 627 void 628 TeamWindow::ClearBreakpointRequested(UserBreakpoint* breakpoint) 629 { 630 fListener->ClearBreakpointRequested(breakpoint); 631 } 632 633 634 void 635 TeamWindow::SetBreakpointRequested(target_addr_t address, bool enabled) 636 { 637 fListener->SetBreakpointRequested(address, enabled); 638 } 639 640 641 void 642 TeamWindow::ClearBreakpointRequested(target_addr_t address) 643 { 644 fListener->ClearBreakpointRequested(address); 645 } 646 647 648 void 649 TeamWindow::WatchpointSelectionChanged(Watchpoint* watchpoint) 650 { 651 fBreakpointsView->SetBreakpoint(NULL, watchpoint); 652 } 653 654 655 void 656 TeamWindow::SetWatchpointEnabledRequested(Watchpoint* watchpoint, 657 bool enabled) 658 { 659 fListener->SetWatchpointEnabledRequested(watchpoint, enabled); 660 } 661 662 663 void 664 TeamWindow::ClearWatchpointRequested(Watchpoint* watchpoint) 665 { 666 fListener->ClearWatchpointRequested(watchpoint); 667 } 668 669 670 void 671 TeamWindow::ValueNodeValueRequested(CpuState* cpuState, 672 ValueNodeContainer* container, ValueNode* valueNode) 673 { 674 fListener->ValueNodeValueRequested(cpuState, container, valueNode); 675 } 676 677 678 void 679 TeamWindow::ThreadStateChanged(const Team::ThreadEvent& event) 680 { 681 BMessage message(MSG_THREAD_STATE_CHANGED); 682 message.AddInt32("thread", event.GetThread()->ID()); 683 PostMessage(&message); 684 } 685 686 687 void 688 TeamWindow::ThreadCpuStateChanged(const Team::ThreadEvent& event) 689 { 690 BMessage message(MSG_THREAD_CPU_STATE_CHANGED); 691 message.AddInt32("thread", event.GetThread()->ID()); 692 PostMessage(&message); 693 } 694 695 696 void 697 TeamWindow::ThreadStackTraceChanged(const Team::ThreadEvent& event) 698 { 699 BMessage message(MSG_THREAD_STACK_TRACE_CHANGED); 700 message.AddInt32("thread", event.GetThread()->ID()); 701 PostMessage(&message); 702 } 703 704 705 void 706 TeamWindow::ImageDebugInfoChanged(const Team::ImageEvent& event) 707 { 708 BMessage message(MSG_IMAGE_DEBUG_INFO_CHANGED); 709 message.AddInt32("image", event.GetImage()->ID()); 710 PostMessage(&message); 711 } 712 713 714 void 715 TeamWindow::UserBreakpointChanged(const Team::UserBreakpointEvent& event) 716 { 717 BMessage message(MSG_USER_BREAKPOINT_CHANGED); 718 BReference<UserBreakpoint> breakpointReference(event.GetBreakpoint()); 719 if (message.AddPointer("breakpoint", event.GetBreakpoint()) == B_OK 720 && PostMessage(&message) == B_OK) { 721 breakpointReference.Detach(); 722 } 723 } 724 725 726 void 727 TeamWindow::WatchpointChanged(const Team::WatchpointEvent& event) 728 { 729 BMessage message(MSG_WATCHPOINT_CHANGED); 730 BReference<Watchpoint> watchpointReference(event.GetWatchpoint()); 731 if (message.AddPointer("watchpoint", event.GetWatchpoint()) == B_OK 732 && PostMessage(&message) == B_OK) { 733 watchpointReference.Detach(); 734 } 735 } 736 737 738 void 739 TeamWindow::DebugReportChanged(const Team::DebugReportEvent& event) 740 { 741 BMessage message(MSG_DEBUG_REPORT_SAVED); 742 message.AddString("path", event.GetReportPath()); 743 PostMessage(&message); 744 } 745 746 747 void 748 TeamWindow::FunctionSourceCodeChanged(Function* function) 749 { 750 TRACE_GUI("TeamWindow::FunctionSourceCodeChanged(%p): source: %p, " 751 "state: %d\n", function, function->GetSourceCode(), 752 function->SourceCodeState()); 753 754 PostMessage(MSG_FUNCTION_SOURCE_CODE_CHANGED); 755 } 756 757 758 void 759 TeamWindow::_Init() 760 { 761 BScrollView* sourceScrollView; 762 763 BLayoutBuilder::Group<>(this, B_VERTICAL) 764 .Add(fMenuBar = new BMenuBar("Menu")) 765 .AddSplit(B_VERTICAL, 3.0f) 766 .GetSplitView(&fFunctionSplitView) 767 .SetInsets(4.0f, 4.0f, 4.0f, 4.0f) 768 .Add(fTabView = new BTabView("tab view"), 0.4f) 769 .AddGroup(B_VERTICAL, 4.0f) 770 .AddGroup(B_HORIZONTAL, 4.0f) 771 .Add(fRunButton = new BButton("Run")) 772 .Add(fStepOverButton = new BButton("Step Over")) 773 .Add(fStepIntoButton = new BButton("Step Into")) 774 .Add(fStepOutButton = new BButton("Step Out")) 775 .AddGlue() 776 .End() 777 .Add(fSourcePathView = new BStringView( 778 "source path", 779 "Source path unavailable."), 4.0f) 780 .AddSplit(B_HORIZONTAL, 3.0f) 781 .GetSplitView(&fSourceSplitView) 782 .Add(sourceScrollView = new BScrollView("source scroll", 783 NULL, 0, true, true), 3.0f) 784 .Add(fLocalsTabView = new BTabView("locals view")) 785 .End() 786 .End() 787 .End(); 788 789 // add source view 790 sourceScrollView->SetTarget(fSourceView = SourceView::Create(fTeam, this)); 791 792 // add threads tab 793 BSplitView* threadGroup = new BSplitView(B_HORIZONTAL); 794 threadGroup->SetName("Threads"); 795 fTabView->AddTab(threadGroup); 796 BLayoutBuilder::Split<>(threadGroup) 797 .GetSplitView(&fThreadSplitView) 798 .Add(fThreadListView = ThreadListView::Create(fTeam, this)) 799 .Add(fStackTraceView = StackTraceView::Create(this)); 800 801 // add images tab 802 BSplitView* imagesGroup = new BSplitView(B_HORIZONTAL); 803 imagesGroup->SetName("Images"); 804 fTabView->AddTab(imagesGroup); 805 BLayoutBuilder::Split<>(imagesGroup) 806 .GetSplitView(&fImageSplitView) 807 .Add(fImageListView = ImageListView::Create(fTeam, this)) 808 .Add(fImageFunctionsView = ImageFunctionsView::Create(this)); 809 810 // add breakpoints tab 811 BGroupView* breakpointsGroup = new BGroupView(B_HORIZONTAL, 4.0f); 812 breakpointsGroup->SetName("Breakpoints"); 813 fTabView->AddTab(breakpointsGroup); 814 BLayoutBuilder::Group<>(breakpointsGroup) 815 .SetInsets(4.0f, 4.0f, 4.0f, 4.0f) 816 .Add(fBreakpointsView = BreakpointsView::Create(fTeam, this)); 817 818 // add local variables tab 819 BView* tab = fVariablesView = VariablesView::Create(this); 820 fLocalsTabView->AddTab(tab); 821 822 // add registers tab 823 tab = fRegistersView = RegistersView::Create(fTeam->GetArchitecture()); 824 fLocalsTabView->AddTab(tab); 825 826 fRunButton->SetMessage(new BMessage(MSG_THREAD_RUN)); 827 fStepOverButton->SetMessage(new BMessage(MSG_THREAD_STEP_OVER)); 828 fStepIntoButton->SetMessage(new BMessage(MSG_THREAD_STEP_INTO)); 829 fStepOutButton->SetMessage(new BMessage(MSG_THREAD_STEP_OUT)); 830 fRunButton->SetTarget(this); 831 fStepOverButton->SetTarget(this); 832 fStepIntoButton->SetTarget(this); 833 fStepOutButton->SetTarget(this); 834 835 fSourcePathView->SetExplicitMinSize(BSize(100.0, B_SIZE_UNSET)); 836 fSourcePathView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); 837 BMessageFilter* filter = new(std::nothrow) PathViewMessageFilter( 838 BMessenger(this)); 839 if (filter != NULL) 840 fSourcePathView->AddFilter(filter); 841 842 // add menus and menu items 843 BMenu* menu = new BMenu("File"); 844 fMenuBar->AddItem(menu); 845 BMenuItem* item = new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED), 846 'Q'); 847 menu->AddItem(item); 848 item->SetTarget(this); 849 menu = new BMenu("Edit"); 850 fMenuBar->AddItem(menu); 851 item = new BMenuItem("Copy", new BMessage(B_COPY), 'C'); 852 menu->AddItem(item); 853 item->SetTarget(this); 854 item = new BMenuItem("Select All", new BMessage(B_SELECT_ALL), 'A'); 855 menu->AddItem(item); 856 item->SetTarget(this); 857 menu = new BMenu("Tools"); 858 fMenuBar->AddItem(menu); 859 item = new BMenuItem("Save Debug Report", 860 new BMessage(MSG_CHOOSE_DEBUG_REPORT_LOCATION)); 861 menu->AddItem(item); 862 item->SetTarget(this); 863 item = new BMenuItem("Inspect Memory", 864 new BMessage(MSG_SHOW_INSPECTOR_WINDOW), 'I'); 865 menu->AddItem(item); 866 item->SetTarget(this); 867 868 AutoLocker< ::Team> locker(fTeam); 869 _UpdateRunButtons(); 870 } 871 872 873 void 874 TeamWindow::_SetActiveThread(::Thread* thread) 875 { 876 if (thread == fActiveThread) 877 return; 878 879 if (fActiveThread != NULL) 880 fActiveThread->ReleaseReference(); 881 882 fActiveThread = thread; 883 884 if (fActiveThread != NULL) 885 fActiveThread->AcquireReference(); 886 887 AutoLocker< ::Team> locker(fTeam); 888 _UpdateRunButtons(); 889 890 StackTrace* stackTrace = fActiveThread != NULL 891 ? fActiveThread->GetStackTrace() : NULL; 892 BReference<StackTrace> stackTraceReference(stackTrace); 893 // hold a reference until we've set it 894 895 locker.Unlock(); 896 897 fThreadListView->SetThread(fActiveThread); 898 899 _SetActiveStackTrace(stackTrace); 900 _UpdateCpuState(); 901 } 902 903 904 void 905 TeamWindow::_SetActiveImage(Image* image) 906 { 907 if (image == fActiveImage) 908 return; 909 910 if (fActiveImage != NULL) 911 fActiveImage->ReleaseReference(); 912 913 fActiveImage = image; 914 915 AutoLocker< ::Team> locker(fTeam); 916 917 ImageDebugInfo* imageDebugInfo = NULL; 918 BReference<ImageDebugInfo> imageDebugInfoReference; 919 920 if (fActiveImage != NULL) { 921 fActiveImage->AcquireReference(); 922 923 imageDebugInfo = fActiveImage->GetImageDebugInfo(); 924 imageDebugInfoReference.SetTo(imageDebugInfo); 925 926 // If the debug info is not loaded yet, request it. 927 if (fActiveImage->ImageDebugInfoState() == IMAGE_DEBUG_INFO_NOT_LOADED) 928 fListener->ImageDebugInfoRequested(fActiveImage); 929 } 930 931 locker.Unlock(); 932 933 fImageListView->SetImage(fActiveImage); 934 fImageFunctionsView->SetImageDebugInfo(imageDebugInfo); 935 } 936 937 938 void 939 TeamWindow::_SetActiveStackTrace(StackTrace* stackTrace) 940 { 941 if (stackTrace == fActiveStackTrace) 942 return; 943 944 if (fActiveStackTrace != NULL) 945 fActiveStackTrace->ReleaseReference(); 946 947 fActiveStackTrace = stackTrace; 948 949 if (fActiveStackTrace != NULL) 950 fActiveStackTrace->AcquireReference(); 951 952 fStackTraceView->SetStackTrace(fActiveStackTrace); 953 fSourceView->SetStackTrace(fActiveStackTrace); 954 955 if (fActiveStackTrace != NULL) 956 _SetActiveStackFrame(fActiveStackTrace->FrameAt(0)); 957 } 958 959 960 void 961 TeamWindow::_SetActiveStackFrame(StackFrame* frame) 962 { 963 if (frame == fActiveStackFrame) 964 return; 965 966 if (fActiveStackFrame != NULL) { 967 AutoLocker< ::Team> locker(fTeam); 968 fActiveStackFrame->RemoveListener(this); 969 locker.Unlock(); 970 971 fActiveStackFrame->ReleaseReference(); 972 } 973 974 fActiveStackFrame = frame; 975 976 if (fActiveStackFrame != NULL) { 977 fActiveStackFrame->AcquireReference(); 978 979 AutoLocker< ::Team> locker(fTeam); 980 fActiveStackFrame->AddListener(this); 981 locker.Unlock(); 982 983 fActiveSourceObject = ACTIVE_SOURCE_STACK_FRAME; 984 985 _SetActiveFunction(fActiveStackFrame->Function()); 986 } 987 988 _UpdateCpuState(); 989 990 fStackTraceView->SetStackFrame(fActiveStackFrame); 991 if (fActiveStackFrame != NULL) 992 fVariablesView->SetStackFrame(fActiveThread, fActiveStackFrame); 993 else 994 fVariablesView->SetStackFrame(NULL, NULL); 995 fSourceView->SetStackFrame(fActiveStackFrame); 996 } 997 998 999 void 1000 TeamWindow::_SetActiveBreakpoint(UserBreakpoint* breakpoint) 1001 { 1002 if (breakpoint == fActiveBreakpoint) 1003 return; 1004 1005 if (fActiveBreakpoint != NULL) 1006 fActiveBreakpoint->ReleaseReference(); 1007 1008 fActiveBreakpoint = breakpoint; 1009 1010 if (fActiveBreakpoint != NULL) { 1011 fActiveBreakpoint->AcquireReference(); 1012 1013 // get the breakpoint's function (more exactly: some function instance) 1014 AutoLocker< ::Team> locker(fTeam); 1015 1016 Function* function = fTeam->FunctionByID( 1017 breakpoint->Location().GetFunctionID()); 1018 FunctionInstance* functionInstance = function != NULL 1019 ? function->FirstInstance() : NULL; 1020 BReference<FunctionInstance> functionInstanceReference( 1021 functionInstance); 1022 1023 locker.Unlock(); 1024 1025 fActiveSourceObject = ACTIVE_SOURCE_BREAKPOINT; 1026 1027 _SetActiveFunction(functionInstance); 1028 1029 // scroll to the breakpoint's source code line number (it is not done 1030 // automatically, if the active function remains the same) 1031 _ScrollToActiveFunction(); 1032 } 1033 1034 fBreakpointsView->SetBreakpoint(fActiveBreakpoint, NULL); 1035 } 1036 1037 1038 void 1039 TeamWindow::_SetActiveFunction(FunctionInstance* functionInstance) 1040 { 1041 if (functionInstance == fActiveFunction) 1042 return; 1043 1044 AutoLocker< ::Team> locker(fTeam); 1045 1046 if (fActiveFunction != NULL) { 1047 fActiveFunction->GetFunction()->RemoveListener(this); 1048 fActiveFunction->ReleaseReference(); 1049 } 1050 1051 // to avoid listener feedback problems, first unset the active function and 1052 // set the new image, if any 1053 locker.Unlock(); 1054 1055 fActiveFunction = NULL; 1056 1057 if (functionInstance != NULL) 1058 _SetActiveImage(fTeam->ImageByAddress(functionInstance->Address())); 1059 1060 fActiveFunction = functionInstance; 1061 1062 locker.Lock(); 1063 1064 SourceCode* sourceCode = NULL; 1065 BReference<SourceCode> sourceCodeReference; 1066 1067 if (fActiveFunction != NULL) { 1068 fActiveFunction->AcquireReference(); 1069 fActiveFunction->GetFunction()->AddListener(this); 1070 1071 Function* function = fActiveFunction->GetFunction(); 1072 sourceCode = function->GetSourceCode(); 1073 if (sourceCode == NULL) 1074 sourceCode = fActiveFunction->GetSourceCode(); 1075 sourceCodeReference.SetTo(sourceCode); 1076 1077 // If the source code is not loaded yet, request it. 1078 if (function->SourceCodeState() == FUNCTION_SOURCE_NOT_LOADED) 1079 fListener->FunctionSourceCodeRequested(fActiveFunction); 1080 } 1081 1082 locker.Unlock(); 1083 1084 _SetActiveSourceCode(sourceCode); 1085 1086 fImageFunctionsView->SetFunction(fActiveFunction); 1087 1088 locker.Lock(); 1089 1090 // look if our current stack trace has a frame matching the selected 1091 // function. If so, set it to match. 1092 StackFrame* matchingFrame = NULL; 1093 BReference<StackFrame> frameRef; 1094 1095 if (fActiveStackTrace != NULL) { 1096 for (int32 i = 0; i < fActiveStackTrace->CountFrames(); i++) { 1097 StackFrame* frame = fActiveStackTrace->FrameAt(i); 1098 if (frame->Function() == fActiveFunction) { 1099 matchingFrame = frame; 1100 frameRef.SetTo(frame); 1101 break; 1102 } 1103 } 1104 } 1105 1106 locker.Unlock(); 1107 1108 if (matchingFrame != NULL) 1109 _SetActiveStackFrame(matchingFrame); 1110 } 1111 1112 1113 void 1114 TeamWindow::_SetActiveSourceCode(SourceCode* sourceCode) 1115 { 1116 if (sourceCode == fActiveSourceCode) { 1117 _ScrollToActiveFunction(); 1118 return; 1119 } 1120 1121 if (fActiveSourceCode != NULL) 1122 fActiveSourceCode->ReleaseReference(); 1123 1124 fActiveSourceCode = sourceCode; 1125 1126 if (fActiveSourceCode != NULL) 1127 fActiveSourceCode->AcquireReference(); 1128 1129 fSourceView->SetSourceCode(fActiveSourceCode); 1130 1131 _UpdateSourcePathState(); 1132 _ScrollToActiveFunction(); 1133 } 1134 1135 void 1136 TeamWindow::_UpdateCpuState() 1137 { 1138 // get the CPU state 1139 CpuState* cpuState = NULL; 1140 BReference<CpuState> cpuStateReference; 1141 // hold a reference until the register view has one 1142 1143 if (fActiveThread != NULL) { 1144 // Get the CPU state from the active stack frame or the thread directly. 1145 if (fActiveStackFrame == NULL) { 1146 AutoLocker< ::Team> locker(fTeam); 1147 cpuState = fActiveThread->GetCpuState(); 1148 cpuStateReference.SetTo(cpuState); 1149 locker.Unlock(); 1150 } else 1151 cpuState = fActiveStackFrame->GetCpuState(); 1152 } 1153 1154 fRegistersView->SetCpuState(cpuState); 1155 } 1156 1157 1158 void 1159 TeamWindow::_UpdateRunButtons() 1160 { 1161 uint32 threadState = fActiveThread != NULL 1162 ? fActiveThread->State() : THREAD_STATE_UNKNOWN; 1163 1164 switch (threadState) { 1165 case THREAD_STATE_UNKNOWN: 1166 fRunButton->SetEnabled(false); 1167 fStepOverButton->SetEnabled(false); 1168 fStepIntoButton->SetEnabled(false); 1169 fStepOutButton->SetEnabled(false); 1170 break; 1171 case THREAD_STATE_RUNNING: 1172 fRunButton->SetLabel("Debug"); 1173 fRunButton->SetMessage(new BMessage(MSG_THREAD_STOP)); 1174 fRunButton->SetEnabled(true); 1175 fStepOverButton->SetEnabled(false); 1176 fStepIntoButton->SetEnabled(false); 1177 fStepOutButton->SetEnabled(false); 1178 break; 1179 case THREAD_STATE_STOPPED: 1180 fRunButton->SetLabel("Run"); 1181 fRunButton->SetMessage(new BMessage(MSG_THREAD_RUN)); 1182 fRunButton->SetEnabled(true); 1183 fStepOverButton->SetEnabled(true); 1184 fStepIntoButton->SetEnabled(true); 1185 fStepOutButton->SetEnabled(true); 1186 break; 1187 } 1188 } 1189 1190 1191 void 1192 TeamWindow::_UpdateSourcePathState() 1193 { 1194 LocatableFile* sourceFile = NULL; 1195 BString sourceText = "Source file unavailable."; 1196 BString truncatedText; 1197 1198 if (fActiveSourceCode != NULL) { 1199 sourceFile = fActiveFunction->GetFunctionDebugInfo()->SourceFile(); 1200 1201 if (sourceFile != NULL && !sourceFile->GetLocatedPath(sourceText)) 1202 sourceFile->GetPath(sourceText); 1203 1204 if (fActiveSourceCode->GetSourceFile() == NULL && sourceFile != NULL) { 1205 sourceText.Prepend("Click to locate source file '"); 1206 sourceText += "'"; 1207 truncatedText = sourceText; 1208 fSourcePathView->TruncateString(&truncatedText, B_TRUNCATE_MIDDLE, 1209 fSourcePathView->Bounds().Width()); 1210 } else if (sourceFile != NULL) { 1211 sourceText.Prepend("File: "); 1212 } 1213 } 1214 1215 if (!truncatedText.IsEmpty() && truncatedText != sourceText) { 1216 fSourcePathView->SetToolTip(sourceText); 1217 fSourcePathView->SetText(truncatedText); 1218 } else 1219 fSourcePathView->SetText(sourceText); 1220 } 1221 1222 1223 void 1224 TeamWindow::_ScrollToActiveFunction() 1225 { 1226 // Scroll to the active function, if it has been selected manually. 1227 if (fActiveFunction == NULL || fActiveSourceCode == NULL) 1228 return; 1229 1230 switch (fActiveSourceObject) { 1231 case ACTIVE_SOURCE_FUNCTION: 1232 fSourceView->ScrollToAddress(fActiveFunction->Address()); 1233 break; 1234 case ACTIVE_SOURCE_BREAKPOINT: 1235 { 1236 if (fActiveBreakpoint == NULL) 1237 break; 1238 1239 const UserBreakpointLocation& location 1240 = fActiveBreakpoint->Location(); 1241 int32 line = location.GetSourceLocation().Line(); 1242 1243 if (location.SourceFile() != NULL && line >= 0 1244 && fActiveSourceCode->GetSourceFile() 1245 == location.SourceFile()) { 1246 fSourceView->ScrollToLine(line); 1247 } else { 1248 fSourceView->ScrollToAddress( 1249 fActiveFunction->Address() 1250 + location.RelativeAddress()); 1251 } 1252 break; 1253 } 1254 case ACTIVE_SOURCE_NONE: 1255 case ACTIVE_SOURCE_STACK_FRAME: 1256 break; 1257 } 1258 } 1259 1260 1261 void 1262 TeamWindow::_HandleThreadStateChanged(thread_id threadID) 1263 { 1264 AutoLocker< ::Team> locker(fTeam); 1265 1266 ::Thread* thread = fTeam->ThreadByID(threadID); 1267 if (thread == NULL) 1268 return; 1269 1270 // If the thread has been stopped and we don't have an active thread yet 1271 // (or it isn't stopped), switch to this thread. Otherwise ignore the event. 1272 if (thread->State() == THREAD_STATE_STOPPED 1273 && (fActiveThread == NULL 1274 || (thread != fActiveThread 1275 && fActiveThread->State() != THREAD_STATE_STOPPED))) { 1276 _SetActiveThread(thread); 1277 } else if (thread != fActiveThread) { 1278 // otherwise ignore the event, if the thread is not the active one 1279 return; 1280 } 1281 1282 // Switch to the threads tab view when the thread has stopped. 1283 if (thread->State() == THREAD_STATE_STOPPED) 1284 fTabView->Select(MAIN_TAB_INDEX_THREADS); 1285 1286 _UpdateRunButtons(); 1287 } 1288 1289 1290 void 1291 TeamWindow::_HandleCpuStateChanged(thread_id threadID) 1292 { 1293 // We're only interested in the currently selected thread 1294 if (fActiveThread == NULL || threadID != fActiveThread->ID()) 1295 return; 1296 1297 _UpdateCpuState(); 1298 } 1299 1300 1301 void 1302 TeamWindow::_HandleStackTraceChanged(thread_id threadID) 1303 { 1304 // We're only interested in the currently selected thread 1305 if (fActiveThread == NULL || threadID != fActiveThread->ID()) 1306 return; 1307 1308 AutoLocker< ::Team> locker(fTeam); 1309 1310 StackTrace* stackTrace = fActiveThread != NULL 1311 ? fActiveThread->GetStackTrace() : NULL; 1312 BReference<StackTrace> stackTraceReference(stackTrace); 1313 // hold a reference until the register view has one 1314 1315 locker.Unlock(); 1316 1317 _SetActiveStackTrace(stackTrace); 1318 } 1319 1320 1321 void 1322 TeamWindow::_HandleImageDebugInfoChanged(image_id imageID) 1323 { 1324 TRACE_GUI("TeamWindow::_HandleImageDebugInfoChanged(%" B_PRId32 ")\n", 1325 imageID); 1326 1327 // We're only interested in the currently selected thread 1328 if (fActiveImage == NULL || imageID != fActiveImage->ID()) 1329 return; 1330 1331 AutoLocker< ::Team> locker(fTeam); 1332 1333 ImageDebugInfo* imageDebugInfo = fActiveImage != NULL 1334 ? fActiveImage->GetImageDebugInfo() : NULL; 1335 1336 TRACE_GUI(" image debug info: %p\n", imageDebugInfo); 1337 1338 BReference<ImageDebugInfo> imageDebugInfoReference(imageDebugInfo); 1339 // hold a reference until we've set it 1340 1341 locker.Unlock(); 1342 1343 fImageFunctionsView->SetImageDebugInfo(imageDebugInfo); 1344 } 1345 1346 1347 void 1348 TeamWindow::_HandleSourceCodeChanged() 1349 { 1350 // If we don't have an active function anymore, the message is obsolete. 1351 if (fActiveFunction == NULL) 1352 return; 1353 1354 // get a reference to the source code 1355 AutoLocker< ::Team> locker(fTeam); 1356 1357 SourceCode* sourceCode = fActiveFunction->GetFunction()->GetSourceCode(); 1358 if (sourceCode == NULL) 1359 sourceCode = fActiveFunction->GetSourceCode(); 1360 1361 BReference<SourceCode> sourceCodeReference(sourceCode); 1362 1363 locker.Unlock(); 1364 1365 _SetActiveSourceCode(sourceCode); 1366 } 1367 1368 1369 void 1370 TeamWindow::_HandleUserBreakpointChanged(UserBreakpoint* breakpoint) 1371 { 1372 fSourceView->UserBreakpointChanged(breakpoint); 1373 fBreakpointsView->UserBreakpointChanged(breakpoint); 1374 } 1375 1376 1377 void 1378 TeamWindow::_HandleWatchpointChanged(Watchpoint* watchpoint) 1379 { 1380 fBreakpointsView->WatchpointChanged(watchpoint); 1381 } 1382 1383 1384 void 1385 TeamWindow::_HandleResolveMissingSourceFile(entry_ref& locatedPath) 1386 { 1387 if (fActiveFunction != NULL) { 1388 LocatableFile* sourceFile = fActiveFunction->GetFunctionDebugInfo() 1389 ->SourceFile(); 1390 if (sourceFile != NULL) { 1391 BString sourcePath; 1392 BString targetPath; 1393 sourceFile->GetPath(sourcePath); 1394 BPath path(&locatedPath); 1395 targetPath = path.Path(); 1396 fListener->SourceEntryLocateRequested(sourcePath, targetPath); 1397 fListener->FunctionSourceCodeRequested(fActiveFunction); 1398 } 1399 } 1400 } 1401 1402 1403 status_t 1404 TeamWindow::_SaveInspectorSettings(const BMessage* settings) 1405 { 1406 if (fUiSettings.AddSettings("inspectorWindow", *settings) != B_OK) 1407 return B_NO_MEMORY; 1408 1409 return B_OK; 1410 } 1411