/*
* Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2010-2017, Rene Gollent, rene@gollent.com.
* Distributed under the terms of the MIT License.
*/
#include "TeamWindow.h"
#include <stdio.h>
#include <Alert.h>
#include <Button.h>
#include <FilePanel.h>
#include <FindDirectory.h>
#include <LayoutBuilder.h>
#include <Menu.h>
#include <MenuBar.h>
#include <MenuItem.h>
#include <Message.h>
#include <MessageFilter.h>
#include <MessageRunner.h>
#include <Path.h>
#include <PopUpMenu.h>
#include <Query.h>
#include <StringView.h>
#include <TabView.h>
#include <ScrollView.h>
#include <SplitView.h>
#include <TextView.h>
#include <VolumeRoster.h>
#include <AutoDeleter.h>
#include <AutoLocker.h>
#include "AppMessageCodes.h"
#include "Breakpoint.h"
#include "BreakpointEditWindow.h"
#include "ConsoleOutputView.h"
#include "CppLanguage.h"
#include "CpuState.h"
#include "DisassembledCode.h"
#include "BreakpointEditWindow.h"
#include "ExpressionEvaluationWindow.h"
#include "ExpressionPromptWindow.h"
#include "FileSourceCode.h"
#include "GuiSettingsUtils.h"
#include "GuiTeamUiSettings.h"
#include "Image.h"
#include "ImageDebugInfo.h"
#include "InspectorWindow.h"
#include "LocatableFile.h"
#include "MessageCodes.h"
#include "RegistersView.h"
#include "StackTrace.h"
#include "StackTraceView.h"
#include "TeamSettingsWindow.h"
#include "Tracing.h"
#include "TypeComponentPath.h"
#include "UiUtils.h"
#include "UserInterface.h"
#include "ValueNodeManager.h"
#include "Value.h"
#include "Variable.h"
#include "WatchPromptWindow.h"
enum {
MAIN_TAB_INDEX_THREADS = 0,
MAIN_TAB_INDEX_IMAGES = 1
};
enum {
MSG_CHOOSE_DEBUG_REPORT_LOCATION = 'ccrl',
MSG_DEBUG_REPORT_SAVED = 'drsa',
MSG_CHOOSE_CORE_FILE_LOCATION = 'ccfl',
MSG_CORE_FILE_WRITTEN = 'cfsa',
MSG_LOCATE_SOURCE_IF_NEEDED = 'lsin',
MSG_SOURCE_ENTRY_QUERY_COMPLETE = 'seqc',
MSG_CLEAR_STACK_TRACE = 'clst',
MSG_HANDLE_LOAD_SETTINGS = 'hlst',
MSG_UPDATE_STATUS_BAR = 'upsb'
};
// #pragma mark - ThreadStackFrameSelectionKey
struct TeamWindow::ThreadStackFrameSelectionKey {
::Thread* thread;
ThreadStackFrameSelectionKey(::Thread* thread)
:
thread(thread)
{
thread->AcquireReference();
}
~ThreadStackFrameSelectionKey()
{
thread->ReleaseReference();
}
uint32 HashValue() const
{
return (uint32)thread->ID();
}
bool operator==(const ThreadStackFrameSelectionKey& other) const
{
return thread == other.thread;
}
};
// #pragma mark - ThreadStackFrameSelectionEntry
struct TeamWindow::ThreadStackFrameSelectionEntry
: ThreadStackFrameSelectionKey {
ThreadStackFrameSelectionEntry* next;
StackFrame* selectedFrame;
ThreadStackFrameSelectionEntry(::Thread* thread, StackFrame* frame)
:
ThreadStackFrameSelectionKey(thread),
selectedFrame(frame)
{
}
inline StackFrame* SelectedFrame() const
{
return selectedFrame;
}
void SetSelectedFrame(StackFrame* frame)
{
selectedFrame = frame;
}
};
// #pragma mark - ThreadStackFrameSelectionEntryHashDefinition
struct TeamWindow::ThreadStackFrameSelectionEntryHashDefinition {
typedef ThreadStackFrameSelectionKey KeyType;
typedef ThreadStackFrameSelectionEntry ValueType;
size_t HashKey(const ThreadStackFrameSelectionKey& key) const
{
return key.HashValue();
}
size_t Hash(const ThreadStackFrameSelectionKey* value) const
{
return value->HashValue();
}
bool Compare(const ThreadStackFrameSelectionKey& key,
const ThreadStackFrameSelectionKey* value) const
{
return key == *value;
}
ThreadStackFrameSelectionEntry*& GetLink(
ThreadStackFrameSelectionEntry* value) const
{
return value->next;
}
};
// #pragma mark - PathViewMessageFilter
class PathViewMessageFilter : public BMessageFilter {
public:
PathViewMessageFilter(BMessenger teamWindow)
:
BMessageFilter(B_MOUSE_UP),
fTeamWindowMessenger(teamWindow)
{
}
virtual filter_result Filter(BMessage*, BHandler**)
{
fTeamWindowMessenger.SendMessage(MSG_LOCATE_SOURCE_IF_NEEDED);
return B_DISPATCH_MESSAGE;
}
private:
BMessenger fTeamWindowMessenger;
};
// #pragma mark - TeamWindow
TeamWindow::TeamWindow(::Team* team, UserInterfaceListener* listener)
:
BWindow(BRect(100, 100, 899, 699), "Team", B_TITLED_WINDOW,
B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS),
fTeam(team),
fActiveThread(NULL),
fActiveImage(NULL),
fActiveStackTrace(NULL),
fActiveStackFrame(NULL),
fThreadSelectionInfoTable(NULL),
fActiveBreakpoint(NULL),
fActiveFunction(NULL),
fActiveSourceCode(NULL),
fActiveSourceObject(ACTIVE_SOURCE_NONE),
fListener(listener),
fTraceUpdateRunner(NULL),
fTabView(NULL),
fLocalsTabView(NULL),
fThreadListView(NULL),
fImageListView(NULL),
fImageFunctionsView(NULL),
fBreakpointsView(NULL),
fVariablesView(NULL),
fRegistersView(NULL),
fStackTraceView(NULL),
fSourceView(NULL),
fRunButton(NULL),
fStepOverButton(NULL),
fStepIntoButton(NULL),
fStepOutButton(NULL),
fMenuBar(NULL),
fSourcePathView(NULL),
fStatusBarView(NULL),
fConsoleOutputView(NULL),
fFunctionSplitView(NULL),
fSourceSplitView(NULL),
fImageSplitView(NULL),
fThreadSplitView(NULL),
fConsoleSplitView(NULL),
fTeamSettingsWindow(NULL),
fBreakpointEditWindow(NULL),
fInspectorWindow(NULL),
fExpressionEvalWindow(NULL),
fExpressionPromptWindow(NULL),
fFilePanel(NULL),
fActiveSourceWorker(-1)
{
_UpdateTitle();
fTeam->AddListener(this);
}
TeamWindow::~TeamWindow()
{
if (fThreadListView != NULL)
fThreadListView->UnsetListener();
if (fStackTraceView != NULL)
fStackTraceView->UnsetListener();
if (fSourceView != NULL)
fSourceView->UnsetListener();
if (fInspectorWindow != NULL) {
if (fInspectorWindow->Lock())
fInspectorWindow->Quit();
}
if (fExpressionEvalWindow != NULL) {
if (fExpressionEvalWindow->Lock())
fExpressionEvalWindow->Quit();
}
if (fExpressionPromptWindow != NULL) {
if (fExpressionPromptWindow->Lock())
fExpressionPromptWindow->Quit();
}
fTeam->RemoveListener(this);
_SetActiveSourceCode(NULL);
_SetActiveFunction(NULL);
_SetActiveBreakpoint(NULL);
_SetActiveStackFrame(NULL);
_SetActiveStackTrace(NULL);
_SetActiveImage(NULL);
_SetActiveThread(NULL);
delete fFilePanel;
ThreadStackFrameSelectionEntry* entry
= fThreadSelectionInfoTable->Clear(true);
while (entry != NULL) {
ThreadStackFrameSelectionEntry* next = entry->next;
delete entry;
entry = next;
}
delete fThreadSelectionInfoTable;
if (fActiveSourceWorker > 0)
wait_for_thread(fActiveSourceWorker, NULL);
}
/*static*/ TeamWindow*
TeamWindow::Create(::Team* team, UserInterfaceListener* listener)
{
TeamWindow* self = new TeamWindow(team, listener);
try {
self->_Init();
} catch (...) {
delete self;
throw;
}
return self;
}
void
TeamWindow::DispatchMessage(BMessage* message, BHandler* handler)
{
// Handle function key shortcuts for stepping
switch (message->what) {
case B_KEY_DOWN:
if (fActiveThread != NULL && fTraceUpdateRunner == NULL) {
int32 key;
uint32 modifiers;
if (message->FindInt32("key", &key) == B_OK
&& message->FindInt32("modifiers", (int32*)&modifiers)
== B_OK) {
switch (key) {
case B_F5_KEY:
fListener->ThreadActionRequested(
fActiveThread->ID(), MSG_THREAD_RUN);
break;
case B_F10_KEY:
fListener->ThreadActionRequested(
fActiveThread->ID(), MSG_THREAD_STEP_OVER);
break;
case B_F11_KEY:
if ((modifiers & B_SHIFT_KEY) != 0) {
fListener->ThreadActionRequested(
fActiveThread->ID(), MSG_THREAD_STEP_OUT);
} else {
fListener->ThreadActionRequested(
fActiveThread->ID(), MSG_THREAD_STEP_INTO);
}
break;
default:
break;
}
}
}
break;
case B_COPY:
case B_SELECT_ALL:
BView* focusView = CurrentFocus();
if (focusView != NULL) {
focusView->MessageReceived(message);
return;
}
break;
}
BWindow::DispatchMessage(message, handler);
}
void
TeamWindow::MessageReceived(BMessage* message)
{
switch (message->what) {
case MSG_TEAM_RESTART_REQUESTED:
{
fListener->TeamRestartRequested();
break;
}
case MSG_CHOOSE_DEBUG_REPORT_LOCATION:
case MSG_CHOOSE_CORE_FILE_LOCATION:
{
try {
char filename[B_FILE_NAME_LENGTH];
if (message->what == MSG_CHOOSE_DEBUG_REPORT_LOCATION)
UiUtils::ReportNameForTeam(fTeam, filename, sizeof(filename));
else
UiUtils::CoreFileNameForTeam(fTeam, filename, sizeof(filename));
BMessenger msgr(this);
fFilePanel = new BFilePanel(B_SAVE_PANEL, &msgr,
NULL, 0, false, new BMessage(
message->what == MSG_CHOOSE_DEBUG_REPORT_LOCATION ?
MSG_GENERATE_DEBUG_REPORT : MSG_WRITE_CORE_FILE));
fFilePanel->SetSaveText(filename);
fFilePanel->Show();
} catch (...) {
delete fFilePanel;
fFilePanel = NULL;
}
break;
}
case MSG_GENERATE_DEBUG_REPORT:
case MSG_WRITE_CORE_FILE:
{
delete fFilePanel;
fFilePanel = NULL;
BPath path;
entry_ref ref;
if (message->FindRef("directory", &ref) == B_OK
&& message->HasString("name")) {
path.SetTo(&ref);
path.Append(message->FindString("name"));
if (get_ref_for_path(path.Path(), &ref) == B_OK) {
if (message->what == MSG_GENERATE_DEBUG_REPORT)
fListener->DebugReportRequested(&ref);
else
fListener->WriteCoreFileRequested(&ref);
}
}
break;
}
case MSG_DEBUG_REPORT_SAVED:
{
status_t finalStatus = message->GetInt32("status", B_OK);
BString data;
if (finalStatus == B_OK) {
data.SetToFormat("Debug report successfully saved to '%s'",
message->FindString("path"));
} else {
data.SetToFormat("Failed to save debug report: '%s'",
strerror(finalStatus));
}
BAlert *alert = new(std::nothrow) BAlert("Report saved",
data.String(), "Close");
if (alert == NULL)
break;
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
break;
}
case MSG_CORE_FILE_WRITTEN:
{
status_t finalStatus = message->GetInt32("status", B_OK);
BString data;
if (finalStatus == B_OK) {
data.SetToFormat("Core file successfully written to '%s'",
message->FindString("path"));
} else {
data.SetToFormat("Failed to write core file: '%s'",
strerror(finalStatus));
}
BAlert *alert = new(std::nothrow) BAlert("Core file written",
data.String(), "Close");
if (alert == NULL)
break;
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
break;
}
case MSG_SHOW_INSPECTOR_WINDOW:
{
if (fInspectorWindow) {
AutoLocker<BWindow> lock(fInspectorWindow);
if (lock.IsLocked())
fInspectorWindow->Activate(true);
} else {
try {
fInspectorWindow = InspectorWindow::Create(fTeam,
fListener, this);
if (fInspectorWindow != NULL) {
fInspectorWindow->LoadSettings(fUiSettings);
fInspectorWindow->Show();
}
} catch (...) {
// TODO: notify user
}
}
target_addr_t address;
if (message->FindUInt64("address", &address) == B_OK) {
BMessage addressMessage(MSG_INSPECT_ADDRESS);
addressMessage.AddUInt64("address", address);
fInspectorWindow->PostMessage(&addressMessage);
}
break;
}
case MSG_INSPECTOR_WINDOW_CLOSED:
{
_SaveInspectorSettings(CurrentMessage());
fInspectorWindow = NULL;
break;
}
case MSG_SHOW_EXPRESSION_WINDOW:
{
if (fExpressionEvalWindow != NULL) {
AutoLocker<BWindow> lock(fExpressionEvalWindow);
if (lock.IsLocked())
fExpressionEvalWindow->Activate(true);
} else {
try {
fExpressionEvalWindow = ExpressionEvaluationWindow::Create(
this, fTeam, fListener);
if (fExpressionEvalWindow != NULL)
fExpressionEvalWindow->Show();
} catch (...) {
// TODO: notify user
}
}
break;
}
case MSG_EXPRESSION_WINDOW_CLOSED:
{
fExpressionEvalWindow = NULL;
break;
}
case MSG_SHOW_EXPRESSION_PROMPT_WINDOW:
{
if (fExpressionPromptWindow != NULL) {
AutoLocker<BWindow> lock(fExpressionPromptWindow);
if (lock.IsLocked())
fExpressionPromptWindow->Activate(true);
} else {
try {
fExpressionPromptWindow = ExpressionPromptWindow::Create(
fVariablesView, this);
if (fExpressionPromptWindow != NULL)
fExpressionPromptWindow->Show();
} catch (...) {
// TODO: notify user
}
}
break;
}
case MSG_EXPRESSION_PROMPT_WINDOW_CLOSED:
{
fExpressionPromptWindow = NULL;
break;
}
case MSG_SHOW_TEAM_SETTINGS_WINDOW:
{
if (fTeamSettingsWindow != NULL) {
AutoLocker<BWindow> lock(fTeamSettingsWindow);
if (lock.IsLocked())
fTeamSettingsWindow->Activate(true);
} else {
try {
fTeamSettingsWindow
= TeamSettingsWindow::Create(
fTeam, fListener, this);
if (fTeamSettingsWindow != NULL)
fTeamSettingsWindow->Show();
} catch (...) {
// TODO: notify user
}
}
break;
}
case MSG_TEAM_SETTINGS_WINDOW_CLOSED:
{
fTeamSettingsWindow = NULL;
break;
}
case MSG_SHOW_BREAKPOINT_EDIT_WINDOW:
{
if (fBreakpointEditWindow != NULL) {
AutoLocker<BWindow> lock(fBreakpointEditWindow);
if (lock.IsLocked())
fBreakpointEditWindow->Activate(true);
} else {
UserBreakpoint* breakpoint;
if (message->FindPointer("breakpoint",
reinterpret_cast<void**>(&breakpoint)) != B_OK) {
break;
}
try {
fBreakpointEditWindow
= BreakpointEditWindow::Create(
fTeam, breakpoint, fListener, this);
if (fBreakpointEditWindow != NULL)
fBreakpointEditWindow->Show();
} catch (...) {
// TODO: notify user
}
}
break;
}
case MSG_BREAKPOINT_EDIT_WINDOW_CLOSED:
{
fBreakpointEditWindow = NULL;
break;
}
case MSG_SHOW_WATCH_VARIABLE_PROMPT:
{
target_addr_t address;
uint32 type;
int32 length;
if (message->FindUInt64("address", &address) != B_OK
|| message->FindUInt32("type", &type) != B_OK
|| message->FindInt32("length", &length) != B_OK) {
break;
}
try {
WatchPromptWindow* window = WatchPromptWindow::Create(
fTeam->GetArchitecture(), address, type, length,
fListener);
window->Show();
} catch (...) {
// TODO: notify user
}
break;
}
case B_REFS_RECEIVED:
{
entry_ref locatedPath;
if (message->FindRef("refs", &locatedPath) != B_OK)
break;
_HandleResolveMissingSourceFile(locatedPath);
delete fFilePanel;
fFilePanel = NULL;
break;
}
case MSG_LOCATE_SOURCE_IF_NEEDED:
{
_HandleLocateSourceRequest();
break;
}
case MSG_SOURCE_ENTRY_QUERY_COMPLETE:
{
BStringList* entries;
if (message->FindPointer("entries", (void**)&entries) == B_OK) {
ObjectDeleter<BStringList> entryDeleter(entries);
_HandleLocateSourceRequest(entries);
}
fActiveSourceWorker = -1;
break;
}
case MSG_THREAD_RUN:
case MSG_THREAD_STOP:
case MSG_THREAD_STEP_OVER:
case MSG_THREAD_STEP_INTO:
case MSG_THREAD_STEP_OUT:
if (fActiveThread != NULL && fTraceUpdateRunner == NULL) {
fListener->ThreadActionRequested(fActiveThread->ID(),
message->what);
}
break;
case MSG_CLEAR_STACK_TRACE:
{
if (fTraceUpdateRunner != NULL) {
_SetActiveStackTrace(NULL);
_UpdateRunButtons();
}
break;
}
case MSG_HANDLE_LOAD_SETTINGS:
{
GuiTeamUiSettings* settings;
if (message->FindPointer("settings",
reinterpret_cast<void**>(&settings)) != B_OK) {
break;
}
_LoadSettings(settings);
break;
}
case MSG_UPDATE_STATUS_BAR:
{
const char* messageText;
if (message->FindString("message", &messageText) == B_OK)
fStatusBarView->SetText(messageText);
break;
}
case MSG_TEAM_RENAMED:
{
_UpdateTitle();
break;
}
case MSG_THREAD_STATE_CHANGED:
{
int32 threadID;
if (message->FindInt32("thread", &threadID) != B_OK)
break;
_HandleThreadStateChanged(threadID);
break;
}
case MSG_THREAD_CPU_STATE_CHANGED:
{
int32 threadID;
if (message->FindInt32("thread", &threadID) != B_OK)
break;
_HandleCpuStateChanged(threadID);
break;
}
case MSG_THREAD_STACK_TRACE_CHANGED:
{
int32 threadID;
if (message->FindInt32("thread", &threadID) != B_OK)
break;
_HandleStackTraceChanged(threadID);
break;
}
case MSG_IMAGE_DEBUG_INFO_CHANGED:
{
int32 imageID;
if (message->FindInt32("image", &imageID) != B_OK)
break;
_HandleImageDebugInfoChanged(imageID);
break;
}
case MSG_CONSOLE_OUTPUT_RECEIVED:
{
int32 fd;
BString output;
if (message->FindInt32("fd", &fd) != B_OK
|| message->FindString("output", &output) != B_OK) {
break;
}
fConsoleOutputView->ConsoleOutputReceived(fd, output);
break;
}
case MSG_USER_BREAKPOINT_CHANGED:
{
UserBreakpoint* breakpoint;
if (message->FindPointer("breakpoint", (void**)&breakpoint) != B_OK)
break;
BReference<UserBreakpoint> breakpointReference(breakpoint, true);
_HandleUserBreakpointChanged(breakpoint);
break;
}
case MSG_WATCHPOINT_CHANGED:
{
Watchpoint* watchpoint;
if (message->FindPointer("watchpoint", (void**)&watchpoint) != B_OK)
break;
BReference<Watchpoint> watchpointReference(watchpoint, true);
_HandleWatchpointChanged(watchpoint);
break;
}
case MSG_FUNCTION_SOURCE_CODE_CHANGED:
{
_HandleSourceCodeChanged();
break;
}
default:
BWindow::MessageReceived(message);
break;
}
}
bool
TeamWindow::QuitRequested()
{
fListener->UserInterfaceQuitRequested();
return false;
}
status_t
TeamWindow::LoadSettings(const GuiTeamUiSettings* settings)
{
BMessage message(MSG_HANDLE_LOAD_SETTINGS);
message.AddPointer("settings", settings);
return PostMessage(&message);
}
status_t
TeamWindow::SaveSettings(GuiTeamUiSettings* settings)
{
AutoLocker<BWindow> lock(this);
if (!lock.IsLocked())
return B_ERROR;
BMessage inspectorSettings;
if (fUiSettings.Settings("inspectorWindow", inspectorSettings) == B_OK) {
if (!settings->AddSettings("inspectorWindow", inspectorSettings))
return B_NO_MEMORY;
}
BMessage archive;
BMessage teamWindowSettings;
if (teamWindowSettings.AddRect("frame", Frame()) != B_OK)
return B_NO_MEMORY;
if (GuiSettingsUtils::ArchiveSplitView(archive, fSourceSplitView) != B_OK)
return B_NO_MEMORY;
if (teamWindowSettings.AddMessage("sourceSplit", &archive) != B_OK)
return B_NO_MEMORY;
if (GuiSettingsUtils::ArchiveSplitView(archive, fFunctionSplitView) != B_OK)
return B_NO_MEMORY;
if (teamWindowSettings.AddMessage("functionSplit", &archive) != B_OK)
return B_NO_MEMORY;
if (GuiSettingsUtils::ArchiveSplitView(archive, fImageSplitView) != B_OK)
return B_NO_MEMORY;
if (teamWindowSettings.AddMessage("imageSplit", &archive))
return B_NO_MEMORY;
if (GuiSettingsUtils::ArchiveSplitView(archive, fThreadSplitView) != B_OK)
return B_NO_MEMORY;
if (teamWindowSettings.AddMessage("threadSplit", &archive))
return B_NO_MEMORY;
if (GuiSettingsUtils::ArchiveSplitView(archive, fConsoleSplitView) != B_OK)
return B_NO_MEMORY;
if (teamWindowSettings.AddMessage("consoleSplit", &archive))
return B_NO_MEMORY;
if (fImageListView->SaveSettings(archive) != B_OK)
return B_NO_MEMORY;
if (teamWindowSettings.AddMessage("imageListView", &archive))
return B_NO_MEMORY;
if (fImageFunctionsView->SaveSettings(archive) != B_OK)
return B_NO_MEMORY;
if (teamWindowSettings.AddMessage("imageFunctionsView", &archive))
return B_NO_MEMORY;
if (fThreadListView->SaveSettings(archive) != B_OK)
return B_NO_MEMORY;
if (teamWindowSettings.AddMessage("threadListView", &archive))
return B_NO_MEMORY;
if (fVariablesView->SaveSettings(archive) != B_OK)
return B_NO_MEMORY;
if (teamWindowSettings.AddMessage("variablesView", &archive))
return B_NO_MEMORY;
if (fRegistersView->SaveSettings(archive) != B_OK)
return B_NO_MEMORY;
if (teamWindowSettings.AddMessage("registersView", &archive))
return B_NO_MEMORY;
if (fStackTraceView->SaveSettings(archive) != B_OK)
return B_NO_MEMORY;
if (teamWindowSettings.AddMessage("stackTraceView", &archive))
return B_NO_MEMORY;
if (fBreakpointsView->SaveSettings(archive) != B_OK)
return B_NO_MEMORY;
if (teamWindowSettings.AddMessage("breakpointsView", &archive))
return B_NO_MEMORY;
if (fConsoleOutputView->SaveSettings(archive) != B_OK)
return B_NO_MEMORY;
if (teamWindowSettings.AddMessage("consoleOutputView", &archive))
return B_NO_MEMORY;
if (!settings->AddSettings("teamWindow", teamWindowSettings))
return B_NO_MEMORY;
return B_OK;
}
void
TeamWindow::DisplayBackgroundStatus(const char* message)
{
BMessage updateMessage(MSG_UPDATE_STATUS_BAR);
updateMessage.AddString("message", message);
PostMessage(&updateMessage);
}
void
TeamWindow::ThreadSelectionChanged(::Thread* thread)
{
_SetActiveThread(thread);
}
void
TeamWindow::ImageSelectionChanged(Image* image)
{
_SetActiveImage(image);
}
void
TeamWindow::StackFrameSelectionChanged(StackFrame* frame)
{
_SetActiveStackFrame(frame);
}
void
TeamWindow::FunctionSelectionChanged(FunctionInstance* function)
{
// If the function wasn't already active, it was just selected by the user.
if (function != NULL && function != fActiveFunction)
fActiveSourceObject = ACTIVE_SOURCE_FUNCTION;
_SetActiveFunction(function);
}
void
TeamWindow::BreakpointSelectionChanged(BreakpointProxyList &proxies)
{
if (proxies.CountItems() == 0 && fActiveBreakpoint != NULL) {
fActiveBreakpoint->ReleaseReference();
fActiveBreakpoint = NULL;
} else if (proxies.CountItems() == 1) {
BreakpointProxy* proxy = proxies.ItemAt(0);
if (proxy->Type() == BREAKPOINT_PROXY_TYPE_BREAKPOINT)
_SetActiveBreakpoint(proxy->GetBreakpoint());
}
// if more than one item is selected, do nothing.
}
void
TeamWindow::SetBreakpointEnabledRequested(UserBreakpoint* breakpoint,
bool enabled)
{
fListener->SetBreakpointEnabledRequested(breakpoint, enabled);
}
void
TeamWindow::ClearBreakpointRequested(UserBreakpoint* breakpoint)
{
fListener->ClearBreakpointRequested(breakpoint);
}
void
TeamWindow::SetBreakpointRequested(target_addr_t address, bool enabled)
{
fListener->SetBreakpointRequested(address, enabled);
}
void
TeamWindow::ClearBreakpointRequested(target_addr_t address)
{
fListener->ClearBreakpointRequested(address);
}
void
TeamWindow::ThreadActionRequested(::Thread* thread, uint32 action,
target_addr_t address)
{
if (fTraceUpdateRunner == NULL)
fListener->ThreadActionRequested(thread->ID(), action, address);
}
void
TeamWindow::FunctionSourceCodeRequested(FunctionInstance* function,
bool forceDisassembly)
{
fListener->FunctionSourceCodeRequested(function, forceDisassembly);
}
void
TeamWindow::SetWatchpointEnabledRequested(Watchpoint* watchpoint,
bool enabled)
{
fListener->SetWatchpointEnabledRequested(watchpoint, enabled);
}
void
TeamWindow::ClearWatchpointRequested(Watchpoint* watchpoint)
{
fListener->ClearWatchpointRequested(watchpoint);
}
void
TeamWindow::ValueNodeValueRequested(CpuState* cpuState,
ValueNodeContainer* container, ValueNode* valueNode)
{
fListener->ValueNodeValueRequested(cpuState, container, valueNode);
}
void
TeamWindow::ExpressionEvaluationRequested(ExpressionInfo* info,
StackFrame* frame, ::Thread* thread)
{
SourceLanguage* language;
if (_GetActiveSourceLanguage(language) != B_OK)
return;
BReference<SourceLanguage> languageReference(language, true);
fListener->ExpressionEvaluationRequested(language, info, frame, thread);
}
void
TeamWindow::ValueNodeWriteRequested(ValueNode* node, CpuState* state,
Value* newValue)
{
fListener->ValueNodeWriteRequested(node, state, newValue);
}
void
TeamWindow::TeamRenamed(const Team::Event& event)
{
PostMessage(MSG_TEAM_RENAMED);
}
void
TeamWindow::ThreadStateChanged(const Team::ThreadEvent& event)
{
BMessage message(MSG_THREAD_STATE_CHANGED);
message.AddInt32("thread", event.GetThread()->ID());
PostMessage(&message);
}
void
TeamWindow::ThreadCpuStateChanged(const Team::ThreadEvent& event)
{
BMessage message(MSG_THREAD_CPU_STATE_CHANGED);
message.AddInt32("thread", event.GetThread()->ID());
PostMessage(&message);
}
void
TeamWindow::ThreadStackTraceChanged(const Team::ThreadEvent& event)
{
BMessage message(MSG_THREAD_STACK_TRACE_CHANGED);
message.AddInt32("thread", event.GetThread()->ID());
PostMessage(&message);
}
void
TeamWindow::ImageDebugInfoChanged(const Team::ImageEvent& event)
{
BMessage message(MSG_IMAGE_DEBUG_INFO_CHANGED);
message.AddInt32("image", event.GetImage()->ID());
PostMessage(&message);
}
void
TeamWindow::ConsoleOutputReceived(const Team::ConsoleOutputEvent& event)
{
BMessage message(MSG_CONSOLE_OUTPUT_RECEIVED);
message.AddInt32("fd", event.Descriptor());
message.AddString("output", event.Output());
PostMessage(&message);
}
void
TeamWindow::UserBreakpointChanged(const Team::UserBreakpointEvent& event)
{
BMessage message(MSG_USER_BREAKPOINT_CHANGED);
BReference<UserBreakpoint> breakpointReference(event.GetBreakpoint());
if (message.AddPointer("breakpoint", event.GetBreakpoint()) == B_OK
&& PostMessage(&message) == B_OK) {
breakpointReference.Detach();
}
}
void
TeamWindow::WatchpointChanged(const Team::WatchpointEvent& event)
{
BMessage message(MSG_WATCHPOINT_CHANGED);
BReference<Watchpoint> watchpointReference(event.GetWatchpoint());
if (message.AddPointer("watchpoint", event.GetWatchpoint()) == B_OK
&& PostMessage(&message) == B_OK) {
watchpointReference.Detach();
}
}
void
TeamWindow::DebugReportChanged(const Team::DebugReportEvent& event)
{
BMessage message(MSG_DEBUG_REPORT_SAVED);
message.AddString("path", event.GetReportPath());
message.AddInt32("status", event.GetFinalStatus());
PostMessage(&message);
}
void
TeamWindow::CoreFileChanged(const Team::CoreFileChangedEvent& event)
{
BMessage message(MSG_CORE_FILE_WRITTEN);
message.AddString("path", event.GetTargetPath());
PostMessage(&message);
}
void
TeamWindow::FunctionSourceCodeChanged(Function* function)
{
TRACE_GUI("TeamWindow::FunctionSourceCodeChanged(%p): source: %p, "
"state: %d\n", function, function->GetSourceCode(),
function->SourceCodeState());
PostMessage(MSG_FUNCTION_SOURCE_CODE_CHANGED);
}
void
TeamWindow::_Init()
{
fThreadSelectionInfoTable = new ThreadStackFrameSelectionInfoTable;
if (fThreadSelectionInfoTable->Init() != B_OK) {
delete fThreadSelectionInfoTable;
fThreadSelectionInfoTable = NULL;
throw std::bad_alloc();
}
BScrollView* sourceScrollView;
const float splitSpacing = 3.0f;
BLayoutBuilder::Group<>(this, B_VERTICAL, 0.0f)
.Add(fMenuBar = new BMenuBar("Menu"))
.AddSplit(B_VERTICAL, splitSpacing)
.GetSplitView(&fFunctionSplitView)
.SetInsets(B_USE_SMALL_INSETS)
.Add(fTabView = new BTabView("tab view"), 0.4f)
.AddSplit(B_HORIZONTAL, splitSpacing)
.GetSplitView(&fSourceSplitView)
.AddGroup(B_VERTICAL, B_USE_SMALL_SPACING)
.AddGroup(B_HORIZONTAL, B_USE_SMALL_SPACING)
.Add(fRunButton = new BButton("Run"))
.Add(fStepOverButton = new BButton("Step over"))
.Add(fStepIntoButton = new BButton("Step into"))
.Add(fStepOutButton = new BButton("Step out"))
.AddGlue()
.End()
.Add(fSourcePathView = new BStringView(
"source path",
"Source path unavailable."), 4.0f)
.Add(sourceScrollView = new BScrollView("source scroll",
NULL, 0, true, true), splitSpacing)
.End()
.Add(fLocalsTabView = new BTabView("locals view"))
.End()
.AddSplit(B_VERTICAL, splitSpacing)
.GetSplitView(&fConsoleSplitView)
.SetInsets(0.0)
.Add(fConsoleOutputView = ConsoleOutputView::Create())
.End()
.End()
.Add(fStatusBarView = new BStringView("status", "Ready."));
fStatusBarView->SetExplicitMinSize(BSize(50.0, B_SIZE_UNSET));
fStatusBarView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
// add source view
sourceScrollView->SetTarget(fSourceView = SourceView::Create(fTeam, this));
// add threads tab
BSplitView* threadGroup = new BSplitView(B_HORIZONTAL, splitSpacing);
threadGroup->SetName("Threads");
fTabView->AddTab(threadGroup);
BLayoutBuilder::Split<>(threadGroup)
.GetSplitView(&fThreadSplitView)
.Add(fThreadListView = ThreadListView::Create(fTeam, this))
.Add(fStackTraceView = StackTraceView::Create(this));
// add images tab
BSplitView* imagesGroup = new BSplitView(B_HORIZONTAL, splitSpacing);
imagesGroup->SetName("Images");
fTabView->AddTab(imagesGroup);
BLayoutBuilder::Split<>(imagesGroup)
.GetSplitView(&fImageSplitView)
.Add(fImageListView = ImageListView::Create(fTeam, this))
.Add(fImageFunctionsView = ImageFunctionsView::Create(this));
// add breakpoints tab
BGroupView* breakpointsGroup = new BGroupView(B_HORIZONTAL,
B_USE_SMALL_SPACING);
breakpointsGroup->SetName("Breakpoints");
fTabView->AddTab(breakpointsGroup);
BLayoutBuilder::Group<>(breakpointsGroup)
// .SetInsets(0.0f)
.Add(fBreakpointsView = BreakpointsView::Create(fTeam, this));
ValueNodeManager* manager = new ValueNodeManager;
// add local variables tab
BView* tab = fVariablesView = VariablesView::Create(this, manager);
fLocalsTabView->AddTab(tab);
// add registers tab
tab = fRegistersView = RegistersView::Create(fTeam->GetArchitecture());
fLocalsTabView->AddTab(tab);
fRunButton->SetMessage(new BMessage(MSG_THREAD_RUN));
fStepOverButton->SetMessage(new BMessage(MSG_THREAD_STEP_OVER));
fStepIntoButton->SetMessage(new BMessage(MSG_THREAD_STEP_INTO));
fStepOutButton->SetMessage(new BMessage(MSG_THREAD_STEP_OUT));
fRunButton->SetTarget(this);
fStepOverButton->SetTarget(this);
fStepIntoButton->SetTarget(this);
fStepOutButton->SetTarget(this);
fSourcePathView->SetExplicitMinSize(BSize(100.0, B_SIZE_UNSET));
fSourcePathView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
BMessageFilter* filter = new(std::nothrow) PathViewMessageFilter(
BMessenger(this));
if (filter != NULL)
fSourcePathView->AddFilter(filter);
// add menus and menu items
BMenu* menu = new BMenu("Debugger");
fMenuBar->AddItem(menu);
BMenuItem* item = new BMenuItem("Start new team" B_UTF8_ELLIPSIS,
new BMessage(MSG_SHOW_START_TEAM_WINDOW));
menu->AddItem(item);
item->SetTarget(be_app);
item = new BMenuItem("Show Teams window" B_UTF8_ELLIPSIS,
new BMessage(MSG_SHOW_TEAMS_WINDOW));
menu->AddItem(item);
item->SetTarget(be_app);
menu = new BMenu("Team");
fMenuBar->AddItem(menu);
item = new BMenuItem("Restart", new BMessage(
MSG_TEAM_RESTART_REQUESTED), 'R', B_SHIFT_KEY);
menu->AddItem(item);
item->SetTarget(this);
item = new BMenuItem("Close", new BMessage(B_QUIT_REQUESTED),
'W');
menu->AddItem(item);
item->SetTarget(this);
menu->AddSeparatorItem();
item = new BMenuItem("Settings" B_UTF8_ELLIPSIS, new BMessage(
MSG_SHOW_TEAM_SETTINGS_WINDOW));
menu->AddItem(item);
item->SetTarget(this);
menu = new BMenu("Edit");
fMenuBar->AddItem(menu);
item = new BMenuItem("Copy", new BMessage(B_COPY), 'C');
menu->AddItem(item);
item->SetTarget(this);
item = new BMenuItem("Select all", new BMessage(B_SELECT_ALL), 'A');
menu->AddItem(item);
item->SetTarget(this);
menu = new BMenu("Tools");
fMenuBar->AddItem(menu);
item = new BMenuItem("Save debug report",
new BMessage(MSG_CHOOSE_DEBUG_REPORT_LOCATION));
menu->AddItem(item);
item->SetTarget(this);
item = new BMenuItem("Write core file",
new BMessage(MSG_CHOOSE_CORE_FILE_LOCATION));
menu->AddItem(item);
item->SetTarget(this);
item = new BMenuItem("Inspect memory",
new BMessage(MSG_SHOW_INSPECTOR_WINDOW), 'I');
menu->AddItem(item);
item->SetTarget(this);
item = new BMenuItem("Evaluate expression",
new BMessage(MSG_SHOW_EXPRESSION_WINDOW), 'E');
menu->AddItem(item);
item->SetTarget(this);
AutoLocker< ::Team> locker(fTeam);
_UpdateRunButtons();
}
void
TeamWindow::_LoadSettings(const GuiTeamUiSettings* settings)
{
BMessage teamWindowSettings;
// no settings stored yet
if (settings->Settings("teamWindow", teamWindowSettings) != B_OK)
return;
BRect frame;
if (teamWindowSettings.FindRect("frame", &frame) == B_OK) {
ResizeTo(frame.Width(), frame.Height());
MoveTo(frame.left, frame.top);
}
BMessage archive;
if (teamWindowSettings.FindMessage("sourceSplit", &archive) == B_OK)
GuiSettingsUtils::UnarchiveSplitView(archive, fSourceSplitView);
if (teamWindowSettings.FindMessage("functionSplit", &archive) == B_OK)
GuiSettingsUtils::UnarchiveSplitView(archive, fFunctionSplitView);
if (teamWindowSettings.FindMessage("imageSplit", &archive) == B_OK)
GuiSettingsUtils::UnarchiveSplitView(archive, fImageSplitView);
if (teamWindowSettings.FindMessage("threadSplit", &archive) == B_OK)
GuiSettingsUtils::UnarchiveSplitView(archive, fThreadSplitView);
if (teamWindowSettings.FindMessage("consoleSplit", &archive) == B_OK)
GuiSettingsUtils::UnarchiveSplitView(archive, fConsoleSplitView);
if (teamWindowSettings.FindMessage("imageListView", &archive) == B_OK)
fImageListView->LoadSettings(archive);
if (teamWindowSettings.FindMessage("imageFunctionsView", &archive) == B_OK)
fImageFunctionsView->LoadSettings(archive);
if (teamWindowSettings.FindMessage("threadListView", &archive) == B_OK)
fThreadListView->LoadSettings(archive);
if (teamWindowSettings.FindMessage("variablesView", &archive) == B_OK)
fVariablesView->LoadSettings(archive);
if (teamWindowSettings.FindMessage("registersView", &archive) == B_OK)
fRegistersView->LoadSettings(archive);
if (teamWindowSettings.FindMessage("stackTraceView", &archive) == B_OK)
fStackTraceView->LoadSettings(archive);
if (teamWindowSettings.FindMessage("breakpointsView", &archive) == B_OK)
fBreakpointsView->LoadSettings(archive);
if (teamWindowSettings.FindMessage("consoleOutputView", &archive) == B_OK)
fConsoleOutputView->LoadSettings(archive);
fUiSettings = *settings;
}
void
TeamWindow::_UpdateTitle()
{
AutoLocker< ::Team> lock(fTeam);
BString name = fTeam->Name();
if (fTeam->ID() >= 0)
name << " (" << fTeam->ID() << ")";
SetTitle(name.String());
}
void
TeamWindow::_SetActiveThread(::Thread* thread)
{
if (thread == fActiveThread)
return;
if (fActiveThread != NULL)
fActiveThread->ReleaseReference();
fActiveThread = thread;
if (fActiveThread != NULL)
fActiveThread->AcquireReference();
AutoLocker< ::Team> locker(fTeam);
_UpdateRunButtons();
StackTrace* stackTrace = fActiveThread != NULL
? fActiveThread->GetStackTrace() : NULL;
BReference<StackTrace> stackTraceReference(stackTrace);
// hold a reference until we've set it
locker.Unlock();
fThreadListView->SetThread(fActiveThread);
_SetActiveStackTrace(stackTrace);
_UpdateCpuState();
}
void
TeamWindow::_SetActiveImage(Image* image)
{
if (image == fActiveImage)
return;
if (fActiveImage != NULL)
fActiveImage->ReleaseReference();
fActiveImage = image;
AutoLocker< ::Team> locker(fTeam);
ImageDebugInfo* imageDebugInfo = NULL;
BReference<ImageDebugInfo> imageDebugInfoReference;
if (fActiveImage != NULL) {
fActiveImage->AcquireReference();
imageDebugInfo = fActiveImage->GetImageDebugInfo();
imageDebugInfoReference.SetTo(imageDebugInfo);
// If the debug info is not loaded yet, request it.
if (fActiveImage->ImageDebugInfoState() == IMAGE_DEBUG_INFO_NOT_LOADED)
fListener->ImageDebugInfoRequested(fActiveImage);
}
locker.Unlock();
fImageListView->SetImage(fActiveImage);
fImageFunctionsView->SetImageDebugInfo(imageDebugInfo);
}
void
TeamWindow::_SetActiveStackTrace(StackTrace* stackTrace)
{
delete fTraceUpdateRunner;
fTraceUpdateRunner = NULL;
if (stackTrace == fActiveStackTrace)
return;
if (fActiveStackTrace != NULL)
fActiveStackTrace->ReleaseReference();
fActiveStackTrace = stackTrace;
if (fActiveStackTrace != NULL)
fActiveStackTrace->AcquireReference();
fStackTraceView->SetStackTrace(fActiveStackTrace);
fSourceView->SetStackTrace(fActiveStackTrace, fActiveThread);
StackFrame* frame = NULL;
if (fActiveStackTrace != NULL) {
ThreadStackFrameSelectionEntry* entry
= fThreadSelectionInfoTable->Lookup(fActiveThread);
if (entry != NULL)
frame = entry->SelectedFrame();
else
frame = fActiveStackTrace->FrameAt(0);
}
_SetActiveStackFrame(frame);
}
void
TeamWindow::_SetActiveStackFrame(StackFrame* frame)
{
if (frame == fActiveStackFrame)
return;
if (fActiveStackFrame != NULL) {
AutoLocker< ::Team> locker(fTeam);
fActiveStackFrame->RemoveListener(this);
locker.Unlock();
fActiveStackFrame->ReleaseReference();
}
fActiveStackFrame = frame;
if (fActiveStackFrame != NULL) {
fActiveStackFrame->AcquireReference();
AutoLocker< ::Team> locker(fTeam);
fActiveStackFrame->AddListener(this);
locker.Unlock();
fActiveSourceObject = ACTIVE_SOURCE_STACK_FRAME;
ThreadStackFrameSelectionEntry* entry
= fThreadSelectionInfoTable->Lookup(fActiveThread);
if (entry == NULL) {
entry = new(std::nothrow) ThreadStackFrameSelectionEntry(
fActiveThread, fActiveStackFrame);
if (entry == NULL)
return;
ObjectDeleter<ThreadStackFrameSelectionEntry> entryDeleter(entry);
if (fThreadSelectionInfoTable->Insert(entry) == B_OK)
entryDeleter.Detach();
} else
entry->SetSelectedFrame(fActiveStackFrame);
_SetActiveFunction(fActiveStackFrame->Function(), false);
}
_UpdateCpuState();
fStackTraceView->SetStackFrame(fActiveStackFrame);
if (fActiveStackFrame != NULL)
fVariablesView->SetStackFrame(fActiveThread, fActiveStackFrame);
else
fVariablesView->SetStackFrame(NULL, NULL);
fSourceView->SetStackFrame(fActiveStackFrame);
}
void
TeamWindow::_SetActiveBreakpoint(UserBreakpoint* breakpoint)
{
if (breakpoint == fActiveBreakpoint)
return;
if (fActiveBreakpoint != NULL)
fActiveBreakpoint->ReleaseReference();
fActiveBreakpoint = breakpoint;
if (fActiveBreakpoint != NULL) {
fActiveBreakpoint->AcquireReference();
// get the breakpoint's function (more exactly: some function instance)
AutoLocker< ::Team> locker(fTeam);
Function* function = fTeam->FunctionByID(
breakpoint->Location().GetFunctionID());
FunctionInstance* functionInstance = function != NULL
? function->FirstInstance() : NULL;
BReference<FunctionInstance> functionInstanceReference(
functionInstance);
locker.Unlock();
fActiveSourceObject = ACTIVE_SOURCE_BREAKPOINT;
_SetActiveFunction(functionInstance);
// scroll to the breakpoint's source code line number (it is not done
// automatically, if the active function remains the same)
_ScrollToActiveFunction();
}
}
void
TeamWindow::_SetActiveFunction(FunctionInstance* functionInstance,
bool searchForFrame)
{
if (functionInstance == fActiveFunction)
return;
AutoLocker< ::Team> locker(fTeam);
if (fActiveFunction != NULL) {
fActiveFunction->GetFunction()->RemoveListener(this);
fActiveFunction->ReleaseReference();
}
// to avoid listener feedback problems, first unset the active function and
// set the new image, if any
locker.Unlock();
fActiveFunction = NULL;
if (functionInstance != NULL)
_SetActiveImage(fTeam->ImageByAddress(functionInstance->Address()));
fActiveFunction = functionInstance;
locker.Lock();
SourceCode* sourceCode = NULL;
BReference<SourceCode> sourceCodeReference;
if (fActiveFunction != NULL) {
fActiveFunction->AcquireReference();
fActiveFunction->GetFunction()->AddListener(this);
Function* function = fActiveFunction->GetFunction();
sourceCode = function->GetSourceCode();
if (sourceCode == NULL)
sourceCode = fActiveFunction->GetSourceCode();
sourceCodeReference.SetTo(sourceCode);
// If the source code is not loaded yet, request it.
if (function->SourceCodeState() == FUNCTION_SOURCE_NOT_LOADED)
fListener->FunctionSourceCodeRequested(fActiveFunction);
}
locker.Unlock();
_SetActiveSourceCode(sourceCode);
fImageFunctionsView->SetFunction(fActiveFunction);
locker.Lock();
if (!searchForFrame || fActiveStackTrace == NULL)
return;
// look if our current stack trace has a frame matching the selected
// function. If so, set it to match.
StackFrame* matchingFrame = NULL;
BReference<StackFrame> frameRef;
for (int32 i = 0; i < fActiveStackTrace->CountFrames(); i++) {
StackFrame* frame = fActiveStackTrace->FrameAt(i);
if (frame->Function() == fActiveFunction) {
matchingFrame = frame;
frameRef.SetTo(frame);
break;
}
}
locker.Unlock();
if (matchingFrame != NULL)
_SetActiveStackFrame(matchingFrame);
}
void
TeamWindow::_SetActiveSourceCode(SourceCode* sourceCode)
{
if (sourceCode == fActiveSourceCode) {
_ScrollToActiveFunction();
return;
}
if (fActiveSourceCode != NULL)
fActiveSourceCode->ReleaseReference();
fActiveSourceCode = sourceCode;
if (fActiveSourceCode != NULL)
fActiveSourceCode->AcquireReference();
fSourceView->SetSourceCode(fActiveSourceCode);
_UpdateSourcePathState();
_ScrollToActiveFunction();
}
void
TeamWindow::_UpdateCpuState()
{
// get the CPU state
CpuState* cpuState = NULL;
BReference<CpuState> cpuStateReference;
// hold a reference until the register view has one
if (fActiveThread != NULL) {
// Get the CPU state from the active stack frame or the thread directly.
if (fActiveStackFrame == NULL) {
AutoLocker< ::Team> locker(fTeam);
cpuState = fActiveThread->GetCpuState();
cpuStateReference.SetTo(cpuState);
locker.Unlock();
} else
cpuState = fActiveStackFrame->GetCpuState();
}
fRegistersView->SetCpuState(cpuState);
}
void
TeamWindow::_UpdateRunButtons()
{
uint32 threadState = fActiveThread != NULL
? fActiveThread->State() : THREAD_STATE_UNKNOWN;
switch (threadState) {
case THREAD_STATE_UNKNOWN:
fRunButton->SetEnabled(false);
fStepOverButton->SetEnabled(false);
fStepIntoButton->SetEnabled(false);
fStepOutButton->SetEnabled(false);
break;
case THREAD_STATE_RUNNING:
if (fTraceUpdateRunner == NULL) {
fRunButton->SetLabel("Debug");
fRunButton->SetMessage(new BMessage(MSG_THREAD_STOP));
fRunButton->SetEnabled(true);
fStepOverButton->SetEnabled(false);
fStepIntoButton->SetEnabled(false);
fStepOutButton->SetEnabled(false);
}
break;
case THREAD_STATE_STOPPED:
fRunButton->SetLabel("Run");
fRunButton->SetMessage(new BMessage(MSG_THREAD_RUN));
fRunButton->SetEnabled(true);
fStepOverButton->SetEnabled(true);
fStepIntoButton->SetEnabled(true);
fStepOutButton->SetEnabled(true);
break;
}
}
void
TeamWindow::_UpdateSourcePathState()
{
LocatableFile* sourceFile = NULL;
BString sourceText = "Source file unavailable.";
BString truncatedText;
if (fActiveSourceCode != NULL) {
sourceFile = fActiveFunction->GetFunctionDebugInfo()->SourceFile();
if (sourceFile != NULL && !sourceFile->GetLocatedPath(sourceText))
sourceFile->GetPath(sourceText);
function_source_state state = fActiveFunction->GetFunction()
->SourceCodeState();
if (state == FUNCTION_SOURCE_SUPPRESSED)
sourceText.Prepend("Disassembly for: ");
else if (state != FUNCTION_SOURCE_NOT_LOADED
&& fActiveSourceCode->GetSourceFile() == NULL
&& sourceFile != NULL) {
sourceText.Prepend("Click to locate source file '");
sourceText += "'";
truncatedText = sourceText;
fSourcePathView->TruncateString(&truncatedText, B_TRUNCATE_MIDDLE,
fSourcePathView->Bounds().Width());
} else if (sourceFile != NULL)
sourceText.Prepend("File: ");
}
if (!truncatedText.IsEmpty() && truncatedText != sourceText) {
fSourcePathView->SetToolTip(sourceText);
fSourcePathView->SetText(truncatedText);
} else {
fSourcePathView->SetText(sourceText);
fSourcePathView->SetToolTip((const char*)NULL);
}
}
void
TeamWindow::_ScrollToActiveFunction()
{
// Scroll to the active function, if it has been selected manually.
if (fActiveFunction == NULL || fActiveSourceCode == NULL)
return;
switch (fActiveSourceObject) {
case ACTIVE_SOURCE_FUNCTION:
fSourceView->ScrollToAddress(fActiveFunction->Address());
break;
case ACTIVE_SOURCE_BREAKPOINT:
{
if (fActiveBreakpoint == NULL)
break;
const UserBreakpointLocation& location
= fActiveBreakpoint->Location();
int32 line = location.GetSourceLocation().Line();
if (location.SourceFile() != NULL && line >= 0
&& fActiveSourceCode->GetSourceFile()
== location.SourceFile()) {
fSourceView->ScrollToLine(line);
} else {
fSourceView->ScrollToAddress(
fActiveFunction->Address()
+ location.RelativeAddress());
}
break;
}
case ACTIVE_SOURCE_NONE:
case ACTIVE_SOURCE_STACK_FRAME:
break;
}
}
void
TeamWindow::_HandleThreadStateChanged(thread_id threadID)
{
AutoLocker< ::Team> locker(fTeam);
::Thread* thread = fTeam->ThreadByID(threadID);
if (thread == NULL)
return;
if (thread->State() != THREAD_STATE_STOPPED) {
ThreadStackFrameSelectionEntry* entry
= fThreadSelectionInfoTable->Lookup(thread);
if (entry != NULL) {
fThreadSelectionInfoTable->Remove(entry);
delete entry;
}
}
// If the thread has been stopped and we don't have an active thread yet
// (or it isn't stopped), switch to this thread. Otherwise ignore the event.
if (thread->State() == THREAD_STATE_STOPPED
&& (fActiveThread == NULL
|| (thread != fActiveThread
&& fActiveThread->State() != THREAD_STATE_STOPPED))) {
_SetActiveThread(thread);
} else if (thread != fActiveThread) {
// otherwise ignore the event, if the thread is not the active one
return;
}
// Switch to the threads tab view when the thread has stopped.
if (thread->State() == THREAD_STATE_STOPPED) {
fTabView->Select(MAIN_TAB_INDEX_THREADS);
// if we hit a breakpoint or exception, raise the window to the
// foreground, since if this occurs while e.g. debugging a GUI
// app, it might not be immediately obvious that such an event
// occurred as the app may simply appear to hang.
Activate();
}
_UpdateRunButtons();
}
void
TeamWindow::_HandleCpuStateChanged(thread_id threadID)
{
// We're only interested in the currently selected thread
if (fActiveThread == NULL || threadID != fActiveThread->ID())
return;
_UpdateCpuState();
}
void
TeamWindow::_HandleStackTraceChanged(thread_id threadID)
{
// We're only interested in the currently selected thread
if (fActiveThread == NULL || threadID != fActiveThread->ID())
return;
AutoLocker< ::Team> locker(fTeam);
StackTrace* stackTrace = fActiveThread != NULL
? fActiveThread->GetStackTrace() : NULL;
BReference<StackTrace> stackTraceReference(stackTrace);
// hold a reference until the register view has one
locker.Unlock();
if (stackTrace == NULL) {
if (fTraceUpdateRunner != NULL)
return;
BMessage message(MSG_CLEAR_STACK_TRACE);
fTraceUpdateRunner = new(std::nothrow) BMessageRunner(this,
message, 250000, 1);
if (fTraceUpdateRunner != NULL
&& fTraceUpdateRunner->InitCheck() == B_OK) {
fStackTraceView->SetStackTraceClearPending();
fVariablesView->SetStackFrameClearPending();
return;
}
}
_SetActiveStackTrace(stackTrace);
}
void
TeamWindow::_HandleImageDebugInfoChanged(image_id imageID)
{
TRACE_GUI("TeamWindow::_HandleImageDebugInfoChanged(%" B_PRId32 ")\n",
imageID);
// We're only interested in the currently selected thread
if (fActiveImage == NULL || imageID != fActiveImage->ID())
return;
AutoLocker< ::Team> locker(fTeam);
ImageDebugInfo* imageDebugInfo = fActiveImage != NULL
? fActiveImage->GetImageDebugInfo() : NULL;
TRACE_GUI(" image debug info: %p\n", imageDebugInfo);
BReference<ImageDebugInfo> imageDebugInfoReference(imageDebugInfo);
// hold a reference until we've set it
locker.Unlock();
fImageFunctionsView->SetImageDebugInfo(imageDebugInfo);
}
void
TeamWindow::_HandleSourceCodeChanged()
{
// If we don't have an active function anymore, the message is obsolete.
if (fActiveFunction == NULL)
return;
// get a reference to the source code
AutoLocker< ::Team> locker(fTeam);
SourceCode* sourceCode = NULL;
if (fActiveFunction->GetFunction()->SourceCodeState()
== FUNCTION_SOURCE_LOADED) {
sourceCode = fActiveFunction->GetFunction()->GetSourceCode();
} else
sourceCode = fActiveFunction->GetSourceCode();
BReference<SourceCode> sourceCodeReference(sourceCode);
locker.Unlock();
_SetActiveSourceCode(sourceCode);
}
void
TeamWindow::_HandleUserBreakpointChanged(UserBreakpoint* breakpoint)
{
fSourceView->UserBreakpointChanged(breakpoint);
fBreakpointsView->UserBreakpointChanged(breakpoint);
}
void
TeamWindow::_HandleWatchpointChanged(Watchpoint* watchpoint)
{
fBreakpointsView->WatchpointChanged(watchpoint);
}
status_t
TeamWindow::_RetrieveMatchingSourceWorker(void* arg)
{
TeamWindow* window = (TeamWindow*)arg;
BStringList* entries = new(std::nothrow) BStringList();
if (entries == NULL)
return B_NO_MEMORY;
ObjectDeleter<BStringList> stringListDeleter(entries);
if (!window->Lock())
return B_BAD_VALUE;
BString path;
window->fActiveFunction->GetFunctionDebugInfo()->SourceFile()
->GetPath(path);
window->Unlock();
status_t error = window->_RetrieveMatchingSourceEntries(path, entries);
entries->Sort();
BMessenger messenger(window);
if (messenger.IsValid() && messenger.LockTarget()) {
if (window->fActiveSourceWorker == find_thread(NULL)) {
BMessage message(MSG_SOURCE_ENTRY_QUERY_COMPLETE);
message.AddInt32("error", error);
message.AddPointer("entries", entries);
if (messenger.SendMessage(&message) == B_OK)
stringListDeleter.Detach();
}
window->Unlock();
}
return B_OK;
}
void
TeamWindow::_HandleResolveMissingSourceFile(entry_ref& locatedPath)
{
if (fActiveFunction != NULL) {
LocatableFile* sourceFile = fActiveFunction->GetFunctionDebugInfo()
->SourceFile();
if (sourceFile != NULL) {
BString sourcePath;
sourceFile->GetPath(sourcePath);
BString sourceFileName(sourcePath);
int32 index = sourcePath.FindLast('/');
if (index >= 0)
sourceFileName.Remove(0, index + 1);
BPath targetFilePath(&locatedPath);
if (targetFilePath.InitCheck() != B_OK)
return;
if (strcmp(sourceFileName.String(), targetFilePath.Leaf()) != 0) {
BString message;
message.SetToFormat("The names of source file '%s' and located"
" file '%s' differ. Use file anyway?",
sourceFileName.String(), targetFilePath.Leaf());
BAlert* alert = new(std::nothrow) BAlert(
"Source path mismatch", message.String(), "Cancel", "Use");
if (alert == NULL)
return;
int32 choice = alert->Go();
if (choice <= 0)
return;
}
LocatableFile* foundSourceFile = fActiveSourceCode
->GetSourceFile();
if (foundSourceFile != NULL)
fListener->SourceEntryInvalidateRequested(foundSourceFile);
fListener->SourceEntryLocateRequested(sourcePath,
targetFilePath.Path());
fListener->FunctionSourceCodeRequested(fActiveFunction);
}
}
}
void
TeamWindow::_HandleLocateSourceRequest(BStringList* entries)
{
if (fActiveFunction == NULL)
return;
else if (fActiveFunction->GetFunctionDebugInfo()->SourceFile() == NULL)
return;
else if (fActiveSourceCode == NULL)
return;
else if (fActiveFunction->GetFunction()->SourceCodeState()
== FUNCTION_SOURCE_NOT_LOADED) {
return;
}
if (entries == NULL) {
if (fActiveSourceWorker < 0) {
fActiveSourceWorker = spawn_thread(&_RetrieveMatchingSourceWorker,
"source file query worker", B_NORMAL_PRIORITY, this);
if (fActiveSourceWorker > 0)
resume_thread(fActiveSourceWorker);
}
return;
}
int32 count = entries->CountStrings();
if (count > 0) {
BPopUpMenu* menu = new(std::nothrow) BPopUpMenu("");
if (menu == NULL)
return;
BPrivate::ObjectDeleter<BPopUpMenu> menuDeleter(menu);
BMenuItem* item = NULL;
for (int32 i = 0; i < count; i++) {
item = new(std::nothrow) BMenuItem(entries->StringAt(i).String(),
NULL);
if (item == NULL || !menu->AddItem(item)) {
delete item;
return;
}
}
menu->AddSeparatorItem();
BMenuItem* manualItem = new(std::nothrow) BMenuItem(
"Locate manually" B_UTF8_ELLIPSIS, NULL);
if (manualItem == NULL || !menu->AddItem(manualItem)) {
delete manualItem;
return;
}
BPoint point;
fSourcePathView->GetMouse(&point, NULL, false);
fSourcePathView->ConvertToScreen(&point);
item = menu->Go(point, false, true);
if (item == NULL)
return;
else if (item != manualItem) {
// if the user picks to locate the entry manually,
// then fall through to the usual file panel logic
// as if we'd found no matches at all.
entry_ref ref;
if (get_ref_for_path(item->Label(), &ref) == B_OK) {
_HandleResolveMissingSourceFile(ref);
return;
}
}
}
try {
if (fFilePanel == NULL) {
fFilePanel = new BFilePanel(B_OPEN_PANEL,
new BMessenger(this));
}
fFilePanel->Show();
} catch (...) {
delete fFilePanel;
fFilePanel = NULL;
}
}
status_t
TeamWindow::_RetrieveMatchingSourceEntries(const BString& path,
BStringList* _entries)
{
BPath filePath(path);
status_t error = filePath.InitCheck();
if (error != B_OK)
return error;
_entries->MakeEmpty();
BQuery query;
BString predicate;
query.PushAttr("name");
query.PushString(filePath.Leaf());
query.PushOp(B_EQ);
error = query.GetPredicate(&predicate);
if (error != B_OK)
return error;
BVolumeRoster roster;
BVolume volume;
while (roster.GetNextVolume(&volume) == B_OK) {
if (!volume.KnowsQuery())
continue;
if (query.SetVolume(&volume) != B_OK)
continue;
error = query.SetPredicate(predicate.String());
if (error != B_OK)
continue;
if (query.Fetch() != B_OK)
continue;
entry_ref ref;
while (query.GetNextRef(&ref) == B_OK) {
filePath.SetTo(&ref);
_entries->Add(filePath.Path());
}
query.Clear();
}
return B_OK;
}
status_t
TeamWindow::_SaveInspectorSettings(const BMessage* settings)
{
if (fUiSettings.AddSettings("inspectorWindow", *settings) != B_OK)
return B_NO_MEMORY;
return B_OK;
}
status_t
TeamWindow::_GetActiveSourceLanguage(SourceLanguage*& _language)
{
AutoLocker< ::Team> locker(fTeam);
if (!locker.IsLocked())
return B_ERROR;
if (fActiveSourceCode != NULL) {
_language = fActiveSourceCode->GetSourceLanguage();
_language->AcquireReference();
return B_OK;
}
// if we made it this far, we were unable to acquire a source
// language corresponding to the active function. As such,
// try to fall back to the C++-style parser.
_language = new(std::nothrow) CppLanguage();
if (_language == NULL)
return B_NO_MEMORY;
return B_OK;
}
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: next.
↑ V773 The function was exited without releasing the 'alert' pointer. A memory leak is possible.