regs and we can step into!
This commit is contained in:
88
.clang-format
Normal file
88
.clang-format
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
---
|
||||||
|
AccessModifierOffset: '0'
|
||||||
|
AlignAfterOpenBracket: Align
|
||||||
|
AlignConsecutiveMacros: 'true'
|
||||||
|
AlignConsecutiveAssignments: 'true'
|
||||||
|
AlignConsecutiveDeclarations: 'false'
|
||||||
|
AlignEscapedNewlines: Left
|
||||||
|
AlignOperands: 'true'
|
||||||
|
AlignTrailingComments: 'true'
|
||||||
|
AllowAllArgumentsOnNextLine: 'true'
|
||||||
|
AllowAllConstructorInitializersOnNextLine: 'true'
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: 'true'
|
||||||
|
AllowShortBlocksOnASingleLine: 'true'
|
||||||
|
AllowShortCaseLabelsOnASingleLine: 'true'
|
||||||
|
AllowShortFunctionsOnASingleLine: All
|
||||||
|
AllowShortIfStatementsOnASingleLine: Never
|
||||||
|
AllowShortLambdasOnASingleLine: All
|
||||||
|
AllowShortLoopsOnASingleLine: 'false'
|
||||||
|
AlwaysBreakAfterReturnType: None
|
||||||
|
AlwaysBreakBeforeMultilineStrings: 'false'
|
||||||
|
AlwaysBreakTemplateDeclarations: 'Yes'
|
||||||
|
BinPackArguments: 'true'
|
||||||
|
BinPackParameters: 'true'
|
||||||
|
BreakBeforeBinaryOperators: NonAssignment
|
||||||
|
BreakBeforeBraces: Custom
|
||||||
|
BraceWrapping:
|
||||||
|
AfterCaseLabel: true
|
||||||
|
AfterClass: true
|
||||||
|
AfterEnum: true
|
||||||
|
AfterFunction: true
|
||||||
|
AfterNamespace: true
|
||||||
|
AfterStruct: true
|
||||||
|
AfterUnion: true
|
||||||
|
AfterExternBlock: true
|
||||||
|
BeforeCatch: false
|
||||||
|
BeforeElse: false
|
||||||
|
SplitEmptyFunction: false
|
||||||
|
SplitEmptyRecord: false
|
||||||
|
SplitEmptyNamespace: false
|
||||||
|
AfterControlStatement: true
|
||||||
|
# TODO: comes in clang-format 10
|
||||||
|
#BraceWrappingAfterControlStatementStyle: MultiLine
|
||||||
|
BreakBeforeTernaryOperators: 'true'
|
||||||
|
BreakConstructorInitializers: BeforeColon
|
||||||
|
BreakInheritanceList: BeforeColon
|
||||||
|
CompactNamespaces: 'false'
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: 'false'
|
||||||
|
ContinuationIndentWidth: '2'
|
||||||
|
Cpp11BracedListStyle: 'true'
|
||||||
|
DerivePointerAlignment: 'false'
|
||||||
|
DisableFormat: 'false'
|
||||||
|
ExperimentalAutoDetectBinPacking: 'false'
|
||||||
|
FixNamespaceComments: 'true'
|
||||||
|
IncludeBlocks: Preserve
|
||||||
|
IndentCaseLabels: 'true'
|
||||||
|
IndentPPDirectives: None
|
||||||
|
IndentWidth: '2'
|
||||||
|
IndentWrappedFunctionNames: 'true'
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: 'false'
|
||||||
|
Language: Cpp
|
||||||
|
MaxEmptyLinesToKeep: '1'
|
||||||
|
NamespaceIndentation: All
|
||||||
|
PointerAlignment: Right
|
||||||
|
ReflowComments: 'false'
|
||||||
|
SortIncludes: 'false'
|
||||||
|
SortUsingDeclarations: 'false'
|
||||||
|
SpaceAfterCStyleCast: 'false'
|
||||||
|
SpaceAfterLogicalNot: 'false'
|
||||||
|
SpaceAfterTemplateKeyword: 'false'
|
||||||
|
SpaceBeforeAssignmentOperators: 'true'
|
||||||
|
SpaceBeforeCpp11BracedList: 'false'
|
||||||
|
SpaceBeforeCtorInitializerColon: 'true'
|
||||||
|
SpaceBeforeInheritanceColon: 'true'
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
SpaceBeforeRangeBasedForLoopColon: 'true'
|
||||||
|
SpaceInEmptyParentheses: 'false'
|
||||||
|
SpacesBeforeTrailingComments: '2'
|
||||||
|
SpacesInAngles: false
|
||||||
|
SpacesInCStyleCastParentheses: 'false'
|
||||||
|
SpacesInContainerLiterals: 'false'
|
||||||
|
SpacesInParentheses: 'false'
|
||||||
|
SpacesInSquareBrackets: 'false'
|
||||||
|
Standard: Cpp11
|
||||||
|
TabWidth: '2'
|
||||||
|
UseTab: ForIndentation
|
||||||
|
CommentPragmas: '^\\.+'
|
||||||
|
|
||||||
|
...
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,4 +1,5 @@
|
|||||||
build/
|
build/
|
||||||
.vscode/
|
.vscode/
|
||||||
|
.cache/
|
||||||
|
|
||||||
imgui.ini
|
imgui.ini
|
||||||
|
|||||||
@@ -23,11 +23,12 @@ set(DBGUI_HEADERS
|
|||||||
src/backend/debug_backend.h
|
src/backend/debug_backend.h
|
||||||
src/backend/lldb/lldb_backend.h
|
src/backend/lldb/lldb_backend.h
|
||||||
src/frontend/frontend.h
|
src/frontend/frontend.h
|
||||||
|
src/frontend/target.h
|
||||||
src/frontend/window.h
|
src/frontend/window.h
|
||||||
src/msg.h)
|
src/msg.h)
|
||||||
|
|
||||||
set(DBGUI_SOURCES
|
set(DBGUI_SOURCES
|
||||||
src/main.cpp src/frontend/frontend.cpp
|
src/main.cpp src/frontend/frontend.cpp src/frontend/window.cpp
|
||||||
src/backend/backend.cpp
|
src/backend/backend.cpp
|
||||||
src/backend/lldb/lldb_backend.cpp
|
src/backend/lldb/lldb_backend.cpp
|
||||||
${IMGUI_SOURCES}
|
${IMGUI_SOURCES}
|
||||||
@@ -35,6 +36,5 @@ set(DBGUI_SOURCES
|
|||||||
|
|
||||||
add_executable(dbgui ${DBGUI_SOURCES})
|
add_executable(dbgui ${DBGUI_SOURCES})
|
||||||
|
|
||||||
|
|
||||||
target_link_libraries(dbgui ${OPENGL_LIBRARIES} glfw)
|
target_link_libraries(dbgui ${OPENGL_LIBRARIES} glfw)
|
||||||
target_link_libraries(dbgui lldb)
|
target_link_libraries(dbgui lldb)
|
||||||
1
known_bugs.txt
Normal file
1
known_bugs.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
# because of LLDB SB, currently impossible to step over/into on an assembly level if a frame has source information available
|
||||||
@@ -2,6 +2,26 @@
|
|||||||
|
|
||||||
using namespace dbgui::backend;
|
using namespace dbgui::backend;
|
||||||
|
|
||||||
Backend::~Backend() {
|
Backend::~Backend() {}
|
||||||
|
|
||||||
|
void Backend::send_state_change(TargetState new_state, StateChangeReason reason)
|
||||||
|
{
|
||||||
|
auto msg = std::make_unique<BackToFront::Msg>(
|
||||||
|
BackToFront::MsgType::state_change,
|
||||||
|
BackToFront::StateChange{new_state, reason});
|
||||||
|
this->send_msg(std::move(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Backend::send_proc_info(BackToFront::InitialProcessInfo &&info)
|
||||||
|
{
|
||||||
|
auto msg = std::make_unique<BackToFront::Msg>(
|
||||||
|
BackToFront::MsgType::initial_proc_info, std::move(info));
|
||||||
|
this->send_msg(std::move(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Backend::send_reg_change(BackToFront::RegsChanged &&info)
|
||||||
|
{
|
||||||
|
auto msg = std::make_unique<BackToFront::Msg>(
|
||||||
|
BackToFront::MsgType::regs_changed, std::move(info));
|
||||||
|
this->send_msg(std::move(msg));
|
||||||
}
|
}
|
||||||
@@ -8,39 +8,49 @@
|
|||||||
|
|
||||||
#include "msg.h"
|
#include "msg.h"
|
||||||
|
|
||||||
namespace dbgui::backend {
|
namespace dbgui::backend
|
||||||
|
{
|
||||||
|
|
||||||
enum class TargetState : uint8_t {
|
struct Backend : std::enable_shared_from_this<Backend>
|
||||||
stopped,
|
{
|
||||||
running,
|
virtual ~Backend();
|
||||||
paused,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Backend {
|
virtual void start() = 0;
|
||||||
virtual ~Backend();
|
|
||||||
|
|
||||||
virtual void start() = 0;
|
// returns whether the command was submitted
|
||||||
|
virtual bool step_into() = 0;
|
||||||
|
// TODO: allow these two to not be implemented or emulated by the base class or frontend?
|
||||||
|
virtual bool step_over() = 0;
|
||||||
|
virtual bool step_out() = 0;
|
||||||
|
|
||||||
auto retrieve_msg_for_frontend() -> std::optional<std::unique_ptr<BackToFrontMsg>> {
|
auto retrieve_msg_for_frontend()
|
||||||
auto g = std::lock_guard(_back_front_msg_mutex);
|
-> std::optional<std::unique_ptr<BackToFront::Msg>>
|
||||||
if (_back_front_msgs.empty()) {
|
{
|
||||||
return {};
|
auto g = std::lock_guard(_back_front_msg_mutex);
|
||||||
}
|
if (_back_front_msgs.empty())
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
auto result = std::move(_back_front_msgs[0]);
|
auto result = std::move(_back_front_msgs[0]);
|
||||||
_back_front_msgs.erase(_back_front_msgs.begin());
|
_back_front_msgs.erase(_back_front_msgs.begin());
|
||||||
// std::make_optional(std::move(result))
|
// std::make_optional(std::move(result))
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void send_msg(std::unique_ptr<BackToFrontMsg> msg) {
|
void send_msg(std::unique_ptr<BackToFront::Msg> msg)
|
||||||
auto g = std::lock_guard(_back_front_msg_mutex);
|
{
|
||||||
_back_front_msgs.emplace_back(std::move(msg));
|
auto g = std::lock_guard(_back_front_msg_mutex);
|
||||||
}
|
_back_front_msgs.emplace_back(std::move(msg));
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
void send_state_change(TargetState new_state, StateChangeReason reason);
|
||||||
std::mutex _back_front_msg_mutex;
|
void send_proc_info(BackToFront::InitialProcessInfo &&);
|
||||||
std::vector<std::unique_ptr<BackToFrontMsg>> _back_front_msgs;
|
void send_reg_change(BackToFront::RegsChanged &&);
|
||||||
};
|
|
||||||
}
|
private:
|
||||||
|
std::mutex _back_front_msg_mutex;
|
||||||
|
std::vector<std::unique_ptr<BackToFront::Msg>> _back_front_msgs;
|
||||||
|
};
|
||||||
|
} // namespace dbgui::backend
|
||||||
@@ -1,24 +1,555 @@
|
|||||||
#include "lldb_backend.h"
|
#include "lldb_backend.h"
|
||||||
#include <lldb/API/SBListener.h>
|
#include <lldb/API/SBListener.h>
|
||||||
|
#include <lldb/API/SBEvent.h>
|
||||||
|
#include <lldb/API/SBStream.h>
|
||||||
|
#include <lldb/API/SBThread.h>
|
||||||
|
#include <lldb/API/SBFrame.h>
|
||||||
|
#include <lldb/API/SBValueList.h>
|
||||||
|
#include <lldb/API/SBValue.h>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <array>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
using namespace dbgui::backend;
|
using namespace dbgui::backend;
|
||||||
|
|
||||||
LLDBBackend::~LLDBBackend() {
|
namespace
|
||||||
|
{}
|
||||||
|
|
||||||
|
LLDBBackend::~LLDBBackend() {}
|
||||||
|
|
||||||
|
LLDBBackend::LLDBBackend(std::string filename)
|
||||||
|
{
|
||||||
|
_filename = filename;
|
||||||
|
lldb::SBDebugger::Initialize();
|
||||||
|
_instance = lldb::SBDebugger::Create(false);
|
||||||
|
_target = _instance.CreateTarget(filename.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
LLDBBackend::LLDBBackend(std::string filename) {
|
void LLDBBackend::start()
|
||||||
_filename = filename;
|
{
|
||||||
_instance = lldb::SBDebugger::Create(false);
|
const char *argv[2] = {_filename.c_str(), nullptr};
|
||||||
_target = _instance.CreateTarget(filename.c_str());
|
const auto cwd = std::filesystem::current_path();
|
||||||
|
|
||||||
|
auto error = lldb::SBError();
|
||||||
|
auto listener = lldb::SBListener();
|
||||||
|
_process = _target.Launch(listener, argv, nullptr, nullptr, nullptr, nullptr,
|
||||||
|
cwd.c_str(), lldb::LaunchFlags::eLaunchFlagNone,
|
||||||
|
true, error);
|
||||||
|
|
||||||
|
_msg_thread = std::thread{[this]() {
|
||||||
|
auto ptr = this->shared_from_this();
|
||||||
|
static_cast<LLDBBackend *>(ptr.get())->run_msg_loop();
|
||||||
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLDBBackend::start() {
|
void LLDBBackend::run_msg_loop()
|
||||||
const char* argv[2] = {_filename.c_str(), nullptr};
|
{
|
||||||
const auto cwd = std::filesystem::current_path();
|
std::thread event_thread{[this]() { this->wait_for_debug_events(); }};
|
||||||
|
|
||||||
auto error = lldb::SBError();
|
while (true)
|
||||||
auto listener = lldb::SBListener();
|
{
|
||||||
_process = _target.Launch(listener, argv, nullptr, nullptr, nullptr, nullptr, cwd.c_str(), lldb::LaunchFlags::eLaunchFlagNone, true, error);
|
std::this_thread::sleep_for(std::chrono::milliseconds{1000});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLDBBackend::wait_for_debug_events()
|
||||||
|
{
|
||||||
|
using namespace lldb;
|
||||||
|
auto listener = _instance.GetListener();
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
auto event = lldb::SBEvent();
|
||||||
|
if (listener.WaitForEvent(0xFFFFFFFF, event))
|
||||||
|
{
|
||||||
|
std::lock_guard g{_data_lock};
|
||||||
|
printf("Got Event:\n");
|
||||||
|
auto stream = lldb::SBStream{};
|
||||||
|
if (event.GetDescription(stream))
|
||||||
|
{
|
||||||
|
printf(" Desc: %.*s\n", static_cast<int>(stream.GetSize()),
|
||||||
|
stream.GetData());
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
printf(" Failed to get stream\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SBProcess::EventIsProcessEvent(event))
|
||||||
|
{
|
||||||
|
// Check if state changed
|
||||||
|
auto state = SBProcess::GetStateFromEvent(event);
|
||||||
|
// TODO: get state from process if we can't get it from the event?
|
||||||
|
if (state != StateType::eStateInvalid)
|
||||||
|
{
|
||||||
|
this->handle_state_change(state);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
printf(" Unknown event source!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLDBBackend::handle_state_change(lldb::StateType state)
|
||||||
|
{
|
||||||
|
using namespace lldb;
|
||||||
|
if (_first_run)
|
||||||
|
{
|
||||||
|
_first_run = false;
|
||||||
|
// TODO: do initialization
|
||||||
|
// TODO: we should only do this when the process was stopped, no?
|
||||||
|
auto proc_info = BackToFront::InitialProcessInfo{};
|
||||||
|
auto reg_infos = std::vector<BackToFront::RegsChanged>{};
|
||||||
|
this->prepare_proc_info(proc_info, reg_infos);
|
||||||
|
this->dump_threads();
|
||||||
|
|
||||||
|
this->send_proc_info(std::move(proc_info));
|
||||||
|
for (size_t i = 0; i < reg_infos.size(); ++i)
|
||||||
|
{
|
||||||
|
this->send_reg_change(std::move(reg_infos[i]));
|
||||||
|
}
|
||||||
|
reg_infos.clear();
|
||||||
|
|
||||||
|
if (state == StateType::eStateStopped)
|
||||||
|
{
|
||||||
|
this->send_state_change(TargetState::paused,
|
||||||
|
StateChangeReason::initial_entry);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case eStateStopped:
|
||||||
|
this->check_reg_changes();
|
||||||
|
this->send_state_change(TargetState::paused, StateChangeReason::unknown);
|
||||||
|
break;
|
||||||
|
case eStateRunning:
|
||||||
|
case eStateStepping:
|
||||||
|
this->send_state_change(TargetState::running, StateChangeReason::unknown);
|
||||||
|
return;
|
||||||
|
default: printf("Unknown StateType %u encountered\n", state); exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<const char *, 24> x86_64_gpr = {
|
||||||
|
"rax", "rbx", "rcx", "rdx", "rdi", "rsi", "rbp", "rsp",
|
||||||
|
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
|
||||||
|
"rip", "rflags", "cs", "fs", "gs", "ss", "ds", "es"};
|
||||||
|
|
||||||
|
std::array<const char *, 14> x86_64_fp_special = {
|
||||||
|
"fctrl", "fstat", "ftag", "fop", "fiseg", "fioff", "fip",
|
||||||
|
"foseg", "fooff", "foseg", "fooff", "fdp", "mxcsr", "mxcsrmask"};
|
||||||
|
|
||||||
|
std::array<const char *, 8> x86_64_fp = {"st0", "st1", "st2", "st3",
|
||||||
|
"st4", "st5", "st6", "st7"};
|
||||||
|
|
||||||
|
std::array<const char *, 16> x86_64_xmm = {
|
||||||
|
"xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
|
||||||
|
"xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15"};
|
||||||
|
|
||||||
|
std::array<const char *, 16> x86_64_ymm = {
|
||||||
|
"ymm0", "ymm1", "ymm2", "ymm3", "ymm4", "ymm5", "ymm6", "ymm7",
|
||||||
|
"ymm8", "ymm9", "ymm10", "ymm11", "ymm12", "ymm13", "ymm14", "ymm15"};
|
||||||
|
|
||||||
|
std::array<const char *, 16> x86_64_zmm = {
|
||||||
|
"zmm0", "zmm1", "zmm2", "zmm3", "zmm4", "zmm5", "zmm6", "zmm7",
|
||||||
|
"zmm8", "zmm9", "zmm10", "zmm11", "zmm12", "zmm13", "zmm14", "zmm15"};
|
||||||
|
|
||||||
|
void LLDBBackend::prepare_proc_info(
|
||||||
|
BackToFront::InitialProcessInfo &info,
|
||||||
|
std::vector<BackToFront::RegsChanged> ®_infos)
|
||||||
|
{
|
||||||
|
using namespace lldb;
|
||||||
|
info.pid = _process->GetProcessID();
|
||||||
|
const auto target_triple =
|
||||||
|
std::string_view{_process->GetProcessInfo().GetTriple()};
|
||||||
|
printf("Target Triple: %s\n", target_triple.data());
|
||||||
|
if (target_triple.starts_with("x86_64-"))
|
||||||
|
{
|
||||||
|
info.arch = Arch::x86_64;
|
||||||
|
} /* else if (target_triple.starts_with("x86-")) {
|
||||||
|
// TODO: is this the right triple?
|
||||||
|
info.arch = Arch::x86;
|
||||||
|
}*/
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("Arch not supported!\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto frame = _process->GetThreadAtIndex(0).GetFrameAtIndex(0);
|
||||||
|
const auto regs = frame.GetRegisters();
|
||||||
|
/*const auto len = regs.GetSize();
|
||||||
|
for (size_t i = 0; i < len; ++i) {
|
||||||
|
auto reg_or_set = regs.GetValueAtIndex(i);
|
||||||
|
if (reg_or_set.GetValueType() == eValueTypeRegister) {
|
||||||
|
printf("Got register %s (%s) with value: %lX\n", reg_or_set.GetName(), reg_or_set.GetTypeName(), reg_or_set.GetValueAsUnsigned(0));
|
||||||
|
info.reg_names.emplace_back(reg_or_set.GetName());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
printf("Got register set %s\n", reg_or_set.GetName());
|
||||||
|
const auto is_gp_set = std::string_view{reg_or_set.GetName()} == "General Purpose Registers";
|
||||||
|
for (uint32_t child_idx = 0; child_idx < reg_or_set.GetNumChildren(); ++child_idx) {
|
||||||
|
auto reg = reg_or_set.GetChildAtIndex(child_idx);
|
||||||
|
printf("Got register %s (%s) with value: %lX\n", reg.GetName(), reg.GetTypeName(), reg.GetValueAsUnsigned(0));
|
||||||
|
if (is_gp_set && info.arch == Arch::x86_64 && reg.GetByteSize() < 8) {
|
||||||
|
// skip non-full regs here
|
||||||
|
printf(" -> Skipped\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
info.reg_names.emplace_back(reg.GetName());
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
if (info.arch == Arch::x86_64)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
auto reg_set = RegSet{};
|
||||||
|
auto info_set = BackToFront::InitialProcessInfo::RegSet{};
|
||||||
|
auto reg_info = BackToFront::RegsChanged{};
|
||||||
|
reg_info.set_idx = 0;
|
||||||
|
|
||||||
|
info_set.name = "GPR";
|
||||||
|
reg_set.set_name = "General Purpose Registers";
|
||||||
|
auto lldb_set = regs.GetFirstValueByName("General Purpose Registers");
|
||||||
|
for (size_t i = 0; i < x86_64_gpr.size(); ++i)
|
||||||
|
{
|
||||||
|
auto val = lldb_set.GetChildMemberWithName(x86_64_gpr[i]);
|
||||||
|
if (!val.IsValid())
|
||||||
|
{
|
||||||
|
printf("GPR %s not found!\n", x86_64_gpr[i]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
info_set.reg_names.push_back(x86_64_gpr[i]);
|
||||||
|
reg_set.names.push_back(x86_64_gpr[i]);
|
||||||
|
reg_set.values.push_back({});
|
||||||
|
|
||||||
|
auto data = val.GetData();
|
||||||
|
auto len = data.GetByteSize();
|
||||||
|
reg_set.values.back().resize(data.GetByteSize());
|
||||||
|
auto error = SBError{};
|
||||||
|
data.ReadRawData(error, 0, reg_set.values.back().data(), len);
|
||||||
|
|
||||||
|
reg_info.changes.push_back(
|
||||||
|
std::make_pair((uint16_t)i, reg_set.values.back()));
|
||||||
|
}
|
||||||
|
|
||||||
|
_reg_sets.push_back(reg_set);
|
||||||
|
info.reg_sets.push_back(info_set);
|
||||||
|
reg_infos.push_back(reg_info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLDBBackend::dump_threads()
|
||||||
|
{
|
||||||
|
using namespace lldb;
|
||||||
|
const auto thread_count = _process->GetNumThreads();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < thread_count; ++i)
|
||||||
|
{
|
||||||
|
auto thread = _process->GetThreadAtIndex(i);
|
||||||
|
|
||||||
|
auto stop_reason = thread.GetStopReason();
|
||||||
|
switch (stop_reason)
|
||||||
|
{
|
||||||
|
case eStopReasonSignal:
|
||||||
|
{
|
||||||
|
auto signal_num = thread.GetStopReasonDataAtIndex(0);
|
||||||
|
printf("Thread %lu: TID: %lu, Stop Reason: Signal: %lu\n", i,
|
||||||
|
thread.GetThreadID(), signal_num);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
printf("Thread %lu: TID: %lu, Stop Reason: %lu\n", i,
|
||||||
|
thread.GetThreadID(), stop_reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto sel = _process->GetSelectedThread();
|
||||||
|
if (sel.IsValid())
|
||||||
|
{
|
||||||
|
printf("Selected Thread: %lu\n", sel.GetThreadID());
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
printf("Selected thread not valid\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LLDBBackend::step_into()
|
||||||
|
{
|
||||||
|
std::lock_guard g{_data_lock};
|
||||||
|
if (!_process)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto thread = _process->GetSelectedThread();
|
||||||
|
if (!thread.IsValid())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: figure out what the runmodes mean
|
||||||
|
thread.StepInto();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LLDBBackend::step_over()
|
||||||
|
{
|
||||||
|
std::lock_guard g{_data_lock};
|
||||||
|
if (!_process)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto thread = _process->GetSelectedThread();
|
||||||
|
if (!thread.IsValid())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
thread.StepOver();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LLDBBackend::step_out()
|
||||||
|
{
|
||||||
|
std::lock_guard g{_data_lock};
|
||||||
|
if (!_process)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto thread = _process->GetSelectedThread();
|
||||||
|
if (!thread.IsValid())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
thread.StepOut();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LLDBBackend::check_reg_changes()
|
||||||
|
{
|
||||||
|
auto thread = _process->GetSelectedThread();
|
||||||
|
if (!thread.IsValid())
|
||||||
|
{
|
||||||
|
// TODO: try to find another thread to select
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: figure out if 0 is always the lowest one?
|
||||||
|
auto frame = thread.GetFrameAtIndex(0);
|
||||||
|
const auto regs = frame.GetRegisters();
|
||||||
|
uint8_t tmp_buf[64];
|
||||||
|
for (size_t set_idx = 0; set_idx < _reg_sets.size(); ++set_idx)
|
||||||
|
{
|
||||||
|
auto &set = _reg_sets[set_idx];
|
||||||
|
auto lldb_set = regs.GetFirstValueByName(set.set_name.c_str());
|
||||||
|
if (!lldb_set.IsValid())
|
||||||
|
{
|
||||||
|
printf("Failed to get set %s for frame\n", set.set_name.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto change_info = BackToFront::RegsChanged{.set_idx = set_idx};
|
||||||
|
|
||||||
|
assert(set.names.size() == set.values.size());
|
||||||
|
for (size_t i = 0; i < set.names.size(); ++i)
|
||||||
|
{
|
||||||
|
auto val = lldb_set.GetChildMemberWithName(set.names[i]);
|
||||||
|
if (!val.IsValid())
|
||||||
|
{
|
||||||
|
printf("Failed to get %s for frame\n", set.names[i]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto data = val.GetData();
|
||||||
|
const auto len = data.GetByteSize();
|
||||||
|
if (len != set.values[i].size())
|
||||||
|
{
|
||||||
|
set.values[i].resize(len);
|
||||||
|
// TODO: error handling
|
||||||
|
auto error = lldb::SBError{};
|
||||||
|
data.ReadRawData(error, 0, set.values[i].data(), len);
|
||||||
|
change_info.changes.emplace_back(std::make_pair(i, set.values[i]));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len > sizeof(tmp_buf))
|
||||||
|
{
|
||||||
|
printf("Register too large\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: error handling
|
||||||
|
auto error = lldb::SBError{};
|
||||||
|
data.ReadRawData(error, 0, tmp_buf, len);
|
||||||
|
if (std::memcmp(tmp_buf, set.values[i].data(), len))
|
||||||
|
{
|
||||||
|
std::copy(tmp_buf, tmp_buf + len, set.values[i].begin());
|
||||||
|
change_info.changes.emplace_back(std::make_pair(i, set.values[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!change_info.changes.empty())
|
||||||
|
{
|
||||||
|
this->send_reg_change(std::move(change_info));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Reg output for x64
|
||||||
|
Got register set General Purpose Registers
|
||||||
|
Got register rax (unsigned long) with value: 0
|
||||||
|
Got register rbx (unsigned long) with value: 0
|
||||||
|
Got register rcx (unsigned long) with value: 0
|
||||||
|
Got register rdx (unsigned long) with value: 0
|
||||||
|
Got register rdi (unsigned long) with value: 0
|
||||||
|
Got register rsi (unsigned long) with value: 0
|
||||||
|
Got register rbp (unsigned long) with value: 0
|
||||||
|
Got register rsp (unsigned long) with value: 7FFF30F924E0
|
||||||
|
Got register r8 (unsigned long) with value: 0
|
||||||
|
Got register r9 (unsigned long) with value: 0
|
||||||
|
Got register r10 (unsigned long) with value: 0
|
||||||
|
Got register r11 (unsigned long) with value: 0
|
||||||
|
Got register r12 (unsigned long) with value: 0
|
||||||
|
Got register r13 (unsigned long) with value: 0
|
||||||
|
Got register r14 (unsigned long) with value: 0
|
||||||
|
Got register r15 (unsigned long) with value: 0
|
||||||
|
Got register rip (unsigned long) with value: 7FD5ABB9B3B0
|
||||||
|
Got register rflags (unsigned long) with value: 200
|
||||||
|
Got register cs (unsigned long) with value: 33
|
||||||
|
Got register fs (unsigned long) with value: 0
|
||||||
|
Got register gs (unsigned long) with value: 0
|
||||||
|
Got register ss (unsigned long) with value: 2B
|
||||||
|
Got register ds (unsigned long) with value: 0
|
||||||
|
Got register es (unsigned long) with value: 0
|
||||||
|
Got register eax (unsigned int) with value: 0
|
||||||
|
Got register ebx (unsigned int) with value: 0
|
||||||
|
Got register ecx (unsigned int) with value: 0
|
||||||
|
Got register edx (unsigned int) with value: 0
|
||||||
|
Got register edi (unsigned int) with value: 0
|
||||||
|
Got register esi (unsigned int) with value: 0
|
||||||
|
Got register ebp (unsigned int) with value: 0
|
||||||
|
Got register esp (unsigned int) with value: 30F924E0
|
||||||
|
Got register r8d (unsigned int) with value: 0
|
||||||
|
Got register r9d (unsigned int) with value: 0
|
||||||
|
Got register r10d (unsigned int) with value: 0
|
||||||
|
Got register r11d (unsigned int) with value: 0
|
||||||
|
Got register r12d (unsigned int) with value: 0
|
||||||
|
Got register r13d (unsigned int) with value: 0
|
||||||
|
Got register r14d (unsigned int) with value: 0
|
||||||
|
Got register r15d (unsigned int) with value: 0
|
||||||
|
Got register ax (unsigned short) with value: 0
|
||||||
|
Got register bx (unsigned short) with value: 0
|
||||||
|
Got register cx (unsigned short) with value: 0
|
||||||
|
Got register dx (unsigned short) with value: 0
|
||||||
|
Got register di (unsigned short) with value: 0
|
||||||
|
Got register si (unsigned short) with value: 0
|
||||||
|
Got register bp (unsigned short) with value: 0
|
||||||
|
Got register sp (unsigned short) with value: 24E0
|
||||||
|
Got register r8w (unsigned short) with value: 0
|
||||||
|
Got register r9w (unsigned short) with value: 0
|
||||||
|
Got register r10w (unsigned short) with value: 0
|
||||||
|
Got register r11w (unsigned short) with value: 0
|
||||||
|
Got register r12w (unsigned short) with value: 0
|
||||||
|
Got register r13w (unsigned short) with value: 0
|
||||||
|
Got register r14w (unsigned short) with value: 0
|
||||||
|
Got register r15w (unsigned short) with value: 0
|
||||||
|
Got register ah (unsigned char) with value: 0
|
||||||
|
Got register bh (unsigned char) with value: 0
|
||||||
|
Got register ch (unsigned char) with value: 0
|
||||||
|
Got register dh (unsigned char) with value: 0
|
||||||
|
Got register al (unsigned char) with value: 0
|
||||||
|
Got register bl (unsigned char) with value: 0
|
||||||
|
Got register cl (unsigned char) with value: 0
|
||||||
|
Got register dl (unsigned char) with value: 0
|
||||||
|
Got register dil (unsigned char) with value: 0
|
||||||
|
Got register sil (unsigned char) with value: 0
|
||||||
|
Got register bpl (unsigned char) with value: 0
|
||||||
|
Got register spl (unsigned char) with value: E0
|
||||||
|
Got register r8l (unsigned char) with value: 0
|
||||||
|
Got register r9l (unsigned char) with value: 0
|
||||||
|
Got register r10l (unsigned char) with value: 0
|
||||||
|
Got register r11l (unsigned char) with value: 0
|
||||||
|
Got register r12l (unsigned char) with value: 0
|
||||||
|
Got register r13l (unsigned char) with value: 0
|
||||||
|
Got register r14l (unsigned char) with value: 0
|
||||||
|
Got register r15l (unsigned char) with value: 0
|
||||||
|
Got register set Floating Point Registers
|
||||||
|
Got register fctrl (unsigned short) with value: 37F
|
||||||
|
Got register fstat (unsigned short) with value: 0
|
||||||
|
Got register ftag (unsigned short) with value: FFFF
|
||||||
|
Got register fop (unsigned short) with value: 0
|
||||||
|
Got register fiseg (unsigned int) with value: 0
|
||||||
|
Got register fioff (unsigned int) with value: 0
|
||||||
|
Got register fip (unsigned long) with value: 0
|
||||||
|
Got register foseg (unsigned int) with value: 0
|
||||||
|
Got register fooff (unsigned int) with value: 0
|
||||||
|
Got register fdp (unsigned long) with value: 0
|
||||||
|
Got register mxcsr (unsigned int) with value: 1F80
|
||||||
|
Got register mxcsrmask (unsigned int) with value: 2FFFF
|
||||||
|
Got register st0 (unsigned char __attribute__((ext_vector_type(10)))) with value: 0
|
||||||
|
Got register st1 (unsigned char __attribute__((ext_vector_type(10)))) with value: 0
|
||||||
|
Got register st2 (unsigned char __attribute__((ext_vector_type(10)))) with value: 0
|
||||||
|
Got register st3 (unsigned char __attribute__((ext_vector_type(10)))) with value: 0
|
||||||
|
Got register st4 (unsigned char __attribute__((ext_vector_type(10)))) with value: 0
|
||||||
|
Got register st5 (unsigned char __attribute__((ext_vector_type(10)))) with value: 0
|
||||||
|
Got register st6 (unsigned char __attribute__((ext_vector_type(10)))) with value: 0
|
||||||
|
Got register st7 (unsigned char __attribute__((ext_vector_type(10)))) with value: 0
|
||||||
|
Got register mm0 (unsigned long) with value: 0
|
||||||
|
Got register mm1 (unsigned long) with value: 0
|
||||||
|
Got register mm2 (unsigned long) with value: 0
|
||||||
|
Got register mm3 (unsigned long) with value: 0
|
||||||
|
Got register mm4 (unsigned long) with value: 0
|
||||||
|
Got register mm5 (unsigned long) with value: 0
|
||||||
|
Got register mm6 (unsigned long) with value: 0
|
||||||
|
Got register mm7 (unsigned long) with value: 0
|
||||||
|
Got register xmm0 (unsigned char __attribute__((ext_vector_type(16)))) with value: 0
|
||||||
|
Got register xmm1 (unsigned char __attribute__((ext_vector_type(16)))) with value: 0
|
||||||
|
Got register xmm2 (unsigned char __attribute__((ext_vector_type(16)))) with value: 0
|
||||||
|
Got register xmm3 (unsigned char __attribute__((ext_vector_type(16)))) with value: 0
|
||||||
|
Got register xmm4 (unsigned char __attribute__((ext_vector_type(16)))) with value: 0
|
||||||
|
Got register xmm5 (unsigned char __attribute__((ext_vector_type(16)))) with value: 0
|
||||||
|
Got register xmm6 (unsigned char __attribute__((ext_vector_type(16)))) with value: 0
|
||||||
|
Got register xmm7 (unsigned char __attribute__((ext_vector_type(16)))) with value: 0
|
||||||
|
Got register xmm8 (unsigned char __attribute__((ext_vector_type(16)))) with value: 0
|
||||||
|
Got register xmm9 (unsigned char __attribute__((ext_vector_type(16)))) with value: 0
|
||||||
|
Got register xmm10 (unsigned char __attribute__((ext_vector_type(16)))) with value: 0
|
||||||
|
Got register xmm11 (unsigned char __attribute__((ext_vector_type(16)))) with value: 0
|
||||||
|
Got register xmm12 (unsigned char __attribute__((ext_vector_type(16)))) with value: 0
|
||||||
|
Got register xmm13 (unsigned char __attribute__((ext_vector_type(16)))) with value: 0
|
||||||
|
Got register xmm14 (unsigned char __attribute__((ext_vector_type(16)))) with value: 0
|
||||||
|
Got register xmm15 (unsigned char __attribute__((ext_vector_type(16)))) with value: 0
|
||||||
|
Got register set Advanced Vector Extensions
|
||||||
|
Got register ymm0 (unsigned char __attribute__((ext_vector_type(32)))) with value: 0
|
||||||
|
Got register ymm1 (unsigned char __attribute__((ext_vector_type(32)))) with value: 0
|
||||||
|
Got register ymm2 (unsigned char __attribute__((ext_vector_type(32)))) with value: 0
|
||||||
|
Got register ymm3 (unsigned char __attribute__((ext_vector_type(32)))) with value: 0
|
||||||
|
Got register ymm4 (unsigned char __attribute__((ext_vector_type(32)))) with value: 0
|
||||||
|
Got register ymm5 (unsigned char __attribute__((ext_vector_type(32)))) with value: 0
|
||||||
|
Got register ymm6 (unsigned char __attribute__((ext_vector_type(32)))) with value: 0
|
||||||
|
Got register ymm7 (unsigned char __attribute__((ext_vector_type(32)))) with value: 0
|
||||||
|
Got register ymm8 (unsigned char __attribute__((ext_vector_type(32)))) with value: 0
|
||||||
|
Got register ymm9 (unsigned char __attribute__((ext_vector_type(32)))) with value: 0
|
||||||
|
Got register ymm10 (unsigned char __attribute__((ext_vector_type(32)))) with value: 0
|
||||||
|
Got register ymm11 (unsigned char __attribute__((ext_vector_type(32)))) with value: 0
|
||||||
|
Got register ymm12 (unsigned char __attribute__((ext_vector_type(32)))) with value: 0
|
||||||
|
Got register ymm13 (unsigned char __attribute__((ext_vector_type(32)))) with value: 0
|
||||||
|
Got register ymm14 (unsigned char __attribute__((ext_vector_type(32)))) with value: 0
|
||||||
|
Got register ymm15 (unsigned char __attribute__((ext_vector_type(32)))) with value: 0
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
using namespace dbgui;
|
||||||
}
|
}
|
||||||
@@ -5,19 +5,55 @@
|
|||||||
#include <lldb/API/SBTarget.h>
|
#include <lldb/API/SBTarget.h>
|
||||||
#include <lldb/API/SBProcess.h>
|
#include <lldb/API/SBProcess.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
namespace dbgui::backend {
|
namespace dbgui::backend
|
||||||
struct LLDBBackend : Backend {
|
{
|
||||||
// TODO: source_init_file: false
|
struct LLDBBackend : Backend
|
||||||
LLDBBackend(std::string filename);
|
{
|
||||||
virtual ~LLDBBackend();
|
struct RegSet
|
||||||
|
{
|
||||||
|
std::string set_name;
|
||||||
|
// map from debugger reg idx to frontend reg idx
|
||||||
|
// std::vector<uint16_t> idx_map;
|
||||||
|
std::vector<const char *> names;
|
||||||
|
// TODO: make this data structure not garbage
|
||||||
|
std::vector<std::vector<uint8_t>> values;
|
||||||
|
};
|
||||||
|
|
||||||
void start() override;
|
// TODO: source_init_file: false
|
||||||
|
LLDBBackend(std::string filename);
|
||||||
|
virtual ~LLDBBackend();
|
||||||
|
|
||||||
private:
|
void start() override;
|
||||||
std::string _filename;
|
|
||||||
lldb::SBDebugger _instance;
|
bool step_into() override;
|
||||||
lldb::SBTarget _target;
|
bool step_over() override;
|
||||||
std::optional<lldb::SBProcess> _process;
|
bool step_out() override;
|
||||||
};
|
|
||||||
}
|
private:
|
||||||
|
void run_msg_loop();
|
||||||
|
void wait_for_debug_events();
|
||||||
|
|
||||||
|
void handle_state_change(lldb::StateType);
|
||||||
|
|
||||||
|
void prepare_proc_info(BackToFront::InitialProcessInfo &,
|
||||||
|
std::vector<BackToFront::RegsChanged> &);
|
||||||
|
void dump_threads();
|
||||||
|
void check_reg_changes();
|
||||||
|
|
||||||
|
std::string _filename;
|
||||||
|
lldb::SBDebugger _instance;
|
||||||
|
|
||||||
|
std::mutex _data_lock;
|
||||||
|
lldb::SBTarget _target;
|
||||||
|
std::optional<lldb::SBProcess> _process;
|
||||||
|
std::thread _msg_thread;
|
||||||
|
|
||||||
|
// process state
|
||||||
|
bool _first_run = true;
|
||||||
|
TargetState _state = TargetState::stopped;
|
||||||
|
std::vector<RegSet> _reg_sets = {};
|
||||||
|
};
|
||||||
|
} // namespace dbgui::backend
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "frontend.h"
|
#include "frontend.h"
|
||||||
|
#include "backend/lldb/lldb_backend.h"
|
||||||
|
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
#include "imgui_internal.h"
|
#include "imgui_internal.h"
|
||||||
@@ -6,134 +7,287 @@
|
|||||||
using namespace dbgui;
|
using namespace dbgui;
|
||||||
using namespace dbgui::frontend;
|
using namespace dbgui::frontend;
|
||||||
|
|
||||||
void Frontend::run_frame() {
|
Target::Target(std::string filename)
|
||||||
this->draw_open_popup();
|
{
|
||||||
this->draw_header();
|
state = TargetState::startup;
|
||||||
|
this->filename = filename;
|
||||||
// main window
|
id = 0;
|
||||||
float height = ImGui::GetFrameHeight();
|
backend = std::make_shared<backend::LLDBBackend>(this->filename.c_str());
|
||||||
|
|
||||||
const auto vp_size = ImGui::GetMainViewport()->Size;
|
|
||||||
const auto win_pos = ImVec2{0, height*2};
|
|
||||||
const auto win_size = ImVec2{vp_size.x, vp_size.y - height*3};
|
|
||||||
|
|
||||||
const auto win_flags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoMove |
|
|
||||||
ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoSavedSettings;
|
|
||||||
|
|
||||||
ImGui::SetNextWindowPos(win_pos, ImGuiCond_Always);
|
|
||||||
ImGui::SetNextWindowSize(win_size, ImGuiCond_Always);
|
|
||||||
|
|
||||||
if (ImGui::Begin("Test", nullptr, win_flags)) {
|
|
||||||
_dock_id = ImGui::GetID("MyDockSpace");
|
|
||||||
ImGui::DockSpace(_dock_id, ImVec2{0, 0}, ImGuiDockNodeFlags_PassthruCentralNode);
|
|
||||||
|
|
||||||
ImGui::End();
|
|
||||||
}
|
|
||||||
|
|
||||||
this->draw_status();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Frontend::draw_header() {
|
void Frontend::run_frame()
|
||||||
ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_MenuBar;
|
{
|
||||||
float height = ImGui::GetFrameHeight();
|
this->handle_msgs();
|
||||||
|
this->draw_open_popup();
|
||||||
|
this->draw_header();
|
||||||
|
|
||||||
if (ImGui::BeginMainMenuBar()) {
|
// main window
|
||||||
if (ImGui::BeginMenu("File")) {
|
float height = ImGui::GetFrameHeight();
|
||||||
if (ImGui::MenuItem("Open")) {
|
|
||||||
_draw_open_popup = true;
|
|
||||||
ImGui::OpenPopup(_open_popup_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::EndMenu();
|
const auto vp_size = ImGui::GetMainViewport()->Size;
|
||||||
}
|
const auto win_pos = ImVec2{0, height * 2};
|
||||||
ImGui::EndMainMenuBar();
|
const auto win_size = ImVec2{vp_size.x, vp_size.y - height * 3};
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui::BeginViewportSideBar("##SecondaryMenuBar", NULL, ImGuiDir_Up, height, window_flags)) {
|
const auto win_flags =
|
||||||
if (ImGui::BeginMenuBar()) {
|
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoNav
|
||||||
const auto orig_cursor_x = ImGui::GetCursorPosX();
|
| ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollWithMouse
|
||||||
if (_target) {
|
| ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoSavedSettings;
|
||||||
switch (_target->state) {
|
|
||||||
using enum backend::TargetState;
|
|
||||||
|
|
||||||
case stopped:
|
ImGui::SetNextWindowPos(win_pos, ImGuiCond_Always);
|
||||||
ImGui::Button("Run");
|
ImGui::SetNextWindowSize(win_size, ImGuiCond_Always);
|
||||||
break;
|
|
||||||
case paused:
|
|
||||||
ImGui::Button("Continue");
|
|
||||||
break;
|
|
||||||
case running:
|
|
||||||
ImGui::Button("Pause");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ImGui::BeginDisabled();
|
|
||||||
ImGui::Button("Continue");
|
|
||||||
ImGui::EndDisabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: this depends on font-size, we just want to make sure the the other buttons don't flicker even if the state is changed
|
if (ImGui::Begin("Test", nullptr, win_flags))
|
||||||
ImGui::SetCursorPosX(orig_cursor_x + 71.f);
|
{
|
||||||
|
this->dock_id = ImGui::GetID("MyDockSpace");
|
||||||
|
ImGui::DockSpace(this->dock_id, ImVec2{0, 0},
|
||||||
|
ImGuiDockNodeFlags_PassthruCentralNode);
|
||||||
|
|
||||||
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical, 2.f);
|
for (auto &window : _windows)
|
||||||
|
{
|
||||||
|
window.draw(*this);
|
||||||
|
}
|
||||||
|
|
||||||
const auto is_paused = _target && _target->state == backend::TargetState::paused;
|
ImGui::End();
|
||||||
const auto is_running = _target && _target->state == backend::TargetState::running;
|
}
|
||||||
|
|
||||||
if (!is_paused) {
|
this->draw_status();
|
||||||
ImGui::BeginDisabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::Button("Step Over");
|
|
||||||
ImGui::Button("Step Into");
|
|
||||||
ImGui::Button("Step Out");
|
|
||||||
|
|
||||||
if (!is_paused) {
|
|
||||||
ImGui::EndDisabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical, 2.f);
|
|
||||||
|
|
||||||
if (!is_paused && !is_running) {
|
|
||||||
ImGui::BeginDisabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::Button("Stop");
|
|
||||||
ImGui::Button("Restart");
|
|
||||||
|
|
||||||
if (!is_paused && !is_running) {
|
|
||||||
ImGui::EndDisabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::EndMenuBar();
|
|
||||||
}
|
|
||||||
ImGui::End();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Frontend::draw_status() {
|
void Frontend::draw_header()
|
||||||
ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_MenuBar;
|
{
|
||||||
float height = ImGui::GetFrameHeight();
|
ImGuiWindowFlags window_flags =
|
||||||
|
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings
|
||||||
|
| ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_MenuBar;
|
||||||
|
float height = ImGui::GetFrameHeight();
|
||||||
|
|
||||||
if (ImGui::BeginViewportSideBar("##MainStatusBar", NULL, ImGuiDir_Down, height, window_flags)) {
|
if (ImGui::BeginMainMenuBar())
|
||||||
if (ImGui::BeginMenuBar()) {
|
{
|
||||||
ImGui::Text("Happy status bar");
|
if (ImGui::BeginMenu("File"))
|
||||||
ImGui::EndMenuBar();
|
{
|
||||||
}
|
if (ImGui::MenuItem("Open"))
|
||||||
ImGui::End();
|
{
|
||||||
}
|
_draw_open_popup = true;
|
||||||
|
ImGui::OpenPopup(_open_popup_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::BeginMenu("Window"))
|
||||||
|
{
|
||||||
|
if (ImGui::MenuItem("Registers"))
|
||||||
|
{
|
||||||
|
_windows.push_back(Window::create_regs(this->window_id++));
|
||||||
|
}
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
ImGui::EndMainMenuBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::BeginViewportSideBar("##SecondaryMenuBar", NULL, ImGuiDir_Up,
|
||||||
|
height, window_flags))
|
||||||
|
{
|
||||||
|
if (ImGui::BeginMenuBar())
|
||||||
|
{
|
||||||
|
const auto orig_cursor_x = ImGui::GetCursorPosX();
|
||||||
|
if (this->target)
|
||||||
|
{
|
||||||
|
switch (this->target->state)
|
||||||
|
{
|
||||||
|
using enum TargetState;
|
||||||
|
|
||||||
|
case startup:
|
||||||
|
ImGui::BeginDisabled();
|
||||||
|
// buttons shouldn't flicker that way
|
||||||
|
ImGui::Button("Continue");
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case stopped: ImGui::Button("Run"); break;
|
||||||
|
case paused: ImGui::Button("Continue"); break;
|
||||||
|
case running: ImGui::Button("Pause"); break;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
ImGui::BeginDisabled();
|
||||||
|
ImGui::Button("Continue");
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: this depends on font-size, we just want to make sure the the other buttons don't flicker even if the state is changed
|
||||||
|
ImGui::SetCursorPosX(orig_cursor_x + 71.f);
|
||||||
|
|
||||||
|
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical, 2.f);
|
||||||
|
|
||||||
|
const auto is_paused =
|
||||||
|
this->target && this->target->state == TargetState::paused;
|
||||||
|
const auto is_running =
|
||||||
|
this->target && this->target->state == TargetState::running;
|
||||||
|
|
||||||
|
if (!is_paused)
|
||||||
|
{
|
||||||
|
ImGui::BeginDisabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button("Step Over")) {}
|
||||||
|
if (ImGui::Button("Step Into"))
|
||||||
|
{
|
||||||
|
if (this->target->backend->step_into())
|
||||||
|
{
|
||||||
|
// TODO: already disable the UI into running mode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::Button("Step Out");
|
||||||
|
|
||||||
|
if (!is_paused)
|
||||||
|
{
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SeparatorEx(ImGuiSeparatorFlags_Vertical, 2.f);
|
||||||
|
|
||||||
|
if (!is_paused && !is_running)
|
||||||
|
{
|
||||||
|
ImGui::BeginDisabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Button("Stop");
|
||||||
|
ImGui::Button("Restart");
|
||||||
|
|
||||||
|
if (!is_paused && !is_running)
|
||||||
|
{
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndMenuBar();
|
||||||
|
}
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Frontend::draw_open_popup() {
|
void Frontend::draw_status()
|
||||||
if (!_open_popup_id) {
|
{
|
||||||
_open_popup_id = ImGui::GetID("Open Executable##OpenPopup");
|
ImGuiWindowFlags window_flags =
|
||||||
}
|
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings
|
||||||
if (ImGui::BeginPopupModal("Open Executable##OpenPopup", &_draw_open_popup, ImGuiWindowFlags_AlwaysAutoResize)) {
|
| ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_MenuBar;
|
||||||
ImGui::InputText("File Path", _open_popup_name_buf.data(), _open_popup_name_buf.size());
|
float height = ImGui::GetFrameHeight();
|
||||||
|
|
||||||
if (ImGui::Button("Start")) {
|
if (ImGui::BeginViewportSideBar("##MainStatusBar", NULL, ImGuiDir_Down,
|
||||||
|
height, window_flags))
|
||||||
}
|
{
|
||||||
ImGui::EndPopup();
|
if (ImGui::BeginMenuBar())
|
||||||
}
|
{
|
||||||
|
ImGui::Text("Happy status bar");
|
||||||
|
ImGui::EndMenuBar();
|
||||||
|
}
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Frontend::draw_open_popup()
|
||||||
|
{
|
||||||
|
if (!_open_popup_id)
|
||||||
|
{
|
||||||
|
_open_popup_id = ImGui::GetID("Open Executable##OpenPopup");
|
||||||
|
}
|
||||||
|
if (ImGui::BeginPopupModal("Open Executable##OpenPopup", &_draw_open_popup,
|
||||||
|
ImGuiWindowFlags_AlwaysAutoResize))
|
||||||
|
{
|
||||||
|
ImGui::InputText("File Path", _open_popup_name_buf.data(),
|
||||||
|
_open_popup_name_buf.size());
|
||||||
|
|
||||||
|
if (ImGui::Button("Start"))
|
||||||
|
{
|
||||||
|
this->target = Target{_open_popup_name_buf.data()};
|
||||||
|
this->target->backend->start();
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Frontend::handle_msgs()
|
||||||
|
{
|
||||||
|
if (!this->target)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
auto opt = this->target->backend->retrieve_msg_for_frontend();
|
||||||
|
if (!opt)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto *msg = opt->get();
|
||||||
|
|
||||||
|
printf("Got msg %u\n", msg->type);
|
||||||
|
|
||||||
|
switch (msg->type)
|
||||||
|
{
|
||||||
|
using enum BackToFront::MsgType;
|
||||||
|
|
||||||
|
case state_change:
|
||||||
|
{
|
||||||
|
const auto &info = std::get<BackToFront::StateChange>(msg->data);
|
||||||
|
this->handle_state_change(info);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ip_change:
|
||||||
|
// TODO
|
||||||
|
break;
|
||||||
|
case initial_proc_info:
|
||||||
|
this->handle_proc_info(
|
||||||
|
std::move(std::get<BackToFront::InitialProcessInfo>(msg->data)));
|
||||||
|
break;
|
||||||
|
case regs_changed:
|
||||||
|
this->handle_reg_change(std::get<BackToFront::RegsChanged>(msg->data));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Frontend::handle_state_change(const BackToFront::StateChange &info)
|
||||||
|
{
|
||||||
|
this->target->state = info.new_state;
|
||||||
|
|
||||||
|
// TODO: display in status bar
|
||||||
|
printf("State changed to %u because %u\n", info.new_state, info.reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Frontend::handle_proc_info(BackToFront::InitialProcessInfo &&info)
|
||||||
|
{
|
||||||
|
this->target->arch = info.arch;
|
||||||
|
this->target->id = info.pid;
|
||||||
|
|
||||||
|
auto &sets = this->target->reg_sets;
|
||||||
|
printf("RegSet Size: %lu\n", info.reg_sets.size());
|
||||||
|
for (size_t set_idx = 0; set_idx < info.reg_sets.size(); ++set_idx)
|
||||||
|
{
|
||||||
|
sets.emplace_back();
|
||||||
|
auto &set = sets.back();
|
||||||
|
printf("Got set %s\n", info.reg_sets[set_idx].name.c_str());
|
||||||
|
set.name = std::move(info.reg_sets[set_idx].name);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < info.reg_sets[set_idx].reg_names.size(); ++i)
|
||||||
|
{
|
||||||
|
set.regs.emplace_back();
|
||||||
|
printf(" Got reg %s\n", info.reg_sets[set_idx].reg_names[i].c_str());
|
||||||
|
set.regs.back().name = std::move(info.reg_sets[set_idx].reg_names[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Frontend::handle_reg_change(const BackToFront::RegsChanged &info)
|
||||||
|
{
|
||||||
|
auto &set = this->target->reg_sets[info.set_idx];
|
||||||
|
for (const auto &[idx, val] : info.changes)
|
||||||
|
{
|
||||||
|
auto ® = set.regs[idx];
|
||||||
|
reg.bytes.resize(val.size());
|
||||||
|
|
||||||
|
// TODO: opt for uint64?
|
||||||
|
std::copy(val.begin(), val.end(), reg.bytes.begin());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -5,39 +5,47 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
|
#include "frontend/target.h"
|
||||||
#include "backend/debug_backend.h"
|
#include "backend/debug_backend.h"
|
||||||
|
#include "frontend/window.h"
|
||||||
|
|
||||||
namespace dbgui::frontend {
|
namespace dbgui::frontend
|
||||||
|
{
|
||||||
|
|
||||||
struct Target {
|
struct Frontend
|
||||||
backend::TargetState state = backend::TargetState::stopped;
|
{
|
||||||
std::string filename;
|
Frontend()
|
||||||
uint64_t id;
|
{
|
||||||
|
_open_popup_name_buf.resize(512);
|
||||||
|
const char *str = "/home/klee/projects/dbgui/tmp/main";
|
||||||
|
std::memcpy(_open_popup_name_buf.data(), str, strlen(str) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<backend::Backend> backend = nullptr;
|
void run_frame();
|
||||||
};
|
|
||||||
|
|
||||||
struct Frontend {
|
std::optional<Target> target = {};
|
||||||
Frontend() {
|
ImGuiID dock_id = 0;
|
||||||
_open_popup_name_buf.resize(512);
|
uint64_t window_id = 0;
|
||||||
}
|
|
||||||
|
|
||||||
void run_frame();
|
private:
|
||||||
|
void draw_header();
|
||||||
|
void draw_open_popup();
|
||||||
|
void draw_status();
|
||||||
|
|
||||||
private:
|
void handle_msgs();
|
||||||
void draw_header();
|
void handle_state_change(const BackToFront::StateChange &);
|
||||||
void draw_open_popup();
|
void handle_proc_info(BackToFront::InitialProcessInfo &&);
|
||||||
void draw_status();
|
void handle_reg_change(const BackToFront::RegsChanged &);
|
||||||
|
|
||||||
bool _draw_second = false;
|
bool _draw_second = false;
|
||||||
ImGuiID _dock_id = 0;
|
|
||||||
|
|
||||||
bool _draw_open_popup = false;
|
bool _draw_open_popup = false;
|
||||||
ImGuiID _open_popup_id = 0;
|
ImGuiID _open_popup_id = 0;
|
||||||
std::vector<char> _open_popup_name_buf = {};
|
std::vector<char> _open_popup_name_buf = {};
|
||||||
|
|
||||||
std::optional<Target> _target = {};
|
std::vector<Window> _windows = {};
|
||||||
};
|
};
|
||||||
}
|
} // namespace dbgui::frontend
|
||||||
39
src/frontend/target.h
Normal file
39
src/frontend/target.h
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "backend/debug_backend.h"
|
||||||
|
|
||||||
|
namespace dbgui::frontend
|
||||||
|
{
|
||||||
|
struct Target
|
||||||
|
{
|
||||||
|
struct Reg
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
// TODO: handle registers as sets of 64bit?
|
||||||
|
std::vector<uint8_t> bytes;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RegSet
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
std::vector<Reg> regs;
|
||||||
|
};
|
||||||
|
|
||||||
|
Target(std::string filename);
|
||||||
|
|
||||||
|
TargetState state = TargetState::stopped;
|
||||||
|
std::string filename;
|
||||||
|
uint64_t id;
|
||||||
|
Arch arch;
|
||||||
|
|
||||||
|
std::vector<RegSet> reg_sets;
|
||||||
|
|
||||||
|
std::shared_ptr<backend::Backend> backend = nullptr;
|
||||||
|
};
|
||||||
|
} // namespace dbgui::frontend
|
||||||
101
src/frontend/window.cpp
Normal file
101
src/frontend/window.cpp
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
#include "window.h"
|
||||||
|
#include "frontend.h"
|
||||||
|
|
||||||
|
using namespace dbgui;
|
||||||
|
using namespace dbgui::frontend;
|
||||||
|
|
||||||
|
void Window::draw(const Frontend &frontend)
|
||||||
|
{
|
||||||
|
switch (this->type)
|
||||||
|
{
|
||||||
|
using enum WindowType;
|
||||||
|
|
||||||
|
case regs: std::get<RegWindow>(this->data).draw(frontend); break;
|
||||||
|
default: printf("Unhandled window draw: %u\n", this->type); exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Window Window::create_regs(size_t id)
|
||||||
|
{
|
||||||
|
auto id_str = std::string{"Registers##"};
|
||||||
|
id_str.append(std::to_string(id));
|
||||||
|
|
||||||
|
return Window{.type = WindowType::regs,
|
||||||
|
.data = RegWindow{.id = id_str, .open = true}};
|
||||||
|
}
|
||||||
|
|
||||||
|
void RegWindow::draw(const Frontend &frontend)
|
||||||
|
{
|
||||||
|
ImGui::SetNextWindowDockID(frontend.dock_id, ImGuiCond_Appearing);
|
||||||
|
if (!ImGui::Begin(this->id.c_str()))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!frontend.target)
|
||||||
|
{
|
||||||
|
ImGui::End();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::BeginTable("table", 1,
|
||||||
|
ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg))
|
||||||
|
{
|
||||||
|
const auto &sets = frontend.target->reg_sets;
|
||||||
|
for (size_t set_idx = 0; set_idx < sets.size(); ++set_idx)
|
||||||
|
{
|
||||||
|
const auto &set = sets[set_idx];
|
||||||
|
ImGui::TableNextRow();
|
||||||
|
ImGui::TableSetColumnIndex(0);
|
||||||
|
ImGui::Text(set.name.c_str());
|
||||||
|
|
||||||
|
char buf[20];
|
||||||
|
std::snprintf(buf, sizeof(buf), "nested_%lu", set_idx);
|
||||||
|
if (ImGui::BeginTable(buf, 2,
|
||||||
|
ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg))
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < set.regs.size(); ++i)
|
||||||
|
{
|
||||||
|
const auto ® = set.regs[i];
|
||||||
|
ImGui::TableNextRow();
|
||||||
|
ImGui::TableSetColumnIndex(0);
|
||||||
|
ImGui::Text(reg.name.c_str());
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
|
||||||
|
if (reg.bytes.size() == 0)
|
||||||
|
{
|
||||||
|
ImGui::Text("<unk>");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (reg.bytes.size())
|
||||||
|
{
|
||||||
|
case 1: std::snprintf(buf, sizeof(buf), "%X", reg.bytes[0]); break;
|
||||||
|
case 2:
|
||||||
|
std::snprintf(
|
||||||
|
buf, sizeof(buf), "%X",
|
||||||
|
*reinterpret_cast<const uint16_t *>(reg.bytes.data()));
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
std::snprintf(
|
||||||
|
buf, sizeof(buf), "%X",
|
||||||
|
*reinterpret_cast<const uint32_t *>(reg.bytes.data()));
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
std::snprintf(
|
||||||
|
buf, sizeof(buf), "%lX",
|
||||||
|
*reinterpret_cast<const uint64_t *>(reg.bytes.data()));
|
||||||
|
break;
|
||||||
|
default: std::snprintf(buf, sizeof(buf), "<val too large>"); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Text(buf);
|
||||||
|
}
|
||||||
|
ImGui::EndTable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
@@ -3,29 +3,40 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <variant>
|
||||||
|
#include "msg.h"
|
||||||
|
#include "frontend/target.h"
|
||||||
|
|
||||||
namespace dbgui::frontend {
|
namespace dbgui::frontend
|
||||||
enum class WindowType : uint8_t {
|
{
|
||||||
regs,
|
struct Frontend;
|
||||||
source,
|
|
||||||
memory,
|
|
||||||
variables,
|
|
||||||
stack,
|
|
||||||
threads,
|
|
||||||
disassembly,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct RegWindow {
|
enum class WindowType : uint8_t
|
||||||
enum class RegValType : uint8_t {
|
{
|
||||||
flag,
|
regs,
|
||||||
u64,
|
source,
|
||||||
u128,
|
memory,
|
||||||
u256,
|
variables,
|
||||||
u512,
|
stack,
|
||||||
};
|
threads,
|
||||||
|
disassembly,
|
||||||
|
};
|
||||||
|
|
||||||
struct Reg {
|
struct RegWindow
|
||||||
std::string name;
|
{
|
||||||
|
enum class RegValType : uint8_t
|
||||||
|
{
|
||||||
|
flag,
|
||||||
|
u64,
|
||||||
|
u128,
|
||||||
|
u256,
|
||||||
|
u512,
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: store last_drawn val and which regs we should draw
|
||||||
|
/*struct Reg {
|
||||||
|
uint16_t set_idx;
|
||||||
|
uint16_t reg_idx;
|
||||||
RegValType type;
|
RegValType type;
|
||||||
union {
|
union {
|
||||||
bool bval;
|
bool bval;
|
||||||
@@ -36,12 +47,21 @@ namespace dbgui::frontend {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<Reg> regs;
|
std::vector<Reg> regs;*/
|
||||||
|
|
||||||
void draw();
|
void draw(const Frontend &);
|
||||||
};
|
|
||||||
|
|
||||||
struct Window {
|
std::string id;
|
||||||
WindowType type;
|
bool open;
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
struct Window
|
||||||
|
{
|
||||||
|
WindowType type;
|
||||||
|
std::variant<std::monostate, RegWindow> data;
|
||||||
|
|
||||||
|
void draw(const Frontend &);
|
||||||
|
|
||||||
|
static Window create_regs(size_t window_id);
|
||||||
|
};
|
||||||
|
} // namespace dbgui::frontend
|
||||||
219
src/main.cpp
219
src/main.cpp
@@ -10,136 +10,143 @@
|
|||||||
#include "backends/imgui_impl_opengl3.h"
|
#include "backends/imgui_impl_opengl3.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#define GL_SILENCE_DEPRECATION
|
#define GL_SILENCE_DEPRECATION
|
||||||
#include <GLFW/glfw3.h> // Will drag system OpenGL headers
|
#include <GLFW/glfw3.h> // Will drag system OpenGL headers
|
||||||
|
|
||||||
#include "frontend/frontend.h"
|
#include "frontend/frontend.h"
|
||||||
|
|
||||||
static void glfw_error_callback(int error, const char* description)
|
static void glfw_error_callback(int error, const char *description)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "GLFW Error %d: %s\n", error, description);
|
fprintf(stderr, "GLFW Error %d: %s\n", error, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main code
|
// Main code
|
||||||
int main(int, char**)
|
int main(int, char **)
|
||||||
{
|
{
|
||||||
glfwSetErrorCallback(glfw_error_callback);
|
glfwSetErrorCallback(glfw_error_callback);
|
||||||
if (!glfwInit())
|
if (!glfwInit())
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
// Decide GL+GLSL versions
|
// Decide GL+GLSL versions
|
||||||
// GL 3.0 + GLSL 130
|
// GL 3.0 + GLSL 130
|
||||||
const char* glsl_version = "#version 130";
|
const char *glsl_version = "#version 130";
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
|
||||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
|
||||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 3.0+ only
|
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 3.0+ only
|
||||||
|
|
||||||
// Create window with graphics context
|
// Create window with graphics context
|
||||||
GLFWwindow* window = glfwCreateWindow(1280, 720, "Dear ImGui GLFW+OpenGL3 example", nullptr, nullptr);
|
GLFWwindow *window = glfwCreateWindow(
|
||||||
if (window == nullptr)
|
1280, 720, "Dear ImGui GLFW+OpenGL3 example", nullptr, nullptr);
|
||||||
return 1;
|
if (window == nullptr)
|
||||||
glfwMakeContextCurrent(window);
|
return 1;
|
||||||
glfwSwapInterval(1); // Enable vsync
|
glfwMakeContextCurrent(window);
|
||||||
|
glfwSwapInterval(1); // Enable vsync
|
||||||
|
|
||||||
// Setup Dear ImGui context
|
// Setup Dear ImGui context
|
||||||
IMGUI_CHECKVERSION();
|
IMGUI_CHECKVERSION();
|
||||||
ImGui::CreateContext();
|
ImGui::CreateContext();
|
||||||
ImGuiIO& io = ImGui::GetIO(); (void)io;
|
ImGuiIO &io = ImGui::GetIO();
|
||||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
|
(void)io;
|
||||||
io.ConfigFlags &= ~ImGuiConfigFlags_NavEnableGamepad; // Disable Gamepad Controls
|
io.ConfigFlags |=
|
||||||
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
|
ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
|
||||||
io.ConfigFlags &= ~ImGuiConfigFlags_ViewportsEnable; // Disable Multi-Viewport / Platform Windows
|
io.ConfigFlags &=
|
||||||
//io.ConfigViewportsNoAutoMerge = true;
|
~ImGuiConfigFlags_NavEnableGamepad; // Disable Gamepad Controls
|
||||||
//io.ConfigViewportsNoTaskBarIcon = true;
|
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
|
||||||
|
io.ConfigFlags &=
|
||||||
|
~ImGuiConfigFlags_ViewportsEnable; // Disable Multi-Viewport / Platform Windows
|
||||||
|
//io.ConfigViewportsNoAutoMerge = true;
|
||||||
|
//io.ConfigViewportsNoTaskBarIcon = true;
|
||||||
|
|
||||||
// Setup Dear ImGui style
|
// Setup Dear ImGui style
|
||||||
ImGui::StyleColorsDark();
|
ImGui::StyleColorsDark();
|
||||||
//ImGui::StyleColorsLight();
|
//ImGui::StyleColorsLight();
|
||||||
|
|
||||||
// When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones.
|
// When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones.
|
||||||
ImGuiStyle& style = ImGui::GetStyle();
|
ImGuiStyle &style = ImGui::GetStyle();
|
||||||
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
|
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
|
||||||
{
|
{
|
||||||
style.WindowRounding = 0.0f;
|
style.WindowRounding = 0.0f;
|
||||||
style.Colors[ImGuiCol_WindowBg].w = 1.0f;
|
style.Colors[ImGuiCol_WindowBg].w = 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup Platform/Renderer backends
|
// Setup Platform/Renderer backends
|
||||||
ImGui_ImplGlfw_InitForOpenGL(window, true);
|
ImGui_ImplGlfw_InitForOpenGL(window, true);
|
||||||
ImGui_ImplOpenGL3_Init(glsl_version);
|
ImGui_ImplOpenGL3_Init(glsl_version);
|
||||||
|
|
||||||
// Load Fonts
|
// Load Fonts
|
||||||
// - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
|
// - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
|
||||||
// - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple.
|
// - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple.
|
||||||
// - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit).
|
// - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit).
|
||||||
// - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call.
|
// - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call.
|
||||||
// - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering.
|
// - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering.
|
||||||
// - Read 'docs/FONTS.md' for more instructions and details.
|
// - Read 'docs/FONTS.md' for more instructions and details.
|
||||||
// - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
|
// - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
|
||||||
// - Our Emscripten build process allows embedding fonts to be accessible at runtime from the "fonts/" folder. See Makefile.emscripten for details.
|
// - Our Emscripten build process allows embedding fonts to be accessible at runtime from the "fonts/" folder. See Makefile.emscripten for details.
|
||||||
//io.Fonts->AddFontDefault();
|
//io.Fonts->AddFontDefault();
|
||||||
//io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f);
|
//io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f);
|
||||||
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f);
|
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f);
|
||||||
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f);
|
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f);
|
||||||
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f);
|
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f);
|
||||||
//ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese());
|
//ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese());
|
||||||
//IM_ASSERT(font != nullptr);
|
//IM_ASSERT(font != nullptr);
|
||||||
io.Fonts->AddFontFromMemoryCompressedBase85TTF(roboto_medium_compressed_data_base85, 16.0f);
|
io.Fonts->AddFontFromMemoryCompressedBase85TTF(
|
||||||
|
roboto_medium_compressed_data_base85, 16.0f);
|
||||||
|
|
||||||
// Our state
|
// Our state
|
||||||
bool show_demo_window = true;
|
bool show_demo_window = true;
|
||||||
bool show_another_window = false;
|
bool show_another_window = false;
|
||||||
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
|
ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
|
||||||
|
|
||||||
auto frontend = dbgui::frontend::Frontend{};
|
auto frontend = dbgui::frontend::Frontend{};
|
||||||
|
|
||||||
// Main loop
|
// Main loop
|
||||||
while (!glfwWindowShouldClose(window))
|
while (!glfwWindowShouldClose(window))
|
||||||
{
|
{
|
||||||
// Poll and handle events (inputs, window resize, etc.)
|
// Poll and handle events (inputs, window resize, etc.)
|
||||||
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
|
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
|
||||||
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
|
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
|
||||||
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
|
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
|
||||||
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
|
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
|
||||||
glfwPollEvents();
|
glfwPollEvents();
|
||||||
|
|
||||||
// Start the Dear ImGui frame
|
// Start the Dear ImGui frame
|
||||||
ImGui_ImplOpenGL3_NewFrame();
|
ImGui_ImplOpenGL3_NewFrame();
|
||||||
ImGui_ImplGlfw_NewFrame();
|
ImGui_ImplGlfw_NewFrame();
|
||||||
ImGui::NewFrame();
|
ImGui::NewFrame();
|
||||||
|
|
||||||
frontend.run_frame();
|
frontend.run_frame();
|
||||||
|
|
||||||
// Rendering
|
// Rendering
|
||||||
ImGui::Render();
|
ImGui::Render();
|
||||||
int display_w, display_h;
|
int display_w, display_h;
|
||||||
glfwGetFramebufferSize(window, &display_w, &display_h);
|
glfwGetFramebufferSize(window, &display_w, &display_h);
|
||||||
glViewport(0, 0, display_w, display_h);
|
glViewport(0, 0, display_w, display_h);
|
||||||
glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w);
|
glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w,
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
clear_color.z * clear_color.w, clear_color.w);
|
||||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||||
|
|
||||||
// Update and Render additional Platform Windows
|
// Update and Render additional Platform Windows
|
||||||
// (Platform functions may change the current OpenGL context, so we save/restore it to make it easier to paste this code elsewhere.
|
// (Platform functions may change the current OpenGL context, so we save/restore it to make it easier to paste this code elsewhere.
|
||||||
// For this specific demo app we could also call glfwMakeContextCurrent(window) directly)
|
// For this specific demo app we could also call glfwMakeContextCurrent(window) directly)
|
||||||
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
|
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
|
||||||
{
|
{
|
||||||
GLFWwindow* backup_current_context = glfwGetCurrentContext();
|
GLFWwindow *backup_current_context = glfwGetCurrentContext();
|
||||||
ImGui::UpdatePlatformWindows();
|
ImGui::UpdatePlatformWindows();
|
||||||
ImGui::RenderPlatformWindowsDefault();
|
ImGui::RenderPlatformWindowsDefault();
|
||||||
glfwMakeContextCurrent(backup_current_context);
|
glfwMakeContextCurrent(backup_current_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
glfwSwapBuffers(window);
|
glfwSwapBuffers(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
ImGui_ImplOpenGL3_Shutdown();
|
ImGui_ImplOpenGL3_Shutdown();
|
||||||
ImGui_ImplGlfw_Shutdown();
|
ImGui_ImplGlfw_Shutdown();
|
||||||
ImGui::DestroyContext();
|
ImGui::DestroyContext();
|
||||||
|
|
||||||
glfwDestroyWindow(window);
|
glfwDestroyWindow(window);
|
||||||
glfwTerminate();
|
glfwTerminate();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
99
src/msg.h
99
src/msg.h
@@ -3,24 +3,93 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace dbgui {
|
namespace dbgui
|
||||||
struct FrontToBackMsg {
|
{
|
||||||
|
enum class TargetState : uint8_t
|
||||||
|
{
|
||||||
|
startup,
|
||||||
|
stopped,
|
||||||
|
running,
|
||||||
|
paused,
|
||||||
|
};
|
||||||
|
|
||||||
};
|
enum class StateChangeReason : uint8_t
|
||||||
|
{
|
||||||
|
unknown,
|
||||||
|
initial_entry,
|
||||||
|
breakpoint,
|
||||||
|
exception,
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
|
||||||
struct BackToFrontMsg {
|
enum class Arch : uint8_t
|
||||||
enum class Type : uint8_t {
|
{
|
||||||
log
|
unknown,
|
||||||
};
|
x86_64,
|
||||||
|
// x86
|
||||||
|
};
|
||||||
|
|
||||||
struct LogMsg {
|
namespace FrontToBack
|
||||||
// TODO: level
|
{
|
||||||
|
enum class MsgType : uint8_t
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
std::string msg;
|
struct Msg
|
||||||
};
|
{
|
||||||
|
MsgType type;
|
||||||
|
std::variant<std::monostate> data;
|
||||||
|
};
|
||||||
|
} // namespace FrontToBack
|
||||||
|
|
||||||
Type type;
|
namespace BackToFront
|
||||||
std::variant<std::monostate, LogMsg> data;
|
{
|
||||||
};
|
enum class MsgType : uint8_t
|
||||||
}
|
{
|
||||||
|
state_change,
|
||||||
|
ip_change,
|
||||||
|
initial_proc_info,
|
||||||
|
regs_changed,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StateChange
|
||||||
|
{
|
||||||
|
TargetState new_state;
|
||||||
|
StateChangeReason reason;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IPChange
|
||||||
|
{
|
||||||
|
uint64_t new_ip;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct InitialProcessInfo
|
||||||
|
{
|
||||||
|
struct RegSet
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
std::vector<std::string> reg_names;
|
||||||
|
};
|
||||||
|
|
||||||
|
uint64_t pid;
|
||||||
|
Arch arch;
|
||||||
|
std::vector<RegSet> reg_sets;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RegsChanged
|
||||||
|
{
|
||||||
|
size_t set_idx;
|
||||||
|
std::vector<std::pair<uint16_t, std::vector<uint8_t>>> changes;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Msg
|
||||||
|
{
|
||||||
|
MsgType type;
|
||||||
|
std::variant<std::monostate, StateChange, IPChange, InitialProcessInfo,
|
||||||
|
RegsChanged>
|
||||||
|
data;
|
||||||
|
};
|
||||||
|
} // namespace BackToFront
|
||||||
|
} // namespace dbgui
|
||||||
5
tmp/main.c
Normal file
5
tmp/main.c
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user