This is supposed to be an elementary little tutorial for the Caml debugger...
Consider the following obviously wrong program written in the file
(* file uncaught.ml *) let l = ref ;; let find_address name = List.assoc name !l;; let add_address name adress = l := (name, address) :: ! l;; add_address "IRIA" "Rocquencourt";; print_string (find_address "INRIA"); print_newline ();;
At runtime, the program raises an uncaught exception
Suppose we want to find where and why this exception has been raised, we can proceed as follows:
ocamlc -g uncaught.ml
Then the debugger answers with a banner and a prompt:
Objective Caml Debugger version 3.1 (ocd)
r (for run); you get
(ocd) r Loading program... done. Time : 12 Program end. Uncaught exception: Not_found (ocd)
Self explanatory, isn't it ?
So, you want to step backward to set the program counter before the
time the exception is raised; hence type in
backstep, and you get
(ocd) b Time : 11 - pc : 15500 - module List 143  -> <|b|>raise Not_found
The debugger tells you that you are in module
inside a pattern matching on a list that already chose the
 case and is about to execute
Not_found, since the program is stopped just before this
expression (as witnessed by the
But, as you know, you want the debugger to tell you which procedure calls the one from list, and also who calls the procedure that calls the one from list; hence, you want a back trace of the execution stack:
(ocd) bt #0 Pc : 15500 List char 3562 #1 Pc : 19128 Uncaught char 221
So the last function called is from module List at character 3562, that is :
let rec assoc x = function  -> raise Not_found ^ | (a,b)::l -> if a = x then b else assoc x lThe function that calls it is in module
print_string (find_address "INRIA"); print_newline ();; ^
In conclusion: if you're developing a program you can compile it
-g option, to be ready to debug the program if
necessary. Hence, to find a spurious exception you just need to type
bt gives you the back trace.
To get more info about the current status of the debugger you can ask it directly at the toplevel prompt of the debugger; for instance:
(ocd) info breakpoints No breakpoint. (ocd) help break 1 15396 in List, character 3539 break : Set breakpoint at specified line or function. Syntax: break function-name break @ [module] linenum break @ [module] # characternum
Let's set up a breakpoint and rerun the entire program from the
(ocd) break @Uncaught 9 Breakpoint 3 at 19112 : file Uncaught, line 9 column 34 (ocd) g 0 Time : 0 Beginning of program. (ocd) r Time : 6 - pc : 19112 - module Uncaught Breakpoint : 1 9 add "IRIA" "Rocquencourt"<|a|>;;
Then, we can step and find what happens when find_address is about to be called
(ocd) s Time : 7 - pc : 19012 - module Uncaught 5 let find_address name = <|b|>List.assoc name !l;; (ocd) p name name : string = "INRIA" (ocd) p !l $1 : (string * string) list = ["IRIA", "Rocquencourt"] (ocd)
Now we can guess why
List.assoc will fail to find "INRIA"
in the list...
Note also that under Emacs you call the debugger using
emacs will set you directly to the file and character reported by the
debugger, and you can step back and forth using
ESC-s, you can set up break points using
CTRL-X space, and so on...
Contact the author Pierre.Weis@inria.fr