

============================================================

  About MzTake

_MzTake_ is a _scripted debugger_ for PLT Scheme.  It helps
programmers monitor the execution of a target program as it
unfolds (and optionally pause or resume its execution).  MzTake
gives you the power to easily write real programs that debug real
programs.  You are no longer limited to a tool chest of buttons
like "add breakpoint", "step-next", "step-into", and "step-over".

MzTake scripts are written in the FrTime programming
language, which is bundled with DrScheme.  FrTime supports the
implementation of reactive systems in a functional style.
The key abstraction it adds is a type of value called a 'signal',
which can change over time.  FrTime infers dataflow dependencies
between signals and automatically recomputes them when necessary.  In
order to use MzTake, you will need to familiarize yourself with the
FrTime language by reading its own documentation.

With signals it is possible to respond to outside events concisely,
without using callbacks.  Consider a MzTake script to monitor the
behavior of the program "highway-mztake.ss", in the demos directory of
the MzTake collection:

  (require (lib "mztake.ss" "mztake"))
  (define/bind (loc "highway.ss" 4) speed)
  (printf-b "current speed: ~a" speed)
       ;; more code
  (set-running! true)

This code executes a target module in the file "highway.ss"
after installing a _trace point_ (also known as a _watch
point_) just before the Scheme expression on the fourth line
of "highway.ss".  SPEED is a FrTime behavior that always
contains the *current* value of the variable named SPEED in
the target program.

PRINTF-B works like Scheme's PRINTF function, consuming a
format-string and fill-values, printing the result in
DrScheme's interaction pane.  Whereas PRINTF accumulates
outdated text on the screen, PRINTF-B will replace old text
with updated text if any of the fill-values change.  In this
invocation, it prints the current speed to screen, throughout
the execution of "highway.ss".  The last line invokes SET-RUNNING!,
which lunches the execution of highway.ss

MzTake scripts are also powerful tools for building external
test suites.  Whereas typical test cases may only assert that
the result of a computation is correct, MzTake scripts
can dynamically break open an execution, record inner state,
and *compute* with it.  This allows you to confirm that the
intermediate steps which lead to a correct answer were
also correct.  In the highway example, perhaps knowing the last ten speeds
would prove useful.  You could PRINTF the value
onto a new line each time, but after ten updates the screen
is already starting to fill up with information -- we are
only interested in the last ten speeds, after all.
One possible solution:

  (printf-b "last ten speeds: ~a" (history-b (changes speed) 10))

HISTORY-B consumes an event stream (CHANGES SPEED) and an
optional number n, returning a FrTime behavior containing a
FIFO ordered list of the n values emitted on that event
stream.  In this case, HISTORY-B maintains a list of the ten
most recent SPEEDS seen on SPEED.

We might want to pause the program when something goes awry.  We do
this by exploiting the fact that the SET-RUNNING! function consumes a
FrTime behavior.  The value of a behavior can change over time, and
SET-RUNNING! monitors these changes.  Whenever the behavior is true,
the target program runs, and whenever it is false, the target program
pauses.  We can indicate to MzTake to pause when the speed exceeds 55
as follow:

  (printf-b "last ten speeds: ~a" (history-b (changes speed) 10))
  (set-running! (< speed 55))

Once paused, it is possible to interactively explore the state of the
paused process.  You may enter names of variables bound in the target
program, and MzTake will look up and return their values.  For
example, typing 'nap-time' in the REPL while the program is paused
yields the value 0.8.
  
You can resume execution with "(set-running #t)", or
some other behavior, or end the execution altogether with "(kill)".

Finally, FrTime provides a rich animation library.  Combined
with the MzTake debugger, it takes only a few lines to animate
your algorithms and see them in action, easily letting you
confirm (or refute!) that they are working correctly.

  (require (lib "animation.ss" "frtime"))
  (display-shapes (make-speed-gauge (hold speed)))


============================================================

  Demos

The demos directory contains a sub-directory for each of the demos.
For instance, the highway directory contains "highway.ss" and
"highway-mztake.ss".  To run this demo, switch to the "FrTime" language
level from the "Experimental Languages" section of DrScheme's language
dialog, load "highway-mztake.ss", and click "Run".  What you see is
generated by the debugging script.  Each demo directory contains
the following two files: one is the program being debugged
(named after the directory), and the other is a file ending
in "...-mztake.ss" (the MzTake script).

The demos are (starting with the simplest one):

  ./highway/highway-mztake.ss         - The program simulates a very simple
                                      speedometer, and the MzTake script
                                      monitors it.  

  ./sine/sine-mztake.ss               - Plots values extracted from a program
                                      which generates coordinates for a
                                      single sine wave.

  ./random/random-mztake.ss           - Tests the quality of Scheme's random
                                      number generator with a histogram.

  ./exception/exception-mztake.ss     - Demonstrates how MzTake catches exceptions.

  ./djikstra/dijkstra-mztake.ss       - Debugs a buggy implementation of 
                                      Dijkstra's algorithm
 

The demos demonstrate many ways to debug with MzTake using
FrTime, even if you are not very familiar with the language.
That said, in order to become more proficient in using MzTake,
you will want to learn more about the FrTime language.

You can refer to FrTime's own documentation by searching for
"FrTime" in DrScheme's Help window.  It explains how to use
time-varying behaviors and event streams in greater depth, and
also describes the many useful functions FrTime provides to work
with them.



============================================================

_Debugging with MzTake_

MzTake is a library for the FrTime languages which provides functions
that execute a target program (or many), and "connect" to points in
its code.  MzTake then provides the running FrTime script with
interesting information (such as a variable's current value) which it
derives from these "connections".  FrTime then handles the rest.

MzTake defines the following functions and macros:

_Installing Trace Points_

> (loc require-spec line-number)
> (loc require-spec line-number column-number)

   Creates a LOC structure containing the target file, the target line
   number, and (optionally) the target column number.  LOC structures
   are consumed by TRACE and by DEFINE/BIND.  The first argument to LOC
   is a file specification suitable for require, provided as a
   datum.  For instance, to install a trace point on the tenth line of
   the MzLib's list library, use:

     (trace (loc '(lib "list.ss") 10)   <body>   )

> (loc require-spec pattern)

   The LOC function can also accept a pattern.  MzTake will scan the
   text of the target source file and install a breakpoint at every
   location where the text matches the pattern.  The pattern language
   is very simple.  Every symbol in the pattern is taken literally,
   with the exception of the underscore.  In a pattern, and underscore
   stand for zero, one, or many items.  Unlike the patterns used by
   match.ss, MzTake does not make use of an ellipsis to indicate
   repetition.  The repetition is always implicit. The following
   expression inserts a tracepoint on every lambda expression that has
   exactly one argument, called "l" :

     (trace (loc '(lib "list.ss") '(lambda (l) _))  <body> )
  
> (trace loc body ...)

   Install a trace point at the location indicated by the LOC value.
   The result is a FrTime event stream containing one value each time
   the target reaches the location specified.  To get the value event,
   TRACE evaluates its body (once per event.  During the evaluation of
   the body, the target process is paused, and the body can inspect
   the state of the paused program.

   The body is optional.  If no body is provided, the value #t is used
   by default.

   Unless SET-MAIN! is used, the first call to trace sets the file
   name that will be run when SET-RUNNING! is invoked the first time.

> (trace* process loc thunk)

   Like TRACE, but takes an explicit process argument, and a thunk
   rather than a body.
   
> (bind (name ...) body ...)

   When the target process is paused (or during the execution of a
   trace body), BIND reaches in the lexical context at the point of
   the pause (or of the trace point) and finds the values for the
   variables whose names are given.  These values are then bound in the
   body (in the MzTake script) to the variables of the same name.

   It is an error to call BIND while the target process is running.

   You can use BIND to look up values of identifiers in the target
   program that are shadowed by identifiers in the script.

> (bind* process symbol)

   In the given process, find the variable whose name has the given
   symbol, and returns its value.
   

> (define/bind loc name ...)

   Define the NAMEs to behaviors reflecting the values of the
   given names in the target program, at the given
   location.  DEFINE/BIND is short for:

     (define name (hold (trace loc (bind (name) name))))

     
> (define/bind-e loc name ...)

   Same as DEFINE/BIND, but binds event streams to the names instead
   of behaviors.

   
> (exceptions)
> (exceptions process)

  Returns an event stream containing one EXN structure for each
  exception raised in the target process and not caught.


> (exited?)
> (exited? process)

   Returns a behavior which starts as #f and take on the value #t when
   the target process exits.


> (set-running! val)
> (set-running! val process)
> (set-running! event)
> (set-running! event process)

   Launches the execution of the target process.  Execution continues
   as long as the given behavior is true (aka, any value beside #f),
   or until an event comes on the given event stream with the value
   #f.  When execution pauses, the target remains on the line where
   the pause occured.  You can then inspect the state of the program,
   or resume execution with another call to SET-RUNNING!.


> (set-main! require-spec)
> (set-main! require-spec process)

   Sets the file where execution begins when SET-RUNNING! is called
   for the first time.  When SET-MAIN! is not used explicitly,
   execution begins with the file specified in the first call to
   TRACE.  It is an error to call SET-RUNNING! without first calling
   either TRACE or SET-MAIN!.
   
   
> (where)
> (where process)

   Returns an event stream that contains one event for each expression
   evaluated in the target process.  Combined with HISTORY-B, this makes
   it possible to record entire execution traces for the target program.
   
  
> (kill)

   Kills the target process and releases all resources
   it used -- you cannot resume after a KILL.

   This will not stop evaluation of the MzTake script, however.  In
   particular, if the script depends on input the varies independently
   of the target process, FrTime will continue to update them.  You can
   use "Kill" command from DrScheme's "Scheme" menu to stop both the
   MzTake script and its target process at once.
   
   Also, note that closing a FrTime animation/graphics window does *not*
   kill a running MzTake process.

> (kill-all)

   When using more than one target process at a time, KILL-ALL invokes
   KILL on all of them at once.


> (current-process)
> (current-process process)

   The CURRENT-PROCESS parameter gets or sets the process manipulated
   by the MzTake function when they are not provided with a process
   argument.  The CURRENT-PROCESS parameter is initialized with a blank
   process, and you can create additional processes using the
   CREATE-DEBUG-PROCESS function.  Using more than one process at a
   time lets your MzTake run multiple programs different at once and
   compare their output using a single script.


> (create-debug-process)

   Creates a fresh blank debug process.  Each debug process has its own
   set of trace points, its own run trigger (set via SET-RUNNING!),
   its own exceptions stream, etc.  Each debug process run
   independently from the others, and they can be paused and killed
   individually.  All debug processes in a single MzTake script share
   the same FrTime event space, and so it is possible to compare
   output and traces between each of them.

> (current-policy)
> (current-policy policy)

   Every file executed under MzTake can run either in fast mode or in
   debuggable mode.  The CURRENT-POLICY decides which.

   - debuggable mode: the file is instrumented with MzTake debugging
     information.  It can be the target of tracepoint and it generate
     events on the WHERE stream.  Execution can also be paused in the middle
     of code running in debuggable mode.  The instrumentation overhead
     is considerable, however, of the order of 10x-20x slowdown.

   - fast mode: the file is not instrumented, and runs at its normal
     speed, but cannot be debugged.  Inserting trace points into fast
     mode files after the beginning of the execution has no
     effect.  Also, pausing while executing a fast mode file will be
     delayed until execution reaches a debuggable mode file.

   Files that are the target of a trace point when first lunching the
   process run in debuggable mode, so is the main file set by
   SET-MAIN!. Otherwise, the current policy is consulted to decide
   between fast and debuggable mode. In this case, if the policy does
   not decide, MzTake raises an error.

   Policies have the following contract:   

     (listof (list/c (symbols 'fast 'debuggable)
                     (union (symbols 'everything-else)
                            path?
                            string?
                            (listof (union path? string?)))))

   A policy consist of a list of entries.  Each entry is a pair
   specifying either fast mode or debuggable mode, then a directory,
   or a list of directories.  Files in these directories, or their
   subdirectories will run under the given mode.  The special symbol
   'everything-else can be used instead of a directory, and this will
   match any file.  The policy is checked in order, and the first entry
   that applies to the given filename assign a mode the file.

   The default policy run files of the directories specified by
   CURRENT-LIBRARY-COLLECTIONS-PATHS in fast mode, and runs everything
   else in debuggable mode.  This poloicy is set as follow:

     (current-policy `((fast ,(current-library-collection-paths))
                       (debuggable everything-else)))

   You can change this policy by calling the
   CURRENT-POLICY function with a new policy as an argument.  The
   policy is assigned to a process when the process lunches.
     
      
_Useful Functions for Time-Varying Values_

Note: FrTime uses a naming convention where functions which
      return behaviors have names that end in "-b", and
      functions that return event streams end in "-e".

Tips: When you have a behavior that you want to turn into
      an event, use "(changes behavior)".

      When you have an event that you want to be a
      behavior, use "(hold event)"


MzTake defines a few functions on time-varying values
that are particularly useful when debugging.  You can require these
functions with (require (lib "useful-code.ss" "mztake"))

> (history-e stream)
> (history-b stream)

   Keeps a complete history of all the values seen
   on an event stream as a list, oldest events last.

   Use with BINDs: (history-b x-trace)

> (history-e stream n)
> (history-b stream n)

   Keeps a list of the last n values of a behavior
   Returns a list of at most n elements, where the
   elements are the n last values seem on the stream,
   in order, oldest first.

> (count-b stream)

   Counts number of events seen on an eventstream.
   
   Often used directly on ENTRY traces, counting how many
   times ENTRY occured: (count-b entry-trace)
   
   Also useful to count how many times a BIND changed
   by calling: (count-b (changes bind-trace))

> (largest-val-b stream)
> (smallest-val-b stream)

  Keeps track of the largest/smallest values seen on a stream.
  Use with BINDs: (largest-val-b (changes bind-trace)).

> (sequence-match? seq stream)

   Matches a sequence of items in a list to the history
   of event pings, on the event stream evs.  Returns #t
   when it matches, and #f otherwise.  Use when you expect
   a certain pattern to show up, and want to know when:
     (sequence-match? '(0 1 2 1 0) (changes bind-trace))

> (printf-b format-string arg ...)

   Displays the value of the behaviors with the given format,
   using "~a" just like in Scheme's FORMAT function.


============================================================

Known Problems

* In general, you should not REQUIRE in your MzTake script any
  functions that act on the structures of your target program.

  ORIGINAL FILE:
    (define-struct foo (a b))
    (let ([x (make-foo 1 2)])
      x)

  MZTAKE SCRIPT:
    (require "original-file.ss")
    (define/bind (loc "original-file.ss" 3) x)
    (foo-a x) ;; this will fail!

  The target program and the MzTake will have different
  instances of the struct, and the call to FOO-A will fail.
  Instead, use BIND to bring the function from the target program to
  the script:

    (define/bind (loc "original-file.ss" 3) x foo-a)
    (foo-a x) ;; this succeeds 
  
* The break button will *not* kill runaway client processes.
  You must type (kill) or (kill-all).

* Some legal syntax locations (used in setting trace points)
  are unreachable during program execution (they do not get
  triggered and produce empty eventstreams).  For instance,
  the name clause of a LET is never the current point of execution:
  
     (define x 12)
     (let ([x (add1 x)]) x)
          ^ 
  Recommended syntax locations to use for trace points:

     (define x 12)
     (let ([x (add1 x)]) x)
     ^        ^^    ^    ^

* Watch out: when you change target code, your line/col locations in
  the script will drift out of align.

* Error handling is not perfect -- e.g., the little "bug"
  buttons on syntax errors don't reference the correct code.
  However, the messages that are printed are as accurate as
  possible.

* You can add trace points to the body first-class functions, and they
  will send trace update from anywhere they are passed to and invoked.

============================================================

Authors and Thanks 

MzTake is an experimental debugger.  It should enable new
debugging approaches that were not possible (easily) before.
Please send feedback to the PLT-Scheme mailing list:
   http://www.plt-scheme.org/maillist/

We are eager to hear about how you are using MzTake!

   Jonathan Spiro
   Guillaume Marceau
   Gregory Cooper
   John Clements
   Shriram Krishnamurthi


Please send bug reports to: jspiro@cs.brown.edu

---
Icons for MzTake come from the Gnome Project: Nautilus Emblems.
These are provided under the GPL license.
   http://jimmac.musichall.cz/ikony.php3
