The variables view does now save/restore part of its state when the stack

frame changes. Currently that's only the expanded/collapsed state of the
items in the tree view, but that already makes single stepping through code
much less annoying.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@33512 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2009-10-11 05:46:45 +00:00
parent 9f018b7c77
commit e8e4e613b2
7 changed files with 697 additions and 0 deletions
+5
View File
@@ -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
@@ -0,0 +1,209 @@
/*
* Copyright 2009, Ingo Weinhold, [email protected].
* Distributed under the terms of the MIT License.
*/
#include "VariablesViewState.h"
#include <new>
#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);
}
@@ -0,0 +1,81 @@
/*
* Copyright 2009, Ingo Weinhold, [email protected].
* Distributed under the terms of the MIT License.
*/
#ifndef VARIABLES_VIEW_STATE_H
#define VARIABLES_VIEW_STATE_H
#include <Referenceable.h>
#include <util/OpenHashTable.h>
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<InfoEntryHashDefinition> 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
@@ -0,0 +1,204 @@
/*
* Copyright 2009, Ingo Weinhold, [email protected].
* Distributed under the terms of the MIT License.
*/
#include "VariablesViewStateHistory.h"
#include <new>
#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;
}
@@ -0,0 +1,43 @@
/*
* Copyright 2009, Ingo Weinhold, [email protected].
* Distributed under the terms of the MIT License.
*/
#ifndef VARIABLES_VIEW_STATE_HISTORY_H
#define VARIABLES_VIEW_STATE_HISTORY_H
#include <util/OpenHashTable.h>
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<StateEntryHashDefinition> StateTable;
private:
StateTable* fStates;
};
#endif // VARIABLES_VIEW_STATE_HISTORY_H
@@ -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<FunctionID> functionIDReference(functionID, true);
// create an empty view state
VariablesViewState* viewState = new(std::nothrow) VariablesViewState;
if (viewState == NULL)
return;
Reference<VariablesViewState> 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<FunctionID> 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
@@ -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;
};