LinuxCNCKnowledgeBase | RecentChanges | PageIndex | Preferences | LinuxCNC.org

Difference (from prior minor revision) (major diff)

Added: 129a130,131
Update: the easiest method to deal with it: in the action, raise InterpreterException?,"message". In the calling code, detect that and abort the interpreter with that message.

Interpreter Watchpoints, and Run-from-Line

I recently laid out some thoughts about line number handling in the interpreter, which - among many other uses - relates to the Run-From-Line feature (see http://wiki.linuxcnc.org/cgi-bin/wiki.pl?LineNumbers). While I have not implemented the proposal outlined there, I've made some progress on a different approach to the Run-From-Line theme.

The mechanism - interpreter watchpoints - can be used for RFL, but is much more generally applicable than that.

Restating the problem

The current RFL implementation works roughly like this (I'm skipping some details here like boundary tests):

Viewed a bit differently, task is looking for a start condition, and does so by inspecting the line numbers. Consequently, the line number - with all its deficiencies - is the *only* start condition which we have.

Moving evalution of conditions to the interpreter: introducing watchpoints

Task clearly isnt a great place for ex-post analysis of a condition which the interpreter could have determined much better in the first place, and it's kind of too late at that point to get clever about it.

Let's steal an idea from a different field: Debuggers like gdb have an interesting feature: conditional breakpoints (see for example: http://blog.vinceliu.com/2009/07/gdbs-conditional-breakpoints.html). What this does is: the program is stepped, and after each step, the condition is evaluated, with some action attached to it.

We can do the same with the RS274NGC interpreter, in particular since the embedded Python interpreter gives pretty much full access to internal state.

The overall idea is as follows:

An example Interpreter method could look like so:

 interp.set_watchpoint(0, "this.sequence_number >= 7")

Without going through the details: the expression would be true after line 7 of the NGC program has been reached.

Now, if task could in turn inspect the results of this watchpoint, this would not only give the equivalent for Run-from-line, but the capability to associate arbitrary expressions as a start condition.


The branch at http://git.mah.priv.at/gitweb/emc2-dev.git/shortlog/refs/heads/watchpoints-run-from-line shows how it can be done. What is does is:

This emulates the current RFL feature by constructing a trivial watchpoing ("this.sequence_number >= startline") and watching the conditon.

To see how it works, build this branch, turn on debugging to 0xFFFFFFFF and do a run-from-line of an arbitrary NGC program. You should see the WATCHPOINT_UPDATE changes and the moves only starting after the given line.

The watchpoint mask might change often; the Run-From-Line condition is derived as ' execute commands after watchpoint 0 has been found true for the first time'.

Python bindings

The new methods are exposed to Python internally and can be used through the ';py,...python code...' pseudocomment feature, see tests/interp/watchpoints .


 ; define two watchpoints (default to active)
 ;py,this.set_watchpoint(0,"this.sequence_number > 7")
 ;py,this.set_watchpoint(1,"this.sequence_number < 10")

 ; this is a watch statement - it has no value, but might execute some Python code:
 ;py,this.set_watchpoint(1,"if this.sequence_number > 10: print 'l>10:',this.sequence_number")

 ;py,print "line=",this.sequence_number

 ;py,this.set_watchpoint(1,0)  # disable wp 1

Other uses

Watchpoints are not limited to emulating run-from-line. Some other uses could be:

I havent fully thought through the implications for stepping, and motion for that matter. I think some use can be made, but for that I have to first understand the current stepping code better.

The current code also permits non-expression watchpoints like so:



 if <expression>: action()

to call arbitrary Python code (eg a boundary test). However, there is currently no action associated with such a statement (like aborting the interpreter).

Update: the easiest method to deal with it: in the action, raise InterpreterException?,"message". In the calling code, detect that and abort the interpreter with that message.

Example for a complex start condition

The watchpoint in the following example reads as:

 ;load and point on the next line in Axis, then RFL - this will override the default run-from-line watchpoint
 ;py,this.set_watchpoint(0,"this.sub_context[this.call_level].subname == 'mysub' and this.params.exists('val') and this.params['val'] < 3")

 o<mysub> sub

 o200 if [#1 GT 0]
    (debug, before val assignment)
    #<val> = #1
    o<mysub> call [#1 - 1]
 o200 endif
 o<mysub> endsub

 o<mysub> call [5]


"Oh my god, he's executing Python code on every block". To gauge the problem, I did some ballpark measurements. The upshot is:

Executing more complex Python code, like an arbitrary boundary test, will naturally take longer. The implementation uses compiled Python code objects for better performance.


All this doesnt solve the line number problem outlined here:http://wiki.linuxcnc.org/cgi-bin/wiki.pl?LineNumbers, but it improves upon the kind of conditions considerably.

Stepping: not clear yet how this fits in.

Motion stepping: Looking at src/emc/motion I find:

  /motion_debug.h:96:	int idForStep?;
  ./command.c:1043:                emcmotDebug->idForStep? = emcmotStatus->id;
  ./control.c:1955:    if (emcmotDebug->stepping && emcmotDebug->idForStep? != emcmotStatus->id) {

which I read as: when stepping, motion is looking for a change in idForStep? (which is the line_number). Since only inequality is used, I conjecture it would be enough to have a single bit which changes on each line. A different bit in the condition mask could fulfill this purpose, and a watchpoint which changes this bit on every new line encountered. Sounds right ?

Update: this doesnt work - there seems to be an assumption about increasing motion id's, maybe in the tp.

Motion: Passing the watchpoint condition mask down to motion along with the linenumber is easy. Not sure about the use scenario.

Task: there's no reason why task couldnt have such watchpoints as well, although I dont see a useful scenario at this point (safety?). The footwork is mostly done - much of task and interpreter internals are exposed to Python, and doing it should be easy - if one comes up with a good use case.

LinuxCNCKnowledgeBase | RecentChanges | PageIndex | Preferences | LinuxCNC.org
This page is read-only. Follow the BasicSteps to edit pages. | View other revisions
Last edited April 9, 2012 8:22 am by MichaelHaberler (diff)
Published under a Creative Commons License