From e941b563d9ac6d58125c9d77c580a713ae20141e Mon Sep 17 00:00:00 2001 From: eidheim Date: Wed, 30 Dec 2015 13:18:13 +0100 Subject: [PATCH] Added run debug command, and various fixes including more thread safe debug commands --- src/debug.cc | 259 ++++++++++++++++++++++++++------------------------ src/debug.h | 21 +++- src/files.h | 3 +- src/menu.cc | 6 +- src/window.cc | 39 ++++++-- src/window.h | 1 + 6 files changed, 188 insertions(+), 141 deletions(-) diff --git a/src/debug.cc b/src/debug.cc index b3ed6e3..e1744e2 100644 --- a/src/debug.cc +++ b/src/debug.cc @@ -1,15 +1,16 @@ #include "debug.h" -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -#include "lldb/API/SBTarget.h" -#include "lldb/API/SBProcess.h" -#include "lldb/API/SBListener.h" -#include "lldb/API/SBEvent.h" -#include "lldb/API/SBBreakpoint.h" -#include "lldb/API/SBThread.h" -#include "lldb/API/SBStream.h" -#include "lldb/API/SBDeclaration.h" +#include "terminal.h" #include //TODO: remove using namespace std; @@ -18,7 +19,7 @@ void log(const char *msg, void *) { cout << "debugger log: " << msg << endl; } -Debug::Debug(): stopped(false) { +Debug::Debug(): listener("juCi++ lldb listener"), state(lldb::StateType::eStateInvalid), buffer_size(131072) { lldb::SBDebugger::Initialize(); debugger=lldb::SBDebugger::Create(true, log, nullptr); @@ -27,157 +28,167 @@ Debug::Debug(): stopped(false) { 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) { - std::thread debug_thread([this, breakpoints, executable, path, callback, status_callback, stop_callback]() { - auto target=debugger.CreateTarget(executable.string().c_str()); - auto listener=lldb::SBListener("juCi++ lldb listener"); - - if(!target.IsValid()) { - cerr << "Error: Could not create debug target to: " << executable << endl; //TODO: output to terminal instead - return; - } - - for(auto &breakpoint: *breakpoints) { - if(!(target.BreakpointCreateByLocation(breakpoint.first.string().c_str(), breakpoint.second)).IsValid()) { - cerr << "Error: Could not create breakpoint at: " << breakpoint.first << ":" << breakpoint.second << endl; //TODO: output to terminal instead - 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()) { - cerr << "Error (debug): " << error.GetCString() << endl; //TODO: output to terminal instead + 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) { - if(listener.WaitForEvent(3, event)) { - auto state=process->GetStateFromEvent(event); - - //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)); - - bool expected=false; - if(state==lldb::StateType::eStateStopped && stopped.compare_exchange_strong(expected, true)) { - 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()); - } + event_mutex.lock(); + if(listener.WaitForEvent(1, 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)); - /*lldb::SBStream stream; - process->GetSelectedThread().GetDescription(stream); - cout << stream.GetData() << endl;*/ - for(uint32_t thread_index=0;thread_indexGetNumThreads();thread_index++) { - auto thread=process->GetThreadAtIndex(thread_index); - for(uint32_t frame_index=0;frame_indexGetDescription(stream); - cout << stream.GetData() << endl; - - stream.Clear(); - process->GetSelectedThread().GetSelectedFrame().GetLineEntry().GetDescription(stream); - cout << stream.GetData() << endl;*/ - /*lldb::SBStream stream; - auto value=values.GetValueAtIndex(value_index); - - cout << value.GetFrame().GetSymbol().GetName() << endl; - - auto declaration = value.GetDeclaration(); - if(declaration.IsValid()) - cout << declaration.GetFileSpec().GetFilename() << ":" << declaration.GetLine() << ":" << declaration.GetColumn() << endl; - - value.GetDescription(stream); - cout << " " << stream.GetData() << endl; - stream.Clear(); - - value.GetData().GetDescription(stream); - cout << " " << stream.GetData() << endl;*/ - } + 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; + } } - - 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(); - stopped=false; - 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)); } - - else if(state==lldb::StateType::eStateCrashed) { - if(callback) - callback(-1); - if(status_callback) - status_callback(""); - if(stop_callback) - stop_callback("", 0); - process.reset(); - stopped=false; - return; + //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)); } }); - debug_thread.detach(); } void Debug::continue_debug() { - bool expected=true; - if(stopped.compare_exchange_strong(expected, false)) + event_mutex.lock(); + if(state==lldb::StateType::eStateStopped) process->Continue(); + event_mutex.unlock(); } void Debug::stop() { - auto error=process->Stop(); - if(error.Fail()) { - cerr << "Error (debug): " << error.GetCString() << endl; //TODO: output to terminal instead - return; + 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() { - auto error=process->Kill(); - if(error.Fail()) { - cerr << "Error (debug): " << error.GetCString() << endl; //TODO: output to terminal instead - return; + 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) { - if(stopped) { + std::string variable_value; + event_mutex.lock(); + if(state==lldb::StateType::eStateStopped) { auto frame=process->GetSelectedThread().GetSelectedFrame(); - auto values=frame.GetVariables(false, true, false, false); + auto values=frame.GetVariables(true, true, true, true); for(uint32_t value_index=0;value_index #include -#include "lldb/API/SBDebugger.h" -#include "lldb/API/SBProcess.h" +#include +#include +#include +#include class Debug { private: @@ -19,17 +21,26 @@ public: const boost::filesystem::path &executable, const boost::filesystem::path &path="", std::function callback=nullptr, std::function status_callback=nullptr, - std::function stop_callback=nullptr); + std::function stop_callback=nullptr); void continue_debug(); //can't use continue as function name void stop(); void kill(); + std::pair run_command(const std::string &command); - std::string get_value(const std::string &variable); + void delete_debug(); //can't use delete as function name + std::string get_value(const std::string &variable); + private: lldb::SBDebugger debugger; + lldb::SBListener listener; std::unique_ptr process; - std::atomic stopped; + std::thread debug_thread; + + lldb::StateType state; + std::mutex event_mutex; + + size_t buffer_size; }; #endif diff --git a/src/files.h b/src/files.h index 8628b56..1cd0965 100644 --- a/src/files.h +++ b/src/files.h @@ -100,7 +100,8 @@ const std::string configjson = " \"force_kill_last_running\": \"Escape\",\n" " \"debug_start_continue\": \"y\",\n" " \"debug_stop\": \"y\",\n" -" \"debug_kill\": \"k\",\n" +" \"debug_kill\": \"k\",\n" +" \"debug_run_command\": \"Return\",\n" " \"debug_toggle_breakpoint\": \"b\",\n" #ifdef __linux " \"next_tab\": \"Tab\",\n" diff --git a/src/menu.cc b/src/menu.cc index 1fccdeb..b8900e8 100644 --- a/src/menu.cc +++ b/src/menu.cc @@ -289,9 +289,9 @@ Menu::Menu() { " " "
" " " - " _Continue" - " app.debug_continue" - +accels["debug_continue"]+ //For Ubuntu... + " _Run Command" + " app.debug_run_command" + +accels["debug_run_command"]+ //For Ubuntu... " " "
" "
" diff --git a/src/window.cc b/src/window.cc index 64cfabc..37c729c 100644 --- a/src/window.cc +++ b/src/window.cc @@ -658,14 +658,16 @@ void Window::set_menu_actions() { if(project_path==view->project_path) { auto iter=view->get_buffer()->begin(); if(view->get_source_buffer()->get_source_marks_at_iter(iter, "debug_breakpoint").size()>0) - breakpoints->emplace_back(view->file_path.filename(), iter.get_line()+1); + breakpoints->emplace_back(view->file_path, iter.get_line()+1); while(view->get_source_buffer()->forward_iter_to_source_mark(iter, "debug_breakpoint")) - breakpoints->emplace_back(view->file_path.filename(), iter.get_line()+1); + breakpoints->emplace_back(view->file_path, iter.get_line()+1); } } - Terminal::get().print("Compiling and running "+executable_path.string()+"\n"); + Terminal::get().print("Compiling and debugging "+executable_path.string()+"\n"); Terminal::get().async_process(Config::get().terminal.make_command, debug_build_path, [this, breakpoints, executable_path, debug_build_path](int exit_status){ - if(exit_status==EXIT_SUCCESS) { + if(exit_status!=EXIT_SUCCESS) + debugging=false; + else { auto executable_path_spaces_fixed=executable_path.string(); char last_char=0; for(size_t c=0;cfile_path==file) { - view->get_source_buffer()->create_source_mark("debug_stop", view->get_buffer()->get_iter_at_line(line-1)); - debug_last_stop_line={file, line}; + if(view->file_path==file_path) { + view->get_source_buffer()->create_source_mark("debug_stop", view->get_buffer()->get_iter_at_line(line_nr-1)); + debug_last_stop_line={file_path, line_nr}; } } }); @@ -726,6 +728,26 @@ void Window::set_menu_actions() { Debug::get().kill(); } }); + menu.add_action("debug_run_command", [this]() { + entry_box.clear(); + entry_box.entries.emplace_back(last_run_debug_command, [this](const std::string& content){ + if(content!="") { + if(debugging) { + auto command_return=Debug::get().run_command(content); + Terminal::get().async_print(command_return.first); + Terminal::get().async_print(command_return.second, true); + } + last_run_debug_command=content; + } + entry_box.hide(); + }, 30); + auto entry_it=entry_box.entries.begin(); + entry_it->set_placeholder_text("Debug Command"); + entry_box.buttons.emplace_back("Run debug command", [this, entry_it](){ + entry_it->activate(); + }); + entry_box.show(); + }); menu.add_action("debug_toggle_breakpoint", [this](){ if(notebook.get_current_page()!=-1) { auto view=notebook.get_current_view(); @@ -824,6 +846,7 @@ bool Window::on_delete_event(GdkEventAny *event) { return true; } Terminal::get().kill_async_processes(); + Debug::get().delete_debug(); return false; } diff --git a/src/window.h b/src/window.h index b8439c6..fe9e71a 100644 --- a/src/window.h +++ b/src/window.h @@ -47,6 +47,7 @@ private: std::string last_search; std::string last_replace; std::string last_run_command; + std::string last_run_debug_command; bool case_sensitive_search=true; bool regex_search=false; bool search_entry_shown=false;