Adding base callstack control.
This commit is contained in:
parent
ae183f918f
commit
2b012f37bf
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2015 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_DEBUG_UI_CONTROL_H_
|
||||
#define XENIA_DEBUG_UI_CONTROL_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "el/elements.h"
|
||||
#include "el/event_handler.h"
|
||||
#include "xenia/debug/ui/application.h"
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
namespace ui {
|
||||
|
||||
class Control {
|
||||
public:
|
||||
virtual ~Control() = default;
|
||||
|
||||
el::LayoutBox* root_element() { return &root_element_; }
|
||||
xe::ui::Loop* loop() const { return Application::current()->loop(); }
|
||||
DebugClient* client() const { return client_; }
|
||||
model::System* system() const { return Application::current()->system(); }
|
||||
|
||||
virtual el::Element* BuildUI() = 0;
|
||||
|
||||
virtual void Setup(xe::debug::DebugClient* client) = 0;
|
||||
|
||||
protected:
|
||||
Control() = default;
|
||||
|
||||
el::LayoutBox root_element_;
|
||||
std::unique_ptr<el::EventHandler> handler_;
|
||||
xe::debug::DebugClient* client_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
} // namespace debug
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_DEBUG_UI_CONTROL_H_
|
|
@ -0,0 +1,152 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2015 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "el/animation_manager.h"
|
||||
#include "xenia/base/string_buffer.h"
|
||||
#include "xenia/debug/ui/views/cpu/call_stack_control.h"
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
namespace ui {
|
||||
namespace views {
|
||||
namespace cpu {
|
||||
|
||||
class CallStackItem : public el::GenericStringItem {
|
||||
public:
|
||||
CallStackItem(size_t ordinal, const ThreadCallStackFrame* frame)
|
||||
: GenericStringItem(""), ordinal_(ordinal), frame_(frame) {
|
||||
StringBuffer sb;
|
||||
if (frame_->guest_pc) {
|
||||
sb.AppendFormat(" %.2lld %.16llX %.8X %s", ordinal_, frame_->host_pc,
|
||||
frame_->guest_pc, frame_->name);
|
||||
} else {
|
||||
sb.AppendFormat(" %.2lld %.16llX %s", ordinal_, frame_->host_pc,
|
||||
frame_->name);
|
||||
}
|
||||
str = sb.to_string();
|
||||
}
|
||||
|
||||
private:
|
||||
size_t ordinal_ = 0;
|
||||
const ThreadCallStackFrame* frame_ = nullptr;
|
||||
};
|
||||
|
||||
class CallStackItemElement : public el::LayoutBox {
|
||||
public:
|
||||
CallStackItemElement(CallStackItem* item, CallStackItemSource* source,
|
||||
el::ListItemObserver* source_viewer, size_t index)
|
||||
: item_(item),
|
||||
source_(source),
|
||||
source_viewer_(source_viewer),
|
||||
index_(index) {
|
||||
set_background_skin(TBIDC("ListItem"));
|
||||
set_axis(el::Axis::kY);
|
||||
set_gravity(el::Gravity::kAll);
|
||||
set_layout_position(el::LayoutPosition::kLeftTop);
|
||||
set_layout_distribution(el::LayoutDistribution::kAvailable);
|
||||
set_layout_distribution_position(el::LayoutDistributionPosition::kLeftTop);
|
||||
set_layout_size(el::LayoutSize::kAvailable);
|
||||
set_paint_overflow_fadeout(false);
|
||||
|
||||
using namespace el::dsl; // NOLINT(build/namespaces)
|
||||
el::AnimationBlocker animation_blocker;
|
||||
|
||||
auto node = LabelNode(item_->str.c_str())
|
||||
.gravity(Gravity::kLeft)
|
||||
.ignore_input(true)
|
||||
.text_align(el::TextAlign::kLeft);
|
||||
content_root()->LoadNodeTree(node);
|
||||
}
|
||||
|
||||
bool OnEvent(const el::Event& ev) override {
|
||||
return el::LayoutBox::OnEvent(ev);
|
||||
}
|
||||
|
||||
private:
|
||||
CallStackItem* item_ = nullptr;
|
||||
CallStackItemSource* source_ = nullptr;
|
||||
el::ListItemObserver* source_viewer_ = nullptr;
|
||||
size_t index_ = 0;
|
||||
};
|
||||
|
||||
class CallStackItemSource : public el::ListItemSourceList<CallStackItem> {
|
||||
public:
|
||||
el::Element* CreateItemElement(size_t index,
|
||||
el::ListItemObserver* viewer) override {
|
||||
return new CallStackItemElement(at(index), this, viewer, index);
|
||||
}
|
||||
};
|
||||
|
||||
CallStackControl::CallStackControl()
|
||||
: item_source_(new CallStackItemSource()) {}
|
||||
|
||||
CallStackControl::~CallStackControl() = default;
|
||||
|
||||
el::Element* CallStackControl::BuildUI() {
|
||||
using namespace el::dsl; // NOLINT(build/namespaces)
|
||||
el::AnimationBlocker animation_blocker;
|
||||
|
||||
auto node =
|
||||
LayoutBoxNode()
|
||||
.gravity(Gravity::kAll)
|
||||
.distribution(LayoutDistribution::kAvailable)
|
||||
.child(ListBoxNode().id("stack_listbox").gravity(Gravity::kAll));
|
||||
|
||||
root_element_.set_gravity(Gravity::kAll);
|
||||
root_element_.set_layout_distribution(LayoutDistribution::kAvailable);
|
||||
root_element_.LoadNodeTree(node);
|
||||
|
||||
auto stack_listbox =
|
||||
root_element_.GetElementById<el::ListBox>(TBIDC("stack_listbox"));
|
||||
stack_listbox->set_source(item_source_.get());
|
||||
stack_listbox->scroll_container()->set_scroll_mode(
|
||||
el::ScrollMode::kAutoXAutoY);
|
||||
|
||||
handler_ = std::make_unique<el::EventHandler>(&root_element_);
|
||||
|
||||
return &root_element_;
|
||||
}
|
||||
|
||||
void CallStackControl::Setup(DebugClient* client) {
|
||||
client_ = client;
|
||||
|
||||
system()->on_thread_call_stack_updated.AddListener(
|
||||
[this](model::Thread* thread) {
|
||||
if (thread == thread_) {
|
||||
InvalidateCallStack();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void CallStackControl::set_thread(model::Thread* thread) {
|
||||
thread_ = thread;
|
||||
InvalidateCallStack();
|
||||
}
|
||||
|
||||
void CallStackControl::InvalidateCallStack() {
|
||||
item_source_->clear();
|
||||
if (!thread_) {
|
||||
return;
|
||||
}
|
||||
auto& call_stack = thread_->call_stack();
|
||||
for (size_t i = 0; i < call_stack.size(); ++i) {
|
||||
auto& frame = call_stack[i];
|
||||
size_t ordinal = call_stack.size() - i - 1;
|
||||
auto item = std::make_unique<CallStackItem>(ordinal, &frame);
|
||||
item->tag.set_integer(static_cast<int>(i));
|
||||
item_source_->push_back(std::move(item));
|
||||
}
|
||||
item_source_->InvokeAllItemsRemoved();
|
||||
}
|
||||
|
||||
} // namespace cpu
|
||||
} // namespace views
|
||||
} // namespace ui
|
||||
} // namespace debug
|
||||
} // namespace xe
|
|
@ -0,0 +1,51 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2015 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_DEBUG_UI_VIEWS_CPU_CALL_STACK_CONTROL_H_
|
||||
#define XENIA_DEBUG_UI_VIEWS_CPU_CALL_STACK_CONTROL_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "xenia/debug/ui/control.h"
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
namespace ui {
|
||||
namespace views {
|
||||
namespace cpu {
|
||||
|
||||
class CallStackItemSource;
|
||||
|
||||
class CallStackControl : public Control {
|
||||
public:
|
||||
CallStackControl();
|
||||
~CallStackControl() override;
|
||||
|
||||
el::Element* BuildUI() override;
|
||||
|
||||
void Setup(xe::debug::DebugClient* client) override;
|
||||
|
||||
model::Thread* thread() const { return thread_; }
|
||||
void set_thread(model::Thread* thread);
|
||||
|
||||
private:
|
||||
void InvalidateCallStack();
|
||||
|
||||
model::Thread* thread_ = nullptr;
|
||||
std::unique_ptr<CallStackItemSource> item_source_;
|
||||
};
|
||||
|
||||
} // namespace cpu
|
||||
} // namespace views
|
||||
} // namespace ui
|
||||
} // namespace debug
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_DEBUG_UI_VIEWS_CPU_CALL_STACK_CONTROL_H_
|
|
@ -80,7 +80,8 @@ el::Element* CpuView::BuildUI() {
|
|||
TabContainerNode()
|
||||
.gravity(Gravity::kAll)
|
||||
.align(Align::kLeft)
|
||||
.tab(ButtonNode("Stack"), LabelNode("STACK"))
|
||||
.tab(ButtonNode("Stack"),
|
||||
LabelNode("<callstack control>").id("call_stack_placeholder"))
|
||||
.tab(ButtonNode("BPs"), LabelNode("BREAKPOINTS"));
|
||||
|
||||
auto source_pane_node =
|
||||
|
@ -104,7 +105,7 @@ el::Element* CpuView::BuildUI() {
|
|||
.gravity(Gravity::kAll)
|
||||
.axis(Axis::kX)
|
||||
.fixed_pane(FixedPane::kSecond)
|
||||
.value(128)
|
||||
.value(250)
|
||||
.pane(SplitContainerNode()
|
||||
.gravity(Gravity::kAll)
|
||||
.axis(Axis::kY)
|
||||
|
@ -133,9 +134,15 @@ el::Element* CpuView::BuildUI() {
|
|||
root_element_.set_gravity(Gravity::kAll);
|
||||
root_element_.set_layout_distribution(LayoutDistribution::kAvailable);
|
||||
root_element_.LoadNodeTree(node);
|
||||
|
||||
el::Label* call_stack_placeholder;
|
||||
root_element_.GetElementsById({
|
||||
//
|
||||
{TBIDC("call_stack_placeholder"), &call_stack_placeholder},
|
||||
});
|
||||
call_stack_placeholder->parent()->ReplaceChild(call_stack_placeholder,
|
||||
call_stack_control_.BuildUI());
|
||||
call_stack_control_.Setup(client_);
|
||||
|
||||
handler_ = std::make_unique<el::EventHandler>(&root_element_);
|
||||
|
||||
handler_->Listen(el::EventType::kChanged, TBIDC("module_dropdown"),
|
||||
|
@ -154,7 +161,7 @@ el::Element* CpuView::BuildUI() {
|
|||
} else {
|
||||
current_thread_ = nullptr;
|
||||
}
|
||||
UpdateThreadCallStack(current_thread_);
|
||||
SwitchCurrentThread(current_thread_);
|
||||
return true;
|
||||
});
|
||||
|
||||
|
@ -168,12 +175,6 @@ void CpuView::Setup(DebugClient* client) {
|
|||
[this]() { UpdateElementState(); });
|
||||
system()->on_modules_updated.AddListener([this]() { UpdateModuleList(); });
|
||||
system()->on_threads_updated.AddListener([this]() { UpdateThreadList(); });
|
||||
system()->on_thread_call_stack_updated.AddListener(
|
||||
[this](model::Thread* thread) {
|
||||
if (thread == current_thread_) {
|
||||
UpdateThreadCallStack(thread);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void CpuView::UpdateElementState() {
|
||||
|
@ -241,28 +242,9 @@ void CpuView::UpdateThreadList() {
|
|||
}
|
||||
}
|
||||
|
||||
void CpuView::UpdateThreadCallStack(model::Thread* thread) {
|
||||
auto textbox =
|
||||
root_element_.GetElementById<el::TextBox>(TBIDC("source_textbox"));
|
||||
if (!thread) {
|
||||
textbox->set_text("no thread");
|
||||
return;
|
||||
}
|
||||
auto& call_stack = thread->call_stack();
|
||||
StringBuffer str;
|
||||
for (size_t i = 0; i < call_stack.size(); ++i) {
|
||||
auto& frame = call_stack[i];
|
||||
size_t ordinal = call_stack.size() - i - 1;
|
||||
if (frame.guest_pc) {
|
||||
str.AppendFormat(" %.2lld %.16llX %.8X %s", ordinal, frame.host_pc,
|
||||
frame.guest_pc, frame.name);
|
||||
} else {
|
||||
str.AppendFormat(" %.2lld %.16llX %s", ordinal, frame.host_pc,
|
||||
frame.name);
|
||||
}
|
||||
str.Append('\n');
|
||||
}
|
||||
textbox->set_text(str.to_string());
|
||||
void CpuView::SwitchCurrentThread(model::Thread* thread) {
|
||||
current_thread_ = thread;
|
||||
call_stack_control_.set_thread(thread);
|
||||
}
|
||||
|
||||
} // namespace cpu
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <string>
|
||||
|
||||
#include "xenia/debug/ui/view.h"
|
||||
#include "xenia/debug/ui/views/cpu/call_stack_control.h"
|
||||
|
||||
namespace xe {
|
||||
namespace debug {
|
||||
|
@ -35,10 +36,13 @@ class CpuView : public View {
|
|||
void UpdateModuleList();
|
||||
void UpdateFunctionList();
|
||||
void UpdateThreadList();
|
||||
void UpdateThreadCallStack(model::Thread* thread);
|
||||
|
||||
void SwitchCurrentThread(model::Thread* thread);
|
||||
|
||||
// TODO(benvanik): better state machine.
|
||||
model::Thread* current_thread_ = nullptr;
|
||||
|
||||
CallStackControl call_stack_control_;
|
||||
};
|
||||
|
||||
} // namespace cpu
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 696a19055a437e3a96d131bbb5803708e64e0146
|
||||
Subproject commit 4fdbd5fe789da92fd5e23240c63ed5aa2cba5ed2
|
Loading…
Reference in New Issue