Adding base callstack control.

This commit is contained in:
Ben Vanik 2015-08-16 14:28:50 -07:00
parent ae183f918f
commit 2b012f37bf
6 changed files with 272 additions and 34 deletions

View File

@ -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_

View File

@ -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

View File

@ -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_

View File

@ -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

View File

@ -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