breakpoints; thread/frame selection that should work; TODO: is slow

This commit is contained in:
T0b1
2023-06-08 01:07:57 +02:00
parent 78e7f5cca7
commit b393f3dd28
14 changed files with 1098 additions and 73 deletions

View File

@@ -8,12 +8,16 @@
#include <lldb/API/SBValue.h>
#include <lldb/API/SBInstructionList.h>
#include <lldb/API/SBInstruction.h>
#include <lldb/API/SBBreakpoint.h>
#include <lldb/API/SBBreakpointLocation.h>
#include <filesystem>
#include <array>
#include <cassert>
#include <cstring>
#include <algorithm>
#include <pthread.h>
using namespace dbgui::backend;
namespace
@@ -24,9 +28,15 @@ LLDBBackend::~LLDBBackend() {}
LLDBBackend::LLDBBackend(std::string filename)
{
_filename = filename;
char buf[256];
buf[0] = '\0';
pthread_getname_np(pthread_self(), buf, sizeof(buf));
// name lldb threads
pthread_setname_np(pthread_self(), "lldb_internal_thread");
lldb::SBDebugger::Initialize();
_instance = lldb::SBDebugger::Create(false);
_target = _instance.CreateTarget(filename.c_str());
pthread_setname_np(pthread_self(), buf);
}
void LLDBBackend::start()
@@ -36,9 +46,19 @@ void LLDBBackend::start()
auto error = lldb::SBError();
auto listener = lldb::SBListener();
char buf[256];
buf[0] = '\0';
{
pthread_getname_np(pthread_self(), buf, sizeof(buf));
// name lldb threads
pthread_setname_np(pthread_self(), "lldb_internal_thread");
}
_process = _target.Launch(listener, argv, nullptr, nullptr, nullptr, nullptr,
cwd.c_str(), lldb::LaunchFlags::eLaunchFlagNone,
true, error);
{
pthread_setname_np(pthread_self(), buf);
}
_msg_thread = std::thread{[this]() {
auto ptr = this->shared_from_this();
@@ -48,6 +68,7 @@ void LLDBBackend::start()
void LLDBBackend::run_msg_loop()
{
pthread_setname_np(pthread_self(), "msg_loop");
std::thread event_thread{[this]() { this->wait_for_debug_events(); }};
while (true)
@@ -59,6 +80,7 @@ void LLDBBackend::run_msg_loop()
void LLDBBackend::wait_for_debug_events()
{
using namespace lldb;
pthread_setname_np(pthread_self(), "lldb_msg_loop");
auto listener = _instance.GetListener();
while (true)
@@ -329,7 +351,38 @@ void LLDBBackend::dump_threads()
printf("Symctx: %.*s\n", static_cast<int>(stream.GetSize()),
stream.GetData());
auto list = _target.FindTypes("test::MyType");
/*auto mod = symctx.GetModule();
auto cu = symctx.GetCompileUnit();
auto block = symctx.GetBlock();
auto le = symctx.GetLineEntry();
stream.Clear();
mod.GetDescription(stream);
printf("Module: %.*s\n", static_cast<int>(stream.GetSize()),
stream.GetData());
stream.Clear();
cu.GetDescription(stream);
printf("CU: %.*s\n", static_cast<int>(stream.GetSize()), stream.GetData());
stream.Clear();
block.GetDescription(stream);
printf("Block: %.*s\n", static_cast<int>(stream.GetSize()),
stream.GetData());
if (!le.IsValid())
{
printf("LE not valid\n");
}
/*uint32_t line, col;
line = le.GetLine();
col = le.GetColumn();
printf("LE: %u:%u\n", line, col);*
stream.Clear();
le.GetDescription(stream);
printf("LE: %.*s\n", static_cast<int>(stream.GetSize()), stream.GetData());*/
/*auto list = _target.FindTypes("test::MyType");
printf("List len: %lu\n", list.GetSize());
auto len = list.GetSize();
for (uint32_t i = 0; i < len; ++i)
@@ -340,9 +393,9 @@ void LLDBBackend::dump_threads()
printf("Type %u: %.*s\n", i, static_cast<int>(stream.GetSize()),
stream.GetData());
}
}*/
auto pc = frame.GetPC();
/*auto pc = frame.GetPC();
stream.Clear();
//auto sc =
@@ -366,7 +419,7 @@ void LLDBBackend::dump_threads()
{
start = pc;
end = start + 0x100;
}
}*/
/*auto buf = std::vector<uint8_t>{};
buf.resize(end - start);
@@ -399,7 +452,7 @@ void LLDBBackend::dump_threads()
}
}
bool LLDBBackend::step_into()
bool LLDBBackend::step_into(bool source_step)
{
std::lock_guard g{_data_lock};
if (!_process)
@@ -414,15 +467,18 @@ bool LLDBBackend::step_into()
}
// TODO: figure out what the runmodes mean
// TODO: this is source step
// thread.StepInto();
thread.StepInstruction(false);
if (source_step)
{
thread.StepInto();
} else
{
thread.StepInstruction(false);
}
return false;
}
bool LLDBBackend::step_over()
bool LLDBBackend::step_over(bool source_step)
{
std::lock_guard g{_data_lock};
if (!_process)
@@ -436,9 +492,13 @@ bool LLDBBackend::step_over()
return false;
}
//thread.StepOver();
thread.StepInstruction(true);
if (source_step)
{
thread.StepOver();
} else
{
thread.StepInstruction(true);
}
return false;
}
@@ -457,6 +517,7 @@ bool LLDBBackend::step_out()
return false;
}
thread.StepOut();
// TODO: allow step out of frame?
return false;
}
@@ -485,6 +546,54 @@ void LLDBBackend::pause()
_process->Stop();
}
void LLDBBackend::select_thread(uint16_t idx)
{
std::lock_guard g{_data_lock};
if (!_process)
{
return;
}
if (_threads.size() <= idx)
{
return;
}
printf("Set selected thread: %lu (%u)", _threads[idx].id, idx);
_process->SetSelectedThreadByID(_threads[idx].id);
auto thread = _process->GetSelectedThread();
thread.SetSelectedFrame(0);
this->check_thread_changes();
this->check_frame_changes();
// TODO: these can be a lot more selective
this->check_data_changes();
}
void LLDBBackend::select_frame(uint16_t idx)
{
std::lock_guard g{_data_lock};
if (!_process)
{
return;
}
if (_frames.size() <= idx)
{
return;
}
auto thread = _process->GetSelectedThread();
uint64_t id = thread.GetThreadID();
printf("ID: %lu\n", id);
thread.SetSelectedFrame(_frames[idx].lldb_idx);
uint64_t id2 = thread.GetThreadID();
printf("ID2: %lu\n", id2);
this->check_frame_changes();
// TODO: these can be a lot more selective
this->check_data_changes();
}
void LLDBBackend::check_reg_changes()
{
auto thread = _process->GetSelectedThread();
@@ -569,6 +678,7 @@ void LLDBBackend::check_thread_changes()
this->send_thread_removed(BackToFront::ThreadRemoved{i});
}
_threads.clear();
this->send_selected_thread_changed(0);
}
return;
}
@@ -576,6 +686,7 @@ void LLDBBackend::check_thread_changes()
// TODO: work with SBThread::GetIndexID instead of relying
// on the index order?
uint16_t cache_idx = 0;
auto sel_thread = _process->GetSelectedThread();
for (uint32_t i = 0; i < len; ++i)
{
auto thread = _process->GetThreadAtIndex(i);
@@ -584,14 +695,44 @@ void LLDBBackend::check_thread_changes()
continue;
}
if (thread.GetThreadID() == sel_thread.GetThreadID())
{
if (_selected_thread != cache_idx)
{
_selected_thread = cache_idx;
this->send_selected_thread_changed(cache_idx);
// reset selected frame
if (_selected_frame != 0)
{
this->send_selected_frame_changed(0);
thread.SetSelectedFrame(0);
}
_selected_frame = 0;
}
}
// this causes lldb to compute a proper stacktrace apparently
thread.GetCurrentExceptionBacktrace();
auto frame = thread.GetFrameAtIndex(0);
if (_threads.size() <= cache_idx)
{
_threads.push_back(
Thread{.id = thread.GetThreadID(),
.ip = frame.GetPC(),
.name = thread.GetName(),
.cur_display_fn = frame.GetDisplayFunctionName()});
auto thread_name = std::string{};
auto lldb_thread_name = thread.GetName();
if (lldb_thread_name)
{
// it is not valid to construct a string from a nullptr?
thread_name = lldb_thread_name;
}
auto frame_name = std::string{};
if (auto frame_str = frame.GetDisplayFunctionName(); frame_str)
{
frame_name = frame_str;
}
_threads.push_back(Thread{.id = thread.GetThreadID(),
.ip = frame.GetPC(),
.name = thread_name,
.cur_display_fn = frame_name});
auto &info = _threads.back();
switch (thread.GetStopReason())
@@ -642,13 +783,21 @@ void LLDBBackend::check_thread_changes()
cache_entry.ip = frame.GetPC();
send_change = true;
}
auto name = std::string_view{thread.GetName()};
auto name = std::string_view{};
if (auto lldb_name = thread.GetName(); lldb_name)
{
name = lldb_name;
}
if (name != cache_entry.name)
{
change_entry.name = name;
send_change = true;
}
auto display_name = std::string_view{frame.GetDisplayFunctionName()};
auto display_name = std::string_view{};
if (auto lldb_name = frame.GetDisplayFunctionName(); lldb_name)
{
display_name = lldb_name;
}
if (display_name != cache_entry.cur_display_fn)
{
change_entry.cur_display_fn = display_name;
@@ -703,7 +852,7 @@ void LLDBBackend::check_thread_changes()
for (size_t i = 0; i < rem_count; ++i)
{
this->send_thread_removed(
BackToFront::ThreadRemoved{_threads.size() - i - 1});
BackToFront::ThreadRemoved{_threads.size() - 1});
_threads.pop_back();
}
}
@@ -716,19 +865,26 @@ void LLDBBackend::check_frame_changes()
if (!thread.IsValid())
{
// TODO: cleanup
printf("Current thread not valid\n");
return;
}
uint64_t id = thread.GetThreadID();
printf("ID3: %lu\n", id);
size_t len = thread.GetNumFrames();
uint16_t cache_idx = 0;
size_t selected_idx = 0;
printf("Num frames: %lu\n", len);
for (size_t i = 0; i < len; ++i)
{
auto frame = thread.GetFrameAtIndex(i);
if (!frame.IsValid())
{
printf("Frame %lu invalid\n", i);
continue;
}
printf("Looking at frame %lu\n", i);
if (thread.GetSelectedFrame().IsEqual(frame))
{
@@ -736,14 +892,23 @@ void LLDBBackend::check_frame_changes()
}
auto ip = frame.GetPC();
auto name = std::string_view{frame.GetDisplayFunctionName()};
auto name = std::string_view{};
if (auto lldb_name = frame.GetDisplayFunctionName(); lldb_name)
{
// dont construct from nullptr
name = lldb_name;
}
printf(" IP: %lu, Name: %.*s\n", ip, static_cast<int>(name.size()),
name.data());
if (_frames.size() <= cache_idx)
{
_frames.push_back(Frame{
.ip = ip,
.lldb_idx = i,
.display_name = std::string{name},
});
printf("Frame %u (%lu) is new\n", cache_idx, i);
this->send_frame_changed(BackToFront::FrameChanged{
.ip = ip, .idx = cache_idx, .display_name = std::string{name}});
cache_idx++;
@@ -751,22 +916,40 @@ void LLDBBackend::check_frame_changes()
}
auto &cache_entry = _frames[cache_idx];
printf(" Cached IP: %lu, Name: %s\n", cache_entry.ip,
cache_entry.display_name.c_str());
if (cache_entry.ip != ip || cache_entry.display_name != name)
{
printf("Frame %u (%lu) changed\n", cache_idx, i);
this->send_frame_changed(BackToFront::FrameChanged{
.ip = ip, .idx = cache_idx, .display_name = std::string{name}});
cache_entry.ip = ip;
if (cache_entry.display_name != name)
{
cache_entry.display_name = name;
}
}
if (cache_entry.lldb_idx != i)
{
cache_entry.lldb_idx = i;
}
cache_idx++;
}
if (_selected_frame != selected_idx)
{
this->send_selected_frame_changed(selected_idx);
_selected_frame = selected_idx;
}
printf("cache_idx: %u frame_size: %lu\n", cache_idx, _frames.size());
if (_frames.size() > cache_idx)
{
// here cache_idx == num_frames
size_t rem_count = _frames.size() - cache_idx;
for (size_t i = 0; i < rem_count; ++i)
{
this->send_frame_removed(
BackToFront::FrameRemoved{_frames.size() - i - 1});
this->send_frame_removed(BackToFront::FrameRemoved{_frames.size() - 1});
_frames.pop_back();
}
}
@@ -786,6 +969,7 @@ void LLDBBackend::add_data_node(const data::DataNode &node)
_data_dag.add_node(node.id);
// add child nodes
// TODO: make this somehow automatic
switch (node.type)
{
using enum data::DataNode::Type;
@@ -804,6 +988,17 @@ void LLDBBackend::add_data_node(const data::DataNode &node)
_data_dag.add_edge(node.id, src_id);
break;
}
case line_entry:
{
auto src_id = std::get<data::LineEntry>(node.data).src_id;
if (!_data_dag.nodes.contains(src_id))
{
printf("Invalid add sequence\n");
exit(1);
}
_data_dag.add_edge(node.id, src_id);
break;
}
}
_dag_linear_valid = false;
@@ -934,6 +1129,30 @@ dbgui::data::DataResult LLDBBackend::calc_data_res(const data::DataNode &node)
.type = data::TypeInfo{.type = data::TypeInfo::Type::u64},
.data = _reg_sets[info.set].values[info.idx]};
}
case frame_ip:
{
auto thread = _process->GetSelectedThread();
auto frame = thread.GetSelectedFrame();
if (!thread.IsValid() || !frame.IsValid())
{
return data::DataResult{
.id = node.id,
.success = false,
};
}
uint64_t pc = frame.GetPC();
std::vector<uint8_t> out{};
out.resize(sizeof(uint64_t));
*reinterpret_cast<uint64_t *>(out.data()) = pc;
return data::DataResult{
.id = node.id,
.success = true,
.type = data::TypeInfo{.type = data::TypeInfo::Type::u64},
.data = std::move(out),
};
}
}
break;
}
@@ -961,6 +1180,15 @@ dbgui::data::DataResult LLDBBackend::calc_data_res(const data::DataNode &node)
auto sc = _target.ResolveSymbolContextForAddress(
SBAddress{pc, _target}, eSymbolContextFunction | eSymbolContextSymbol);
auto le = sc.GetLineEntry();
if (le.IsValid())
{
SBStream stream{};
le.GetDescription(stream);
printf("LE: %.*s\n", static_cast<int>(stream.GetSize()),
stream.GetData());
}
uint64_t start, end;
if (sc.GetFunction().IsValid())
{
@@ -1006,17 +1234,35 @@ dbgui::data::DataResult LLDBBackend::calc_data_res(const data::DataNode &node)
for (size_t i = 0; i < inst_list.GetSize(); ++i)
{
auto inst = inst_list.GetInstructionAtIndex(i);
const auto mnem = std::string_view{inst.GetMnemonic(_target)};
const auto op = std::string_view{inst.GetOperands(_target)};
const auto comm = std::string_view{inst.GetComment(_target)};
auto addr = inst.GetAddress().GetLoadAddress(_target);
const auto len = inst.GetByteSize();
auto inst = inst_list.GetInstructionAtIndex(i);
auto mnem = std::string_view{};
auto op = std::string_view{};
auto comm = std::string_view{};
if (auto mnem_str = inst.GetMnemonic(_target); mnem_str)
{
mnem = mnem_str;
}
if (auto op_str = inst.GetOperands(_target); op_str)
{
op = op_str;
}
if (auto comm_str = inst.GetComment(_target); comm_str)
{
comm = comm_str;
}
auto addr = inst.GetAddress().GetLoadAddress(_target);
const auto len = inst.GetByteSize();
//SBStream stream{};
//inst.GetDescription(stream);
//printf("Got inst: %.*s\n", stream.GetSize(), stream.GetData());
if (comm.size() > 255)
{
// TODO: better handling
comm = comm.substr(0, 255);
}
if (mnem.size() > 255 || op.size() > 255 || comm.size() > 255)
{
printf("Instruction length limits exceeded:\n");
@@ -1042,6 +1288,77 @@ dbgui::data::DataResult LLDBBackend::calc_data_res(const data::DataNode &node)
std::copy(comm.begin(), comm.end(), out.begin() + insert_idx);
}
return data::DataResult{
.id = node.id,
.success = true,
.type = data::TypeInfo{.type = data::TypeInfo::Type::custom},
.data = std::move(out)};
}
case line_entry:
{
using namespace lldb;
size_t addr_id = std::get<data::LineEntry>(node.data).src_id;
const auto res_it =
std::find_if(_cached_data_results.begin(), _cached_data_results.end(),
[addr_id](const auto &el) { return el.id == addr_id; });
if (res_it == _cached_data_results.end() || !res_it->success)
{
return data::DataResult{.id = node.id, .success = false};
}
// TODO: for now only accept u64
if (res_it->type.type != data::TypeInfo::Type::u64)
{
return data::DataResult{.id = node.id, .success = false};
}
const auto addr = *reinterpret_cast<uint64_t *>(res_it->data.data());
auto sc = _target.ResolveSymbolContextForAddress(SBAddress{addr, _target},
eSymbolContextLineEntry);
auto le = sc.GetLineEntry();
auto file_spec = le.GetFileSpec();
/*auto stream = SBStream{};
sc.GetDescription(stream);
printf("SC DBG: %.*s\n", static_cast<int>(stream.GetSize()),
stream.GetData());
stream.Clear();
le.GetDescription(stream);
printf("LE DBG: %.*s\n", static_cast<int>(stream.GetSize()),
stream.GetData());
stream.Clear();
file_spec.GetDescription(stream);
printf("FS DBG: %.*s\n", static_cast<int>(stream.GetSize()),
stream.GetData());*/
if (!le.IsValid() || !file_spec.IsValid())
{
return data::DataResult{.id = node.id, .success = false};
}
// TODO: for now the instlist is serialized in a custom format
// use a type structure for it later?
// it's basically an array of
// struct LineEntry {
// uint32_t line_no;
// uint32_t name_len;
// char file_name[];
// };
// we can't know how long the path actually is without at least calling GetPath twice
// because it will return the smaller of the buffer size or actual path len
char path_buf[512];
uint32_t path_len = file_spec.GetPath(path_buf, sizeof(path_buf));
printf("Calculated LE: %.*s\n", static_cast<int>(path_len), path_buf);
std::vector<uint8_t> out;
out.resize(8 + path_len);
*reinterpret_cast<uint32_t *>(out.data()) = le.GetLine();
*reinterpret_cast<uint32_t *>(out.data() + 4) = path_len;
std::memcpy(out.data() + 8, path_buf, path_len);
return data::DataResult{
.id = node.id,
.success = true,
@@ -1074,6 +1391,53 @@ void LLDBBackend::add_breakpoint(uint64_t addr, size_t id)
_breakpoints.push_back(Breakpoint{.id = id, .lldb_id = bp.GetID()});
}
bool LLDBBackend::add_breakpoint(const char *file, uint32_t line, size_t id)
{
using namespace lldb;
std::lock_guard g{_data_lock};
if (!_process)
{
return false;
}
if (std::find_if(_breakpoints.begin(), _breakpoints.end(),
[id](const auto &el) { return el.id == id; })
!= _breakpoints.end())
{
printf("Trying to register same breakpoint id\n");
exit(1);
}
// TODO: try false?
auto file_spec = SBFileSpec{file, true};
auto mod_list = SBFileSpecList{};
auto bp =
_target.BreakpointCreateByLocation(file_spec, line, 0, 0, mod_list, true);
if (!bp.IsValid())
{
printf("BP not valid\n");
return false;
}
/*auto num = bp.GetNumLocations();
auto res_num = bp.GetNumResolvedLocations();
printf("Num: %lu, Res: %lu\n", num, res_num);
auto stream = SBStream{};
for (size_t i = 0; i < num; ++i)
{
stream.Clear();
auto loc = bp.GetLocationAtIndex(i);
loc.GetDescription(stream, eDescriptionLevelVerbose);
printf("Loc %lu: %.*s\n", i, static_cast<int>(stream.GetSize()),
stream.GetData());
}*/
_breakpoints.push_back(Breakpoint{.id = id, .lldb_id = bp.GetID()});
return true;
}
void LLDBBackend::remove_breakpoint(size_t id)
{
std::lock_guard g{_data_lock};