[nemiver/dbg-console: 2/3] Add a command interpreter interface for DBGEngine



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]