Files
dbgui/src/backend/lldb/lldb_backend.h
2024-05-06 04:10:13 +02:00

202 lines
5.8 KiB
C++

#pragma once
#include "backend/debug_backend.h"
#include <lldb/API/SBDebugger.h>
#include <lldb/API/SBTarget.h>
#include <lldb/API/SBProcess.h>
#include <string>
#include <thread>
#include <mutex>
#include <utility>
#include "util/dag.h"
namespace dbgui::backend
{
struct LLDBBackend : Backend
{
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;
};
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;
size_t lldb_idx;
std::string display_name;
};
struct Breakpoint
{
size_t id;
lldb::break_id_t lldb_id;
};
struct CachedDataRes
{
struct DisasmInfo
{
uint64_t range_start, range_end;
std::vector<uint8_t> bytes;
};
data::result::Node node;
// bool child_node;
bool no_delete_on_src_delete = false;
size_t src_id; // invalid = size_t::MAX
// std::variant<std::monostate, DisasmInfo> extra_info;
};
struct Local
{
lldb::SBValue lldb_val;
std::optional<uint16_t> cached_node;
};
struct VarCacheEntry
{
// for globals/statics compare location + expr path (bc unions)
// for others compare location + expr path + symctx (without line entry)
// TODO: this will repeatedly retrieve cyclic values
// probably bad so maybe detect this some other way mby without expr paths?
// but then we need to cache types somehow...
lldb::SBValue lldb_val;
lldb::SBSymbolContext sym_ctx;
std::string expr_path;
// NOTE: when used is set to true, this means that the var was
// diffed this step already and will no longer be so careful with changing this
bool used;
bool
global_or_static; // need to only compare compilation unit and module
bool address_of_or_pointer;
std::optional<data::result::NodeIdx> cached_node;
};
// TODO: source_init_file: false
LLDBBackend(std::string filename, bool stop_at_entry);
virtual ~LLDBBackend();
void start() override;
bool step_into(bool source_step) override;
bool step_over(bool source_step) override;
bool step_out() override;
void cont() override;
void pause() override;
void add_data_node(const data::source::Node &) override;
void remove_data_node(uint64_t id) override;
void add_breakpoint(uint64_t addr, size_t id) override;
bool add_breakpoint(const char *file, uint32_t line, size_t id) override;
void remove_breakpoint(size_t id) override;
void select_thread(uint16_t idx);
void select_frame(uint16_t idx);
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 dump_variables();
void check_reg_changes();
void check_thread_changes();
void check_frame_changes();
void check_data_changes();
// TODO: func to clear locals if they are no longer requested
// node_id, did_change
// std::pair<uint16_t, bool> build_locals();
data::type_info::TypeID build_type_from_lldb_type(lldb::SBType &);
bool is_type_equal(data::type_info::TypeID, lldb::SBType &);
std::optional<data::result::NodeIdx>
build_nodes_for_var(lldb::SBValue &val,
std::vector<data::result::Node> &to_send,
bool address_of_or_pointer);
data::result::NodeIdx
build_nodes_for_var_uncached(lldb::SBValue &,
std::vector<data::result::Node> &to_send,
bool address_of_or_pointer);
void build_nodes_for_var_cached(lldb::SBValue &,
data::result::NodeIdx cache_idx,
std::vector<data::result::Node> &to_send,
bool address_of_or_pointer);
void clear_unused_vars_from_cache();
void delete_node_and_childs(uint16_t);
data::result::NodeIdx get_free_data_node();
std::optional<std::pair<data::result::NodeIdx, size_t>>
calc_data_res(const data::source::Node &,
std::vector<data::result::Node> &data_res);
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;
bool _stop_at_entry = false;
lldb::break_id_t _hidden_main_bp;
TargetState _state = TargetState::stopped;
std::vector<RegSet> _reg_sets = {};
std::vector<Thread> _threads = {};
std::vector<Frame> _frames = {};
uint16_t _selected_frame = 0;
uint16_t _selected_thread = 0;
util::DAG _data_dag = {};
std::vector<data::source::Node> _data_nodes = {};
std::vector<size_t> _dag_linear = {};
bool _dag_linear_valid = false;
// util::DAG _data_res_dag = {};
std::vector<std::optional<CachedDataRes>> _data_res = {};
std::unordered_map<size_t, data::result::NodeIdx> _data_src_id_to_res_idx =
{};
std::vector<Local> _locals = {};
std::optional<data::result::NodeIdx> _locals_node = {};
std::optional<lldb::SBFrame> _last_frame = {};
std::vector<
std::optional<std::pair<lldb::SBType, data::type_info::TypeInfo>>>
_types = {};
std::vector<VarCacheEntry> _var_cache{};
// std::unordered_map<size_t, uint16_t> _src_id_to_data_idx = {};
// std::vector<data::DataResult> _cached_data_results = {};
std::vector<Breakpoint> _breakpoints = {};
};
} // namespace dbgui::backend