#include "debug.h" #include #include #include #include #include #include #include #include #include #include "terminal.h" #include //TODO: remove using namespace std; void log(const char *msg, void *) { cout << "debugger log: " << msg << endl; } Debug::Debug(): listener("juCi++ lldb listener"), state(lldb::StateType::eStateInvalid), buffer_size(131072) { lldb::SBDebugger::Initialize(); debugger=lldb::SBDebugger::Create(true, log, nullptr); } void Debug::start(std::shared_ptr > > breakpoints, const boost::filesystem::path &executable, const boost::filesystem::path &path, std::function callback, std::function status_callback, std::function stop_callback) { auto target=debugger.CreateTarget(executable.string().c_str()); if(!target.IsValid()) { Terminal::get().async_print("Error (debug): Could not create debug target to: "+executable.string()+'\n', true); return; } for(auto &breakpoint: *breakpoints) { if(!(target.BreakpointCreateByLocation(breakpoint.first.string().c_str(), breakpoint.second)).IsValid()) { Terminal::get().async_print("Error (debug): Could not create breakpoint at: "+breakpoint.first.string()+":"+std::to_string(breakpoint.second)+'\n', true); return; } } lldb::SBError error; process = std::unique_ptr(new lldb::SBProcess(target.Launch(listener, nullptr, nullptr, nullptr, nullptr, nullptr, path.string().c_str(), lldb::eLaunchFlagNone, false, error))); if(error.Fail()) { Terminal::get().async_print(std::string("Error (debug): ")+error.GetCString()+'\n', true); return; } if(debug_thread.joinable()) debug_thread.join(); debug_thread=std::thread([this, breakpoints, executable, path, callback, status_callback, stop_callback]() { lldb::SBEvent event; while(true) { event_mutex.lock(); if(listener.GetNextEvent(event)) { if((event.GetType() & lldb::SBProcess::eBroadcastBitStateChanged)>0) { auto state=process->GetStateFromEvent(event); this->state=state; //Update debug status lldb::SBStream stream; event.GetDescription(stream); std::string event_desc=stream.GetData(); event_desc.pop_back(); auto pos=event_desc.rfind(" = "); if(status_callback && pos!=std::string::npos) status_callback(event_desc.substr(pos+3)); if(state==lldb::StateType::eStateStopped) { auto line_entry=process->GetSelectedThread().GetSelectedFrame().GetLineEntry(); if(stop_callback) { lldb::SBStream stream; line_entry.GetFileSpec().GetDescription(stream); stop_callback(stream.GetData(), line_entry.GetLine()); } } else if(state==lldb::StateType::eStateExited) { auto exit_status=process->GetExitStatus(); if(callback) callback(exit_status); if(status_callback) status_callback(""); if(stop_callback) stop_callback("", 0); process.reset(); this->state=lldb::StateType::eStateInvalid; event_mutex.unlock(); return; } else if(state==lldb::StateType::eStateCrashed) { if(callback) callback(-1); if(status_callback) status_callback(""); if(stop_callback) stop_callback("", 0); process.reset(); this->state=lldb::StateType::eStateInvalid; event_mutex.unlock(); return; } } if((event.GetType() & lldb::SBProcess::eBroadcastBitSTDOUT)>0) { char buffer[buffer_size]; size_t n; while((n=process->GetSTDOUT(buffer, buffer_size))!=0) Terminal::get().async_print(std::string(buffer, n)); } //TODO: for some reason stderr is redirected to stdout if((event.GetType() & lldb::SBProcess::eBroadcastBitSTDERR)>0) { char buffer[buffer_size]; size_t n; while((n=process->GetSTDERR(buffer, buffer_size))!=0) Terminal::get().async_print(std::string(buffer, n), true); } } event_mutex.unlock(); this_thread::sleep_for(std::chrono::milliseconds(200)); } }); } void Debug::continue_debug() { event_mutex.lock(); if(state==lldb::StateType::eStateStopped) process->Continue(); event_mutex.unlock(); } void Debug::stop() { event_mutex.lock(); if(state==lldb::StateType::eStateRunning) { auto error=process->Stop(); if(error.Fail()) Terminal::get().async_print(std::string("Error (debug): ")+error.GetCString()+'\n', true); } event_mutex.unlock(); } void Debug::kill() { event_mutex.lock(); if(process) { auto error=process->Kill(); if(error.Fail()) Terminal::get().async_print(std::string("Error (debug): ")+error.GetCString()+'\n', true); } event_mutex.unlock(); } std::pair Debug::run_command(const std::string &command) { std::pair command_return; event_mutex.lock(); if(state==lldb::StateType::eStateStopped) { lldb::SBCommandReturnObject command_return_object; debugger.GetCommandInterpreter().HandleCommand(command.c_str(), command_return_object, true); command_return.first=command_return_object.GetOutput(); command_return.second=command_return_object.GetError(); } event_mutex.unlock(); return command_return; } void Debug::delete_debug() { kill(); if(debug_thread.joinable()) debug_thread.join(); } std::string Debug::get_value(const std::string &variable, const boost::filesystem::path &file_path, unsigned int line_nr) { std::string variable_value; event_mutex.lock(); if(state==lldb::StateType::eStateStopped) { auto frame=process->GetSelectedThread().GetSelectedFrame(); auto values=frame.GetVariables(true, true, true, true); //First try to find variable based on name, file and line number for(uint32_t value_index=0;value_index