Last Updated: 2018-12-02 Sun 12:04

CSCI 2041 Lab12: The OCaml Debugger

CODE DISTRIBUTION: lab12-code.zip

  • Download the code distribution every lab
  • See further setup instructions below

CHANGELOG: Empty

1 Rationale

All sane programming environments have debuggers associated with them that allow line-by-line execution of programs. While OCaml's debugger is not fancy having only a text-based command line interface, it is sufficient for most debugging tasks. Among standard features like breakpoints, single stepping, and printing variable values, ocamldebug is a so-called "time-travelling" debugger which allows reverse execution via backstep commands. This allows one to hit an error and simply back up a couple steps to see what program state is like at the problem point, a rather uncommon feature among debuggers.

This lab covers basic debugger techniques for ocamldebug applied to a code base that does lexing, parsing, and evaluation similarly to previous labs.

Associated Reading / Preparation

Grading Policy

  • Check-off 30%: Demonstrate to a TA that a student understands answers to questions. This must be done in person in groups of one or two. Check-offs can happen during the lab period of during a TA office hour.
  • Submit 70%: Submit required files according to the lab instruction. This can be done at any time and from anywhere with a network connection. Submitting does not require attending lab. All students must submit files even if they were checked off in a group during lab.

See the full policy in the syllabus.

2 Codepack

The codepack for the lab contains the following files:

File State Description
QUESTIONS.txt Edit Answer questions in the file to complete the lab
buggy_lpe.ml Edit Problem 1/2 file to analyze and edit
lpe_main.ml Edit Main function which allows experimentation with expression evaluation

3 Questions

Analyze the files in the provided codepack and answer the questions given in QUESTIONS.txt.

                           __________________

                            LAB 12 QUESTIONS
                           __________________


- Name: (FILL THIS in)
- NetID: (THE kauf0095 IN kauf0095@umn.edu)

Answer the questions below according to the lab specification. Write
your answers directly in this text file and submit it to complete the
lab.


Files `buggy_lpe.ml' and `lpe_main.ml'
======================================

  Like previous labs, this lab deals with a lexer, parser, evaluator
  system for a small language that includes arithmetic and `let/in'
  expressions. There are some bugs in the basic implementation though.


PROBLEM 1: Debugger Basics
==========================

(A)
~~~

  Compile the two source files together to create an executable. Make
  sure that debug information is turned on via the `-g' switch as in
  ,----
  | > ocamlc -g buggy_lpe.ml lpe_main.ml 
  | File "buggy_lpe.ml", line 249, characters 5-42:
  | Warning 10: this expression should have type unit.
  `----

  Note that the warning shown is likely to be shown and will be
  addressed later in the lab.

  Run the program as shown which should generate an exception.
  ,----
  | ./a.out 'let x=5 in x+2'
  `----

  Show the results of this exception in the terminal which should be a
  bit perplexing.

  While it might be possible to diagnose the problem simply by examining
  the source code carefully, the remainder of this problem will
  demonstrate how to use the debugger to gather information that can
  lead to a fix.


(B)
~~~

  Start the debugger with the compiled program as in:
  ,----
  | > ocamldebug a.out
  |     OCaml Debugger version 4.xx
  | 
  | (ocd) 
  `----

  Since the `lpe_main.ml' program must take command line arguments, set
  them using the `set arguments <args>' command as in
  ,----
  | (ocd) set arguments 'let x=5 in x+2'
  `----

  Run the program with the `run' command and copy the results below.


(C)
~~~

  A major strength of OCaml's debugger is it enables so-called
  "time-traveling" allowing both forward and backward steps. A standard
  debugger such as is widely available in C and Java allows only forward
  steps, not backwards.  The technique that enables ocamldebug this is
  not conceptually not difficult: it simply saves program state
  occasionally as a 'checkpoint' and when one requests backwards
  movement, the program position is noted and the program is restarted
  from the latest checkpoint to the desired moment in time.

  As an end user, this means on hitting an uncaught excepton, one can
  simply back up a step to see what is happening using the `backstep'
  command. Moving forward is a matter of issuing a `step' command. Do
  some backsteps and steps after hitting the exception from the last
  part. Paste the results below.


(D)
~~~

  Next set some *breakpoints* at functions of interest which will stop
  the debugger from executing when reached. If an uncaught exception has
  been hit, all of the modules associated with program are already
  loaded one can use the name of the function to set the breakpoint as
  in:
  ,----
  | (ocd) break Buggy_lpe.parse_muldiv
  | Breakpoint 1 at 145252: file buggy_lpe.ml, line 137, characters 3-348
  `----
  Follow this with the `reverse' command which will run the program in
  "reverse" until the break point is hit. After hitting it, run the
  `list' and `backtrace' commands and paste the results below.


Breakpoints from the beginning of Programs
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  When running from the beginning of a program, not all modules are
  loaded so functions may not be identifiable by name. In this case, set
  breakpoints at specific source lines like the below where 136 is the
  source line corresponding to the `parse_muldiv' function.
  ,----
  | (ocd) break @ Buggy_lpe 136
  | Loading program... done.
  | Breakpoint 1 at 145252: file buggy_lpe.ml, line 137, characters 3-348
  `----


PROBLEM 2: Debugging the Program
================================

(A)
~~~

  After positioning the debugger as indicated above, print the `toks'
  variable then use the `step' command to step through the execution of
  `parse_muldiv'.

  - What token is at the front of the token list?
  - Does the control flow seem to make sense for parsing a the tokens at
    the beginning of the token list?
  - After examining the source code, is the function call sequence
    appropriate or should changes be made?
  - Describe the bug that is in the source code and how to fix it.


(B)
~~~

  After identifying the bug in the parser, make a change to fix it. Quit
  the debugger (via `quit'), recompile, re-run the program on the
  problem input to show some progress has been made. Paste your results
  below and state how the results differ.


(C)
~~~

  Start the debugger again, set the arguments, and run to the uncaught
  exception. Backstep and then use `list' to show the source position.

  The trouble at this point should be apparent: something is wrong with
  the `varmap'.  Unfortunately, the debugger is not equipped to print
  standard Maps as evidenced by failed attempt to print:
  ,----
  | > ocamldebug a.out
  | 
  | (ocd) set arguments 'let x=5 in x+2'
  | (ocd) run
  | Uncaught exception: Buggy_lpe.EvalError "No variable 'x' bound"
  | 
  | (ocd) backstep
  | 
  | (ocd) print varmap
  | varmap: varval_t Varmap.t = <abstr>
  `----

  This output is hidden behind an abstraction barrier. One can install
  "printers" which will display data for maps and other more customized
  types but this is beyond the scope of the lab.

  Rather, turn your attention to the warning that has been issued on
  every compilation.  Examine the associated code and describe what is
  being done wrong.


(D)
~~~

  Fix the evaluation error, recompile, and show that the trouble code
  now evaluates correctly. Show your corrected lines of code in
  `buggy_lpe.ml' and the results of running on the input.


Optional Extras
===============

Another Bug
~~~~~~~~~~~

  There is another bug present in `buggy_lpe.ml'.  Attempt to diagnose
  and fix it.

  Hint: examine the results of the differences between the following two
  evaluations.
  ,----
  | > ./a.out '10-let x=2*3 in x'
  | ...
  | > ./a.out '10-2*3'
  | ...
  `----


if/then/else (from lab 11)
~~~~~~~~~~~~~~~~~~~~~~~~~~

  Currently the lexer/parser/evaluator does not handle numeric
  comparisons to produce boolean results such as
  ,----
  | 5 < 2 -> Bool false
  | if 1+2 > 0 then 8 else 4  -> Int 8
  `----
  This will be a required part of the final assignment interpreter so it
  would be an excellent exercise to extend the system to handle these
  new expression types.

  - Extend the lexer to include < and >. The = sign is already part of
    the lexer.
  - Extend the expression type to include comparison expressions for
    Less, Greater, Equal with constituent left/right expressions (like
    arithmetic).
  - Extend the parser functions with a new function to parse
    comparisons. This should occur at a lower precedence than
    arithmetic.
  - Extend the evaluator to include evaluation cases for
    comparisons. These should check that their left/right expressions
    are integers, do the appropriate comparison on the numbers, and
    return a Bool.  You may wish to model them after the arithmetic
    evaluation code.

4 What to Understand

Ensure that you understand

  • Basics of starting the debugger, setting command line arguments, and running programs.
  • The capability to run forwards with step and run or in reverse using backstep and reverse.
  • How to set breakpoints and print variable values during debugging sessions.

5 Getting Credit: Submit and Check-off

  • Check-off your answers with a TA in person during lab or office hours.
  • Submit your completed QUESTIONS.txt file to Canvas under the appropriate lab heading. Make sure to paste in any new code and answers you were to write at appropriate locations in the file.

Author: Chris Kauffman (kauffman@umn.edu)
Date: 2018-12-02 Sun 12:04