commit d4bf6731a30662382bfe1a4de0c92b3dec61c305 Author: T0b1 Date: Wed May 31 23:14:05 2023 +0200 WIP diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5cc22a4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +build/ +.vscode/ + +imgui.ini diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..cafbe31 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "deps/imgui"] + path = deps/imgui + url = https://github.com/ocornut/imgui.git diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..8140703 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.18) + +project(dbgui VERSION 0.1 LANGUAGES C CXX) + +set(CMAKE_CXX_STANDARD 23) + +find_package(OpenGL REQUIRED) + +# TODO: build glfw ourselved so that we can link it statically +find_package(glfw3 REQUIRED) + +include_directories(${LLVM_INCLUDE_DIRS}) +include_directories(${OPENGL_INCLUDE_DIR}) +include_directories(deps/imgui/) +include_directories(src/) + +set(IMGUI_SOURCES + deps/imgui/backends/imgui_impl_glfw.cpp deps/imgui/backends/imgui_impl_opengl3.cpp + deps/imgui/imgui.cpp deps/imgui/imgui_draw.cpp deps/imgui/imgui_widgets.cpp deps/imgui/imgui_tables.cpp + deps/imgui/imgui_demo.cpp) + +set(DBGUI_HEADERS + src/backend/debug_backend.h + src/backend/lldb/lldb_backend.h + src/frontend/frontend.h + src/frontend/window.h + src/msg.h) + +set(DBGUI_SOURCES + src/main.cpp src/frontend/frontend.cpp + src/backend/backend.cpp + src/backend/lldb/lldb_backend.cpp + ${IMGUI_SOURCES} + ${DBGUI_HEADERS}) + +add_executable(dbgui ${DBGUI_SOURCES}) + + +target_link_libraries(dbgui ${OPENGL_LIBRARIES} glfw) +target_link_libraries(dbgui lldb) \ No newline at end of file diff --git a/deps/imgui b/deps/imgui new file mode 160000 index 0000000..3418d50 --- /dev/null +++ b/deps/imgui @@ -0,0 +1 @@ +Subproject commit 3418d509490c0f1dd4bc6c3ba2a9312e8efc0ff9 diff --git a/src/backend/backend.cpp b/src/backend/backend.cpp new file mode 100644 index 0000000..9cb9517 --- /dev/null +++ b/src/backend/backend.cpp @@ -0,0 +1,7 @@ +#include "debug_backend.h" + +using namespace dbgui::backend; + +Backend::~Backend() { + +} \ No newline at end of file diff --git a/src/backend/debug_backend.h b/src/backend/debug_backend.h new file mode 100644 index 0000000..b6ace70 --- /dev/null +++ b/src/backend/debug_backend.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "msg.h" + +namespace dbgui::backend { + + enum class TargetState : uint8_t { + stopped, + running, + paused, + }; + + struct Backend { + virtual ~Backend(); + + virtual void start() = 0; + + auto retrieve_msg_for_frontend() -> std::optional> { + auto g = std::lock_guard(_back_front_msg_mutex); + if (_back_front_msgs.empty()) { + return {}; + } + + auto result = std::move(_back_front_msgs[0]); + _back_front_msgs.erase(_back_front_msgs.begin()); + // std::make_optional(std::move(result)) + return result; + } + + protected: + void send_msg(std::unique_ptr msg) { + auto g = std::lock_guard(_back_front_msg_mutex); + _back_front_msgs.emplace_back(std::move(msg)); + } + + private: + std::mutex _back_front_msg_mutex; + std::vector> _back_front_msgs; + }; +} \ No newline at end of file diff --git a/src/backend/lldb/lldb_backend.cpp b/src/backend/lldb/lldb_backend.cpp new file mode 100644 index 0000000..f8f3831 --- /dev/null +++ b/src/backend/lldb/lldb_backend.cpp @@ -0,0 +1,24 @@ +#include "lldb_backend.h" +#include +#include + +using namespace dbgui::backend; + +LLDBBackend::~LLDBBackend() { + +} + +LLDBBackend::LLDBBackend(std::string filename) { + _filename = filename; + _instance = lldb::SBDebugger::Create(false); + _target = _instance.CreateTarget(filename.c_str()); +} + +void LLDBBackend::start() { + const char* argv[2] = {_filename.c_str(), nullptr}; + 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); +} \ No newline at end of file diff --git a/src/backend/lldb/lldb_backend.h b/src/backend/lldb/lldb_backend.h new file mode 100644 index 0000000..f007537 --- /dev/null +++ b/src/backend/lldb/lldb_backend.h @@ -0,0 +1,23 @@ +#pragma once + +#include "backend/debug_backend.h" +#include +#include +#include +#include + +namespace dbgui::backend { + struct LLDBBackend : Backend { + // TODO: source_init_file: false + LLDBBackend(std::string filename); + virtual ~LLDBBackend(); + + void start() override; + + private: + std::string _filename; + lldb::SBDebugger _instance; + lldb::SBTarget _target; + std::optional _process; + }; +} \ No newline at end of file diff --git a/src/frontend/frontend.cpp b/src/frontend/frontend.cpp new file mode 100644 index 0000000..920934c --- /dev/null +++ b/src/frontend/frontend.cpp @@ -0,0 +1,139 @@ +#include "frontend.h" + +#include "imgui.h" +#include "imgui_internal.h" + +using namespace dbgui; +using namespace dbgui::frontend; + +void Frontend::run_frame() { + this->draw_open_popup(); + this->draw_header(); + + // main window + float height = ImGui::GetFrameHeight(); + + 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() { + ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_MenuBar; + float height = ImGui::GetFrameHeight(); + + if (ImGui::BeginMainMenuBar()) { + if (ImGui::BeginMenu("File")) { + if (ImGui::MenuItem("Open")) { + _draw_open_popup = true; + ImGui::OpenPopup(_open_popup_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 (_target) { + switch (_target->state) { + using enum backend::TargetState; + + 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 = _target && _target->state == backend::TargetState::paused; + const auto is_running = _target && _target->state == backend::TargetState::running; + + if (!is_paused) { + 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() { + 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::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")) { + + } + ImGui::EndPopup(); + } +} \ No newline at end of file diff --git a/src/frontend/frontend.h b/src/frontend/frontend.h new file mode 100644 index 0000000..20e53e6 --- /dev/null +++ b/src/frontend/frontend.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "imgui.h" +#include "backend/debug_backend.h" + +namespace dbgui::frontend { + + struct Target { + backend::TargetState state = backend::TargetState::stopped; + std::string filename; + uint64_t id; + + std::unique_ptr backend = nullptr; + }; + + struct Frontend { + Frontend() { + _open_popup_name_buf.resize(512); + } + + void run_frame(); + + private: + void draw_header(); + void draw_open_popup(); + void draw_status(); + + bool _draw_second = false; + ImGuiID _dock_id = 0; + + bool _draw_open_popup = false; + ImGuiID _open_popup_id = 0; + std::vector _open_popup_name_buf = {}; + + std::optional _target = {}; + }; +} \ No newline at end of file diff --git a/src/frontend/window.h b/src/frontend/window.h new file mode 100644 index 0000000..006a81e --- /dev/null +++ b/src/frontend/window.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include + +namespace dbgui::frontend { + enum class WindowType : uint8_t { + regs, + source, + memory, + variables, + stack, + threads, + disassembly, + }; + + struct RegWindow { + enum class RegValType : uint8_t { + flag, + u64, + u128, + u256, + u512, + }; + + struct Reg { + std::string name; + RegValType type; + union { + bool bval; + uint64_t u64val; + uint64_t u128_val[2]; + uint64_t u256_val[4]; + uint64_t u512_val[8]; + }; + }; + + std::vector regs; + + void draw(); + }; + + struct Window { + WindowType type; + }; +} \ No newline at end of file diff --git a/src/imgui_user.h b/src/imgui_user.h new file mode 100644 index 0000000..e69de29 diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..d4b3ae1 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,145 @@ +// Dear ImGui: standalone example application for GLFW + OpenGL 3, using programmable pipeline +// (GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan/Metal graphics context creation, etc.) +// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. +// Read online: https://github.com/ocornut/imgui/tree/master/docs + +#define IMGUI_INCLUDE_IMGUI_USER_H +#include "imgui.h" +#include "roboto_medium.h" +#include "backends/imgui_impl_glfw.h" +#include "backends/imgui_impl_opengl3.h" +#include +#define GL_SILENCE_DEPRECATION +#include // Will drag system OpenGL headers + +#include "frontend/frontend.h" + +static void glfw_error_callback(int error, const char* description) +{ + fprintf(stderr, "GLFW Error %d: %s\n", error, description); +} + +// Main code +int main(int, char**) +{ + glfwSetErrorCallback(glfw_error_callback); + if (!glfwInit()) + return 1; + + // Decide GL+GLSL versions + // GL 3.0 + GLSL 130 + const char* glsl_version = "#version 130"; + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 3.0+ only + + // Create window with graphics context + GLFWwindow* window = glfwCreateWindow(1280, 720, "Dear ImGui GLFW+OpenGL3 example", nullptr, nullptr); + if (window == nullptr) + return 1; + glfwMakeContextCurrent(window); + glfwSwapInterval(1); // Enable vsync + + // Setup Dear ImGui context + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); (void)io; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags &= ~ImGuiConfigFlags_NavEnableGamepad; // Disable Gamepad Controls + 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 + ImGui::StyleColorsDark(); + //ImGui::StyleColorsLight(); + + // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones. + ImGuiStyle& style = ImGui::GetStyle(); + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + style.WindowRounding = 0.0f; + style.Colors[ImGuiCol_WindowBg].w = 1.0f; + } + + // Setup Platform/Renderer backends + ImGui_ImplGlfw_InitForOpenGL(window, true); + ImGui_ImplOpenGL3_Init(glsl_version); + + // 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. + // - 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). + // - 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. + // - 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 \\ ! + // - 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->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.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/Cousine-Regular.ttf", 15.0f); + //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese()); + //IM_ASSERT(font != nullptr); + io.Fonts->AddFontFromMemoryCompressedBase85TTF(roboto_medium_compressed_data_base85, 16.0f); + + // Our state + bool show_demo_window = true; + bool show_another_window = false; + ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); + + auto frontend = dbgui::frontend::Frontend{}; + + // Main loop + while (!glfwWindowShouldClose(window)) + { + // 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. + // - 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. + // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. + glfwPollEvents(); + + // Start the Dear ImGui frame + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); + + frontend.run_frame(); + + // Rendering + ImGui::Render(); + int display_w, display_h; + glfwGetFramebufferSize(window, &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); + glClear(GL_COLOR_BUFFER_BIT); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + + // 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. + // For this specific demo app we could also call glfwMakeContextCurrent(window) directly) + if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) + { + GLFWwindow* backup_current_context = glfwGetCurrentContext(); + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); + glfwMakeContextCurrent(backup_current_context); + } + + glfwSwapBuffers(window); + } + + // Cleanup + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplGlfw_Shutdown(); + ImGui::DestroyContext(); + + glfwDestroyWindow(window); + glfwTerminate(); + + return 0; +} diff --git a/src/msg.h b/src/msg.h new file mode 100644 index 0000000..cbdab0c --- /dev/null +++ b/src/msg.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include + +namespace dbgui { + struct FrontToBackMsg { + + }; + + struct BackToFrontMsg { + enum class Type : uint8_t { + log + }; + + struct LogMsg { + // TODO: level + + std::string msg; + }; + + Type type; + std::variant data; + }; +} \ No newline at end of file