[nemiver/dbg-console: 2/3] Add a command interpreter interface for DBGEngine
- From: Fabien Parent <fparent src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [nemiver/dbg-console: 2/3] Add a command interpreter interface for DBGEngine
- Date: Mon, 28 Oct 2013 17:57:50 +0000 (UTC)
commit 5fe98c6f90f09846f6cabd7e39bd4333cb22ec9b
Author: Fabien Parent <parent f gmail com>
Date: Fri Oct 11 22:23:17 2013 +0200
Add a command interpreter interface for DBGEngine
The commands added with the command interpreter are using the same syntax
as in gdb.
The commands implemented by this commit are:
load-exec: load an executable,
break: set a breakpoint,
next: step out,
nexti: step over asm,
step: step in,
stepi: step into asm,
continue: continue the execution of the inferior,
run: run the inferior,
stop: stop the inferior,
thread: change the current thread,
call: call a function,
print: print the value of a variable.
One can provide commands to execute at Nemiver's start-up in the following ways:
- Load a file of commands by passing a file path to the --exec-command-file
option on the command line.
- Write commands to Nemiver's STDIN (echo "next; next" | nemiver ./foobar)
- Use a file as Nemiver's STDIN (nemiver ./foobar < /my/file).
* src/common/nmv-str-utils.h:
(to_string): New API.
(from_string): New API.
* src/dbgengine/Makefile.am: Add new library.
* src/dbgengine/nmv-cmd-interpreter.cc: New API.
* src/dbgengine/nmv-cmd-interpreter.h: New API.
* src/main.cc
(execute_commands): New API.
(on_stopped_signal): New API.
(process_gui_options): Execute commands passed from console.
* src/persp/dbgperspective/nmv-dbg-perspective.cc
(DBGPerspective::execute_commands_from_line): New API.
(DBGPerspective::execute_commands_from_file): New API.
(DBGPerspective::execute_commands_from_fd): New API.
* src/persp/dbgperspective/nmv-dbg-perspective.h
(DBGPerspective::execute_commands_from_line): New API.
(DBGPerspective::execute_commands_from_file): New API.
* tests/Makefile.am: Add new test.
* tests/test-cmd-interpreter.cc: New test file.
configure.ac | 5 +
src/common/nmv-str-utils.h | 20 +
src/dbgengine/Makefile.am | 11 +-
src/dbgengine/nmv-cmd-interpreter.cc | 933 +++++++++++++++++++++++
src/dbgengine/nmv-cmd-interpreter.h | 179 +++++
src/main.cc | 59 ++-
src/persp/dbgperspective/nmv-dbg-perspective.cc | 75 ++
src/persp/dbgperspective/nmv-dbg-perspective.h | 8 +
tests/Makefile.am | 8 +-
tests/test-cmd-interpreter.cc | 113 +++
10 files changed, 1407 insertions(+), 4 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index d5d6ad5..134fba6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -382,6 +382,11 @@ PKG_CHECK_MODULES(NEMIVERDBGPERSP, $DEP_UICOMMON $DEP_VFS $DEP_PERSP $DEP_MEMORY
NEMIVERDBGPERSP_LIBS="$NEMIVERDBGPERSP_LIBS $CPPUNIT_LIBS"
NEMIVERDBGPERSP_CFLAGS="$NEMIVERDBGPERSP_CFLAGS $CPPUNIT_CFLAGS"
+AC_CHECK_HEADERS([readline/readline.h readline/history.h],
+ [],
+ [AC_MSG_ERROR([Please, install readline development headers])])
+NEMIVERDBGPERSP_LIBS="$NEMIVERDBGPERSP_LIBS -lreadline"
+
AC_SUBST(NEMIVERDBGPERSP_LIBS)
AC_SUBST(NEMIVERDBGPERSP_CFLAGS)
diff --git a/src/common/nmv-str-utils.h b/src/common/nmv-str-utils.h
index 8c72b8c..9a9f856 100644
--- a/src/common/nmv-str-utils.h
+++ b/src/common/nmv-str-utils.h
@@ -25,6 +25,7 @@
#ifndef __NMV_STR_UTILS_H__
#define __NMV_STR_UTILS_H__
#include "nmv-ustring.h"
+#include <sstream>
NEMIVER_BEGIN_NAMESPACE (nemiver)
NEMIVER_BEGIN_NAMESPACE (str_utils)
@@ -52,6 +53,25 @@ UString join (vector<UString>::const_iterator &a_from,
vector<UString>::const_iterator &a_to,
const UString &a_delim=" ");
+template<typename T>
+T
+from_string (const std::string &a_string)
+{
+ std::istringstream is (a_string);
+ T out;
+ is >> out;
+ return out;
+}
+
+template<typename T>
+std::string
+to_string (const T &a_value)
+{
+ std::ostringstream os;
+ os << a_value;
+ return os.str ();
+}
+
template<typename S>
void
chomp (S &a_string)
diff --git a/src/dbgengine/Makefile.am b/src/dbgengine/Makefile.am
index a4bafe9..8b45e80 100644
--- a/src/dbgengine/Makefile.am
+++ b/src/dbgengine/Makefile.am
@@ -20,7 +20,8 @@ noinst_LTLIBRARIES=\
libgdbmiparser.la \
libdbgcommon.la \
libdebuggerutils.la \
-libgdbengine.la
+libgdbengine.la \
+libcmdinterpreter.la
h=$(abs_srcdir)
@@ -85,6 +86,12 @@ $(h)/nmv-debugger-utils.cc
libdebuggerutils_la_CFLAGS=-fPIC -DPIC
+libcmdinterpreter_la_SOURCES= \
+$(h)/nmv-cmd-interpreter.cc \
+$(h)/nmv-cmd-interpreter.h
+
+libcmdinterpreter_la_CFLAGS=-fPIC -DPIC
+
libgdbengine_la_SOURCES= \
$(h)/nmv-gdb-engine.cc \
$(h)/nmv-gdb-engine.h
@@ -96,7 +103,7 @@ publicheadersdir=$(NEMIVER_INCLUDE_DIR)/dynmods
libgdbmod_la_LDFLAGS=-module -avoid-version -Wl,--as-needed
libgdbmod_la_LIBADD=libgdbmiparser.la \
-libgdbengine.la libdebuggerutils.la @NEMIVERCOMMON_LIBS@ \
+libgdbengine.la libdebuggerutils.la libcmdinterpreter.la @NEMIVERCOMMON_LIBS@ \
$(abs_top_builddir)/src/langs/libnemivercparser.la \
$(abs_top_builddir)/src/common/libnemivercommon.la
diff --git a/src/dbgengine/nmv-cmd-interpreter.cc b/src/dbgengine/nmv-cmd-interpreter.cc
new file mode 100644
index 0000000..70c696d
--- /dev/null
+++ b/src/dbgengine/nmv-cmd-interpreter.cc
@@ -0,0 +1,933 @@
+//Author: Fabien Parent
+/*
+ *This file is part of the Nemiver project
+ *
+ *Nemiver is free software; you can redistribute
+ *it and/or modify it under the terms of
+ *the GNU General Public License as published by the
+ *Free Software Foundation; either version 2,
+ *or (at your option) any later version.
+ *
+ *Nemiver is distributed in the hope that it will
+ *be useful, but WITHOUT ANY WARRANTY;
+ *without even the implied warranty of
+ *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *See the GNU General Public License for more details.
+ *
+ *You should have received a copy of the
+ *GNU General Public License along with Nemiver;
+ *see the file COPYING.
+ *If not, write to the Free Software Foundation,
+ *Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *See COPYRIGHT file copyright information.
+ */
+
+
+#include "nmv-cmd-interpreter.h"
+#include "nmv-i-debugger.h"
+#include "common/nmv-str-utils.h"
+#include <map>
+#include <queue>
+
+NEMIVER_BEGIN_NAMESPACE(nemiver)
+
+const char *const NEMIVER_CMD_INTERPRETER_COOKIE = "nemiver-cmd-interpreter";
+const unsigned int COMMAND_EXECUTION_TIMEOUT_IN_SECONDS = 10;
+
+struct DebuggingData {
+ IDebugger &debugger;
+ IDebugger::Frame current_frame;
+ UString current_file_path;
+ std::vector<UString> source_files;
+
+ DebuggingData (IDebugger &a_debugger) :
+ debugger (a_debugger)
+ {
+ }
+};
+
+struct ContinueCommand : public CmdInterpreter::Command {
+ IDebugger &debugger;
+
+ ContinueCommand (IDebugger &a_debugger) :
+ debugger (a_debugger)
+ {
+ }
+
+ const std::string&
+ name () const
+ {
+ static const std::string &s_name = "continue";
+ return s_name;
+ }
+
+ const std::vector<UString>&
+ aliases () const
+ {
+ static std::vector<UString> s_aliases;
+ if (!s_aliases.size ()) {
+ s_aliases.push_back ("c");
+ }
+ return s_aliases;
+ }
+
+ bool
+ execute (const std::vector<UString>&, std::ostream&)
+ {
+ debugger.do_continue (NEMIVER_CMD_INTERPRETER_COOKIE);
+ return true;
+ }
+};
+
+struct NextCommand : public CmdInterpreter::Command {
+ IDebugger &debugger;
+
+ NextCommand (IDebugger &a_debugger) :
+ debugger (a_debugger)
+ {
+ }
+
+ const std::string&
+ name () const
+ {
+ static const std::string &s_name = "next";
+ return s_name;
+ }
+
+ const std::vector<UString>&
+ aliases () const
+ {
+ static std::vector<UString> s_aliases;
+ if (!s_aliases.size ()) {
+ s_aliases.push_back ("n");
+ }
+ return s_aliases;
+ }
+
+ bool
+ execute (const std::vector<UString>&, std::ostream&)
+ {
+ debugger.step_over (NEMIVER_CMD_INTERPRETER_COOKIE);
+ return true;
+ }
+};
+
+struct StepCommand : public CmdInterpreter::Command {
+ IDebugger &debugger;
+
+ StepCommand (IDebugger &a_debugger) :
+ debugger (a_debugger)
+ {
+ }
+
+ const std::string&
+ name () const
+ {
+ static const std::string &s_name = "step";
+ return s_name;
+ }
+
+ const std::vector<UString>&
+ aliases () const
+ {
+ static std::vector<UString> s_aliases;
+ if (!s_aliases.size ()) {
+ s_aliases.push_back ("s");
+ }
+ return s_aliases;
+ }
+
+ bool
+ execute (const std::vector<UString>&, std::ostream&)
+ {
+ debugger.step_in (NEMIVER_CMD_INTERPRETER_COOKIE);
+ return true;
+ }
+};
+
+struct NextiCommand : public CmdInterpreter::Command {
+ IDebugger &debugger;
+
+ NextiCommand (IDebugger &a_debugger) :
+ debugger (a_debugger)
+ {
+ }
+
+ const std::string&
+ name () const
+ {
+ static const std::string &s_name = "nexti";
+ return s_name;
+ }
+
+ const std::vector<UString>&
+ aliases () const
+ {
+ static std::vector<UString> s_aliases;
+ if (!s_aliases.size ()) {
+ s_aliases.push_back ("ni");
+ }
+ return s_aliases;
+ }
+
+ bool
+ execute (const std::vector<UString>&, std::ostream&)
+ {
+ debugger.step_over_asm (NEMIVER_CMD_INTERPRETER_COOKIE);
+ return true;
+ }
+};
+
+struct StepiCommand : public CmdInterpreter::Command {
+ IDebugger &debugger;
+
+ StepiCommand (IDebugger &a_debugger) :
+ debugger (a_debugger)
+ {
+ }
+
+ const std::string&
+ name () const
+ {
+ static const std::string &s_name = "stepi";
+ return s_name;
+ }
+
+ const std::vector<UString>&
+ aliases () const
+ {
+ static std::vector<UString> s_aliases;
+ if (!s_aliases.size ()) {
+ s_aliases.push_back ("si");
+ }
+ return s_aliases;
+ }
+
+ bool
+ execute (const std::vector<UString>&, std::ostream&)
+ {
+ debugger.step_in_asm (NEMIVER_CMD_INTERPRETER_COOKIE);
+ return true;
+ }
+};
+
+struct StopCommand : public CmdInterpreter::Command {
+ IDebugger &debugger;
+
+ StopCommand (IDebugger &a_debugger) :
+ debugger (a_debugger)
+ {
+ }
+
+ const std::string&
+ name () const
+ {
+ static const std::string &s_name = "stop";
+ return s_name;
+ }
+
+ bool
+ execute (const std::vector<UString>&, std::ostream&)
+ {
+ debugger.stop_target ();
+ return true;
+ }
+};
+
+struct FinishCommand : public CmdInterpreter::Command {
+ IDebugger &debugger;
+
+ FinishCommand (IDebugger &a_debugger) :
+ debugger (a_debugger)
+ {
+ }
+
+ const std::string&
+ name () const
+ {
+ static const std::string &s_name = "finish";
+ return s_name;
+ }
+
+ bool
+ execute (const std::vector<UString>&, std::ostream&)
+ {
+ debugger.step_out (NEMIVER_CMD_INTERPRETER_COOKIE);
+ return true;
+ }
+};
+
+struct CallCommand : public CmdInterpreter::Command {
+ IDebugger &debugger;
+ std::string cmd;
+
+ CallCommand (IDebugger &a_debugger) :
+ debugger (a_debugger)
+ {
+ }
+
+ const std::string&
+ name () const
+ {
+ static const std::string &s_name = "call";
+ return s_name;
+ }
+
+ bool
+ execute (const std::vector<UString> &a_argv, std::ostream &a_stream)
+ {
+ if (a_argv.size ()) {
+ cmd = str_utils::join (a_argv);
+ }
+
+ if (cmd.empty ()) {
+ a_stream << "The history is empty.\n";
+ } else {
+ debugger.call_function (cmd, NEMIVER_CMD_INTERPRETER_COOKIE);
+ }
+
+ return true;
+ }
+};
+
+struct ThreadCommand : public CmdInterpreter::Command {
+ IDebugger &debugger;
+
+ ThreadCommand (IDebugger &a_debugger) :
+ debugger (a_debugger)
+ {
+ }
+
+ const std::string&
+ name () const
+ {
+ static const std::string &s_name = "thread";
+ return s_name;
+ }
+
+ void
+ completions (const std::vector<UString> &a_argv,
+ std::vector<UString> &a_completion_vector) const
+ {
+ if (a_argv.size () == 0) {
+ a_completion_vector.push_back ("list");
+ }
+ }
+
+ void
+ display_usage (const std::vector<UString> &a_argv,
+ std::ostream &a_stream) const
+ {
+ if (a_argv.size ()) {
+ return;
+ }
+
+ a_stream << "Usage:\n"
+ << "\tthread\n"
+ << "\tthread [THREAD ID]\n"
+ << "\tthread list\n";
+ }
+
+ void
+ threads_listed_signal (const std::list<int> a_thread_ids,
+ const UString &a_cookie,
+ std::ostream &a_stream)
+ {
+ NEMIVER_TRY;
+
+ if (a_cookie != NEMIVER_CMD_INTERPRETER_COOKIE) {
+ return;
+ }
+
+ a_stream << "Threads:\n";
+ for (std::list<int>::const_iterator iter = a_thread_ids.begin ();
+ iter != a_thread_ids.end ();
+ ++iter) {
+ a_stream << *iter << "\n";
+ }
+ done_signal ().emit ();
+
+ NEMIVER_CATCH_NOX;
+ }
+
+ bool
+ execute (const std::vector<UString> &a_argv, std::ostream &a_stream)
+ {
+ if (a_argv.size () > 1) {
+ a_stream << "Too much parameters.\n";
+ } else if (!a_argv.size ()) {
+ a_stream << "Current thread ID: " << debugger.get_current_thread ()
+ << ".\n";
+ } else if (str_utils::string_is_number (a_argv[0])) {
+ debugger.select_thread
+ (str_utils::from_string<unsigned int> (a_argv[0]));
+ } else if (a_argv[0] == "list") {
+ debugger.threads_listed_signal ().connect
+ (sigc::bind<std::ostream&> (sigc::mem_fun
+ (*this, &ThreadCommand::threads_listed_signal),
+ a_stream));
+ debugger.list_threads (NEMIVER_CMD_INTERPRETER_COOKIE);
+ return false;
+ } else {
+ a_stream << "Invalid argument: " << a_argv[0] << ".\n";
+ }
+
+ return true;
+ }
+};
+
+struct BreakCommand : public CmdInterpreter::Command {
+ DebuggingData &dbg_data;
+
+ BreakCommand (DebuggingData &a_dbg_data) :
+ dbg_data (a_dbg_data)
+ {
+ }
+
+ const std::string&
+ name () const
+ {
+ static const std::string &s_name = "break";
+ return s_name;
+ }
+
+ const std::vector<UString>&
+ aliases () const
+ {
+ static std::vector<UString> s_aliases;
+ if (!s_aliases.size ()) {
+ s_aliases.push_back ("b");
+ }
+ return s_aliases;
+ }
+
+ void
+ display_usage (const std::vector<UString>&, std::ostream &a_stream) const
+ {
+ a_stream << "Usage:\n"
+ << "\tbreak\n"
+ << "\tbreak [LINE]\n"
+ << "\tbreak [FUNCTION]\n"
+ << "\tbreak *[ADDRESS]\n"
+ << "\tbreak +[OFFSET]\n"
+ << "\tbreak -[OFFSET]\n";
+ }
+
+ void
+ break_at_current_line (std::ostream &a_stream)
+ {
+ IDebugger::Frame &frame = dbg_data.current_frame;
+ IDebugger &debugger = dbg_data.debugger;
+
+ if (frame.file_full_name ().empty ()) {
+ a_stream << "Cannot set a breakpoint at this position.\n";
+ } else {
+ debugger.set_breakpoint (frame.file_full_name (), frame.line ());
+ }
+ }
+
+ void
+ break_at_line (const std::vector<UString> &a_argv,
+ std::ostream &a_stream)
+ {
+ IDebugger &debugger = dbg_data.debugger;
+
+ if (dbg_data.current_file_path.empty ()) {
+ a_stream << "Cannot set a breakpoint at this position.\n";
+ } else {
+ THROW_IF_FAIL (a_argv.size());
+ debugger.set_breakpoint (dbg_data.current_file_path,
+ str_utils::from_string<int> (a_argv[0]));
+ }
+ }
+
+ void
+ break_at_offset (const std::vector<UString> &a_argv,
+ std::ostream &a_stream)
+ {
+ IDebugger::Frame &frame = dbg_data.current_frame;
+ IDebugger &debugger = dbg_data.debugger;
+
+ if (frame.file_full_name ().empty ()) {
+ a_stream << "Cannot set a breakpoint at this position.\n";
+ return;
+ }
+
+ THROW_IF_FAIL (a_argv.size());
+ std::string offset (a_argv[0].substr (1));
+ if (!str_utils::string_is_decimal_number (offset)) {
+ a_stream << "Invalid offset: " << offset << ".\n";
+ return;
+ }
+
+ int line = frame.line ();
+ if (a_argv[0][0] == '+') {
+ line += str_utils::from_string<int> (offset);
+ } else {
+ line -= str_utils::from_string<int> (offset);
+ }
+
+ debugger.set_breakpoint
+ (frame.file_full_name (), line, NEMIVER_CMD_INTERPRETER_COOKIE);
+ }
+
+ void
+ break_at_address (const std::vector<UString> &a_argv,
+ std::ostream &a_stream)
+ {
+ IDebugger &debugger = dbg_data.debugger;
+
+ THROW_IF_FAIL (a_argv.size());
+ std::string addr (a_argv[0].substr (1));
+ if (!str_utils::string_is_hexa_number (addr)) {
+ a_stream << "Invalid address: " << addr << ".\n";
+ } else {
+ debugger.set_breakpoint
+ (Address (addr), NEMIVER_CMD_INTERPRETER_COOKIE);
+ }
+ }
+
+ bool
+ execute (const std::vector<UString> &a_argv, std::ostream &a_stream)
+ {
+ IDebugger &debugger = dbg_data.debugger;
+
+ if (a_argv.size () > 1) {
+ a_stream << "Too much parameters.\n";
+ return true;
+ }
+
+ if (a_argv.size () == 0) {
+ break_at_current_line (a_stream);
+ return true;
+ }
+
+ const char first_param_char = a_argv[0][0];
+ if (str_utils::string_is_number (a_argv[0])) {
+ break_at_line (a_argv, a_stream);
+ } else if ((first_param_char >= 'a' && first_param_char <= 'z')
+ || first_param_char == '_') {
+ debugger.set_breakpoint
+ (a_argv[0], "", 0, NEMIVER_CMD_INTERPRETER_COOKIE);
+ } else if (first_param_char == '*') {
+ break_at_address (a_argv, a_stream);
+ } else if (first_param_char == '+' || first_param_char == '-') {
+ break_at_offset (a_argv, a_stream);
+ } else {
+ a_stream << "Invalid argument: " << a_argv[0] << ".\n";
+ }
+
+ return true;
+ }
+};
+
+struct PrintCommand : public CmdInterpreter::Command {
+ IDebugger &debugger;
+ std::string expression;
+
+ PrintCommand (IDebugger &a_debugger) :
+ debugger (a_debugger)
+ {
+ }
+
+ const std::string&
+ name () const
+ {
+ static const std::string &s_name = "print";
+ return s_name;
+ }
+
+ void
+ on_variable_created_signal (const IDebugger::VariableSafePtr a_var,
+ std::ostream &a_stream)
+ {
+ NEMIVER_TRY;
+
+ THROW_IF_FAIL (a_var);
+ a_stream << a_var->name () << " = " << a_var->value () << "\n";
+ debugger.delete_variable (a_var);
+ done_signal ().emit ();
+
+ NEMIVER_CATCH_NOX;
+ }
+
+ bool
+ execute (const std::vector<UString> &a_argv, std::ostream &a_stream)
+ {
+ if (a_argv.size ()) {
+ expression.clear ();
+ }
+
+ for (std::vector<UString>::const_iterator iter = a_argv.begin ();
+ iter != a_argv.end ();
+ ++iter) {
+ expression += *iter;
+ }
+
+ if (expression.empty ()) {
+ a_stream << "No history\n";
+ return true;
+ }
+
+ debugger.create_variable
+ (expression, sigc::bind<std::ostream&>
+ (sigc::mem_fun
+ (*this, &PrintCommand::on_variable_created_signal),
+ a_stream));
+ return false;
+ }
+};
+
+struct LoadExecCommand : public CmdInterpreter::Command {
+ IDebugger &debugger;
+
+ LoadExecCommand (IDebugger &a_debugger) :
+ debugger (a_debugger)
+ {
+ }
+
+ const std::string&
+ name () const
+ {
+ static const std::string &s_name = "load-exec";
+ return s_name;
+ }
+
+ void
+ display_usage (const std::vector<UString>&, std::ostream &a_stream) const
+ {
+ a_stream << "Usage:\n"
+ << "\tload-exec PROGRAM_NAME [ARG1 ARG2 ...]\n";
+ }
+
+ bool
+ execute (const std::vector<UString> &a_argv, std::ostream &a_stream)
+ {
+ std::vector<UString> argv;
+ if (!a_argv.size ()) {
+ display_usage (argv, a_stream);
+ return true;
+ }
+
+ for (std::vector<UString>::const_iterator iter = a_argv.begin ();
+ iter != a_argv.end ();
+ ++iter) {
+ UString path = *iter;
+ if (path.size () && path[0] == '~') {
+ path = path.replace (0, 1, Glib::get_home_dir ());
+ }
+ argv.push_back (path);
+ }
+
+ UString prog = argv[0];
+ argv.erase (argv.begin ());
+
+ if (!debugger.load_program (prog, argv)) {
+ a_stream << "Could not load program '" << prog << "'.\n";
+ }
+
+ return true;
+ }
+};
+
+struct RunCommand : public CmdInterpreter::Command {
+ IDebugger &debugger;
+
+ RunCommand (IDebugger &a_debugger) :
+ debugger (a_debugger)
+ {
+ }
+
+ const std::string&
+ name () const
+ {
+ static const std::string &s_name = "run";
+ return s_name;
+ }
+
+ bool
+ execute (const std::vector<UString>&, std::ostream&)
+ {
+ debugger.run (NEMIVER_CMD_INTERPRETER_COOKIE);
+ return true;
+ }
+};
+
+struct CmdInterpreter::Priv {
+ std::vector<CommandSafePtr> commands;
+ std::vector<CmdInterpreter::Command*> command_vector;
+ std::map<std::string, CmdInterpreter::Command&> command_map;
+ std::queue<UString> command_queue;
+ std::ostream &output_stream;
+ sigc::signal<void> ready_signal;
+
+ sigc::connection cmd_execution_done_connection;
+ sigc::connection cmd_execution_timeout_connection;
+ bool has_a_command_running;
+
+ DebuggingData data;
+
+ Priv (IDebugger &a_debugger, std::ostream &a_output_stream) :
+ output_stream (a_output_stream),
+ has_a_command_running (false),
+ data (a_debugger)
+ {
+ init_commands ();
+ init_signals ();
+ }
+
+ void
+ init_commands ()
+ {
+ commands.push_back (CommandSafePtr (new NextCommand (data.debugger)));
+ commands.push_back (CommandSafePtr (new StepCommand (data.debugger)));
+ commands.push_back (CommandSafePtr (new BreakCommand (data)));
+ commands.push_back (CommandSafePtr (new PrintCommand (data.debugger)));
+ commands.push_back (CommandSafePtr (new CallCommand (data.debugger)));
+ commands.push_back (CommandSafePtr (new FinishCommand (data.debugger)));
+ commands.push_back (CommandSafePtr (new ThreadCommand (data.debugger)));
+ commands.push_back (CommandSafePtr (new StopCommand (data.debugger)));
+ commands.push_back (CommandSafePtr (new NextiCommand (data.debugger)));
+ commands.push_back (CommandSafePtr (new StepiCommand (data.debugger)));
+ commands.push_back (CommandSafePtr (new RunCommand (data.debugger)));
+ commands.push_back
+ (CommandSafePtr (new LoadExecCommand (data.debugger)));
+ commands.push_back
+ (CommandSafePtr (new ContinueCommand (data.debugger)));
+ }
+
+ void
+ init_signals ()
+ {
+ data.debugger.stopped_signal ().connect
+ (sigc::mem_fun (*this, &CmdInterpreter::Priv::on_stopped_signal));
+ data.debugger.files_listed_signal ().connect (sigc::mem_fun
+ (*this, &CmdInterpreter::Priv::on_files_listed_signal));
+ data.debugger.state_changed_signal ().connect (sigc::mem_fun
+ (*this, &CmdInterpreter::Priv::on_state_changed_signal));
+ }
+
+ void
+ on_state_changed_signal (IDebugger::State a_state)
+ {
+ if (a_state == IDebugger::READY) {
+ process_command_queue ();
+ }
+ }
+
+ void
+ on_stopped_signal (IDebugger::StopReason,
+ bool,
+ const IDebugger::Frame &a_frame,
+ int,
+ const string&,
+ const UString&)
+ {
+ data.current_frame = a_frame;
+ data.current_file_path = a_frame.file_full_name ();
+ }
+
+ void
+ on_files_listed_signal (const std::vector<UString> &a_files, const UString&)
+ {
+ data.source_files = a_files;
+ }
+
+ bool
+ execute_command (const UString &a_buffer)
+ {
+ THROW_IF_FAIL (!has_a_command_running);
+
+ std::string command_name;
+ std::vector<UString> cmd_argv;
+
+ std::istringstream is (a_buffer);
+ is >> command_name;
+
+ while (is.good ()) {
+ std::string arg;
+ is >> arg;
+ cmd_argv.push_back (arg);
+ }
+
+ if (command_name.empty ()) {
+ ready_signal.emit ();
+ return has_a_command_running;
+ }
+
+ if (!command_map.count (command_name)) {
+ output_stream << "Undefined command: " << command_name << ".\n";
+ ready_signal.emit ();
+ return has_a_command_running;
+ }
+
+ Command &command = command_map.at (command_name);
+ has_a_command_running = true;
+ cmd_execution_done_connection = command.done_signal ().connect
+ (sigc::mem_fun (*this, &CmdInterpreter::Priv::on_done_signal));
+ command (cmd_argv, output_stream);
+ cmd_execution_timeout_connection =
+ Glib::signal_timeout().connect_seconds (sigc::mem_fun
+ (*this, &CmdInterpreter::Priv::on_cmd_execution_timeout_signal),
+ COMMAND_EXECUTION_TIMEOUT_IN_SECONDS);
+
+ return has_a_command_running;
+ }
+
+ bool
+ on_cmd_execution_timeout_signal ()
+ {
+ NEMIVER_TRY;
+ on_done_signal ();
+ NEMIVER_CATCH_NOX;
+
+ return true;
+ }
+
+ void
+ on_done_signal ()
+ {
+ NEMIVER_TRY;
+
+ if (!has_a_command_running) {
+ return;
+ }
+
+ has_a_command_running = false;
+ cmd_execution_done_connection.disconnect ();
+ cmd_execution_timeout_connection.disconnect ();
+
+ if (command_queue.size ()) {
+ process_command_queue ();
+ } else {
+ ready_signal.emit ();
+ }
+
+ NEMIVER_CATCH_NOX;
+ }
+
+ void
+ process_command_queue ()
+ {
+ bool is_busy = has_a_command_running;
+ while (!is_busy && command_queue.size ()
+ && data.debugger.get_state () != IDebugger::RUNNING) {
+ NEMIVER_TRY;
+ UString command = command_queue.front ();
+ command_queue.pop ();
+ is_busy = execute_command (command);
+ NEMIVER_CATCH_NOX;
+ }
+ }
+
+ void
+ queue_command (const UString &a_command)
+ {
+ NEMIVER_TRY;
+
+ if (a_command.empty ()) {
+ ready_signal.emit ();
+ return;
+ }
+
+ command_queue.push (a_command);
+ process_command_queue ();
+
+ NEMIVER_CATCH_NOX;
+ }
+
+ void
+ register_command_alias (const std::string& a_alias,
+ CmdInterpreter::Command &a_command)
+ {
+ if (command_map.count (a_alias)) {
+ LOG ("Command '" << a_alias << "' is already registered in"
+ " the command interpreter. The previous command will be"
+ " overwritten");
+ }
+
+ command_map.insert (std::make_pair<std::string, Command&>
+ (a_alias, a_command));
+ }
+};
+
+CmdInterpreter::CmdInterpreter (IDebugger &a_debugger,
+ std::ostream &a_output_stream) :
+ m_priv (new Priv (a_debugger, a_output_stream))
+{
+ THROW_IF_FAIL (m_priv);
+
+ for (std::vector<CommandSafePtr >::iterator iter =
+ m_priv->commands.begin ();
+ iter != m_priv->commands.end ();
+ ++iter) {
+ THROW_IF_FAIL (*iter);
+ register_command (**iter);
+ }
+}
+
+CmdInterpreter::~CmdInterpreter ()
+{
+}
+
+void
+CmdInterpreter::register_command (CmdInterpreter::Command &a_command)
+{
+ THROW_IF_FAIL (m_priv);
+
+ m_priv->command_vector.push_back (&a_command);
+ m_priv->register_command_alias (a_command.name (), a_command);
+
+ const std::vector<UString> &aliases = a_command.aliases ();
+ for (std::vector<UString>::const_iterator iter = aliases.begin ();
+ iter != aliases.end ();
+ ++iter) {
+ m_priv->register_command_alias (*iter, a_command);
+ }
+}
+
+void
+CmdInterpreter::current_file_path (const UString &a_file_path)
+{
+ THROW_IF_FAIL (m_priv);
+ m_priv->data.current_file_path = a_file_path;
+}
+
+const UString&
+CmdInterpreter::current_file_path () const
+{
+ THROW_IF_FAIL (m_priv);
+ return m_priv->data.current_file_path;
+}
+
+sigc::signal<void>&
+CmdInterpreter::ready_signal () const
+{
+ THROW_IF_FAIL (m_priv);
+ return m_priv->ready_signal;
+}
+
+const std::vector<CmdInterpreter::Command*>&
+CmdInterpreter::commands() const
+{
+ THROW_IF_FAIL (m_priv);
+ return m_priv->command_vector;
+}
+
+void
+CmdInterpreter::execute_command (const UString &a_command)
+{
+ THROW_IF_FAIL (m_priv);
+ m_priv->queue_command (a_command);
+}
+
+bool
+CmdInterpreter::ready () const
+{
+ THROW_IF_FAIL (m_priv);
+ return !m_priv->has_a_command_running && !m_priv->command_queue.size ();
+}
+
+NEMIVER_END_NAMESPACE(nemiver)
+
diff --git a/src/dbgengine/nmv-cmd-interpreter.h b/src/dbgengine/nmv-cmd-interpreter.h
new file mode 100644
index 0000000..e5df618
--- /dev/null
+++ b/src/dbgengine/nmv-cmd-interpreter.h
@@ -0,0 +1,179 @@
+//Author: Fabien Parent
+/*
+ *This file is part of the Nemiver project
+ *
+ *Nemiver is free software; you can redistribute
+ *it and/or modify it under the terms of
+ *the GNU General Public License as published by the
+ *Free Software Foundation; either version 2,
+ *or (at your option) any later version.
+ *
+ *Nemiver is distributed in the hope that it will
+ *be useful, but WITHOUT ANY WARRANTY;
+ *without even the implied warranty of
+ *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *See the GNU General Public License for more details.
+ *
+ *You should have received a copy of the
+ *GNU General Public License along with Nemiver;
+ *see the file COPYING.
+ *If not, write to the Free Software Foundation,
+ *Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *See COPYRIGHT file copyright information.
+ */
+
+
+#ifndef __NMV_CMD_INTERPRETER_H__
+#define __NMV_CMD_INTERPRETER_H__
+
+#include "common/nmv-safe-ptr.h"
+#include "common/nmv-namespace.h"
+#include "common/nmv-ustring.h"
+#include "common/nmv-safe-ptr-utils.h"
+#include <sigc++/signal.h>
+#include <ostream>
+#include <vector>
+
+NEMIVER_BEGIN_NAMESPACE(nemiver)
+
+using common::UString;
+using common::SafePtr;
+using common::Object;
+using common::ObjectRef;
+using common::ObjectUnref;
+
+class IDebugger;
+
+/// Command Interpreter used as interface to nemiver and specifically
+/// to IDebugger.
+///
+/// The command interpreter is able to execute command which has the form of
+/// the following string: "command-name option1 option2".
+/// One can add additional commands through \a register_command.
+///
+/// The Command Interpreter primary goal is to create a console to IDebugger,
+/// and thus provides features useful for terminals like completion, help,
+/// and aliases.
+class CmdInterpreter {
+ //non copyable
+ CmdInterpreter (const CmdInterpreter&);
+ CmdInterpreter& operator= (const CmdInterpreter&);
+
+ struct Priv;
+ SafePtr<Priv> m_priv;
+
+public:
+ /// Base class to write commands which can be registered into the \a
+ /// CmdInterpreter.
+ class Command : public Object {
+ sigc::signal<void> m_done_signal;
+
+ protected:
+ virtual bool execute (const std::vector<UString> &a_argv,
+ std::ostream &a_output) = 0;
+
+ public:
+ /// This signal must be emited at the end of the command execution.
+ sigc::signal<void>& done_signal ()
+ {
+ return m_done_signal;
+ }
+
+ /// Get the name of the command.
+ virtual const std::string& name () const = 0;
+
+ /// Get a vector of all the aliases available for the command.
+ virtual const std::vector<UString>& aliases () const
+ {
+ static std::vector<UString> s_aliases;
+ return s_aliases;
+ }
+
+ /// Provide the possible completions for the options of a command.
+ ///
+ /// \param a_argv Vector of the options used for the command.
+ /// A command must provide the correct completion following the options
+ /// the user has already written on the command line.
+ ///
+ /// \param a_completion_vector Vector of all the possible completion.
+ virtual void completions (const std::vector<UString> &/*a_argv*/,
+ std::vector<UString> &/*a_completion_vector*/)
+ const
+ {
+ }
+
+ /// Display the help message for the command.
+ ///
+ /// \param a_argv Vector of the options used for the command.
+ /// A command must provide the correct usage following the options
+ /// the user has already written on the command line.
+ ///
+ /// \param a_stream Stream to used to display the usage of the command.
+ virtual void display_usage (const std::vector<UString> &/*a_argv*/,
+ std::ostream &/*a_stream*/) const
+ {
+ }
+
+ /// Execute the command and emit the \a done_signal () if the command
+ /// has finish executing.
+ /// \param a_argv Options given to the command.
+ /// \param a_output Stream to used to display the command output.
+ void operator () (const std::vector<UString> &a_argv,
+ std::ostream &a_output)
+ {
+ if (execute (a_argv, a_output)) {
+ done_signal ().emit ();
+ }
+ }
+
+ virtual ~Command ()
+ {
+ }
+ };
+
+ typedef SafePtr<Command, ObjectRef, ObjectUnref> CommandSafePtr;
+
+ CmdInterpreter (IDebugger &a_debugger, std::ostream &a_output_stream);
+ ~CmdInterpreter ();
+
+ /// Register a new command into the command interpreter
+ /// \param a_command Command to register
+ void register_command (Command &a_command);
+
+ /// Execute a command
+ /// \param a_command Command to execute
+ void execute_command (const UString &a_command);
+
+ /// Get a vector of all the commands registered into
+ /// the command interpreter.
+ const std::vector<Command*>& commands () const;
+
+ /// Set the path of the source code on which the command interpreter is
+ /// acting.
+ /// \param a_file_path
+ void current_file_path (const UString &a_file_path);
+
+ /// Get the path of the source code on which the command interpreter is
+ /// acting.
+ /// It is usually the path of the source code of the current frame except
+ /// if the user change the path by calling \a current_file_path ().
+ const UString& current_file_path () const;
+
+ /// Get whether the command interpreter is ready to execute new commands.
+ bool ready () const;
+
+ /// \name signals
+ /// @{
+
+ /// This signal is emited when the command interpreter becomes ready to
+ /// execute new commands.
+ sigc::signal<void>& ready_signal () const;
+
+ /// @}
+};
+
+NEMIVER_END_NAMESPACE(nemiver)
+
+#endif /* __NMV_CMD_INTERPRETER_H__ */
+
diff --git a/src/main.cc b/src/main.cc
index 2ce9320..a9dba1d 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -64,6 +64,7 @@ static bool gv_use_launch_terminal = false;
static gchar *gv_remote = 0;
static gchar *gv_solib_prefix = 0;
static gchar *gv_gdb_binary_filepath = 0;
+static gchar *gv_command_filepath = 0;
static gchar *gv_core_path = 0;
static bool gv_just_load = false;
@@ -189,6 +190,15 @@ static GOptionEntry entries[] =
_("Do not set a breakpoint in 'main' and do not run the inferior either"),
0
},
+ {
+ "exec-command-file",
+ 0,
+ 0,
+ G_OPTION_ARG_STRING,
+ &gv_command_filepath,
+ _("Set the path of a file of commands to execute at start-up"),
+ "</path/to/command/file>"
+ },
{
"version",
0,
@@ -201,6 +211,8 @@ static GOptionEntry entries[] =
{0, 0, 0, (GOptionArg) 0, 0, 0, 0}
};
+static sigc::connection dbg_stopped_signal;
+
struct GOptionContextUnref {
void operator () (GOptionContext *a_opt)
{
@@ -427,6 +439,39 @@ load_debugger_perspective ()
(DBGPERSPECTIVE_PLUGIN_NAME));
}
+static void
+execute_commands (IDBGPerspective *a_debug_persp)
+{
+ if (gv_command_filepath) {
+ char *command_filepath = realpath (gv_command_filepath, 0);
+ if (command_filepath) {
+ a_debug_persp->execute_commands_from_file (command_filepath);
+ free (command_filepath);
+ } else {
+ LOG_ERROR ("Could not resolve the full path "
+ "of the command file");
+ }
+ } else {
+ a_debug_persp->execute_commands_from_fd (STDIN_FILENO);
+ }
+}
+
+static void
+on_stopped_signal (nemiver::IDebugger::StopReason a_reason,
+ bool a_has_frame,
+ const nemiver::IDebugger::Frame &a_frame,
+ int /*thread id*/,
+ const string& /*breakpoint number*/,
+ const UString& /*cookie*/,
+ IDBGPerspective *a_debug_persp)
+{
+ if (a_reason == nemiver::IDebugger::BREAKPOINT_HIT
+ && a_has_frame && a_frame.function_name () == "main") {
+ dbg_stopped_signal.disconnect ();
+ execute_commands (a_debug_persp);
+ }
+}
+
/// Return true if Nemiver should keep going after the GUI option(s)
/// have been processed.
static bool
@@ -596,11 +641,11 @@ process_gui_options (int& a_argc, char** a_argv)
IDBGPerspective *debug_persp =
dynamic_cast<IDBGPerspective*> (s_workbench->get_perspective
(DBGPERSPECTIVE_PLUGIN_NAME));
+ nemiver::IDebuggerSafePtr debugger = debug_persp->debugger ();
if (debug_persp) {
if (gv_gdb_binary_filepath) {
char *debugger_full_path = realpath (gv_gdb_binary_filepath, 0);
if (debugger_full_path) {
- nemiver::IDebuggerSafePtr debugger = debug_persp->debugger ();
if (!debugger) {
cerr << "Could not get the debugger instance" << endl;
return false;
@@ -664,6 +709,18 @@ process_gui_options (int& a_argc, char** a_argv)
/*a_clone_opened_files=*/false,
/*a_break_in_main_run=*/!gv_just_load);
}
+
+ // if the user execute a local program and breaks in main then
+ // wait for the 'main' breakpoint to be hit before executing
+ // commands
+ if (!gv_remote && !prog_path.empty () && !gv_just_load) {
+ dbg_stopped_signal =
+ debugger->stopped_signal ().connect
+ (sigc::bind<IDBGPerspective*>
+ (sigc::ptr_fun (&on_stopped_signal), debug_persp));
+ } else {
+ execute_commands (debug_persp);
+ }
} else {
cerr << "Could not find the debugger perspective plugin\n";
return false;
diff --git a/src/persp/dbgperspective/nmv-dbg-perspective.cc b/src/persp/dbgperspective/nmv-dbg-perspective.cc
index 6e07ce7..669b391 100644
--- a/src/persp/dbgperspective/nmv-dbg-perspective.cc
+++ b/src/persp/dbgperspective/nmv-dbg-perspective.cc
@@ -29,9 +29,11 @@
#include <sys/types.h>
// For OpenBSD
#include <unistd.h>
+#include <fcntl.h>
#include <algorithm>
#include <iostream>
#include <sstream>
+#include <fstream>
#include <glib/gi18n.h>
#include <giomm/file.h>
@@ -98,6 +100,7 @@
#endif // WITH_DYNAMICLAYOUT
#include "nmv-layout-manager.h"
#include "nmv-expr-monitor.h"
+#include "nmv-cmd-interpreter.h"
using namespace std;
using namespace nemiver::common;
@@ -566,6 +569,12 @@ public:
ISessMgr& session_manager ();
+ void execute_commands_from_line (const UString &a_line);
+
+ void execute_commands_from_file (const UString &a_file);
+
+ void execute_commands_from_fd (int a_fd);
+
void execute_session (ISessMgr::Session &a_session);
void execute_program ();
@@ -921,6 +930,7 @@ struct DBGPerspective::Priv {
Path2MonitorMap path_2_monitor_map;
SafePtr<LocalVarsInspector> variables_editor;
SafePtr<Gtk::ScrolledWindow> variables_editor_scrolled_win;
+ SafePtr<CmdInterpreter> interpreter;
SafePtr<Terminal> terminal;
SafePtr<Gtk::Box> terminal_box;
SafePtr<Gtk::ScrolledWindow> breakpoints_scrolled_win;
@@ -5879,6 +5889,71 @@ DBGPerspective::session_manager ()
}
void
+DBGPerspective::execute_commands_from_line (const UString &a_line)
+{
+ THROW_IF_FAIL (debugger ());
+
+ if (!m_priv->interpreter) {
+ m_priv->interpreter.reset
+ (new CmdInterpreter (*debugger (), std::cout));
+ }
+
+ THROW_IF_FAIL (m_priv->interpreter);
+
+ std::vector<UString> commands = str_utils::split (a_line, ";");
+ for (std::vector<UString>::iterator iter = commands.begin ();
+ iter != commands.end ();
+ ++iter) {
+ str_utils::chomp (*iter);
+ m_priv->interpreter->execute_command (*iter);
+ }
+}
+
+void
+DBGPerspective::execute_commands_from_file (const UString &a_file)
+{
+ THROW_IF_FAIL (m_priv);
+
+ std::ifstream file (a_file.c_str ());
+ while (file.good ()) {
+ std::string line;
+ std::getline (file, line);
+ execute_commands_from_line (line);
+ }
+ file.close ();
+}
+
+void
+DBGPerspective::execute_commands_from_fd (int a_fd)
+{
+ THROW_IF_FAIL (m_priv);
+
+ struct File {
+ FILE *fd;
+ char buffer[4096];
+
+ explicit File (int a_fd) :
+ fd (fdopen (dup (a_fd), "r"))
+ {
+ THROW_IF_FAIL (fd);
+ int flags = fcntl (fileno (fd), F_GETFL, 0);
+ fcntl (fileno (fd), F_SETFL, flags | O_NONBLOCK);
+ }
+
+ ~File ()
+ {
+ if (fd) {
+ fclose (fd);
+ }
+ }
+ } file (a_fd);
+
+ while (fgets (file.buffer, sizeof (file.buffer), file.fd)) {
+ execute_commands_from_line (file.buffer);
+ }
+}
+
+void
DBGPerspective::execute_session (ISessMgr::Session &a_session)
{
LOG_FUNCTION_SCOPE_NORMAL_DD;
diff --git a/src/persp/dbgperspective/nmv-dbg-perspective.h b/src/persp/dbgperspective/nmv-dbg-perspective.h
index 5874e31..c22ed92 100644
--- a/src/persp/dbgperspective/nmv-dbg-perspective.h
+++ b/src/persp/dbgperspective/nmv-dbg-perspective.h
@@ -97,6 +97,14 @@ public:
virtual ISessMgr& session_manager () = 0;
+ /// Execute commands from a file.
+ /// \param a_file File of commands to execute.
+ virtual void execute_commands_from_file (const UString &a_file) = 0;
+
+ /// Execute commands from an opened file descriptor.
+ /// \param a_fd File descriptor used to read commands.
+ virtual void execute_commands_from_fd (int a_fd) = 0;
+
virtual void execute_session (ISessMgr::Session &a_session) = 0;
virtual void execute_program () = 0;
diff --git a/tests/Makefile.am b/tests/Makefile.am
index b28b1c5..337b1c5 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -18,7 +18,7 @@ runtestcppparser runtestvarpathexpr \
runtestlibtoolwrapperdetection \
runtesttypes runtestdisassemble \
runtestvariableformat runtestprettyprint \
-runtestthreads runtestdbgstates
+runtestthreads runtestdbgstates runtestcmdinterpreter
else
@@ -64,6 +64,12 @@ runtestdbgstates_LDADD= NEMIVERCOMMON_LIBS@ \
$(top_builddir)/src/common/libnemivercommon.la \
$(top_builddir)/src/dbgengine/libdebuggerutils.la
+runtestcmdinterpreter_SOURCES=$(h)/test-cmd-interpreter.cc
+runtestcmdinterpreter_LDADD= NEMIVERCOMMON_LIBS@ \
+$(top_builddir)/src/common/libnemivercommon.la \
+$(top_builddir)/src/dbgengine/libdebuggerutils.la \
+$(top_builddir)/src/dbgengine/libcmdinterpreter.la
+
runtestbreakpoint_SOURCES=$(h)/test-breakpoint.cc
runtestbreakpoint_LDADD= NEMIVERCOMMON_LIBS@ \
$(top_builddir)/src/common/libnemivercommon.la \
diff --git a/tests/test-cmd-interpreter.cc b/tests/test-cmd-interpreter.cc
new file mode 100644
index 0000000..d044839
--- /dev/null
+++ b/tests/test-cmd-interpreter.cc
@@ -0,0 +1,113 @@
+#include "config.h"
+#include <iostream>
+#include <boost/test/minimal.hpp>
+#include <glibmm.h>
+#include "common/nmv-initializer.h"
+#include "common/nmv-safe-ptr-utils.h"
+#include "common/nmv-exception.h"
+#include "nmv-i-debugger.h"
+#include "nmv-debugger-utils.h"
+#include "nmv-cmd-interpreter.h"
+
+using namespace nemiver;
+using namespace nemiver::common;
+
+Glib::RefPtr<Glib::MainLoop> loop =
+ Glib::MainLoop::create (Glib::MainContext::get_default ());
+SafePtr<CmdInterpreter> interpreter;
+std::ostringstream out;
+unsigned int nb_stops = 0;
+
+void
+on_engine_died_signal ()
+{
+ MESSAGE ("engine died");
+ loop->quit ();
+}
+
+void
+on_program_finished_signal ()
+{
+ MESSAGE ("program finished");
+ loop->quit ();
+}
+
+
+void
+display_help ()
+{
+ MESSAGE ("test-basic <prog-to-debug>\n");
+}
+
+void
+on_stopped_signal (IDebugger::StopReason a_reason,
+ bool a_has_frame,
+ const IDebugger::Frame &a_frame,
+ int /*a_thread_id*/,
+ const string &/*a_bp_num*/,
+ const UString &/*a_cookie*/)
+{
+ if (!a_has_frame) {
+ return;
+ }
+
+ nb_stops++;
+
+ if (a_reason == nemiver::IDebugger::BREAKPOINT_HIT) {
+ if (a_frame.function_name () == "func1_1") {
+ interpreter->execute_command ("next");
+ interpreter->execute_command ("print i_i");
+ interpreter->execute_command ("break func2");
+ interpreter->execute_command ("continue");
+ } else if (a_frame.function_name () == "func2") {
+ BOOST_REQUIRE (out.str () == "i_i = 19\n");
+ interpreter->execute_command ("break func3");
+ interpreter->execute_command ("continue");
+ } else if (a_frame.function_name () == "func3") {
+ interpreter->execute_command ("step");
+ }
+ } else {
+ if (a_frame.function_name () == "Person::do_this") {
+ interpreter->execute_command ("finish");
+ } else if (a_frame.function_name () == "func3") {
+ interpreter->execute_command ("continue");
+ }
+ }
+}
+
+NEMIVER_API int
+test_main (int argc, char *argv[])
+{
+ if (argc || argv) {/*keep compiler happy*/}
+
+ NEMIVER_TRY
+
+ Initializer::do_init ();
+
+ THROW_IF_FAIL (loop);
+
+ IDebuggerSafePtr debugger =
+ debugger_utils::load_debugger_iface_with_confmgr ();
+
+ debugger->set_event_loop_context (loop->get_context ());
+ debugger->enable_pretty_printing (false);
+
+ //*****************************
+ //<connect to IDebugger events>
+ //*****************************
+ debugger->engine_died_signal ().connect (&on_engine_died_signal);
+ debugger->program_finished_signal ().connect (&on_program_finished_signal);
+ debugger->stopped_signal ().connect (&on_stopped_signal);
+
+ interpreter.reset (new CmdInterpreter (*debugger, out));
+ interpreter->execute_command ("load-exec ./fooprog");
+ interpreter->execute_command ("break func1_1");
+ interpreter->execute_command ("run");
+ loop->run ();
+
+ BOOST_REQUIRE (nb_stops == 6);
+
+ NEMIVER_CATCH_NOX
+
+ return 0;
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]