Debugging Metropolis

Metropolis now includes an interface to gdb that allows the user to set breakpoints by line number in .mmm files at runtime. See $METRO/util/mgdb/README.txt for details.

This page discusses debugging Metropolis Java code using GNU Emacs and jdb.

We need both styles of debugging because the Metropolis Java code reads in the .mmm files and generates C++ files.

Zoltan Kemenczy of Research In Motion Ltd. contributed changes to Ptolemy that provided source level debugging of Ptolemy II. These changes were then migrated over to Metropolis.
Christopher Brooks modified Zoltan's original notes. The way debugging works is that $METRO/bin/metroinvoke.in can use $METRO/util/lisp/gud.el to provide source level debugging of Metropolis

Contents

  • Emacs
  • Setting up Emacs
  • Invoking the debugger
  • Debugger Example
  • Emacs

    This page includes documentation on how to use the Java Debugger jdb and GNU Emacs to debug Java code.

    GNU Emacs is a powerful and complex editing and development environment. If you are unfamiliar with GNU Emacs, you should try running the GNU Emacs tutorial. In theory, it should be possible to debug Ptolemy using other debuggers, but we have not tried them.

    The gud.el file included in Metropolis requires a version of GNU emacs more recent than Emacs-20.7.1.

    The gud.el file will not work with Emacs-20.7.1 because M-x jdb results in:

    Symbol's function definition is void: easy-mmode-define-keymap
    
    We have tested the interface under Emacs-21.2.1.
    
    
    Below are the steps necessary to install Emacs under Windows.
    1. Emacs for Windows can be found at ftp://ftp.gnu.org/gnu/windows/emacs/latest

      The file to download isemacs-xx.x-bin-i386.tar.gz

    2. Untar or unzip the file in c:\Program Files so that c:\Program Files\emacs-xx.xx is created
    3. The c:\Program Files\emacs-xx.xx\README.W32 file says:
      To install Emacs, simply unpack all the files into a directory of your choice, but note that you might encounter minor problems if there is a space anywhere in the directory name. To complete the installation process, you can optionally run the program addpm.exe in the bin subdirectory. This will add some entries to the registry that tell Emacs where to find its support files, and put an icon for Emacs in the Start Menu under "Start -> Programs -> Gnu Emacs -> Emacs".
      So, go ahead and click on the addpm.exe icon, which will add an Emacs icon to the start menu.

    The GNU Emacs FAQ For Windows 95/98/ME/NT/XP and 2000 can be found at http://www.gnu.org/software/emacs/windows/ntemacs.html

    Setting up Emacs

    Emacs reads the file $HOME/.emacs at start up time.

    Under Windows 2000, you set the environment variables via the Environment tab of the System control panel (Start Menu -> Settings -> Control Panels -> System -> Advanced -> Environment Variables

    The HOME variable name your home directory using the DOS drive name with backslashes. For example, if your home directory is in c:\users\yourname, then you would enter the value c:\users\yourname

    While you are editing environment variables, be sure that $METRO is set. For details, see

    The GNU Emacs FAQ For Windows 95/98/ME/NT/XP and 2000 at http://www.gnu.org/software/emacs/windows/ntemacs.html discusses the .emacs file further.

    To debug Metropolis, Emacs needs to be told where to find the gud.el file. Start up Emacs by using Start -> Programs -> Gnu Emacs -> Emacs and add the following to your $HOME/.emacs file.

    (setq load-path (append
                     (list
                      (expand-file-name 
    		   (concat (getenv "METRO") "/util/lisp")))
                     load-path
                     ))
    
    A more complete example .emacs file can be found at $METRO/util/lisp/metroemacs.el If you wish, you can copy this file to your $HOME/.emacs file. This file includes support for
    M-x shell
    Brings up a bash shell
    Java indentation
    Java indentation that follows the Ptolemy II Style guide.

    Invoking the debugger

    The Metropolis start up scripts in $METRO/bin have the following options that interact with the debugger.
    Under Windows, these options will not work with the DOS scripts, you must run the shell script versions of these scripts.
    The easiest way to run these scripts under Windows is to start up the Cygwin bash shell.
    Another way is to use the DOS shell and preface the commands with bash -C:
    cd %METRO%
    bash -C systemc -debug Your other arguments
    
    -debug
    This starts the java VM in a mode that allows debugger connections. The nice thing is that you can run, load models, run without a debugger until you encounter a problem (exception :-). Then you attach jdb to your Metropolis process, set to catch the exception and re-run... The exception is now caught by jdb in Emacs with all the source files and call stack available for examination.

    Notes:

    1. Running a bit slower with this option.
    2. I have only tested the JVM attach on Windows with shared memory attaches (I would have preferred the socket approach but Sun didn't do it on Windows...) Someone should try the socket connection method on Solaris/Linux and update metroinvoke to select based on the system type...
    -jdb
    This starts using jdb instead of java to allow debugger control right from the start. Typically you provide this option in response to Emacs' M-x jdb prompt Run jdb (like this):
    systemc -jdb other systemc args
    
    -q
    for "quiet" - it is not really a debug option, but eliminates the echo of the command line constructed by metroinvoke to start java.
    Note: Don't use -q with -jdb within Emacs. If you do, then the Emacs GUD mode will not be able to get the classpath.
    -profiler
    has the java profiler startup options canned (these could be passed using JAVAFLAGS, but then you have to remember and set/type them every time). Very useful to find out where Metropolis is spending most of the CPU time...

    Emacs GUD updates

    These have already been submitted to Richard Stallman and incorporated into the www.gnu.org Emacs CVS, but there are not yet in 21.2

    The following is a useful addition to a user's .emacs file (for the Windows shared memory attach) (setq gud-jdb-command-name "jdb -attach javadebug")

    The documentation of changes is in the gud-jdb-use-classpath and gud-jdb-classpath (and other gud-jdb-xxx) variables. The new method of finding java source files through the classpath of the JVM is automatically enabled so no special setup is needed.

    There are two primary ways to use the Emacs interface to debug Metropolis:

    1. Use the -debug flag so that java starts up and synchronizes with Emacs.
    2. Use the -jdb flag so that jdb starts up.

    -debug

    The -debug option is more commonly used than -jdb because with -debug, you can attach the debugger at any point during the run. To use -debug, just start your Metropolis binary from a bash or Emacs shell with:
    system -debug &
    
    You should see a message:
    Listening for transport dt_shmem at address: javadebug
    
    and the program should contine

    when you'd like to debug (e.g set breakpoints or catch exceptions), start jdb from Emacs by:

    M-x jdb
    
    Then type
    jdb -attach javadebug
    
    if you're on windows (on Unix "javadebug" would be replaced by the JVM debug server socket port number).

    You should get a buffer named *gud-...* with the Initializing jdb... message. After that switch to any Emacs buffer holding a source and go to a function that will be used during a run (e.g. src/metropolis/metamodel/Compiler. main()). On a source line, press C-x SPACE. In the *gud-...* buffer, a breakpoint command is automatically entered for you (this is GUD at work).

    Now run the model... When the breakpoint is hit the source file (Compiler.java) will be positioned at the breakpoint with a "=>" at the beginning of the line where the stop occurred. Now you can use C-c C-n (for "next" or "step-over") or C-c C-s (for "step" or "step-into") and a bunch of other short-cuts (like call stack browsing up/down)... An the "=>" follows your steps and brings up source files as needed (GUD again...:).

    Most of the standard gud-style commands are supported (check out the "GUD" menu in the *gud-..*

    -jdb

    The difference between -debug and -jdb is that -jdb can be used to debug initialization code that would have already been run by the time the application comes up.

    You can start a Metropolis script in $METRO/bin with the -jdb argument, such as systemc -jdb, from a bash shell within Emacs, but that doesn't get you into GUD mode, which allows for easy breakpoint setting etc.

    To start a Metropolis script with jdb instead of java, enter start jdb from Emacs by:

    M-x jdb
    
    and then type:
    bash -C systemc -jdb jdb binaries
    

    Don't use the -q flag here because that will prevent GUD from getting the classpath (it finds it in the command line echo provided by metroinvoke. If you used a manual way of starting jdb (instead of metroinvoke), you would normally have to provide a -classpath argument which is what GUD is looking for).

    The windows version of Emacs is not cygwin-aware and it uses its own win32 api to start any subprocess and this looks for an ".exe" (or .bat)... This is why we have to insert "bash -C" (which is an exe in the path) to make sure that the systemc link will be caught and processed...

    If you then switch to a java source (that is in the classpath and/or sourcepath) and do C-x SPC, a breakpoint will be set.

    Debugger Example

    Below is an example of how to use the debugger
    1. Install emacs as per the Emacs step above.
    2. Set the Emacs load path as per the Setting up Emacs step above.
    3. Type M-x shell. In Emacs, M-x means
      "Hit the Esc key and then the x key."
    4. You will now be in a buffer named *shell*. In this buffer, change to the test_new directory
      cd $METRO/examples/test_new
      
    5. Remove any results from an earlier run:
      make clean
      
    6. Run make, which will run $METRO/bin/systemc -debug:
      make DEBUG=-debug
      
    7. Start up the debugger by typing M-x jdb. If your .emacs file contains:
      (setq gud-jdb-command-name "jdb -attach javadebug")
      
      then after typing M-x jdb, the following lines should appear in the minibuffer at the bottom of the emacs window:
      Run jdb (like this): jdb -attach javadebug 
      
      If jdb -attach javadebug does not appear, then type it in. After the minibuffer contains jdb -attach javadebug hit Enter and jdb starts up.
    8. Now we will set a break point in the main() method of the Compiler actor. To do this, type in the following in the *gud-javadebug* buffer
      stop in metropolis.metamodel.Compiler.main
      
    9. Then continue with the run by typing:
      cont
      
    10. The debugger will stop execution in the main() method
      For example:
      
      main[1] stop in metropolis.metamodel.Compiler.main
      Deferring breakpoint metropolis.metamodel.Compiler.main.
      It will be set after the class is loaded.
      bmain[1] cont
      > Set deferred breakpoint metropolis.metamodel.Compiler.main
      
      Breakpoint hit: "thread=main", metropolis.metamodel.Compiler.main(), line=93 bci=0
      93    	_processArguments(args);
      
      main[1]
      
    11. The Emacs debugger interface can bring up the source file where the break point is set, but the interface sometimes needs help finding the file. The quickest way is to view the file by typing C-x C-f and then $METRO/src/metropolis/metamodel/Compiler.java In Emacs documentation, C-x means
      "Hold the Control key down and then hit the x key" and then typing
      up
      down
      where
      
      in the *gud-javadebug* buffer
    12. To step through the code, type next
    13. To view the value of a variable, type print variable name
      In the example below, I typed next twice, and then viewed the value of the args variable:
      
      main[1] where
        [1] metropolis.metamodel.Compiler.main (Compiler.java:93)
      main[1] next
      > 
      Step completed: "thread=main", metropolis.metamodel.Compiler.main(), line=94 bci=4
      94    	System.out.print("Loading libraries");
      
      main[1] next
      > 
      Step completed: "thread=main", metropolis.metamodel.Compiler.main(), line=95 bci=12
      95    	_initCompiler();
      
      main[1] print args[0]
       args[0] = "-classpath"
      main[1] 
      

    To get further help with jdb, type help while in the *gud-javadebug* buffer.
    For further information about jdb, see JavaTM Platform Debugger Architecture.

    For further information about the Emacs Grand Unified Debugger (GUD) Interface, use the GNU Emacs Info help system.

    1. Click on the Help menu, or type M-x info
    2. Move the cursor to the Emacs line and type a m followed by hitting the Enter key.
    3. Move the cursor down to the Starting GUD line and type a m and hit return