What is libmonda?
libmonda is a library that provides improved support for the OCaml language in platform debuggers. The majority of the library is designed to be independent of the particular debugger in use. Currently the only supported debugger is gdb but it is expected that lldb support will be implemented in due course.
Using gdb to debug OCaml programs is especially useful for the debugging of multi-threaded or low-level systems programs.
libmonda has been tested mostly on x86-64 Linux. OS X support should be available in the not too distant future. There is not currently support on the gdb branch of the compiler for non-x86 CPUs, although this will be added during the process of upstream submission.
To use libmonda to debug OCaml programs it is necessary to have installed:
- the OPAM switch 4.05.0+monda (this currently requires adding github.com/mshinwell/opam-repo-dev as an OPAM remote);
- the libmonda-gdb package (likewise).
Installation of libmonda-gdb will also install a bespoke version of gdb with modifications for debugging OCaml code. This version of gdb is called using the gdb-ocaml command. It is not possible to use libmonda with a standard version of gdb. (The modifications to gdb will be submitted upstream in due course.)
The modifications to the OCaml compiler will be submitted upstream. This should be done in time for the 4.05 release of the compiler distribution.
Try it with Docker
Thanks to KC there is a Docker file that can be used to get a libmonda environment up and running: https://gist.github.com/kayceesrk/94cdd19931b324a247bbd1a58ad47b47
Using gdb to debug OCaml code
Build your code with the -g option to the compiler. Note that the 4.05.0+monda switch has Flambda enabled.
gdb is invoked in the same way as normal except that gdb-ocaml is written instead of gdb. A convenient method is using the --args option, like so: gdb-ocaml --args ./myprogram --hello world
At present it is advisable to have all .cmi, .cmt and .ml files available at the same path on the filesystem as where they were compiled. Support will be added for searching in different places in the near future.
(Aside: if attempting to get this to work on OS X, it is necessary to run dsymutil on the executable in question before debugging it. ocamlopt will be extended to perform this step automatically once it has been established why dsymutil takes a very long time to process the compiler's DWARF output for some programs, such as ocamlopt.opt.)
Standard gdb commands
The following gdb commands (and many others) should operate as normal:
- run - start program's execution
- continue - continue program's execution after a breakpoint, etc.
- step - single-step
- next - single-step, but step over function calls
- finish - run until the current function returns (this won't correctly print the return value)
- break / enable / disable / info break - manage breakpoints
- info locals - show local variables (including free variables captured by closures)
- info args - show arguments of the current function
- backtrace - display current stack backtrace
- up / down - select stack frame for examination
- info threads - show active threads
- thread - switch between threads
- print - examine values
- disassemble - print disassembly (calls to OCaml functions will have proper names)
Tab completion may not always behave as expected. If you have trouble completing a module path (e.g. "break Foo.") then try e.g. "break Foo" instead.
In "info locals" variables are disambiguated by their locations in the source text if their names are not unique. In the presence of inlining this may still not be sufficient; they will in due course have their names augmented by the inlined-out frames.
In the case of deficiencies in libmonda (or just wanting to debug at a lower level) it is possible to "set language c" even in OCaml code. This will enable raw manipulation of pointers to values, etc.
Syntax of "print" commands
- starts with a module path (e.g. "Foo.Bar") which identifies that the remainder of the path is a request for some toplevel value within the given module; or
- starts with the name of a local variable or function argument currently in scope (e.g. "myvar"). (This includes free variables of functions captured by closures.)
Paths may be continued by either:
- giving the name of a record field (e.g. ".myfield"); or
- giving a numeric index (e.g. ".(4)").
Numeric indexes may be applied to:
- arrays (to retrieve the nth element);
- lists (to retrieve the nth element);
- boxed constructed values (to retrieve the nth argument of the constructor---this works for "traditional" tupled constructors as well as those using inlined records);
- "ref" cells (to retrieve the contents; only the index 0 is valid).
Some examples of paths might be:
- "foo": find the local variable or argument named "foo" in the current function
- "Foo.Bar.myvar.(0).blah": find the toplevel value "myvar" in the module "Foo.Bar", extract the first component and then extract the record field named
- "arg.bar.baz": find the local variable or argument named "arg" and then perform record field accesses "bar" then "baz".
Error reporting in the debugger is currently rather primitive. A message such as "$2 = 0" indicates that an error occurred when locating a value or traversing the given path. Check that the name is correct and the path valid. It can be helpful to use
info args or
info locals for this.
OCaml-only gdb commands
- set print ocaml-max-depth Set the maximum depth which the OCaml value printer will traverse into a structured value.
- set print ocaml-max-string-length Set the maximum length which the OCaml value printer will print from a string before truncating it.
There is somewhat embryonic support for running gdb-ocaml from within Spacemacs and Emacs in the normal gdb mode. (Type ":gdb" in Spacemacs.)
There is a lot of work to do on libmonda and gdb integration; offers of assistance are most welcome. (This part of the world tends to be quite hard work.)