This is supposed to be an elementary little tutorial for the Caml debugger...
Consider the following obviously wrong program written in the file
uncaught.ml
:
(* 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
Not_found
.
Suppose we want to find where and why this exception has been raised, we can proceed as follows:
ocamlc -g uncaught.ml
ocamldebug a.out
Then the debugger answers with a banner and a prompt:
Objective Caml Debugger version 3.1 (ocd)
Type 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 b
as
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 List
,
inside a pattern matching on a list that already chose the
[]
case and is about to execute raise
Not_found
, since the program is stopped just before this
expression (as witnessed by the <|b|>
mark).
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
Uncaught
, file
uncaught.ml
char 221:
print_string (find_address "INRIA"); print_newline ();; ^
In conclusion: if you're developing a program you can compile it
with the -g
option, to be ready to debug the program if
necessary. Hence, to find a spurious exception you just need to type
ocamldebug
a.out
, then r
,
b
, and 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
beginning ((g)oto 0
then (r)un
):
(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
ESC-x
camldebug
a.out
. Then
emacs will set you directly to the file and character reported by the
debugger, and you can step back and forth using ESC-b
and
ESC-s
, you can set up break points using
CTRL-X
space, and so on...
Contact the author Pierre.Weis@inria.fr