#pragma once #include #include #include #include #include #include "backend/debug_backend.h" namespace dbgui::frontend { struct Target { struct Reg { std::string name; // TODO: handle registers as sets of 64bit? std::vector bytes; }; struct RegSet { std::string name; std::vector regs; }; struct Thread { uint64_t id; uint64_t ip; uint64_t stop_extra; ThreadStopReason stop_reason; std::string name; std::string cur_display_fn; }; struct Frame { uint64_t ip; std::string display_name; }; // TODO: need a way for the backend to report where the breakpoint was actually placed // (might have been moved) or if it could be placed at all // iow let the backend resolve the address + possible file location for the breakpoint // and then display it // this should be needed anyways when you want to add expression-based breakpoints, e.g. 'main' struct Breakpoint { struct FileLoc { std::string name; uint32_t line; }; bool removed = false; std::variant data; bool at_addr(uint64_t addr) const { return this->data.index() == 0 && std::get(this->data) == addr; } bool at_file_loc(std::string_view file, uint32_t line) const { if (this->data.index() != 1) { return false; } const auto &loc = std::get(this->data); if (loc.line == line && loc.name == file) { return true; } return false; } }; struct ExprCacheEntry { std::string expr_path; size_t id; size_t id2; bool used_this_frame = false; bool is_cstr = false; }; Target(std::string filename, bool stop_at_entry); std::optional data_idx_for_src_id(size_t id) { for (auto &[entry_id, idx] : this->src_id_to_data_idx) { if (entry_id == id) { return idx; } } return {}; } data::result::Node *data_node_for_src_id(size_t id) { auto idx = this->data_idx_for_src_id(id); if (!idx) { return nullptr; } if (*idx >= data_res_nodes.size() || !data_res_nodes[*idx]) { return nullptr; } return &*data_res_nodes[*idx]; } size_t find_or_create_expr_path(std::string &&path, bool is_cstr = false) { for (auto &entry : expr_cache) { if (entry.expr_path == path) { entry.used_this_frame = true; return entry.is_cstr ? entry.id2 : entry.id; } } using namespace data::source; auto id = this->id++; size_t cstr_id = -1; this->backend->add_data_node( Node{.id = id, .type = Node::Type::source, .data = Source{.type = Source::Type::variable, .data = Source::Variable{.expr_path = path}}}); if (is_cstr) { cstr_id = this->id++; this->backend->add_data_node(Node{.id = cstr_id, .type = Node::Type::read_cstr, .data = ReadAsCStr{.src_id = id}}); } expr_cache.push_back(ExprCacheEntry{.expr_path = std::move(path), .id = id, .id2 = cstr_id, .used_this_frame = true, .is_cstr = is_cstr}); return id; } TargetState state = TargetState::stopped; std::string filename; uint64_t id; size_t data_node_id = 0; Arch arch; bool step_instruction = false; uint16_t selected_thread = 0; uint16_t selected_frame = 0; std::vector reg_sets; std::vector> threads; std::vector> frames; std::vector breakpoints; std::vector> src_id_to_data_idx; std::vector> data_res_nodes; std::vector types; std::vector expr_cache; std::shared_ptr backend = nullptr; }; } // namespace dbgui::frontend