Browse Source

Added run debug command, and various fixes including more thread safe debug commands

merge-requests/365/head
eidheim 10 years ago
parent
commit
e941b563d9
  1. 155
      src/debug.cc
  2. 19
      src/debug.h
  3. 3
      src/files.h
  4. 6
      src/menu.cc
  5. 39
      src/window.cc
  6. 1
      src/window.h

155
src/debug.cc

@ -1,15 +1,16 @@
#include "debug.h"
#include <thread>
#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 <lldb/API/SBTarget.h>
#include <lldb/API/SBProcess.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 <lldb/API/SBCommandInterpreter.h>
#include <lldb/API/SBCommandReturnObject.h>
#include "terminal.h"
#include <iostream> //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,35 +28,38 @@ Debug::Debug(): stopped(false) {
void Debug::start(std::shared_ptr<std::vector<std::pair<boost::filesystem::path, int> > > breakpoints, const boost::filesystem::path &executable,
const boost::filesystem::path &path, std::function<void(int exit_status)> callback,
std::function<void(const std::string &status)> status_callback,
std::function<void(const boost::filesystem::path &file, int line)> stop_callback) {
std::thread debug_thread([this, breakpoints, executable, path, callback, status_callback, stop_callback]() {
std::function<void(const boost::filesystem::path &file_path, int line_nr)> 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
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()) {
cerr << "Error: Could not create breakpoint at: " << breakpoint.first << ":" << breakpoint.second << endl; //TODO: output to terminal instead
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<lldb::SBProcess>(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
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)) {
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;
@ -66,50 +70,13 @@ void Debug::start(std::shared_ptr<std::vector<std::pair<boost::filesystem::path,
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)) {
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());
}
/*lldb::SBStream stream;
process->GetSelectedThread().GetDescription(stream);
cout << stream.GetData() << endl;*/
for(uint32_t thread_index=0;thread_index<process->GetNumThreads();thread_index++) {
auto thread=process->GetThreadAtIndex(thread_index);
for(uint32_t frame_index=0;frame_index<thread.GetNumFrames();frame_index++) {
auto frame=thread.GetFrameAtIndex(frame_index);
auto values=frame.GetVariables(false, true, true, false);
for(uint32_t value_index=0;value_index<values.GetSize();value_index++) {
/*cout << thread_index << ", " << frame_index << endl;
lldb::SBStream stream;
process->GetDescription(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;*/
}
}
}
}
else if(state==lldb::StateType::eStateExited) {
@ -121,7 +88,8 @@ void Debug::start(std::shared_ptr<std::vector<std::pair<boost::filesystem::path,
if(stop_callback)
stop_callback("", 0);
process.reset();
stopped=false;
this->state=lldb::StateType::eStateInvalid;
event_mutex.unlock();
return;
}
@ -133,51 +101,94 @@ void Debug::start(std::shared_ptr<std::vector<std::pair<boost::filesystem::path,
if(stop_callback)
stop_callback("", 0);
process.reset();
stopped=false;
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));
}
});
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() {
event_mutex.lock();
if(state==lldb::StateType::eStateRunning) {
auto error=process->Stop();
if(error.Fail()) {
cerr << "Error (debug): " << error.GetCString() << endl; //TODO: output to terminal instead
return;
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()) {
cerr << "Error (debug): " << error.GetCString() << endl; //TODO: output to terminal instead
return;
if(error.Fail())
Terminal::get().async_print(std::string("Error (debug): ")+error.GetCString()+'\n', true);
}
event_mutex.unlock();
}
std::pair<std::string, std::string> Debug::run_command(const std::string &command) {
std::pair<std::string, std::string> 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<values.GetSize();value_index++) {
lldb::SBStream stream;
auto value=values.GetValueAtIndex(value_index);
if(value.GetName()==variable) {
value.GetDescription(stream);
return stream.GetData();
variable_value=stream.GetData();
break;
}
}
}
return std::string();
event_mutex.unlock();
return variable_value;
}

19
src/debug.h

@ -3,8 +3,10 @@
#include <boost/filesystem.hpp>
#include <unordered_map>
#include "lldb/API/SBDebugger.h"
#include "lldb/API/SBProcess.h"
#include <lldb/API/SBDebugger.h>
#include <lldb/API/SBListener.h>
#include <lldb/API/SBProcess.h>
#include <thread>
class Debug {
private:
@ -19,17 +21,26 @@ public:
const boost::filesystem::path &executable, const boost::filesystem::path &path="",
std::function<void(int exit_status)> callback=nullptr,
std::function<void(const std::string &status)> status_callback=nullptr,
std::function<void(const boost::filesystem::path &file, int line)> stop_callback=nullptr);
std::function<void(const boost::filesystem::path &file_path, int line_nr)> stop_callback=nullptr);
void continue_debug(); //can't use continue as function name
void stop();
void kill();
std::pair<std::string, std::string> run_command(const std::string &command);
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<lldb::SBProcess> process;
std::atomic<bool> stopped;
std::thread debug_thread;
lldb::StateType state;
std::mutex event_mutex;
size_t buffer_size;
};
#endif

3
src/files.h

@ -100,7 +100,8 @@ const std::string configjson =
" \"force_kill_last_running\": \"<primary><shift>Escape\",\n"
" \"debug_start_continue\": \"<primary>y\",\n"
" \"debug_stop\": \"<primary><shift>y\",\n"
" \"debug_kill\": \"<primary>k\",\n"
" \"debug_kill\": \"<primary><shift>k\",\n"
" \"debug_run_command\": \"<alt><shift>Return\",\n"
" \"debug_toggle_breakpoint\": \"<primary>b\",\n"
#ifdef __linux
" \"next_tab\": \"<primary>Tab\",\n"

6
src/menu.cc

@ -289,9 +289,9 @@ Menu::Menu() {
" </section>"
" <section>"
" <item>"
" <attribute name='label' translatable='yes'>_Continue</attribute>"
" <attribute name='action'>app.debug_continue</attribute>"
+accels["debug_continue"]+ //For Ubuntu...
" <attribute name='label' translatable='yes'>_Run Command</attribute>"
" <attribute name='action'>app.debug_run_command</attribute>"
+accels["debug_run_command"]+ //For Ubuntu...
" </item>"
" </section>"
" <section>"

39
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;c<executable_path_spaces_fixed.size();c++) {
@ -685,7 +687,7 @@ void Window::set_menu_actions() {
debug_status.set_text("");
else
debug_status.set_text("debug: "+status);
}, [this](const boost::filesystem::path &file, int line) {
}, [this](const boost::filesystem::path &file_path, int line_nr) {
//TODO: move to main thread
//Remove debug stop source mark
for(int c=0;c<notebook.size();c++) {
@ -701,9 +703,9 @@ void Window::set_menu_actions() {
//Add debug stop source mark
for(int c=0;c<notebook.size();c++) {
auto view=notebook.get_view(c);
if(view->file_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;
}

1
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;

Loading…
Cancel
Save