202 lines
5.8 KiB
C++
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
|