Mailinglist Archive: yast-devel (46 mails)

< Previous Next >
Re: [yast-devel] YCP debugger: prototype
Hi!

Here is updated patch with implemented some of the suggestions so far.

Changes:
- move almost all debugging flags to Debugger class instance, thus not
influence memory requirements for non-debugger case
- implemented remote debugging:

/usr/lib/YaST2/bin/y2base --debugger-remote <YCP-client> <frontend>

and client via

/usr/lib/YaST2/bin/ycp-debugger <hostname>

- use Y2DEBUGGER environment variable as also means to start local/remote
debugger (set it to value "1" or "2")
- provide better context information during tracing
- do not allow to debug non-YCP code
- refuse starting local debugging if socket already exists
- fixed tracing if stepping from a breakpoint
- some refactoring

Feedback still welcome!

Stano

On Pi 22. Júl 2011 16:09:42 Stanislav Visnovsky wrote:
Hi!

Since there was interest in still providing YCP debugger, I gave it a shot
and here is a first prototype. I did not test it to actually trace a real
module, but it provides the basic functionality and I need feedback what
needs to be improved, possibly reporting bugs.

The patch for yast2-core is attached, the packages for openSUSE 11.4 are
available in my home project:

home:visnov:branches:openSUSE:11.4:Update:Test

Usage:

1) zypper install yast2-core-debugger
2) start YaST2 process with --debugger option:

/usr/lib/YaST2/bin/y2base --debugger <YCP-client> <frontend>

3) start debugger:
/usr/lib/YaST2/bin/ycp-debugger

The currently supported commands:

yast-dbg> help
Available commands:

q quit
help this help
s step
n next
c continue
p <var> print variable value
b <fnc> set breakpoint
rb <fnc> remove breakpoint
bt backtrace
v <var>=<val> set variable value
attach attach to current YaST process
detach detach from current YaST process

WARNING: if you enter non-existent/empty command, the communication will go
out of sync and you have to start over!


TODO:
Infrastructure:
- enable debugger at runtime
- secure socket creation

Client:
- make it robust, most likely a complete rewrite is a way to go

Breakpoints:
- set breakpoint to filename:linenumber
- list breakpoints

Any feedback is welcome!

Stano
diff --git a/SUBDIRS b/SUBDIRS
index 866cdc3..f0ffba4 100644
--- a/SUBDIRS
+++ b/SUBDIRS
@@ -1 +1 @@
-liby2util-r liby2 libycp libscr agent-dummy scr agent-system agent-any
agent-ini agent-modules agent-resolver agents-non-y2 agents-perl wfm
agent-process base autodocs
+liby2util-r liby2 debugger libycp libscr agent-dummy scr agent-system
agent-any agent-ini agent-modules agent-resolver agents-non-y2 agents-perl wfm
agent-process base autodocs
diff --git a/base/src/Makefile.am b/base/src/Makefile.am
index d6eff40..cc61425 100644
--- a/base/src/Makefile.am
+++ b/base/src/Makefile.am
@@ -12,6 +12,7 @@ YAST2CORE = \
../../libycp/src/libycp.la \
../../libycp/src/libycpvalues.la \
../../liby2/src/liby2.la \
+ ../../debugger/liby2debug.la \
../../libscr/src/libscr.la


diff --git a/base/tools/ycpc/Makefile.am b/base/tools/ycpc/Makefile.am
index b82501c..377e504 100644
--- a/base/tools/ycpc/Makefile.am
+++ b/base/tools/ycpc/Makefile.am
@@ -20,6 +20,7 @@ ycpc_LDADD = $(top_builddir)/libycp/src/libycp.la \
$(top_builddir)/liby2/src/liby2.la \
$(top_builddir)/libscr/src/libscr.la \
$(top_builddir)/scr/src/libpy2scr.la \
+ $(top_builddir)/debugger/liby2debug.la \
${Y2UTIL_LIBS}

ybin_SCRIPTS = ybcdump
diff --git a/debugger/Debugger.cc b/debugger/Debugger.cc
new file mode 100644
index 0000000..20a4639
--- /dev/null
+++ b/debugger/Debugger.cc
@@ -0,0 +1,559 @@
+/*---------------------------------------------------------------------\
+| |
+| __ __ ____ _____ ____ |
+| \ \ / /_ _/ ___|_ _|___ \ |
+| \ V / _` \___ \ | | __) | |
+| | | (_| |___) || | / __/ |
+| |_|\__,_|____/ |_| |_____| |
+| |
+| core system |
+| (C) SuSE GmbH |
+\----------------------------------------------------------------------/
+
+ File: Debugger.cc
+
+ Basic infrastructure for YCP debugger
+
+ Author: Stanislav Visnovsky <visnov@xxxxxxx>
+ Maintainer: Stanislav Visnovsky <visnov@xxxxxxx>
+
+/-*/
+
+#include <y2/Y2ComponentBroker.h>
+#include <y2/Y2Component.h>
+#include <y2/Y2Namespace.h>
+#include <ycp/y2log.h>
+#include <ycp/ExecutionEnvironment.h>
+#include <ycp/YBlock.h>
+#include <ycp/YBreakpoint.h>
+#include <ycp/Parser.h>
+#include <y2util/stringutil.h>
+#include "Debugger.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <stdio.h>
+
+#define ADDRESS "/tmp/yast.socket"
+#define PORT 16384
+
+extern ExecutionEnvironment ee;
+
+Debugger::Debugger () :
+ m_socket (-1),
+ m_descriptor (NULL),
+ m_outputstash (""),
+ m_tracing (false),
+ m_remote (false)
+{}
+
+Debugger::~Debugger ()
+{
+ // close the controll socket
+ if (m_socket)
+ {
+ close (m_socket);
+ if (m_remote)
+ unlink (ADDRESS);
+ }
+ m_socket = -1;
+}
+
+bool Debugger::initialize(bool remote)
+{
+ m_remote = remote;
+ return remote ? initializeRemote() : initializeLocal();
+}
+
+bool Debugger::initializeLocal()
+{
+ socklen_t fromlen;
+ int ns, len;
+ struct sockaddr_un saun, fsaun;
+
+ // FIXME: possible leak
+ m_socket = -1;
+ m_descriptor = NULL;
+
+ if ((m_socket = socket (AF_UNIX, SOCK_STREAM, 0)) < 0)
+ {
+ return false;
+ }
+
+ saun.sun_family = AF_UNIX;
+ strcpy (saun.sun_path, ADDRESS);
+
+ int res = access (ADDRESS, F_OK);
+ if ( res == 0 || errno != ENOENT )
+ {
+ y2security ("Cannot create debugger socket: %s", res == 0 ? "File exists"
: strerror (errno) );
+ return false;
+ }
+
+ len = sizeof (saun.sun_family) + strlen (saun.sun_path);
+
+ if (bind (m_socket,(struct sockaddr *) &saun, len) < 0)
+ {
+ return false;
+ }
+
+ // wait for connection
+ if (listen (m_socket, 1) < 0)
+ {
+ y2error ("Debugger: listen failed");
+ return false;
+ }
+ fromlen = 108;
+
+ if ((ns = accept (m_socket, (struct sockaddr *) &fsaun, &fromlen)) < 0)
+ {
+ y2error ("Debugger: accept failed");
+ return false;
+ }
+
+ m_descriptor = fdopen (ns, "r");
+ m_last_command = c_step; // step to enable debugging from the start
+ m_ns = ns;
+
+ return true;
+}
+
+bool Debugger::initializeRemote()
+{
+ socklen_t fromlen;
+ int ns;
+ struct sockaddr_in sain, fsain;
+
+ // FIXME: possible leak
+ m_socket = -1;
+ m_descriptor = NULL;
+
+ if ((m_socket = socket (PF_INET, SOCK_STREAM, 0)) < 0)
+ {
+ return false;
+ }
+
+ sain.sin_family = PF_UNIX;
+ sain.sin_addr.s_addr=htonl(INADDR_ANY);
+ sain.sin_port=htons(PORT);
+
+ if (bind (m_socket,(struct sockaddr *) &sain, sizeof (sain)) < 0)
+ {
+ y2error ("Debugger: bind failed");
+ return false;
+ }
+
+ // wait for connection
+ if (listen (m_socket, 1) < 0)
+ {
+ y2error ("Debugger: listen failed");
+ return false;
+ }
+
+ fromlen = sizeof(fsain);
+
+ if ((ns = accept (m_socket, (struct sockaddr *) &fsain, &fromlen)) < 0)
+ {
+ y2error ("Debugger: accept failed");
+ return false;
+ }
+
+ m_descriptor = fdopen (ns, "r");
+ m_last_command = c_step; // step to enable debugging from the start
+ m_ns = ns;
+
+ return true;
+}
+
+void Debugger::setTracing()
+{
+ m_last_command = c_step;
+ m_tracing = true;
+}
+
+void Debugger::setBreakpoint (std::list<std::string> &args)
+{
+ SymbolEntryPtr sentry = findSymbol (args.front ());
+
+ if (sentry)
+ {
+ if( !sentry->isFunction ())
+ sendOutput ("Identifier is not a function");
+ else
+ {
+ YFunctionPtr fnc =
((YFunctionPtr)((YSymbolEntryPtr)sentry)->code());
+
+ // set the breakpoint wrapper
+ if (fnc->definition ()->kind () == YCode::yiBreakpoint)
+ {
+ ((YBreakpointPtr)fnc->definition ())->setEnabled (true);
+ sendOutput ("Breakpoint enabled");
+ return;
+ }
+
+ YBreakpointPtr bp = new YBreakpoint (fnc->definition(), args.front
() );
+
+ fnc->setDefinition (bp);
+ std::string result = "Set breakpoint to " + sentry->toString();
+ sendOutput (result);
+ }
+ }
+ else
+ sendOutput ("Identifier not found");
+}
+
+void Debugger::removeBreakpoint (std::list<std::string> &args)
+{
+ SymbolEntryPtr sentry = findSymbol (args.front ());
+
+ if (sentry)
+ {
+ if( !sentry->isFunction ())
+ sendOutput ("Identifier is not a function");
+ else
+ {
+ YFunctionPtr fnc =
((YFunctionPtr)((YSymbolEntryPtr)sentry)->code());
+
+ if (fnc->definition ()->kind () == YCode::yiBreakpoint)
+ {
+ // disable the breakpoint wrapper
+ ((YBreakpointPtr)fnc->definition ())->setEnabled (false);
+ sendOutput ("Breakpoint disabled");
+ return;
+ }
+ sendOutput ("Breakpoint not found");
+ }
+ }
+ else
+ sendOutput ("Identifier not found");
+}
+
+void Debugger::generateBacktrace ()
+{
+ std::string result = "Call stack:";
+ ExecutionEnvironment::CallStack stack = ee.callstack();
+ ExecutionEnvironment::CallStack::const_reverse_iterator it =
stack.rbegin();
+ while (it != stack.rend())
+ {
+ result = result
+ + "\n"
+ + ( (*it)->filename )
+ + ":"
+ + stringutil::numstring((*it)->linenumber)
+ + ": "
+ + ((*it)->function->entry()->toString());
+
+ int paramcount =
((constFunctionTypePtr)((*it)->function->entry()->type()))->parameterCount();
+ if( paramcount > 0 )
+ result += " with paramters: ";
+ for( int i = 0; i < paramcount ; i++ )
+ {
+ result += (*it)->params[0]->toString() + " ";
+ }
+ ++it;
+ };
+
+ sendOutput (result);
+}
+
+SymbolEntryPtr Debugger::findSymbol (std::string arg)
+{
+ std::vector<std::string> words;
+ SymbolEntryPtr sentry = NULL;
+
+ stringutil::split(arg, words, ":");
+
+ if (words.size () > 1)
+ {
+ // name contains namespace, handle here
+ std::string ns_name = words[0];
+ std::string name = words[1];
+ Y2Component* c = Y2ComponentBroker::getNamespaceComponent
(ns_name.c_str());
+ if( c )
+ {
+ if (c->name () == "wfm")
+ {
+ Y2Namespace* ns = c->import (ns_name.c_str());
+ if (ns)
+ // this returns NULL in case the name was not found
+ return ns->lookupSymbol (name.c_str());
+ }
+ else
+ return NULL; // this is not YCP symbol
+ }
+ }
+ else
+ {
+ // try parameters
+ ExecutionEnvironment::CallStack stack = ee.callstack();
+
+ if( stack.size() > 0 )
+ {
+ ExecutionEnvironment::CallStack::const_reverse_iterator it =
stack.rbegin();
+
+ YSymbolEntryPtr ysentry =
(YSymbolEntryPtr)((*it)->function->entry());
+
+ // this returns NULL in case the name was not found
+ sentry = ((YFunctionPtr)ysentry->code())->declaration
()->lookupSymbol (words[0].c_str());
+
+ if (sentry)
+ return sentry;
+ }
+
+ // try block stack
+ for( std::list<stackitem_t>::iterator blk = m_blockstack.begin(); blk
!= m_blockstack.end (); blk++)
+ {
+ sentry = blk->ns->lookupSymbol (words[0].c_str());
+ if (sentry)
+ return sentry;
+ }
+ }
+
+ // not found
+ return NULL;
+}
+
+void Debugger::printVariable (std::string arg)
+{
+ SymbolEntryPtr sentry = findSymbol (arg);
+
+ if (sentry)
+ {
+ if( !sentry->isVariable ())
+ sendOutput ("Identifier is not a variable");
+ else
+ {
+ if( sentry->value().isNull() )
+ sendOutput ("nil");
+ else
+ sendOutput (sentry->value()->toString());
+ }
+ }
+ else
+ sendOutput ("Identifier not found");
+}
+
+void Debugger::setVariable (std::string assign)
+{
+ // first, split by '='
+ std::vector<std::string> words;
+ stringutil::split(assign, words, "=");
+
+ if( words.size() != 2 )
+ {
+ sendOutput ("Set variable format is <name>=<constant>");
+ return;
+ }
+
+ SymbolEntryPtr sentry = findSymbol (words.front ());
+
+ if (sentry)
+ {
+ if( !sentry->isVariable ())
+ sendOutput ("Identifier is not a variable");
+ else
+ {
+ // set the new value
+ Parser parser (words[1].c_str()); // set parser to value
+
+ YCodePtr pc = parser.parse ();
+ if (!pc )
+ {
+ sendOutput ("Cannot parse new value");
+ }
+ else
+ {
+ sentry->setValue (pc->evaluate (true)); // set the new value
+ sendOutput ( std::string(sentry->name ()) + " = " +
sentry->value ()->toString () );
+ }
+ }
+ }
+ else
+ sendOutput ("Identifier not found");
+}
+
+bool Debugger::processInput (command_t &command, std::list<std::string>
&arguments)
+{
+ char c;
+ std::string s;
+ std::list<std::string> args;
+
+ // FIXME: use flex
+ if (m_descriptor == NULL)
+ return false;
+
+ // First, send the current context
+ YStatementPtr statement = ee.statement ();
+
+after_internal:
+ if (statement)
+ sendOutput (ee.filename() + ":" +
stringutil::numstring(ee.linenumber()) + " >>> " + statement->toString ());
+ else
+ sendOutput ("no code");
+
+ // clean up for next command
+ s = "";
+ args.clear();
+
+ while ((c = fgetc (m_descriptor)) != EOF)
+ {
+ if (c == '\n')
+ {
+ break;
+ }
+ s += c;
+ }
+
+ if (s.empty ())
+ {
+ y2error ("Communication with debugging UI closed");
+ close (m_socket);
+
+ if (m_remote)
+ unlink (ADDRESS);
+
+ m_socket = -1;
+ m_descriptor = NULL;
+ return false;
+ }
+
+ command = c_unknown;
+ // FIXME: I said flex!
+ if (s == "c")
+ {
+ command = Debugger::c_continue;
+ }
+ else if (s == "n")
+ {
+ command = Debugger::c_next;
+ }
+ else if (s == "s")
+ {
+ command = Debugger::c_step;
+ }
+ else if (s == "bt")
+ {
+ command = Debugger::c_backtrace;
+ }
+ else if (s[0] == 'v')
+ {
+ command = Debugger::c_setvalue;
+ args.push_back(s.substr(2));
+ }
+ else if ( s[0] == 'b' )
+ {
+ command = Debugger::c_breakpoint;
+ args.push_back(s.substr(2));
+ }
+ else if ( s[0] == 'r' && s[1] == 'b')
+ {
+ command = Debugger::c_removebreakpoint;
+ args.push_back(s.substr(3));
+ }
+ else if (s[0] == 'p')
+ {
+ command = Debugger::c_print;
+ args.push_back(s.substr(2));
+ }
+
+ if (command == Debugger::c_print)
+ {
+ printVariable (args.front () );
+ goto after_internal;
+ }
+
+ if (command == Debugger::c_breakpoint)
+ {
+ setBreakpoint (args);
+ goto after_internal;
+ }
+
+ if (command == Debugger::c_removebreakpoint)
+ {
+ removeBreakpoint (args);
+ goto after_internal;
+ }
+
+ if (command == Debugger::c_backtrace)
+ {
+ generateBacktrace ();
+ goto after_internal;
+ }
+
+ if (command == Debugger::c_setvalue)
+ {
+ setVariable (args.front () );
+ goto after_internal;
+ }
+
+ arguments = args;
+ m_last_command = command;
+
+ return true;
+}
+
+bool Debugger::sendOutput( std::string output )
+{
+ output = m_outputstash + output;
+ y2debug ("Sending out output %s", output.c_str() );
+ if( output.empty() )
+ output = " ";
+ output = output + "\n<EOF>\n";
+ send (m_ns, output.c_str (), strlen( output.c_str ()), 0);
+
+ // clean up the output stash, it's sent already
+ m_outputstash = "";
+ return true;
+}
+
+void Debugger::stashOutput( std::string output )
+{
+ m_outputstash += output;
+}
+
+void Debugger::enableTracing (Y2Namespace* block, bool enable)
+ {
+ for( std::list<stackitem_t>::iterator it = m_blockstack.begin (); it !=
m_blockstack.end (); it++)
+ {
+ if ( it->ns == block )
+ it->tracing = enable;
+ return;
+ }
+}
+
+bool Debugger::tracing (Y2Namespace* block) const
+{
+ for( std::list<stackitem_t>::const_iterator it = m_blockstack.begin (); it
!= m_blockstack.end (); it++)
+ {
+ if ( it->ns == block )
+ return it->tracing;
+ }
+ return false;
+}
+
+void Debugger::pushBlock (Y2Namespace* block, bool tracing)
+{
+ stackitem_t si;
+ si.ns = block;
+ si.tracing = tracing;
+
+ m_blockstack.push_front (si);
+}
+
+void Debugger::popBlock ()
+{
+ m_blockstack.pop_front ();
+}
+
+bool Debugger::tracing () const
+{
+ return m_tracing;
+}
+
+void Debugger::setTracing (bool enable)
+{
+ m_tracing = enable;
+}
+
diff --git a/debugger/Debugger.h b/debugger/Debugger.h
new file mode 100644
index 0000000..d5cca46
--- /dev/null
+++ b/debugger/Debugger.h
@@ -0,0 +1,116 @@
+/*---------------------------------------------------------------------\
+| |
+| __ __ ____ _____ ____ |
+| \ \ / /_ _/ ___|_ _|___ \ |
+| \ V / _` \___ \ | | __) | |
+| | | (_| |___) || | / __/ |
+| |_|\__,_|____/ |_| |_____| |
+| |
+| core system |
+| (C) SuSE GmbH |
+\----------------------------------------------------------------------/
+
+ File: Debugger.h
+
+ Author: Stanislav Visnovsky <visnov@xxxxxxx>
+ Maintainer: Stanislav Visnovsky <visnov@xxxxxxx>
+
+/-*/
+// -*- c++ -*-
+
+#ifndef Debugger_h
+#define Debugger_h
+
+#include <stdio.h>
+#include <list>
+#include <string>
+#include <y2/SymbolEntry.h>
+
+class Y2Namespace;
+
+/**
+ * @short Debugger singleton to keep debugging-related status
+ */
+class Debugger
+{
+public:
+ typedef enum {
+ c_unknown,
+ c_next,
+ c_step,
+ c_continue,
+ c_print,
+ c_backtrace,
+ c_breakpoint,
+ c_removebreakpoint,
+ c_setvalue
+ } command_t;
+
+private:
+ int m_socket, m_ns;
+ FILE *m_descriptor;
+ command_t m_last_command;
+ std::string m_outputstash;
+
+ bool m_tracing;
+
+ typedef struct {
+ Y2Namespace* ns;
+ bool tracing;
+ } stackitem_t;
+
+ std::list<stackitem_t> m_blockstack;
+
+ bool m_remote;
+
+public:
+
+ Debugger ();
+
+ ~Debugger ();
+
+ /**
+ * Initialize the socket and reset the communication
+ */
+ bool initialize (bool remote);
+ bool initializeRemote ();
+ bool initializeLocal ();
+
+ /**
+ * @short Read the input from controlling socket and act accordingly.
+ *
+ * For actions needed to be done in context of YCP code being run,
+ * return the information to the caller.
+ */
+ bool processInput (command_t &command, std::list<std::string> &arguments);
+
+ bool sendOutput (std::string output );
+
+ // save the text for the next output sending
+ void stashOutput (std::string output );
+
+ command_t lastCommand () const { return m_last_command; }
+
+ // sets the last command to be c_step, enables tracing of the next block
to be
+ // entered
+ void setTracing ();
+ void setTracing (bool enable);
+
+ void setBreakpoint (std::list<std::string> &arguments);
+ void removeBreakpoint (std::list<std::string> &arguments);
+ void generateBacktrace ();
+ void printVariable (std::string variable_name);
+ void setVariable (std::string arg);
+
+ void enableTracing (Y2Namespace* block, bool enable);
+ bool tracing (Y2Namespace* block) const;
+ bool tracing () const;
+
+ void pushBlock (Y2Namespace* block, bool tracing);
+ void popBlock ();
+
+private:
+ SymbolEntryPtr findSymbol (std::string name);
+};
+
+#endif // Debugger_h
diff --git a/debugger/Makefile.am b/debugger/Makefile.am
new file mode 100644
index 0000000..4975639
--- /dev/null
+++ b/debugger/Makefile.am
@@ -0,0 +1,20 @@
+#
+# Makefile.am for core/debugger
+#
+SUBDIRS=client
+
+AM_CXXFLAGS = -DY2LOG=\"debugger\"
+
+noinst_LTLIBRARIES = liby2debug.la
+
+liby2debug_la_SOURCES = \
+ Debugger.h \
+ Debugger.cc
+
+#liby2debug_la_LIBADD = @AGENT_LIBADD@
+
+INCLUDES = \
+ -I$(srcdir)/../libycp/src/include \
+ -I$(srcdir)/../liby2/src/include \
+ -I$(srcdir)/../liby2util-r/src/include \
+ ${Y2UTIL_CFLAGS}
diff --git a/debugger/TODO b/debugger/TODO
new file mode 100644
index 0000000..aadfa7e
--- /dev/null
+++ b/debugger/TODO
@@ -0,0 +1,11 @@
+This are the items we still might want to implement in the debugger. Please,
keep this file
+up-to-date, remove the finished items.
+
+
+Breakpoints:
+- set breakpoint to filename:linenumber
+- list breakpoints
+
+Infrastructure:
+- enable debugger at runtime
+
diff --git a/debugger/client/Makefile.am b/debugger/client/Makefile.am
new file mode 100644
index 0000000..b90c482
--- /dev/null
+++ b/debugger/client/Makefile.am
@@ -0,0 +1,11 @@
+#
+# Makefile.am for core/debugger/client
+#
+
+pkgdatadir = ${prefix}/lib/YaST2/bin/
+
+pkgdata_SCRIPTS = \
+ ycp-debugger
+
+EXTRA_DIST = $(pkgdata_SCRIPTS)
+
diff --git a/debugger/client/ycp-debugger b/debugger/client/ycp-debugger
new file mode 100755
index 0000000..0aacb2a
--- /dev/null
+++ b/debugger/client/ycp-debugger
@@ -0,0 +1,107 @@
+#!/usr/bin/ruby
+
+require 'socket'
+
+socket = nil;
+
+if (ARGV.size == 1)
+ socket = TCPsocket.new( ARGV[0], 16384 );
+else
+ socket = UNIXsocket.new( "/tmp/yast.socket" );
+end
+
+def get_output(socket)
+ begin
+ res = socket.gets
+ if (res != "<EOF>\n")
+ puts(res)
+ end
+ end until ( res == "<EOF>\n" )
+end
+
+def wait_socket
+ begin
+ print( "yast-dbg (detached)> ");
+ input = STDIN.gets().chomp();
+ if (input == "q")
+ puts "Bye!"
+ exit 0
+ end
+
+ if (input =="attach")
+ socket = UNIXsocket.new( "/tmp/yast.socket" );
+ puts "Attached to YaST process"
+ return socket
+ end
+
+ puts "You must attach to YaST process first using 'attach'"
+ end until false;
+end
+
+while !socket.eof? do
+ get_output(socket);
+ print( "yast-dbg> ");
+ input = STDIN.gets().chomp();
+
+ if (input == "help")
+ puts "Available commands:"
+ puts
+ puts "q quit"
+ puts "help this help"
+ puts "s step"
+ puts "n next"
+ puts "c continue"
+ puts "p <var> print variable value"
+ puts "b <fnc> set breakpoint"
+ puts "rb <fnc> remove breakpoint"
+ puts "bt backtrace"
+ puts "v <var>=<val> set variable value"
+ puts "attach attach to current YaST process"
+ puts "detach detach from current YaST
process"
+ puts
+ print( "yast-dbg> ");
+ input = STDIN.gets().chomp();
+ end
+
+ if (input == "q")
+ puts "Bye!"
+ exit 0
+ end
+
+ if (input == "detach")
+ puts "Closing socket"
+ socket.close();
+ socket = wait_socket();
+ next
+ end
+
+ begin
+ socket.puts(input)
+ rescue Exception => e
+ puts e
+ exit 0
+ end;
+
+ # if printing value, show it, then get another output
+ if( input.split.first=="p" )
+ get_output(socket);
+ end
+
+ if( input=="bt" )
+ get_output(socket);
+ end
+
+ if( input.split.first=="b" )
+ get_output(socket);
+ end
+
+ if( input.split.first=="rb" )
+ get_output(socket);
+ end
+
+ if( input.split.first=="v" )
+ get_output(socket);
+ end
+end
+
+
diff --git a/liby2/src/Makefile.am b/liby2/src/Makefile.am
index 9644a09..7425b24 100644
--- a/liby2/src/Makefile.am
+++ b/liby2/src/Makefile.am
@@ -34,4 +34,5 @@ INCLUDES = \
-I$(srcdir)/../../libycp/src/include \
-I$(srcdir)/include \
-I$(srcdir)/include/y2 \
+ -I$(srcdir)/../debugger \
${Y2UTIL_CFLAGS}
diff --git a/liby2/src/genericfrontend.cc b/liby2/src/genericfrontend.cc
index a7f049c..59b34fb 100644
--- a/liby2/src/genericfrontend.cc
+++ b/liby2/src/genericfrontend.cc
@@ -105,6 +105,7 @@
#include <ycp/Parser.h>
#include <ycp/pathsearch.h>
#include "exitcodes.h"
+#include <debugger/Debugger.h>

/// number of symbols that are handled as error codes
#define MAX_YCP_ERROR_EXIT_SYMBOLS 2
@@ -117,6 +118,7 @@ const char*
ycp_error_exit_symbols[MAX_YCP_ERROR_EXIT_SYMBOLS] = {

using std::string;
ExecutionEnvironment ee;
+Debugger *debugger_instance;

/// fallback name of the program
static const char *progname = "genericfrontend";
@@ -128,6 +130,9 @@ static bool has_parens (const char* arg);

int signal_log_fd; // fd to use for logging in signal handler

+bool debugger = false;
+bool debugger_remote = false;
+
static
void
signal_log_to_fd (int fd, const char * cs)
@@ -710,6 +715,42 @@ main (int argc, char **argv)

// FIXME the whole option parsing sucks **** !

+ // Check, if debugger should be enabled
+ if (!strcmp(argv[arg], "--debugger"))
+ {
+ // set the flag
+ debugger = true;
+ arg+=1;
+ }
+
+ if (!strcmp(argv[arg], "--debugger-remote"))
+ {
+ // set the flag
+ debugger = true;
+ debugger_remote = true;
+ arg+=1;
+ }
+
+ // also handle environment variable
+ if( getenv ("Y2DEBUGGER") )
+ {
+ if (strcmp (getenv ("Y2DEBUGGER"), "1")==0 )
+ debugger = true;
+ else if (strcmp (getenv ("Y2DEBUGGER"), "2")==0 )
+ {
+ debugger = true;
+ debugger_remote = true;
+ }
+ }
+
+ if (debugger)
+ {
+ // initialize the Debugger instance
+ debugger_instance = new Debugger ();
+ debugger_instance->initialize (debugger_remote);
+ }
+
+
// list of -I / -M pathes
// will be pushed to YCPPathSearch later to keep correct order
// (the last added path to YCPPathSearch will be searched first)
@@ -747,6 +788,9 @@ main (int argc, char **argv)
char * client_name;
YCPList arglist;
parse_client_and_options (argc, argv, arg, client_name, arglist);
+ // add debugger information if needed
+ if (debugger)
+ arglist->add ( YCPSymbol("debugger") );

// "arg" and these two are output params
char * server_name;
@@ -824,8 +868,9 @@ main (int argc, char **argv)
y2milestone ("YAST_IS_RUNNING is %s", getenv ("YAST_IS_RUNNING"));


+ YCPValue result = YCPVoid();
// Now start communication
- YCPValue result = client->doActualWork(arglist, server); // give arglist
collected above
+ result = client->doActualWork(arglist, server); // give arglist
collected above

// get result
server->result(result);
@@ -877,17 +922,19 @@ static void
print_help()
{
fprintf (stderr, "\n"
- "Usage: %s [LogOpts] Client [ClientOpts] Server [Generic
ServerOpts] "
+ "Usage: %s [GenericOpts] Client [ClientOpts] Server [Generic
ServerOpts] "
"[Specific ServerOpts]\n",
progname);

fprintf (stderr,
- "LogOptions are:\n"
+ "GenericOptions are:\n"
" -l | --logfile LogFile : Set logfile\n"
" -c | --logconf ConfFile : Configure logging\n"
" -n Namespace=Component : Override component for
namespace\n"
" -I Path : Add include search path\n"
" -M Path : Add module search path\n"
+ " --debugger : Start local debugger\n"
+ " --debugger-remote : Start remote debugger\n"
"ClientOptions are:\n"
" -s : Get options as one YCPList from
stdin\n"
" -f FileName : Get YCPValue(s) from file\n"
diff --git a/liby2/src/include/y2/Y2Namespace.h
b/liby2/src/include/y2/Y2Namespace.h
index 92ccf0b..681001c 100644
--- a/liby2/src/include/y2/Y2Namespace.h
+++ b/liby2/src/include/y2/Y2Namespace.h
@@ -34,6 +34,7 @@ class Point;
class Y2Function;
class StaticDeclaration;
class SymbolTable;
+class Debugger;

/**
* Y2Namespace implements a hash(?) table of nested(?) SymbolEntries and
@@ -48,6 +49,7 @@ protected:
symbols_t m_symbols;

friend class SymbolTable;
+ friend class Debugger;

// add symbol to namespace, it now belongs here
// returns the index into m_symbols
diff --git a/libycp/src/ExecutionEnvironment.cc
b/libycp/src/ExecutionEnvironment.cc
index a462d7f..eacc202 100644
--- a/libycp/src/ExecutionEnvironment.cc
+++ b/libycp/src/ExecutionEnvironment.cc
@@ -100,10 +100,10 @@ ExecutionEnvironment::endlessRecursion ()
}

void
-ExecutionEnvironment::pushframe (string called_function)
+ExecutionEnvironment::pushframe (YECallPtr function, YCPValue m_params[])
{
- y2debug ("Push frame %s", called_function.c_str ());
- CallFrame* frame = new CallFrame (filename(), linenumber (),
called_function);
+ y2debug ("Push frame %s", function->entry()->name());
+ CallFrame* frame = new CallFrame (filename(), linenumber (), function,
m_params);
m_backtrace.push_back (frame);
// backtrace( LOG_MILESTONE, 0 );
}
@@ -134,7 +134,7 @@ ExecutionEnvironment::backtrace (loglevel_t level, uint
omit) const
while (it != m_backtrace.rend())
{
ycp2log (level, (*it)->filename.c_str (), (*it)->linenumber
- , "", "%s", (*it)->called_function.c_str ());
+ , "", "%s", (*it)->function->entry()->toString().c_str());
++it;
};

diff --git a/libycp/src/Makefile.am b/libycp/src/Makefile.am
index 9c326cd..d2112bf 100644
--- a/libycp/src/Makefile.am
+++ b/libycp/src/Makefile.am
@@ -36,6 +36,7 @@ libycp_la_SOURCES = \
ExecutionEnvironment.cc \
StaticDeclaration.cc YCode.cc YCPCode.cc \
YExpression.cc YStatement.cc YBlock.cc \
+ YBreakpoint.cc \
SymbolTable.cc \
Scanner.cc Parser.cc \
parser.yy scanner.ll \
@@ -59,14 +60,15 @@ BUILT_SOURCES = parser.h

AM_YFLAGS = -d -v

-INCLUDES = -I$(srcdir)/include -I$(srcdir)/include/ycp
-I$(top_srcdir)/liby2/src/include ${Y2UTIL_CFLAGS}
+INCLUDES = -I$(srcdir)/include -I$(srcdir)/include/ycp
-I$(top_srcdir)/liby2/src/include ${Y2UTIL_CFLAGS} \
+ -I$(top_srcdir)/debugger

# CURRENT:REVISION:AGE
libycpvalues_la_LDFLAGS = -version-info 3:0:0
libycpvalues_la_LIBADD = ${Y2UTIL_LIBS}

libycp_la_LDFLAGS = -version-info 3:0:0
-libycp_la_LIBADD = ${Y2UTIL_LIBS} -lxcrypt libycpvalues.la
+libycp_la_LIBADD = ${Y2UTIL_LIBS} -lxcrypt libycpvalues.la
$(top_builddir)/debugger/liby2debug.la

CLEANFILES = parser.output parser.cc scanner.cc $(BUILT_SOURCES)

diff --git a/libycp/src/YBlock.cc b/libycp/src/YBlock.cc
index 2f3d5a3..c9ac630 100644
--- a/libycp/src/YBlock.cc
+++ b/libycp/src/YBlock.cc
@@ -43,7 +43,10 @@
#include "ycp/y2log.h"
#include "ExecutionEnvironment.h"

+#include <Debugger.h>
+
extern ExecutionEnvironment ee;
+extern Debugger* debugger_instance;

// ------------------------------------------------------------------

@@ -575,6 +578,15 @@ YBlock::evaluate (bool cse)
y2debug ("YBlock::evaluate([%d]%s)\n", (int)m_kind, toString().c_str());
#endif

+ bool m_debug = false;
+
+ if (debugger_instance)
+ {
+ m_debug = debugger_instance->tracing();
+
+ debugger_instance->pushBlock (this, m_debug);
+ }
+
// recursion handling - not used for modules
if (! isModule () && m_running)
{
@@ -595,14 +607,44 @@ YBlock::evaluate (bool cse)
YCPValue value = YCPVoid ();
while (stmt)
{
+ bool next_hit = false;
YStatementPtr statement = stmt->stmt;

#if DO_DEBUG
y2debug ("%d: %s", statement->line (), statement->toString ().c_str ());
#endif
-
ee.setStatement (statement);
+
+ if (m_debug && statement->kind() != ysFunction )
+ {
+ Debugger::command_t command;
+ std::list<std::string> args;
+ if (debugger_instance->processInput (command, args) &&
command==Debugger::c_continue)
+ {
+ m_debug = false;
+ debugger_instance->setTracing (false);
+ }
+ else if (command == Debugger::c_next)
+ {
+ next_hit = true;
+ debugger_instance->setTracing (false);
+ }
+ }
+
+
value = statement->evaluate ();
+
+ // If we get continue from inner evaluation, we have to respect it
+ if (debugger_instance)
+ {
+ if (m_debug)
+ {
+ m_debug = debugger_instance->lastCommand() !=
Debugger::c_continue;
+ debugger_instance->setTracing (m_debug);
+ }
+ else
+ m_debug = debugger_instance->tracing ();
+ }

if (!value.isNull())
{
@@ -626,6 +668,9 @@ YBlock::evaluate (bool cse)
{
popFromStack ();
}
+
+ if (debugger_instance)
+ debugger_instance->popBlock ();

#if DO_DEBUG
y2debug ("YBlock::evaluate done (stmt %p, kind %d, value '%s')\n", stmt,
m_kind, value.isNull() ? "NULL" : value->toString().c_str());
@@ -646,7 +691,6 @@ YBlock::evaluate (bool cse)
return value;
}

-
// FIXME: consolidate duplicate code of different 'evaluate'
YCPValue
YBlock::evaluateFrom (int statement_index)
diff --git a/libycp/src/YBreakpoint.cc b/libycp/src/YBreakpoint.cc
new file mode 100644
index 0000000..24421c3
--- /dev/null
+++ b/libycp/src/YBreakpoint.cc
@@ -0,0 +1,153 @@
+/*---------------------------------------------------------------------\
+| |
+| __ __ ____ _____ ____ |
+| \ \ / /_ _/ ___|_ _|___ \ |
+| \ V / _` \___ \ | | __) | |
+| | | (_| |___) || | / __/ |
+| |_|\__,_|____/ |_| |_____| |
+| |
+| core system |
+| (C) SuSE GmbH |
+\----------------------------------------------------------------------/
+
+ File: YBreakpoint.cc
+
+ Author: Klaus Kaempf <kkaempf@xxxxxxx>
+ Maintainer: Klaus Kaempf <kkaempf@xxxxxxx>
+
+/-*/
+// -*- c++ -*-
+
+#ifndef DO_DEBUG
+#define DO_DEBUG 0
+#endif
+
+#include <libintl.h>
+#include <debugger/Debugger.h>
+
+#include "ycp/YBreakpoint.h"
+
+#include "ycp/Bytecode.h"
+#include "ycp/Xmlcode.h"
+
+#include "ycp/y2log.h"
+#include "ycp/ExecutionEnvironment.h"
+
+extern Debugger *debugger_instance;
+
+// ------------------------------------------------------------------
+
+IMPL_DERIVED_POINTER(YBreakpoint, YCode);
+
+// ------------------------------------------------------------------
+// YBreakpoint
+
+YBreakpoint::YBreakpoint (YCodePtr code, std::string name) : YCode()
+ , m_code (code)
+ , m_enabled (true)
+ , m_name (name)
+{
+}
+
+
+YBreakpoint::~YBreakpoint ()
+{
+ m_code = 0;
+}
+
+
+bool
+YBreakpoint::isConstant() const
+{
+ return m_code->isConstant();
+}
+
+
+bool
+YBreakpoint::isStatement() const
+{
+ return m_code->isStatement();
+}
+
+
+bool
+YBreakpoint::isBlock () const
+{
+ return m_code->isBlock();
+}
+
+
+bool
+YBreakpoint::isReferenceable () const
+{
+ return m_code->isReferenceable ();
+}
+
+
+string
+YBreakpoint::toString() const
+{
+ return m_code->toString ();
+}
+
+YCodePtr
+YBreakpoint::code() const
+{
+ return m_code;
+}
+
+// write to stream, see Bytecode for read
+std::ostream &
+YBreakpoint::toStream (std::ostream & str) const
+{
+ return m_code->toStream(str);
+}
+
+
+std::ostream &
+YBreakpoint::toXml (std::ostream & str, int indent ) const
+{
+ return m_code->toXml(str, indent);
+}
+
+
+YCPValue
+YBreakpoint::evaluate (bool cse)
+{
+#if DO_DEBUG
+ y2debug ("evaluate(%s) = nil", toString().c_str());
+#endif
+ if (debugger_instance && m_enabled)
+ {
+ debugger_instance->stashOutput ("Breakpoint hit at " + m_name + "\n");
+ if (m_code->isBlock()) {
+ debugger_instance->setTracing();
+ }
+ else
+ y2internal ("Debugger: process input failed");
+ }
+
+ return m_code->evaluate (cse);
+}
+
+
+constTypePtr
+YBreakpoint::type () const
+{
+ return m_code->type ();
+}
+
+bool
+YBreakpoint::enabled () const
+{
+ return m_enabled;
+}
+
+void
+YBreakpoint::setEnabled (bool enable)
+{
+ m_enabled = enable;
+}
+
+
+// EOF
diff --git a/libycp/src/YCode.cc b/libycp/src/YCode.cc
index 531cf6c..da872fc 100644
--- a/libycp/src/YCode.cc
+++ b/libycp/src/YCode.cc
@@ -38,6 +38,7 @@
#include "ycp/YCPCode.h"

#include "ycp/YBlock.h"
+#include "ycp/YBreakpoint.h"

#include "ycp/Bytecode.h"
#include "ycp/Xmlcode.h"
@@ -598,7 +599,7 @@ YFunction::~YFunction ()
}


-YBlockPtr
+YCodePtr
YFunction::definition() const
{
return m_definition;
@@ -622,6 +623,15 @@ YFunction::setDefinition (YBlockPtr definition)


void
+YFunction::setDefinition (YBreakpointPtr definition)
+{
+ m_definition = definition;
+ // skip setKind call, we are just setting a breakpoint wrapper here
+ return;
+}
+
+
+void
YFunction::setDefinition (bytecodeistream & str)
{
if (Bytecode::readBool (str))
@@ -634,9 +644,11 @@ YFunction::setDefinition (bytecodeistream & str)
{
Bytecode::pushNamespace (m_declaration->nameSpace());
}
+
+ YBlockPtr def = (YBlockPtr)Bytecode::readCode (str);
+ def->setKind (YBlock::b_definition);

- m_definition = (YBlockPtr)Bytecode::readCode (str);
- m_definition->setKind (YBlock::b_definition);
+ m_definition = def;

if (m_declaration != 0)
{
diff --git a/libycp/src/YExpression.cc b/libycp/src/YExpression.cc
index bb8742c..d3f9852 100644
--- a/libycp/src/YExpression.cc
+++ b/libycp/src/YExpression.cc
@@ -3085,7 +3085,7 @@ YEFunction::evaluate (bool cse)
}
}

- YCPValue m_params [m_next_param_id];
+ YCPValue evaluated_params [m_next_param_id];

for (unsigned int p = 0; p < m_next_param_id ; p++)
{
@@ -3103,13 +3103,13 @@ YEFunction::evaluate (bool cse)
y2debug ("parameter %d = (%s)", p, value->toString().c_str());
#endif

- m_params [p] = value;
+ evaluated_params [p] = value;
}

// set the parameters for Y2Function
for (unsigned int p = 0; p < m_next_param_id ; p++)
{
- m_functioncall->attachParameter (m_params[p], p);
+ m_functioncall->attachParameter (evaluated_params[p], p);
}

extern ExecutionEnvironment ee;
@@ -3124,7 +3124,7 @@ YEFunction::evaluate (bool cse)
return YCPVoid ();
}

- ee.pushframe (toString ());
+ ee.pushframe ((YECallPtr)this, evaluated_params);

YCPValue value = m_functioncall->evaluateCall ();

diff --git a/libycp/src/include/ycp/ExecutionEnvironment.h
b/libycp/src/include/ycp/ExecutionEnvironment.h
index 5c1aab5..fcf6d5f 100644
--- a/libycp/src/include/ycp/ExecutionEnvironment.h
+++ b/libycp/src/include/ycp/ExecutionEnvironment.h
@@ -20,20 +20,26 @@

#include "y2log.h"
#include "ycp/YStatement.h"
+#include "ycp/YExpression.h"
+
+#include <ycp/YCPValue.h>

using namespace std;

/// Function and source location, for backtraces
struct CallFrame {
- string called_function;
+ YECallPtr function;
string filename;
int linenumber;
+ YCPValue* params;

- CallFrame (string f, int l, string func):
- called_function (func),
+ CallFrame (string f, int l, YECallPtr func, YCPValue* p):
+ function (func),
filename (f),
linenumber (l)
- {}
+ {
+ params = p;
+ }
};

/**
@@ -107,7 +113,7 @@ public:
*
* @param called_function name of the function to be called at this point
*/
- void pushframe (string called_function);
+ void pushframe (YECallPtr called_function, YCPValue params[]);

/**
* Pop the top call frame from the backtrace stack.
diff --git a/libycp/src/include/ycp/Makefile.am
b/libycp/src/include/ycp/Makefile.am
index ffff5d8..1435a40 100644
--- a/libycp/src/include/ycp/Makefile.am
+++ b/libycp/src/include/ycp/Makefile.am
@@ -32,7 +32,7 @@ pkginclude_HEADERS = \
Bytecode.h Import.h Point.h \
YExpression.h YStatement.h YBlock.h \
SymbolTable.h Scanner.h Parser.h \
- YSymbolEntry.h \
+ YSymbolEntry.h YBreakpoint.h \
y2log.h ycpless.h pathsearch.h \
y2string.h \
ExecutionEnvironment.h
diff --git a/libycp/src/include/ycp/YBlock.h b/libycp/src/include/ycp/YBlock.h
index 019d11a..4dc2b9b 100644
--- a/libycp/src/include/ycp/YBlock.h
+++ b/libycp/src/include/ycp/YBlock.h
@@ -129,7 +129,7 @@ private:
constTypePtr m_type;

bool m_running;
-
+
public:
//---------------------------------------------------------------
// Constructor / Destructor
diff --git a/libycp/src/include/ycp/YBreakpoint.h
b/libycp/src/include/ycp/YBreakpoint.h
new file mode 100644
index 0000000..fdab4ab
--- /dev/null
+++ b/libycp/src/include/ycp/YBreakpoint.h
@@ -0,0 +1,136 @@
+/*-----------------------------------------------------------*- c++ -*-\
+| |
+| __ __ ____ _____ ____ |
+| \ \ / /_ _/ ___|_ _|___ \ |
+| \ V / _` \___ \ | | __) | |
+| | | (_| |___) || | / __/ |
+| |_|\__,_|____/ |_| |_____| |
+| |
+| core system |
+| (C) SuSE GmbH |
+\----------------------------------------------------------------------/
+
+ File: YBreakpoint.h
+
+ Author: Stanislav Visnovsky <visnov@xxxxxxx>
+ Maintainer: Stanislav Visnovsky <visnov@xxxxxxx>
+
+/-*/
+// -*- c++ -*-
+
+#ifndef YBreakpoint_h
+#define YBreakpoint_h
+
+#include <string>
+using std::string;
+
+#include "ycp/YCode.h"
+#include "ycp/YCodePtr.h"
+
+#include "ycp/YCPValue.h"
+#include "ycp/YCPString.h"
+#include "ycp/Type.h"
+#include "ycp/YSymbolEntry.h"
+
+/**
+ * \brief YBreakpoint wrapper for YCP debugger
+ *
+ * A class representing a breakpoint in YCP code.
+ */
+class YBreakpoint : public YCode
+{
+ REP_BODY(YBreakpoint);
+
+ YCodePtr m_code;
+ bool m_enabled;
+ string m_name;
+public:
+
+ /**
+ * Creates a new YCode element
+ */
+ YBreakpoint (YCodePtr wrapped_ycp, string name);
+
+ /**
+ * Destructor
+ */
+ virtual ~YBreakpoint();
+
+ /**
+ * Kind of this \ref YCode.
+ *
+ * \return the YCode kind
+ */
+ virtual ykind kind() const { return yiBreakpoint; }
+
+ /**
+ * Return ASCII represtation of this YCP code. Actually no-op.
+ *
+ * \return ASCII string representation
+ */
+ virtual string toString() const;
+
+ /**
+ * Write YCP code to a byte stream (bytecode implementation). No-op.
+ */
+ virtual std::ostream & toStream (std::ostream & str) const;
+
+ /**
+ * Write YCP code as XML representation. No-op.
+ * \param str string stream to store into
+ * \param indend indentation level for pretty print
+ * \return string stream for chaining writing XML (str)
+ */
+ virtual std::ostream & toXml (std::ostream & str, int indent ) const;
+
+ /**
+ * Is this code constant?
+ *
+ * \return true if the \ref YCode represents a constant
+ */
+ virtual bool isConstant () const;
+
+ /**
+ * Is this a YCP statement (e.g. if, while, ...)
+ *
+ * \return true if the \ref YCode represents a statement
+ */
+ virtual bool isStatement () const;
+
+ /**
+ * Is this a YCP block?
+ *
+ * \return true if the \ref YCode represents a block
+ */
+ virtual bool isBlock () const;
+
+ /**
+ * Can this code be stored in a variable of a type reference?
+ *
+ * \return true if the \ref YCode represents something we can reference to
+ */
+ virtual bool isReferenceable () const;
+
+ /**
+ * Execute YCP code to get the resulting \ref YCPValue. Every inherited
class of YCode should reimplement
+ * this method.
+ *
+ * \return \ref YCPValue after executing the code
+ * \param cse should the evaluation be done for parse time evaluation
(i.e. constant subexpression elimination)
+ */
+ virtual YCPValue evaluate (bool cse = false);
+
+ /**
+ * Return type of this YCP code (interesting mostly for function calls).
+ *
+ * \return type of the value to be returned after calling \ref evaluate
+ */
+ virtual constTypePtr type () const;
+
+ YCodePtr code () const;
+
+ bool enabled () const;
+ void setEnabled (bool enable);
+};
+
+#endif // YBreakpoint_h
diff --git a/libycp/src/include/ycp/YCode.h b/libycp/src/include/ycp/YCode.h
index 9ee9ac2..97705cb 100644
--- a/libycp/src/include/ycp/YCode.h
+++ b/libycp/src/include/ycp/YCode.h
@@ -143,7 +143,10 @@ public:
ysImport, // import
ysBlock, // a block as statement
ysSwitch, // switch (since 10.0)
- ysStatement // [54] -- placeholder --
+ ysStatement, // [54] -- placeholder --
+
+ // internal
+ yiBreakpoint // [55] -- debugger breakpoint
};

public:
@@ -361,7 +364,7 @@ class YFunction : public YCode
YBlockPtr m_declaration;

// the function definition ('body') is the block defining this function
- YBlockPtr m_definition;
+ YCodePtr m_definition;

bool m_is_global;

@@ -377,8 +380,9 @@ public:
SymbolEntryPtr parameter (unsigned int position) const;

// access to definition block (= 0 if declaration only)
- YBlockPtr definition () const;
+ YCodePtr definition () const;
void setDefinition (YBlockPtr body);
+ void setDefinition (YBreakpointPtr body);
// read definition from stream
void setDefinition (bytecodeistream & str);

diff --git a/libycp/src/include/ycp/YCodePtr.h
b/libycp/src/include/ycp/YCodePtr.h
index 5c0d28c..beeb050 100644
--- a/libycp/src/include/ycp/YCodePtr.h
+++ b/libycp/src/include/ycp/YCodePtr.h
@@ -30,6 +30,7 @@ DEFINE_DERIVED_POINTER(YError, YCode);
DEFINE_DERIVED_POINTER(YConst, YCode);
DEFINE_DERIVED_POINTER(YLocale, YCode);
DEFINE_DERIVED_POINTER(YFunction, YCode);
+DEFINE_DERIVED_POINTER(YBreakpoint, YCode);

// needed in YCode.h already
DEFINE_DERIVED_POINTER(YBlock, YCode);
diff --git a/libycp/testsuite/Makefile.am b/libycp/testsuite/Makefile.am
index ad1c9b4..1cdf6b9 100644
--- a/libycp/testsuite/Makefile.am
+++ b/libycp/testsuite/Makefile.am
@@ -14,13 +14,13 @@ libdir = ../src/.libs
noinst_PROGRAMS = testSignature runc runycp

runc_SOURCES = runc.cc
-runc_LDADD = ../src/libycp.la ../src/libycpvalues.la ../../liby2/src/liby2.la
${Y2UTIL_LIBS}
+runc_LDADD = ../src/libycp.la ../src/libycpvalues.la ../../liby2/src/liby2.la
../../debugger/liby2debug.la ${Y2UTIL_LIBS}

runycp_SOURCES = runycp.cc
-runycp_LDADD = ../src/libycp.la ../src/libycpvalues.la
../../liby2/src/liby2.la ${Y2UTIL_LIBS}
+runycp_LDADD = ../src/libycp.la ../src/libycpvalues.la
../../liby2/src/liby2.la ../../debugger/liby2debug.la ${Y2UTIL_LIBS}

testSignature_SOURCES = testSignature.cc
-testSignature_LDADD = ../src/libycp.la ../src/libycpvalues.la
../../liby2/src/liby2.la ${Y2UTIL_LIBS}
+testSignature_LDADD = ../src/libycp.la ../src/libycpvalues.la
../../liby2/src/liby2.la ../../debugger/liby2debug.la ${Y2UTIL_LIBS}

PACKAGE=libycp

diff --git a/libycp/testsuite/tests/builtin/Backtrace.err
b/libycp/testsuite/tests/builtin/Backtrace.err
index 3ed5062..c827411 100644
--- a/libycp/testsuite/tests/builtin/Backtrace.err
+++ b/libycp/testsuite/tests/builtin/Backtrace.err
@@ -22,7 +22,7 @@ Parsed:
----------------------------------------------------------------------
[YCP] tests/builtin/Backtrace.ycp:6 My test
[libycp] ExecutionEnvironment.cc(backtrace):132 ------------- Backtrace begin
-------------
-[YCP] tests/builtin/Backtrace.ycp:10 aoo ((value + 1))
-[YCP] tests/builtin/Backtrace.ycp:14 boo ((foo_val + 1))
-[YCP] tests/builtin/Backtrace.ycp:17 foo (41)
+[YCP] tests/builtin/Backtrace.ycp:10 void aoo (integer value)
+[YCP] tests/builtin/Backtrace.ycp:14 void boo (integer value)
+[YCP] tests/builtin/Backtrace.ycp:17 void foo (integer foo_val)
[libycp] ExecutionEnvironment.cc(backtrace):141 ------------- Backtrace end
---------------
diff --git a/wfm/src/Y2WFMComponent.cc b/wfm/src/Y2WFMComponent.cc
index 2a871db..4a3de27 100644
--- a/wfm/src/Y2WFMComponent.cc
+++ b/wfm/src/Y2WFMComponent.cc
@@ -34,6 +34,7 @@
#include <ycp/Parser.h>
#include <ycp/Bytecode.h>
#include <ycp/YBlock.h>
+#include <ycp/YBreakpoint.h>
#include <scr/SCRAgent.h>
#include <scr/SCR.h>

@@ -135,13 +136,30 @@ YCPValue
Y2WFMComponent::doActualWork (const YCPList& arglist, Y2Component
*displayserver)
{
y2debug( "Starting evaluation" );
+
+ bool debugger = false;
+ YCPList client_arglist = arglist;
+
+ // hack: look only at the last entry, if it's debugger or not
+ if (arglist->size () > 0)
+ {
+ YCPValue last = arglist->value (arglist->size ()-1);
+ if (last->isSymbol () && last->asSymbol()->symbol() == "debugger")
+ {
+ y2debug ("Enabling debugger");
+ debugger = true;
+
+ // remove the flag from the arguments
+ client_arglist->remove (arglist->size ()-1);
+ }
+ }

// Prepare the arguments. It has the form [script, [clientargs...]]
YCPList wfm_arglist;
wfm_arglist->add(script);
wfm_arglist->add(YCPString(name()));
wfm_arglist->add (YCPString (fullname));
- wfm_arglist->add(arglist);
+ wfm_arglist->add(client_arglist);

// store the old arguments and module name to preserve reentrancy
YCPList old_arguments = argumentlist;
@@ -175,6 +193,9 @@ Y2WFMComponent::doActualWork (const YCPList& arglist,
Y2Component *displayserver
y2debug ("Script is: %s", script->toString().c_str());

y2debug ("Y2WFMComponent @ %p, displayserver @ %p", this, displayserver);
+
+ if (debugger)
+ script = YCPCode ((YCodePtr)new YBreakpoint (script->asCode ()->code
(), "code start"));

YCPValue v = script->asCode ()->evaluate ();

diff --git a/wfm/testsuite/Makefile.am b/wfm/testsuite/Makefile.am
index c33fff1..c29f80b 100644
--- a/wfm/testsuite/Makefile.am
+++ b/wfm/testsuite/Makefile.am
@@ -27,6 +27,7 @@ runwfm_LDADD = \
$(top_builddir)/libycp/src/libycp.la \
$(top_builddir)/libycp/src/libycpvalues.la \
$(top_builddir)/liby2/src/liby2.la \
+ $(top_builddir)/debugger/liby2debug.la \
${Y2UTIL_LIBS}

runc_SOURCES = runc.cc
@@ -36,6 +37,7 @@ runc_LDADD = ../src/libpy2wfm.la \
$(top_builddir)/libscr/src/libscr.la \
$(top_builddir)/libycp/src/libycp.la \
$(top_builddir)/libycp/src/libycpvalues.la \
+ $(top_builddir)/debugger/liby2debug.la \
$(top_builddir)/liby2/src/liby2.la \
${Y2UTIL_LIBS}

diff --git a/yast2-core.spec.in b/yast2-core.spec.in
index 064592a..22e41e6 100644
--- a/yast2-core.spec.in
+++ b/yast2-core.spec.in
@@ -48,8 +48,17 @@ Requires: flex
This package contains include and documentation files for developing
applications using the YaST2 YCP interpreter.

+%package debugger
+Requires: yast2-core = %version
+Group: Development/Libraries
+Summary: YaST2 - Core Libraries
+
+%description debugger
+YCP debugger client.
+
@PREP@

+
%build

%ifarch %arm
@@ -131,3 +140,7 @@ fi
%doc @docdir@
%doc %{_datadir}/doc/yastdoc
@ydatadir@/devtools/bin/generateYCPWrappers
+
+%files debugger
+%defattr(-,root,root)
+%attr(0755,-,-) /usr/lib/YaST2/bin/ycp-debugger
< Previous Next >
References