mirror of https://gitlab.com/cppit/jucipp
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
571 lines
20 KiB
571 lines
20 KiB
|
6 years ago
|
#include "debug_lldb.hpp"
|
||
|
8 years ago
|
#include <cstdio>
|
||
|
10 years ago
|
#ifdef __APPLE__
|
||
|
8 years ago
|
#include <cstdlib>
|
||
|
10 years ago
|
#endif
|
||
|
6 years ago
|
#include "config.hpp"
|
||
|
|
#include "filesystem.hpp"
|
||
|
10 years ago
|
#include "process.hpp"
|
||
|
6 years ago
|
#include "terminal.hpp"
|
||
|
8 years ago
|
#include <boost/filesystem.hpp>
|
||
|
|
#include <iostream>
|
||
|
10 years ago
|
|
||
|
10 years ago
|
extern char **environ;
|
||
|
10 years ago
|
|
||
|
7 years ago
|
bool Debug::LLDB::initialized = false;
|
||
|
|
|
||
|
10 years ago
|
void log(const char *msg, void *) {
|
||
|
10 years ago
|
std::cout << "debugger log: " << msg << std::endl;
|
||
|
10 years ago
|
}
|
||
|
|
|
||
|
8 years ago
|
Debug::LLDB::LLDB() : state(lldb::StateType::eStateInvalid), buffer_size(131072) {
|
||
|
9 years ago
|
if(!getenv("LLDB_DEBUGSERVER_PATH")) {
|
||
|
7 years ago
|
#ifndef __APPLE__
|
||
|
8 years ago
|
auto debug_server_path = filesystem::get_executable("lldb-server").string();
|
||
|
8 years ago
|
if(debug_server_path != "lldb-server") {
|
||
|
|
#ifdef _WIN32
|
||
|
|
Glib::setenv("LLDB_DEBUGSERVER_PATH", debug_server_path.c_str(), 0);
|
||
|
|
#else
|
||
|
8 years ago
|
setenv("LLDB_DEBUGSERVER_PATH", debug_server_path.c_str(), 0);
|
||
|
8 years ago
|
#endif
|
||
|
|
}
|
||
|
10 years ago
|
#endif
|
||
|
8 years ago
|
}
|
||
|
10 years ago
|
}
|
||
|
10 years ago
|
|
||
|
7 years ago
|
void Debug::LLDB::destroy_() {
|
||
|
|
{
|
||
|
|
LockGuard lock(mutex);
|
||
|
|
if(process)
|
||
|
|
process->Kill();
|
||
|
|
}
|
||
|
|
|
||
|
|
if(debug_thread.joinable())
|
||
|
|
debug_thread.join();
|
||
|
|
|
||
|
|
LockGuard lock(mutex);
|
||
|
|
if(debugger)
|
||
|
|
lldb::SBDebugger::Destroy(*debugger);
|
||
|
|
}
|
||
|
|
|
||
|
8 years ago
|
std::tuple<std::vector<std::string>, std::string, std::vector<std::string>> Debug::LLDB::parse_run_arguments(const std::string &command) {
|
||
|
8 years ago
|
std::vector<std::string> environment;
|
||
|
10 years ago
|
std::string executable;
|
||
|
|
std::vector<std::string> arguments;
|
||
|
8 years ago
|
|
||
|
|
size_t start_pos = std::string::npos;
|
||
|
|
bool quote = false;
|
||
|
|
bool double_quote = false;
|
||
|
|
size_t backslash_count = 0;
|
||
|
|
for(size_t c = 0; c <= command.size(); c++) {
|
||
|
|
if(c == command.size() || (!quote && !double_quote && backslash_count % 2 == 0 && command[c] == ' ')) {
|
||
|
|
if(c > 0 && start_pos != std::string::npos) {
|
||
|
|
auto argument = command.substr(start_pos, c - start_pos);
|
||
|
9 years ago
|
if(executable.empty()) {
|
||
|
8 years ago
|
//Check for environment variable
|
||
|
8 years ago
|
bool env_arg = false;
|
||
|
|
for(size_t c = 0; c < argument.size(); ++c) {
|
||
|
|
if((argument[c] >= 'a' && argument[c] <= 'z') || (argument[c] >= 'A' && argument[c] <= 'Z') ||
|
||
|
|
(argument[c] >= '0' && argument[c] <= '9') || argument[c] == '_')
|
||
|
8 years ago
|
continue;
|
||
|
8 years ago
|
else if(argument[c] == '=' && c + 1 < argument.size()) {
|
||
|
|
environment.emplace_back(argument.substr(0, c + 1) + filesystem::unescape_argument(argument.substr(c + 1)));
|
||
|
|
env_arg = true;
|
||
|
8 years ago
|
break;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
break;
|
||
|
|
}
|
||
|
8 years ago
|
|
||
|
8 years ago
|
if(!env_arg)
|
||
|
8 years ago
|
executable = filesystem::unescape_argument(argument);
|
||
|
9 years ago
|
}
|
||
|
10 years ago
|
else
|
||
|
8 years ago
|
arguments.emplace_back(filesystem::unescape_argument(argument));
|
||
|
8 years ago
|
start_pos = std::string::npos;
|
||
|
10 years ago
|
}
|
||
|
|
}
|
||
|
8 years ago
|
else if(command[c] == '\'' && backslash_count % 2 == 0 && !double_quote)
|
||
|
|
quote = !quote;
|
||
|
|
else if(command[c] == '"' && backslash_count % 2 == 0 && !quote)
|
||
|
|
double_quote = !double_quote;
|
||
|
|
else if(command[c] == '\\' && !quote && !double_quote)
|
||
|
8 years ago
|
++backslash_count;
|
||
|
|
else
|
||
|
8 years ago
|
backslash_count = 0;
|
||
|
|
if(c < command.size() && start_pos == std::string::npos && command[c] != ' ')
|
||
|
|
start_pos = c;
|
||
|
10 years ago
|
}
|
||
|
8 years ago
|
|
||
|
8 years ago
|
return std::make_tuple(environment, executable, arguments);
|
||
|
|
}
|
||
|
|
|
||
|
|
void Debug::LLDB::start(const std::string &command, const boost::filesystem::path &path,
|
||
|
8 years ago
|
const std::vector<std::pair<boost::filesystem::path, int>> &breakpoints,
|
||
|
|
const std::vector<std::string> &startup_commands, const std::string &remote_host) {
|
||
|
7 years ago
|
LockGuard lock(mutex);
|
||
|
|
|
||
|
8 years ago
|
if(!debugger) {
|
||
|
7 years ago
|
initialized = true;
|
||
|
8 years ago
|
lldb::SBDebugger::Initialize();
|
||
|
8 years ago
|
debugger = std::make_unique<lldb::SBDebugger>(lldb::SBDebugger::Create(true, log, nullptr));
|
||
|
|
listener = std::make_unique<lldb::SBListener>("juCi++ lldb listener");
|
||
|
8 years ago
|
}
|
||
|
8 years ago
|
|
||
|
8 years ago
|
//Create executable string and argument array
|
||
|
8 years ago
|
auto parsed_run_arguments = parse_run_arguments(command);
|
||
|
|
auto &environment_from_arguments = std::get<0>(parsed_run_arguments);
|
||
|
|
auto &executable = std::get<1>(parsed_run_arguments);
|
||
|
8 years ago
|
#ifdef _WIN32
|
||
|
|
if(remote_host.empty())
|
||
|
8 years ago
|
executable += ".exe";
|
||
|
8 years ago
|
#endif
|
||
|
8 years ago
|
auto &arguments = std::get<2>(parsed_run_arguments);
|
||
|
|
|
||
|
|
std::vector<const char *> argv;
|
||
|
8 years ago
|
argv.reserve(arguments.size());
|
||
|
8 years ago
|
for(auto &argument : arguments)
|
||
|
|
argv.emplace_back(argument.c_str());
|
||
|
8 years ago
|
argv.emplace_back(nullptr);
|
||
|
8 years ago
|
|
||
|
|
auto target = debugger->CreateTarget(executable.c_str());
|
||
|
10 years ago
|
if(!target.IsValid()) {
|
||
|
8 years ago
|
Terminal::get().async_print("Error (debug): Could not create debug target to: " + executable + '\n', true);
|
||
|
|
for(auto &handler : on_exit)
|
||
|
8 years ago
|
handler(-1);
|
||
|
10 years ago
|
return;
|
||
|
|
}
|
||
|
8 years ago
|
|
||
|
10 years ago
|
//Set breakpoints
|
||
|
8 years ago
|
for(auto &breakpoint : breakpoints) {
|
||
|
10 years ago
|
if(!(target.BreakpointCreateByLocation(breakpoint.first.string().c_str(), breakpoint.second)).IsValid()) {
|
||
|
8 years ago
|
Terminal::get().async_print("Error (debug): Could not create breakpoint at: " + breakpoint.first.string() + ":" + std::to_string(breakpoint.second) + '\n', true);
|
||
|
|
for(auto &handler : on_exit)
|
||
|
8 years ago
|
handler(-1);
|
||
|
10 years ago
|
return;
|
||
|
10 years ago
|
}
|
||
|
10 years ago
|
}
|
||
|
8 years ago
|
|
||
|
10 years ago
|
lldb::SBError error;
|
||
|
10 years ago
|
if(!remote_host.empty()) {
|
||
|
8 years ago
|
auto connect_string = "connect://" + remote_host;
|
||
|
10 years ago
|
process = std::make_unique<lldb::SBProcess>(target.ConnectRemote(*listener, connect_string.c_str(), "gdb-remote", error));
|
||
|
10 years ago
|
if(error.Fail()) {
|
||
|
8 years ago
|
Terminal::get().async_print(std::string("Error (debug): ") + error.GetCString() + '\n', true);
|
||
|
|
for(auto &handler : on_exit)
|
||
|
8 years ago
|
handler(-1);
|
||
|
10 years ago
|
return;
|
||
|
|
}
|
||
|
|
lldb::SBEvent event;
|
||
|
|
while(true) {
|
||
|
|
if(listener->GetNextEvent(event)) {
|
||
|
8 years ago
|
if((event.GetType() & lldb::SBProcess::eBroadcastBitStateChanged) > 0) {
|
||
|
|
auto state = process->GetStateFromEvent(event);
|
||
|
|
this->state = state;
|
||
|
|
if(state == lldb::StateType::eStateConnected)
|
||
|
10 years ago
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
8 years ago
|
|
||
|
8 years ago
|
// Create environment array
|
||
|
8 years ago
|
std::vector<const char *> environment;
|
||
|
8 years ago
|
environment.reserve(environment_from_arguments.size());
|
||
|
8 years ago
|
for(auto &e : environment_from_arguments)
|
||
|
8 years ago
|
environment.emplace_back(e.c_str());
|
||
|
|
environment.emplace_back(nullptr);
|
||
|
8 years ago
|
|
||
|
8 years ago
|
process->RemoteLaunch(argv.data(), environment.data(), nullptr, nullptr, nullptr, nullptr, lldb::eLaunchFlagNone, false, error);
|
||
|
10 years ago
|
if(!error.Fail())
|
||
|
|
process->Continue();
|
||
|
10 years ago
|
}
|
||
|
8 years ago
|
else {
|
||
|
|
// Create environment array
|
||
|
8 years ago
|
std::vector<const char *> environment;
|
||
|
8 years ago
|
environment.reserve(environment_from_arguments.size());
|
||
|
8 years ago
|
for(auto &e : environment_from_arguments)
|
||
|
8 years ago
|
environment.emplace_back(e.c_str());
|
||
|
8 years ago
|
size_t environ_size = 0;
|
||
|
|
while(environ[environ_size] != nullptr)
|
||
|
8 years ago
|
++environ_size;
|
||
|
8 years ago
|
for(size_t c = 0; c < environ_size; ++c)
|
||
|
8 years ago
|
environment.emplace_back(environ[c]);
|
||
|
|
environment.emplace_back(nullptr);
|
||
|
8 years ago
|
|
||
|
8 years ago
|
process = std::make_unique<lldb::SBProcess>(target.Launch(*listener, argv.data(), environment.data(), nullptr, nullptr, nullptr, path.string().c_str(), lldb::eLaunchFlagNone, false, error));
|
||
|
|
}
|
||
|
7 years ago
|
|
||
|
10 years ago
|
if(error.Fail()) {
|
||
|
8 years ago
|
Terminal::get().async_print(std::string("Error (debug): ") + error.GetCString() + '\n', true);
|
||
|
|
for(auto &handler : on_exit)
|
||
|
8 years ago
|
handler(-1);
|
||
|
10 years ago
|
return;
|
||
|
|
}
|
||
|
7 years ago
|
|
||
|
10 years ago
|
if(debug_thread.joinable())
|
||
|
|
debug_thread.join();
|
||
|
8 years ago
|
for(auto &handler : on_start)
|
||
|
8 years ago
|
handler(*process);
|
||
|
8 years ago
|
|
||
|
|
for(auto &command : startup_commands) {
|
||
|
8 years ago
|
lldb::SBCommandReturnObject command_return_object;
|
||
|
|
debugger->GetCommandInterpreter().HandleCommand(command.c_str(), command_return_object, false);
|
||
|
|
}
|
||
|
8 years ago
|
|
||
|
|
debug_thread = std::thread([this]() {
|
||
|
10 years ago
|
lldb::SBEvent event;
|
||
|
10 years ago
|
while(true) {
|
||
|
7 years ago
|
LockGuard lock(mutex);
|
||
|
10 years ago
|
if(listener->GetNextEvent(event)) {
|
||
|
8 years ago
|
if((event.GetType() & lldb::SBProcess::eBroadcastBitStateChanged) > 0) {
|
||
|
|
auto state = process->GetStateFromEvent(event);
|
||
|
|
this->state = state;
|
||
|
|
|
||
|
|
if(state == lldb::StateType::eStateStopped) {
|
||
|
|
for(uint32_t c = 0; c < process->GetNumThreads(); c++) {
|
||
|
|
auto thread = process->GetThreadAtIndex(c);
|
||
|
|
if(thread.GetStopReason() >= 2) {
|
||
|
10 years ago
|
process->SetSelectedThreadByIndexID(thread.GetIndexID());
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
8 years ago
|
|
||
|
8 years ago
|
lock.unlock();
|
||
|
8 years ago
|
for(auto &handler : on_event)
|
||
|
8 years ago
|
handler(event);
|
||
|
8 years ago
|
lock.lock();
|
||
|
8 years ago
|
|
||
|
|
if(state == lldb::StateType::eStateExited || state == lldb::StateType::eStateCrashed) {
|
||
|
|
auto exit_status = state == lldb::StateType::eStateCrashed ? -1 : process->GetExitStatus();
|
||
|
8 years ago
|
lock.unlock();
|
||
|
8 years ago
|
for(auto &handler : on_exit)
|
||
|
8 years ago
|
handler(exit_status);
|
||
|
8 years ago
|
lock.lock();
|
||
|
10 years ago
|
process.reset();
|
||
|
8 years ago
|
this->state = lldb::StateType::eStateInvalid;
|
||
|
10 years ago
|
return;
|
||
|
|
}
|
||
|
10 years ago
|
}
|
||
|
8 years ago
|
if((event.GetType() & lldb::SBProcess::eBroadcastBitSTDOUT) > 0) {
|
||
|
10 years ago
|
char buffer[buffer_size];
|
||
|
|
size_t n;
|
||
|
8 years ago
|
while((n = process->GetSTDOUT(buffer, buffer_size)) != 0)
|
||
|
10 years ago
|
Terminal::get().async_print(std::string(buffer, n));
|
||
|
10 years ago
|
}
|
||
|
10 years ago
|
//TODO: for some reason stderr is redirected to stdout
|
||
|
8 years ago
|
if((event.GetType() & lldb::SBProcess::eBroadcastBitSTDERR) > 0) {
|
||
|
10 years ago
|
char buffer[buffer_size];
|
||
|
|
size_t n;
|
||
|
8 years ago
|
while((n = process->GetSTDERR(buffer, buffer_size)) != 0)
|
||
|
10 years ago
|
Terminal::get().async_print(std::string(buffer, n), true);
|
||
|
10 years ago
|
}
|
||
|
|
}
|
||
|
10 years ago
|
lock.unlock();
|
||
|
10 years ago
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||
|
10 years ago
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
10 years ago
|
|
||
|
10 years ago
|
void Debug::LLDB::continue_debug() {
|
||
|
7 years ago
|
LockGuard lock(mutex);
|
||
|
8 years ago
|
if(state == lldb::StateType::eStateStopped)
|
||
|
10 years ago
|
process->Continue();
|
||
|
|
}
|
||
|
|
|
||
|
10 years ago
|
void Debug::LLDB::stop() {
|
||
|
7 years ago
|
LockGuard lock(mutex);
|
||
|
8 years ago
|
if(state == lldb::StateType::eStateRunning) {
|
||
|
|
auto error = process->Stop();
|
||
|
10 years ago
|
if(error.Fail())
|
||
|
8 years ago
|
Terminal::get().async_print(std::string("Error (debug): ") + error.GetCString() + '\n', true);
|
||
|
10 years ago
|
}
|
||
|
|
}
|
||
|
|
|
||
|
10 years ago
|
void Debug::LLDB::kill() {
|
||
|
7 years ago
|
LockGuard lock(mutex);
|
||
|
10 years ago
|
if(process) {
|
||
|
8 years ago
|
auto error = process->Kill();
|
||
|
10 years ago
|
if(error.Fail())
|
||
|
8 years ago
|
Terminal::get().async_print(std::string("Error (debug): ") + error.GetCString() + '\n', true);
|
||
|
10 years ago
|
}
|
||
|
10 years ago
|
}
|
||
|
|
|
||
|
10 years ago
|
void Debug::LLDB::step_over() {
|
||
|
7 years ago
|
LockGuard lock(mutex);
|
||
|
8 years ago
|
if(state == lldb::StateType::eStateStopped) {
|
||
|
10 years ago
|
process->GetSelectedThread().StepOver();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
10 years ago
|
void Debug::LLDB::step_into() {
|
||
|
7 years ago
|
LockGuard lock(mutex);
|
||
|
8 years ago
|
if(state == lldb::StateType::eStateStopped) {
|
||
|
10 years ago
|
process->GetSelectedThread().StepInto();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
10 years ago
|
void Debug::LLDB::step_out() {
|
||
|
7 years ago
|
LockGuard lock(mutex);
|
||
|
8 years ago
|
if(state == lldb::StateType::eStateStopped) {
|
||
|
10 years ago
|
process->GetSelectedThread().StepOut();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
10 years ago
|
std::pair<std::string, std::string> Debug::LLDB::run_command(const std::string &command) {
|
||
|
7 years ago
|
LockGuard lock(mutex);
|
||
|
10 years ago
|
std::pair<std::string, std::string> command_return;
|
||
|
8 years ago
|
if(state == lldb::StateType::eStateStopped || state == lldb::StateType::eStateRunning) {
|
||
|
10 years ago
|
lldb::SBCommandReturnObject command_return_object;
|
||
|
10 years ago
|
debugger->GetCommandInterpreter().HandleCommand(command.c_str(), command_return_object, true);
|
||
|
8 years ago
|
auto output = command_return_object.GetOutput();
|
||
|
9 years ago
|
if(output)
|
||
|
8 years ago
|
command_return.first = output;
|
||
|
|
auto error = command_return_object.GetError();
|
||
|
9 years ago
|
if(error)
|
||
|
8 years ago
|
command_return.second = error;
|
||
|
10 years ago
|
}
|
||
|
|
return command_return;
|
||
|
|
}
|
||
|
|
|
||
|
10 years ago
|
std::vector<Debug::LLDB::Frame> Debug::LLDB::get_backtrace() {
|
||
|
7 years ago
|
LockGuard lock(mutex);
|
||
|
10 years ago
|
std::vector<Frame> backtrace;
|
||
|
8 years ago
|
if(state == lldb::StateType::eStateStopped) {
|
||
|
|
auto thread = process->GetSelectedThread();
|
||
|
|
for(uint32_t c_f = 0; c_f < thread.GetNumFrames(); c_f++) {
|
||
|
10 years ago
|
Frame backtrace_frame;
|
||
|
8 years ago
|
auto frame = thread.GetFrameAtIndex(c_f);
|
||
|
|
|
||
|
|
backtrace_frame.index = c_f;
|
||
|
|
|
||
|
|
if(frame.GetFunctionName() != nullptr)
|
||
|
|
backtrace_frame.function_name = frame.GetFunctionName();
|
||
|
|
|
||
|
|
auto module_filename = frame.GetModule().GetFileSpec().GetFilename();
|
||
|
|
if(module_filename != nullptr) {
|
||
|
|
backtrace_frame.module_filename = module_filename;
|
||
|
10 years ago
|
}
|
||
|
8 years ago
|
|
||
|
|
auto line_entry = frame.GetLineEntry();
|
||
|
10 years ago
|
if(line_entry.IsValid()) {
|
||
|
|
lldb::SBStream stream;
|
||
|
|
line_entry.GetFileSpec().GetDescription(stream);
|
||
|
8 years ago
|
auto column = line_entry.GetColumn();
|
||
|
|
if(column == 0)
|
||
|
|
column = 1;
|
||
|
|
backtrace_frame.file_path = filesystem::get_normal_path(stream.GetData());
|
||
|
|
backtrace_frame.line_nr = line_entry.GetLine();
|
||
|
|
backtrace_frame.line_index = column;
|
||
|
10 years ago
|
}
|
||
|
|
backtrace.emplace_back(backtrace_frame);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return backtrace;
|
||
|
|
}
|
||
|
|
|
||
|
10 years ago
|
std::vector<Debug::LLDB::Variable> Debug::LLDB::get_variables() {
|
||
|
7 years ago
|
LockGuard lock(mutex);
|
||
|
10 years ago
|
std::vector<Debug::LLDB::Variable> variables;
|
||
|
8 years ago
|
if(state == lldb::StateType::eStateStopped) {
|
||
|
|
for(uint32_t c_t = 0; c_t < process->GetNumThreads(); c_t++) {
|
||
|
|
auto thread = process->GetThreadAtIndex(c_t);
|
||
|
|
for(uint32_t c_f = 0; c_f < thread.GetNumFrames(); c_f++) {
|
||
|
|
auto frame = thread.GetFrameAtIndex(c_f);
|
||
|
|
auto values = frame.GetVariables(true, true, true, false);
|
||
|
|
for(uint32_t value_index = 0; value_index < values.GetSize(); value_index++) {
|
||
|
10 years ago
|
lldb::SBStream stream;
|
||
|
8 years ago
|
auto value = values.GetValueAtIndex(value_index);
|
||
|
|
|
||
|
10 years ago
|
Debug::LLDB::Variable variable;
|
||
|
8 years ago
|
variable.thread_index_id = thread.GetIndexID();
|
||
|
|
variable.frame_index = c_f;
|
||
|
|
if(value.GetName() != nullptr)
|
||
|
|
variable.name = value.GetName();
|
||
|
10 years ago
|
value.GetDescription(stream);
|
||
|
8 years ago
|
variable.value = stream.GetData();
|
||
|
|
|
||
|
|
auto declaration = value.GetDeclaration();
|
||
|
10 years ago
|
if(declaration.IsValid()) {
|
||
|
8 years ago
|
variable.declaration_found = true;
|
||
|
|
variable.line_nr = declaration.GetLine();
|
||
|
|
variable.line_index = declaration.GetColumn();
|
||
|
|
if(variable.line_index == 0)
|
||
|
|
variable.line_index = 1;
|
||
|
|
|
||
|
|
auto file_spec = declaration.GetFileSpec();
|
||
|
|
variable.file_path = filesystem::get_normal_path(file_spec.GetDirectory());
|
||
|
|
variable.file_path /= file_spec.GetFilename();
|
||
|
10 years ago
|
}
|
||
|
10 years ago
|
else {
|
||
|
8 years ago
|
variable.declaration_found = false;
|
||
|
|
auto line_entry = frame.GetLineEntry();
|
||
|
10 years ago
|
if(line_entry.IsValid()) {
|
||
|
8 years ago
|
variable.line_nr = line_entry.GetLine();
|
||
|
|
variable.line_index = line_entry.GetColumn();
|
||
|
|
if(variable.line_index == 0)
|
||
|
|
variable.line_index = 1;
|
||
|
|
|
||
|
|
auto file_spec = line_entry.GetFileSpec();
|
||
|
|
variable.file_path = filesystem::get_normal_path(file_spec.GetDirectory());
|
||
|
|
variable.file_path /= file_spec.GetFilename();
|
||
|
10 years ago
|
}
|
||
|
|
}
|
||
|
10 years ago
|
variables.emplace_back(variable);
|
||
|
10 years ago
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return variables;
|
||
|
|
}
|
||
|
|
|
||
|
10 years ago
|
void Debug::LLDB::select_frame(uint32_t frame_index, uint32_t thread_index_id) {
|
||
|
7 years ago
|
LockGuard lock(mutex);
|
||
|
8 years ago
|
if(state == lldb::StateType::eStateStopped) {
|
||
|
|
if(thread_index_id != 0)
|
||
|
10 years ago
|
process->SetSelectedThreadByIndexID(thread_index_id);
|
||
|
8 years ago
|
process->GetSelectedThread().SetSelectedFrame(frame_index);
|
||
|
|
;
|
||
|
10 years ago
|
}
|
||
|
|
}
|
||
|
|
|
||
|
10 years ago
|
std::string Debug::LLDB::get_value(const std::string &variable, const boost::filesystem::path &file_path, unsigned int line_nr, unsigned int line_index) {
|
||
|
7 years ago
|
LockGuard lock(mutex);
|
||
|
8 years ago
|
if(state == lldb::StateType::eStateStopped) {
|
||
|
|
auto frame = process->GetSelectedThread().GetSelectedFrame();
|
||
|
|
|
||
|
|
auto values = frame.GetVariables(true, true, true, false);
|
||
|
10 years ago
|
//First try to find variable based on name, file and line number
|
||
|
8 years ago
|
if(!file_path.empty()) {
|
||
|
8 years ago
|
for(uint32_t value_index = 0; value_index < values.GetSize(); value_index++) {
|
||
|
8 years ago
|
lldb::SBStream stream;
|
||
|
8 years ago
|
auto value = values.GetValueAtIndex(value_index);
|
||
|
|
|
||
|
|
if(value.GetName() != nullptr && value.GetName() == variable) {
|
||
|
|
auto declaration = value.GetDeclaration();
|
||
|
8 years ago
|
if(declaration.IsValid()) {
|
||
|
8 years ago
|
if(declaration.GetLine() == line_nr && (declaration.GetColumn() == 0 || declaration.GetColumn() == line_index)) {
|
||
|
|
auto file_spec = declaration.GetFileSpec();
|
||
|
|
auto value_decl_path = filesystem::get_normal_path(file_spec.GetDirectory());
|
||
|
|
value_decl_path /= file_spec.GetFilename();
|
||
|
|
if(value_decl_path == file_path) {
|
||
|
8 years ago
|
value.GetDescription(stream);
|
||
|
7 years ago
|
std::string variable_value = stream.GetData();
|
||
|
|
if(variable_value.size() >= 2 && variable_value.compare(variable_value.size() - 2, 2, "\n\n", 2) == 0)
|
||
|
|
variable_value.pop_back(); // Remove newline at end of string
|
||
|
|
return variable_value;
|
||
|
8 years ago
|
}
|
||
|
10 years ago
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
7 years ago
|
}
|
||
|
|
return {};
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string Debug::LLDB::get_value(const std::string &expression) {
|
||
|
7 years ago
|
LockGuard lock(mutex);
|
||
|
7 years ago
|
std::string variable_value;
|
||
|
|
if(state == lldb::StateType::eStateStopped) {
|
||
|
|
auto frame = process->GetSelectedThread().GetSelectedFrame();
|
||
|
10 years ago
|
if(variable_value.empty()) {
|
||
|
7 years ago
|
// Attempt to get variable from variable expression, using the faster GetValueForVariablePath
|
||
|
|
auto value = frame.GetValueForVariablePath(expression.c_str());
|
||
|
10 years ago
|
if(value.IsValid()) {
|
||
|
10 years ago
|
lldb::SBStream stream;
|
||
|
10 years ago
|
value.GetDescription(stream);
|
||
|
8 years ago
|
variable_value = stream.GetData();
|
||
|
10 years ago
|
}
|
||
|
|
}
|
||
|
7 years ago
|
if(variable_value.empty()) {
|
||
|
|
// Attempt to get variable from variable expression, using the slower EvaluateExpression
|
||
|
|
auto value = frame.EvaluateExpression(expression.c_str());
|
||
|
|
if(value.IsValid() && !value.GetError().Fail()) {
|
||
|
|
lldb::SBStream stream;
|
||
|
|
value.GetDescription(stream);
|
||
|
|
variable_value = stream.GetData();
|
||
|
|
}
|
||
|
|
}
|
||
|
10 years ago
|
}
|
||
|
10 years ago
|
return variable_value;
|
||
|
10 years ago
|
}
|
||
|
10 years ago
|
|
||
|
10 years ago
|
std::string Debug::LLDB::get_return_value(const boost::filesystem::path &file_path, unsigned int line_nr, unsigned int line_index) {
|
||
|
7 years ago
|
LockGuard lock(mutex);
|
||
|
10 years ago
|
std::string return_value;
|
||
|
8 years ago
|
if(state == lldb::StateType::eStateStopped) {
|
||
|
|
auto thread = process->GetSelectedThread();
|
||
|
|
auto thread_return_value = thread.GetStopReturnValue();
|
||
|
10 years ago
|
if(thread_return_value.IsValid()) {
|
||
|
8 years ago
|
auto line_entry = thread.GetSelectedFrame().GetLineEntry();
|
||
|
10 years ago
|
if(line_entry.IsValid()) {
|
||
|
|
lldb::SBStream stream;
|
||
|
|
line_entry.GetFileSpec().GetDescription(stream);
|
||
|
8 years ago
|
if(filesystem::get_normal_path(stream.GetData()) == file_path && line_entry.GetLine() == line_nr &&
|
||
|
|
(line_entry.GetColumn() == 0 || line_entry.GetColumn() == line_index)) {
|
||
|
10 years ago
|
lldb::SBStream stream;
|
||
|
|
thread_return_value.GetDescription(stream);
|
||
|
8 years ago
|
return_value = stream.GetData();
|
||
|
10 years ago
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return return_value;
|
||
|
|
}
|
||
|
|
|
||
|
10 years ago
|
bool Debug::LLDB::is_invalid() {
|
||
|
7 years ago
|
LockGuard lock(mutex);
|
||
|
8 years ago
|
return state == lldb::StateType::eStateInvalid;
|
||
|
10 years ago
|
}
|
||
|
|
|
||
|
10 years ago
|
bool Debug::LLDB::is_stopped() {
|
||
|
7 years ago
|
LockGuard lock(mutex);
|
||
|
8 years ago
|
return state == lldb::StateType::eStateStopped;
|
||
|
10 years ago
|
}
|
||
|
|
|
||
|
10 years ago
|
bool Debug::LLDB::is_running() {
|
||
|
7 years ago
|
LockGuard lock(mutex);
|
||
|
8 years ago
|
return state == lldb::StateType::eStateRunning;
|
||
|
10 years ago
|
}
|
||
|
|
|
||
|
10 years ago
|
void Debug::LLDB::add_breakpoint(const boost::filesystem::path &file_path, int line_nr) {
|
||
|
7 years ago
|
LockGuard lock(mutex);
|
||
|
8 years ago
|
if(state == lldb::eStateStopped || state == lldb::eStateRunning) {
|
||
|
10 years ago
|
if(!(process->GetTarget().BreakpointCreateByLocation(file_path.string().c_str(), line_nr)).IsValid())
|
||
|
8 years ago
|
Terminal::get().async_print("Error (debug): Could not create breakpoint at: " + file_path.string() + ":" + std::to_string(line_nr) + '\n', true);
|
||
|
10 years ago
|
}
|
||
|
|
}
|
||
|
|
|
||
|
10 years ago
|
void Debug::LLDB::remove_breakpoint(const boost::filesystem::path &file_path, int line_nr, int line_count) {
|
||
|
7 years ago
|
LockGuard lock(mutex);
|
||
|
8 years ago
|
if(state == lldb::eStateStopped || state == lldb::eStateRunning) {
|
||
|
|
auto target = process->GetTarget();
|
||
|
|
for(int line_nr_try = line_nr; line_nr_try < line_count; line_nr_try++) {
|
||
|
|
for(uint32_t b_index = 0; b_index < target.GetNumBreakpoints(); b_index++) {
|
||
|
|
auto breakpoint = target.GetBreakpointAtIndex(b_index);
|
||
|
|
for(uint32_t l_index = 0; l_index < breakpoint.GetNumLocations(); l_index++) {
|
||
|
|
auto line_entry = breakpoint.GetLocationAtIndex(l_index).GetAddress().GetLineEntry();
|
||
|
|
if(line_entry.GetLine() == static_cast<uint32_t>(line_nr_try)) {
|
||
|
|
auto file_spec = line_entry.GetFileSpec();
|
||
|
|
auto breakpoint_path = filesystem::get_normal_path(file_spec.GetDirectory());
|
||
|
|
breakpoint_path /= file_spec.GetFilename();
|
||
|
|
if(breakpoint_path == file_path) {
|
||
|
10 years ago
|
if(!target.BreakpointDelete(breakpoint.GetID()))
|
||
|
8 years ago
|
Terminal::get().async_print("Error (debug): Could not delete breakpoint at: " + file_path.string() + ":" + std::to_string(line_nr) + '\n', true);
|
||
|
10 years ago
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
10 years ago
|
void Debug::LLDB::write(const std::string &buffer) {
|
||
|
7 years ago
|
LockGuard lock(mutex);
|
||
|
8 years ago
|
if(state == lldb::StateType::eStateRunning) {
|
||
|
10 years ago
|
process->PutSTDIN(buffer.c_str(), buffer.size());
|
||
|
|
}
|
||
|
|
}
|