The debugging facility in Caml

Contact the author

Created November 2000 the first.

Launching the debugger

This is supposed to be an elementary little tutorial for the Caml debugger...

Consider the following obviously wrong program written in the file

(* file *)

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:

  1. we compile the program in debug mode:
    ocamlc -g
  2. we launch the debugger:
    ocamldebug a.out

Then the debugger answers with a banner and a prompt:

        Objective Caml Debugger version 3.1


Finding the cause of a spurious exception

Type r (for run); you get

(ocd) r
Loading program... done.
Time : 12
Program end.
Uncaught exception: Not_found

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 l
The function that calls it is in module Uncaught, file 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.

Help and info in the debugger

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

Setting break points

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"]

Now we can guess why List.assoc will fail to find "INRIA" in the list...

Debugging under Emacs

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...

Caml home page Last modified: Friday, March 26, 2004
Copyright © 1995 - 2004, INRIA all rights reserved.

Contact the author