diff --git a/src/apps/debugger/Jamfile b/src/apps/debugger/Jamfile index d40c8c5c87..3a5457575b 100644 --- a/src/apps/debugger/Jamfile +++ b/src/apps/debugger/Jamfile @@ -12,6 +12,7 @@ SEARCH_SOURCE += [ FDirName $(SUBDIR) debug_info ] ; SEARCH_SOURCE += [ FDirName $(SUBDIR) debugger_interface ] ; SEARCH_SOURCE += [ FDirName $(SUBDIR) elf ] ; SEARCH_SOURCE += [ FDirName $(SUBDIR) files ] ; +SEARCH_SOURCE += [ FDirName $(SUBDIR) gui model ] ; SEARCH_SOURCE += [ FDirName $(SUBDIR) gui team_window ] ; SEARCH_SOURCE += [ FDirName $(SUBDIR) ids ] ; SEARCH_SOURCE += [ FDirName $(SUBDIR) model ] ; @@ -92,6 +93,10 @@ Application Debugger : LocatableFile.cpp SourceFile.cpp + # gui/model + VariablesViewState.cpp + VariablesViewStateHistory.cpp + # gui/team_window ImageFunctionsView.cpp ImageListView.cpp diff --git a/src/apps/debugger/gui/model/VariablesViewState.cpp b/src/apps/debugger/gui/model/VariablesViewState.cpp new file mode 100644 index 0000000000..e47def9710 --- /dev/null +++ b/src/apps/debugger/gui/model/VariablesViewState.cpp @@ -0,0 +1,209 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "VariablesViewState.h" + +#include + +#include "FunctionID.h" +#include "StackFrameValues.h" +#include "TypeComponentPath.h" + + +// #pragma mark - VariablesViewNodeInfo + + +VariablesViewNodeInfo::VariablesViewNodeInfo() + : + fNodeExpanded(false) +{ +} + + +VariablesViewNodeInfo::VariablesViewNodeInfo(const VariablesViewNodeInfo& other) + : + fNodeExpanded(other.fNodeExpanded) +{ +} + + +VariablesViewNodeInfo& +VariablesViewNodeInfo::operator=(const VariablesViewNodeInfo& other) +{ + fNodeExpanded = other.fNodeExpanded; + return *this; +} + + +void +VariablesViewNodeInfo::SetNodeExpanded(bool expanded) +{ + fNodeExpanded = expanded; +} + + +// #pragma mark - Key + + +struct VariablesViewState::Key { + ObjectID* variable; + TypeComponentPath* path; + + Key(ObjectID* variable, TypeComponentPath* path) + : + variable(variable), + path(path) + { + } + + uint32 HashValue() const + { + return variable->HashValue() ^ path->HashValue(); + } + + bool operator==(const Key& other) const + { + return *variable == *other.variable && *path == *other.path; + } +}; + + +// #pragma mark - InfoEntry + + +struct VariablesViewState::InfoEntry : Key, VariablesViewNodeInfo { + InfoEntry* next; + + InfoEntry(ObjectID* variable, TypeComponentPath* path) + : + Key(variable, path) + { + variable->AcquireReference(); + path->AcquireReference(); + } + + ~InfoEntry() + { + variable->ReleaseReference(); + path->ReleaseReference(); + } + + void SetInfo(const VariablesViewNodeInfo& info) + { + VariablesViewNodeInfo::operator=(info); + } +}; + + +struct VariablesViewState::InfoEntryHashDefinition { + typedef Key KeyType; + typedef InfoEntry ValueType; + + size_t HashKey(const Key& key) const + { + return key.HashValue(); + } + + size_t Hash(const InfoEntry* value) const + { + return value->HashValue(); + } + + bool Compare(const Key& key, const InfoEntry* value) const + { + return key == *value; + } + + InfoEntry*& GetLink(InfoEntry* value) const + { + return value->next; + } +}; + + +VariablesViewState::VariablesViewState() + : + fNodeInfos(NULL), + fValues(NULL) +{ +} + + +VariablesViewState::~VariablesViewState() +{ + _Cleanup(); +} + + +status_t +VariablesViewState::Init() +{ + fNodeInfos = new(std::nothrow) NodeInfoTable; + if (fNodeInfos == NULL) + return B_NO_MEMORY; + + return fNodeInfos->Init(); +} + +void +VariablesViewState::SetValues(StackFrameValues* values) +{ + if (fValues == values) + return; + + if (fValues != NULL) + fValues->ReleaseReference(); + + fValues = values; + + if (fValues != NULL) + fValues->AcquireReference(); +} + + +const VariablesViewNodeInfo* +VariablesViewState::GetNodeInfo(ObjectID* variable, + const TypeComponentPath* path) const +{ + return fNodeInfos->Lookup(Key(variable, (TypeComponentPath*)path)); +} + + +status_t +VariablesViewState::SetNodeInfo(ObjectID* variable, TypeComponentPath* path, + const VariablesViewNodeInfo& info) +{ + InfoEntry* entry = fNodeInfos->Lookup(Key(variable, path)); + if (entry == NULL) { + entry = new(std::nothrow) InfoEntry(variable, path); + if (entry == NULL) + return B_NO_MEMORY; + fNodeInfos->Insert(entry); + } + + entry->SetInfo(info); + return B_OK; +} + + +void +VariablesViewState::_Cleanup() +{ + if (fNodeInfos != NULL) { + InfoEntry* entry = fNodeInfos->Clear(true); + + while (entry != NULL) { + InfoEntry* next = entry->next; + delete entry; + entry = next; + } + + delete fNodeInfos; + fNodeInfos = NULL; + } + + SetValues(NULL); +} diff --git a/src/apps/debugger/gui/model/VariablesViewState.h b/src/apps/debugger/gui/model/VariablesViewState.h new file mode 100644 index 0000000000..3e9aad0952 --- /dev/null +++ b/src/apps/debugger/gui/model/VariablesViewState.h @@ -0,0 +1,81 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef VARIABLES_VIEW_STATE_H +#define VARIABLES_VIEW_STATE_H + + +#include +#include + + +class ObjectID; +class StackFrameValues; +class TypeComponentPath; + + +class VariablesViewNodeInfo { +public: + VariablesViewNodeInfo(); + VariablesViewNodeInfo( + const VariablesViewNodeInfo& other); + + VariablesViewNodeInfo& operator=( + const VariablesViewNodeInfo& other); + + bool IsNodeExpanded() const + { return fNodeExpanded; } + void SetNodeExpanded(bool expanded); + +private: + bool fNodeExpanded; +}; + + +class VariablesViewState : public Referenceable { +public: + VariablesViewState(); + virtual ~VariablesViewState(); + + status_t Init(); + + StackFrameValues* Values() const + { return fValues; } + void SetValues(StackFrameValues* values); + + const VariablesViewNodeInfo* GetNodeInfo(ObjectID* variable, + const TypeComponentPath* path) const; + inline const VariablesViewNodeInfo* GetNodeInfo(ObjectID* variable, + const TypeComponentPath& path) const; + + status_t SetNodeInfo(ObjectID* variable, + TypeComponentPath* path, + const VariablesViewNodeInfo& info); + // requires an on-heap path + +private: + struct Key; + struct InfoEntry; + struct InfoEntryHashDefinition; + + typedef BOpenHashTable NodeInfoTable; + +private: + void _Cleanup(); + +private: + NodeInfoTable* fNodeInfos; + StackFrameValues* fValues; +}; + + +const VariablesViewNodeInfo* +VariablesViewState::GetNodeInfo(ObjectID* variable, + const TypeComponentPath& path) const +{ + return GetNodeInfo(variable, &path); +} + + +#endif // VARIABLES_VIEW_STATE_H diff --git a/src/apps/debugger/gui/model/VariablesViewStateHistory.cpp b/src/apps/debugger/gui/model/VariablesViewStateHistory.cpp new file mode 100644 index 0000000000..10df0a4289 --- /dev/null +++ b/src/apps/debugger/gui/model/VariablesViewStateHistory.cpp @@ -0,0 +1,204 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "VariablesViewStateHistory.h" + +#include + +#include "FunctionID.h" +#include "VariablesViewState.h" + + +// #pragma mark - Key + + +struct VariablesViewStateHistory::Key { + thread_id threadID; + FunctionID* functionID; + + Key(thread_id threadID, FunctionID* functionID) + : + threadID(threadID), + functionID(functionID) + { + } + + uint32 HashValue() const + { + return functionID->HashValue() ^ threadID; + } + + bool operator==(const Key& other) const + { + return threadID == other.threadID && *functionID == *other.functionID; + } +}; + + +// #pragma mark - StateEntry + + +struct VariablesViewStateHistory::StateEntry : Key, VariablesViewNodeInfo { + StateEntry* next; + VariablesViewState* state; + + StateEntry(thread_id threadID, FunctionID* functionID) + : + Key(threadID, functionID), + state(NULL) + { + functionID->AcquireReference(); + } + + ~StateEntry() + { + functionID->ReleaseReference(); + if (state != NULL) + state->ReleaseReference(); + } + + void SetState(VariablesViewState* state) + { + if (state == this->state) + return; + + if (state != NULL) + state->AcquireReference(); + + if (this->state != NULL) + this->state->ReleaseReference(); + + this->state = state; + } +}; + + +struct VariablesViewStateHistory::StateEntryHashDefinition { + typedef Key KeyType; + typedef StateEntry ValueType; + + size_t HashKey(const Key& key) const + { + return key.HashValue(); + } + + size_t Hash(const StateEntry* value) const + { + return HashKey(*value); + } + + bool Compare(const Key& key, const StateEntry* value) const + { + return key == *value; + } + + StateEntry*& GetLink(StateEntry* value) const + { + return value->next; + } +}; + + +VariablesViewStateHistory::VariablesViewStateHistory() + : + fStates(NULL) +{ +} + + +VariablesViewStateHistory::~VariablesViewStateHistory() +{ + if (fStates != NULL) { + StateEntry* entry = fStates->Clear(true); + + while (entry != NULL) { + StateEntry* next = entry->next; + delete entry; + entry = next; + } + + delete fStates; + } +} + + +status_t +VariablesViewStateHistory::Init() +{ + fStates = new(std::nothrow) StateTable; + if (fStates == NULL) + return B_NO_MEMORY; + + return fStates->Init(); +} + + +VariablesViewState* +VariablesViewStateHistory::GetState(thread_id threadID, FunctionID* functionID) + const +{ + // first try an exact match with the thread ID + if (threadID >= 0) { + StateEntry* stateEntry = fStates->Lookup(Key(threadID, functionID)); + if (stateEntry != NULL) + return stateEntry->state; + } + + // just match the function ID + StateEntry* stateEntry = fStates->Lookup(Key(-1, functionID)); + return stateEntry != NULL ? stateEntry->state : NULL; +} + + +VariablesViewState* +VariablesViewStateHistory::GetState(FunctionID* functionID) const +{ + StateEntry* stateEntry = fStates->Lookup(Key(-1, functionID)); + return stateEntry != NULL ? stateEntry->state : NULL; +} + + +status_t +VariablesViewStateHistory::SetState(thread_id threadID, FunctionID* functionID, + VariablesViewState* state) +{ + // Make sure the default entry for the function exists. + StateEntry* defaultEntry = fStates->Lookup(Key(-1, functionID)); + bool newDefaultEntry = false; + + if (defaultEntry == NULL) { + defaultEntry = new(std::nothrow) StateEntry(-1, functionID); + if (defaultEntry == NULL) + return B_NO_MEMORY; + fStates->Insert(defaultEntry); + newDefaultEntry = true; + } + + // If we have a valid thread ID, make sure the respective entry for the + // function exists. + StateEntry* threadEntry = NULL; + if (threadID >= 0) { + threadEntry = fStates->Lookup(Key(threadID, functionID)); + + if (threadEntry == NULL) { + threadEntry = new(std::nothrow) StateEntry(threadID, functionID); + if (threadEntry == NULL) { + if (newDefaultEntry) { + fStates->Remove(defaultEntry); + delete defaultEntry; + } + return B_NO_MEMORY; + } + fStates->Insert(threadEntry); + } + } + + defaultEntry->SetState(state); + if (threadEntry != NULL) + threadEntry->SetState(state); + + return B_OK; +} diff --git a/src/apps/debugger/gui/model/VariablesViewStateHistory.h b/src/apps/debugger/gui/model/VariablesViewStateHistory.h new file mode 100644 index 0000000000..4e09c2496f --- /dev/null +++ b/src/apps/debugger/gui/model/VariablesViewStateHistory.h @@ -0,0 +1,43 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef VARIABLES_VIEW_STATE_HISTORY_H +#define VARIABLES_VIEW_STATE_HISTORY_H + + +#include + + +class FunctionID; +class VariablesViewState; + + +class VariablesViewStateHistory { +public: + VariablesViewStateHistory(); + virtual ~VariablesViewStateHistory(); + + status_t Init(); + + VariablesViewState* GetState(thread_id threadID, + FunctionID* functionID) const; + VariablesViewState* GetState(FunctionID* functionID) const; + + status_t SetState(thread_id threadID, + FunctionID* functionID, + VariablesViewState* state); + +private: + struct Key; + struct StateEntry; + struct StateEntryHashDefinition; + + typedef BOpenHashTable StateTable; + +private: + StateTable* fStates; +}; + + +#endif // VARIABLES_VIEW_STATE_HISTORY_H diff --git a/src/apps/debugger/gui/team_window/VariablesView.cpp b/src/apps/debugger/gui/team_window/VariablesView.cpp index b89b08625c..7a1dc00654 100644 --- a/src/apps/debugger/gui/team_window/VariablesView.cpp +++ b/src/apps/debugger/gui/team_window/VariablesView.cpp @@ -15,6 +15,8 @@ #include "table/TableColumns.h" #include "Architecture.h" +#include "FunctionID.h" +#include "FunctionInstance.h" #include "StackFrame.h" #include "StackFrameValues.h" #include "Team.h" @@ -22,6 +24,8 @@ #include "Tracing.h" #include "TypeComponentPath.h" #include "Variable.h" +#include "VariablesViewState.h" +#include "VariablesViewStateHistory.h" enum { @@ -749,6 +753,8 @@ VariablesView::VariablesView(Listener* listener) fStackFrame(NULL), fVariableTable(NULL), fVariableTableModel(NULL), + fPreviousViewState(NULL), + fViewStateHistory(NULL), fListener(listener) { SetName("Variables"); @@ -759,6 +765,9 @@ VariablesView::~VariablesView() { SetStackFrame(NULL, NULL); fVariableTable->SetTreeTableModel(NULL); + if (fPreviousViewState != NULL) + fPreviousViewState->ReleaseReference(); + delete fViewStateHistory; delete fVariableTableModel; } @@ -785,6 +794,8 @@ VariablesView::SetStackFrame(Thread* thread, StackFrame* stackFrame) if (thread == fThread && stackFrame == fStackFrame) return; + _SaveViewState(); + if (fThread != NULL) fThread->ReleaseReference(); if (fStackFrame != NULL) @@ -812,6 +823,8 @@ VariablesView::SetStackFrame(Thread* thread, StackFrame* stackFrame) _RequestVariableValue(variable); } } + + _RestoreViewState(); } @@ -868,6 +881,10 @@ VariablesView::_Init() fVariableTable->SetTreeTableModel(fVariableTableModel); fVariableTable->AddTreeTableListener(this); + + fViewStateHistory = new VariablesViewStateHistory; + if (fViewStateHistory->Init() != B_OK) + throw std::bad_alloc(); } @@ -887,6 +904,131 @@ VariablesView::_RequestVariableValue(Variable* variable) } +void +VariablesView::_SaveViewState() const +{ + if (fThread == NULL || fStackFrame == NULL) + return; + + // get the function ID + FunctionID* functionID = fStackFrame->Function()->GetFunctionID(); + if (functionID == NULL) + return; + Reference functionIDReference(functionID, true); + + // create an empty view state + VariablesViewState* viewState = new(std::nothrow) VariablesViewState; + if (viewState == NULL) + return; + Reference viewStateReference(viewState, true); + + if (viewState->Init() != B_OK) + return; + + // populate it + TreeTablePath path; + if (_AddViewStateDescendentNodeInfos(viewState, fVariableTableModel->Root(), + path) != B_OK) { + return; + } +// TODO: Add values! + + // add the view state to the history + fViewStateHistory->SetState(fThread->ID(), functionID, viewState); +} + + +void +VariablesView::_RestoreViewState() +{ + if (fPreviousViewState != NULL) { + fPreviousViewState->ReleaseReference(); + fPreviousViewState = NULL; + } + + if (fThread == NULL || fStackFrame == NULL) + return; + + // get the function ID + FunctionID* functionID = fStackFrame->Function()->GetFunctionID(); + if (functionID == NULL) + return; + Reference functionIDReference(functionID, true); + + // get the previous view state + VariablesViewState* viewState = fViewStateHistory->GetState(fThread->ID(), + functionID); + if (viewState == NULL) + return; + + // apply the view state + TreeTablePath path; + _ApplyViewStateDescendentNodeInfos(viewState, fVariableTableModel->Root(), + path); +} + + +status_t +VariablesView::_AddViewStateDescendentNodeInfos(VariablesViewState* viewState, + void* parent, TreeTablePath& path) const +{ + int32 childCount = fVariableTableModel->CountChildren(parent); + for (int32 i = 0; i < childCount; i++) { + ValueNode* node = (ValueNode*)fVariableTableModel->ChildAt(parent, i); + if (!path.AddComponent(i)) + return B_NO_MEMORY; + + // add the node's info + VariablesViewNodeInfo nodeInfo; + nodeInfo.SetNodeExpanded(fVariableTable->IsNodeExpanded(path)); + + status_t error = viewState->SetNodeInfo(node->GetVariable()->ID(), + node->Path(), nodeInfo); + if (error != B_OK) + return error; + + // recurse + error = _AddViewStateDescendentNodeInfos(viewState, node, path); + if (error != B_OK) + return error; + + path.RemoveLastComponent(); + } + + return B_OK; +} + + +status_t +VariablesView::_ApplyViewStateDescendentNodeInfos(VariablesViewState* viewState, + void* parent, TreeTablePath& path) +{ + int32 childCount = fVariableTableModel->CountChildren(parent); + for (int32 i = 0; i < childCount; i++) { + ValueNode* node = (ValueNode*)fVariableTableModel->ChildAt(parent, i); + if (!path.AddComponent(i)) + return B_NO_MEMORY; + + // apply the node's info, if any + const VariablesViewNodeInfo* nodeInfo = viewState->GetNodeInfo( + node->GetVariable()->ID(), node->Path()); + if (nodeInfo != NULL) { + fVariableTable->SetNodeExpanded(path, nodeInfo->IsNodeExpanded()); + + // recurse + status_t error = _ApplyViewStateDescendentNodeInfos(viewState, node, + path); + if (error != B_OK) + return error; + } + + path.RemoveLastComponent(); + } + + return B_OK; +} + + // #pragma mark - Listener diff --git a/src/apps/debugger/gui/team_window/VariablesView.h b/src/apps/debugger/gui/team_window/VariablesView.h index 78d3721043..6570c4d6ae 100644 --- a/src/apps/debugger/gui/team_window/VariablesView.h +++ b/src/apps/debugger/gui/team_window/VariablesView.h @@ -15,6 +15,8 @@ class StackFrame; class Thread; class TypeComponentPath; class Variable; +class VariablesViewState; +class VariablesViewStateHistory; class VariablesView : public BGroupView, private TreeTableListener { @@ -49,11 +51,22 @@ private: void _RequestVariableValue(Variable* variable); + void _SaveViewState() const; + void _RestoreViewState(); + status_t _AddViewStateDescendentNodeInfos( + VariablesViewState* viewState, void* parent, + TreeTablePath& path) const; + status_t _ApplyViewStateDescendentNodeInfos( + VariablesViewState* viewState, void* parent, + TreeTablePath& path); + private: Thread* fThread; StackFrame* fStackFrame; TreeTable* fVariableTable; VariableTableModel* fVariableTableModel; + VariablesViewState* fPreviousViewState; + VariablesViewStateHistory* fViewStateHistory; Listener* fListener; };