Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Debug perf manual #18

Closed
wants to merge 29 commits into from
Closed
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
8d9dfa3
Initial sections for profiling with perf and native debugging
tmcgilchrist Oct 30, 2024
783ae38
fixup! Initial sections for profiling with perf and native debugging
tmcgilchrist Oct 31, 2024
d8eb403
fixup! Initial sections for profiling with perf and native debugging
tmcgilchrist Oct 31, 2024
50a5dea
fixup! Initial sections for profiling with perf and native debugging
tmcgilchrist Oct 31, 2024
7357c68
fixup! Initial sections for profiling with perf and native debugging
tmcgilchrist Nov 1, 2024
aa01651
Add changelog
tmcgilchrist Nov 4, 2024
a89b321
fixup! Initial sections for profiling with perf and native debugging
tmcgilchrist Nov 4, 2024
8be983a
fixup! Initial sections for profiling with perf and native debugging
tmcgilchrist Nov 5, 2024
5c74a15
fixup! Initial sections for profiling with perf and native debugging
tmcgilchrist Nov 6, 2024
834792b
fixup! Initial sections for profiling with perf and native debugging
tmcgilchrist Nov 6, 2024
897acdd
fixup! Initial sections for profiling with perf and native debugging
tmcgilchrist Nov 6, 2024
2655831
line editing & formatting README.md
christinerose Nov 8, 2024
c7a4623
two small formatting changes README.md
christinerose Nov 8, 2024
557aad1
minor edits README.md
christinerose Nov 8, 2024
fbc2c47
removed unnecessary word in README.md
christinerose Nov 8, 2024
aeafefe
edit profile-perf.etex for flow, consistency, & formatting
christinerose Nov 12, 2024
4cdccf0
Edited for flow, formatting, and grammar native-debugger.etex
christinerose Nov 12, 2024
00f7b3a
fixed missed capital native-debugger.etex
christinerose Nov 12, 2024
ed030fa
Reorganise sections
tmcgilchrist Nov 15, 2024
8f82b85
fixup! Reorganise sections
tmcgilchrist Nov 19, 2024
a1ec82b
Minor grammar profile-perf.etex
christinerose Nov 19, 2024
f093ae8
minor typos profile-perf.etex
christinerose Nov 19, 2024
22bffa8
typo / syntax profile-perf.etex
christinerose Nov 19, 2024
77b5503
more concise sentence profile-perf.etex
christinerose Nov 19, 2024
766eaa7
formatting perf profile-perf.etex
christinerose Nov 19, 2024
9affa8d
Minor edits for clarity.
tmcgilchrist Nov 25, 2024
cef3415
Add inferno flamegraph suggestion
tmcgilchrist Nov 26, 2024
7225e3b
More edits
tmcgilchrist Nov 26, 2024
260d6a8
verb agreement profile-perf.etex
christinerose Nov 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Changes
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@ Working version
review by Florian Angeletti, Anil Madhavapeddy, Gabriel Scherer,
and Miod Vallat)

- #?????: Document support for Linux perf and frame pointers.
tmcgilchrist marked this conversation as resolved.
Show resolved Hide resolved
(Tim McGilchrist, review by ???)

- #?????: Document support for native debugging with GDB and LLDB.
(Tim McGilchrist, review by ???)

### Compiler user-interface and warnings:

- #13428: support dump=[source | parsetree | lambda | ... | cmm | ...]
Expand Down
6 changes: 5 additions & 1 deletion manual/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,10 @@ chapters (or sometimes sections) are mapped to a distinct `.etex` file:
- Optimisation with Flambda: `flambda.etex`
- Fuzzing with afl-fuzz: `afl-fuzz.etex`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Fuzzing with afl-fuzz: `afl-fuzz.etex`
- Fuzzing with `afl-fuzz`: `afl-fuzz.etex`

- Runtime tracing with Runtime_events: `runtime_tracing.etex`
- The “Tail Modulo Constructor” program transformation: `tail-mod-cons.etex`
- Runtime detection of data races with ThreadSanitizer: `tsan.etex`

Note that ocamlc,ocamlopt and the toplevel options overlap a lot.
Note that ocamlc, ocamlopt and the toplevel options overlap a lot.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Note that ocamlc, ocamlopt and the toplevel options overlap a lot.
Note that `ocamlc`, `ocamlopt`, and the toplevel options overlap a lot.

Consequently, these options are described together in the file
`unified-options.etex` and then included from `comp.etex`, `native.etex`,
and `top.etex`. If you need to update this list of options, the top comment
Expand All @@ -104,6 +106,8 @@ of `unified-options.etex` contains the relevant information.
- The runtime_events library: `libruntime_events.etex`
- The dynlink library: dynamic loading and linking of object files:
`libdynlink.etex`
- Recently removed or moved libraries (Graphics, Bigarray, Num, LablTk):
`old.etex`

Latex extensions
----------------
Expand Down
2 changes: 2 additions & 0 deletions manual/src/allfiles.etex
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@ and as a
\input{ocamldep.tex}
\input{ocamldoc.tex}
\input{debugger.tex}
\input{native-debugger.tex}
\input{profil.tex}
\input{profile-perf.tex}
\input{intf-c.tex}
\input{flambda.tex}
\input{afl-fuzz.tex}
Expand Down
4 changes: 2 additions & 2 deletions manual/src/cmds/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ TEXQUOTE = $(OCAMLRUN) $(TOOLS)/texquote2
TRANSF = $(OCAMLRUN) $(TOOLS)/transf

FILES = comp.tex top.tex runtime.tex native.tex lexyacc.tex intf-c.tex \
ocamldep.tex profil.tex debugger.tex ocamldoc.tex \
warnings-help.tex flambda.tex tail-mod-cons.tex \
ocamldep.tex profil.tex profile-perf.tex debugger.tex native-debugger.tex \
ocamldoc.tex warnings-help.tex flambda.tex tail-mod-cons.tex \
afl-fuzz.tex runtime-tracing.tex unified-options.tex tsan.tex

etex-files: $(FILES)
Expand Down
288 changes: 288 additions & 0 deletions manual/src/cmds/native-debugger.etex
Original file line number Diff line number Diff line change
@@ -0,0 +1,288 @@
\chapter{Native debugger} \label{c:native-debugger}
%HEVEA\cutname{native-debugger.html}

\section{s:native-debugger-preliminaries}{Preliminaries}

This chapter describes the support for debugging executables built with the native-code compiler \texttt{ocamlopt} using GDB or LLDB on Linux, macOS and BSD. We will call this \texttt{native debugging}, compared to bytecode debugging supported via \texttt{ocamldebug} (see chapter~\ref{c:debugger}).

\subsection{ss:native-debugger-dwarf}{DWARF}

The OCaml compiler uses the \href{http://dwarfstd.org/}{DWARF} debugging information file format to describe the debug information it generates. DWARF is a debugging information file format used by many compilers and debuggers to support source level debugging, and it is used by Linux ELF, macOS Mach-O and FreBSD ELF.
tmcgilchrist marked this conversation as resolved.
Show resolved Hide resolved

Within the DWARF standard the compiler specifically uses Call Frame Information (hereafter abbreviated as CFI) to describe a call stack for OCaml code, sections of the runtime written in C. e.g. Garbage Collector and across the Foreign Function Interface (FFI) if the language provides CFI information. (If the language has been compiled to include CFI information).
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sentence reads incomplete to me. Are there words missing, "and sections of the runtime"?
Nit: The usual sequence surrounding "e.g" is , e.g., https://www.merriam-webster.com/dictionary/e.g. (note: two commas and two periods)

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious I've always known "e.g." to be used like sections of the runtime written in C, e.g. Garbage Collector without the trailing comma. That usage seems to be common to the existing manual pages, but I'm no National Grammar Rodeo Champion. ;-)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A quick grep "e\.g" *.etex in manual/src/cmds reveals a different convention (mostly in parens, if not then with an initial comma but omitting the second comma) from the Merriam Webster one. Feel free to ignore this part then.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do use the Oxford Comma in our style guide and also the comma after e.g. or i.e.
I'm totally obsessed with this stuff, so I'm sure to edit things that come across my desk to adhere to our style guide, but I certainly don't expect anyone else to remember all those details! I'll take care of those things. 🙂

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @christinerose ! While we do want to use the style guide when writing our own documentation, blog posts and comms, in this case we are aiming at changes to an existing body of text. It's important that we retain uniformity, rather than impose our style guide. Let's ensure we do so!


OCaml defines its own calling convention (which is architecture specific and differs from the C calling convention) for how arguments are passed to functions, how values are returned and how registers are used. See [proc.ml] for specific details for your architecture.

The compiler also generates line information that maps instructions back to their location in the source program (e.g. the instruction at address x originated from myprogram.ml line 42). This allows native debuggers to display the OCaml source code for the program being debugged and enables stepping through OCaml source code.

Debuggers often need to view or modify the state of any function that is on the call stack, CFI allows the native debugger to find and inspect this state. CFI information is preserved across the OCaml / C boundary so the OCaml runtime can be debugged.

\subsection{ss:native-debugger-name-mangling}{Name Mangling}

Name mangling is the process for describing how the OCaml compiler generates symbol names for OCaml language constructs. The format of these symbols is important for debuggers, performance and observability tools, to uniquely identify the source function for a symbol and to do so without resource to the original source code. In the absensce of source mappings, you often need to use mangled names to set breakpoints or they will appear in information the native debugger will display. As such knowing how OCaml performs name mangling is important when debugging OCaml programs. OCaml 5.1.1 uses the a name mangling scheme of \texttt{caml<MODULE_NAME>.<FUNCTION_NAME>_<NNN>} where \texttt{NNN} is a randomly generated number. Before 5.1.1 the scheme is uses two underscores as the separator e.g. \texttt{caml<MODULE_NAME>__<FUNCTION_NAME>_<NNN>}.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Name mangling is the process for describing how the OCaml compiler generates symbol names for OCaml language constructs. The format of these symbols is important for debuggers, performance and observability tools, to uniquely identify the source function for a symbol and to do so without resource to the original source code. In the absensce of source mappings, you often need to use mangled names to set breakpoints or they will appear in information the native debugger will display. As such knowing how OCaml performs name mangling is important when debugging OCaml programs. OCaml 5.1.1 uses the a name mangling scheme of \texttt{caml<MODULE_NAME>.<FUNCTION_NAME>_<NNN>} where \texttt{NNN} is a randomly generated number. Before 5.1.1 the scheme is uses two underscores as the separator e.g. \texttt{caml<MODULE_NAME>__<FUNCTION_NAME>_<NNN>}.
Name mangling is the process for describing how the OCaml compiler generates symbol names for OCaml language constructs. The format of these symbols is important for debuggers, performance and observability tools, to uniquely identify the source function for a symbol and to do so without reference to the original source code. In the absence of source mappings, you often need to use mangled names to set breakpoints or they will appear in the information the native debugger will display. As such knowing how OCaml performs name mangling is important when debugging OCaml programs. OCaml 5.1.1 uses a name mangling scheme of \texttt{caml<MODULE_NAME>.<FUNCTION_NAME>_<NNN>} where \texttt{NNN} is a randomly generated number. Before 5.1.1 the scheme used two underscores as the separator, e.g., \texttt{caml<MODULE_NAME>__<FUNCTION_NAME>_<NNN>}.

There's the MSVC exception. I don't know if it is worth a footnote/mention?

According to ocaml#11430 packed modules uses the <SUB_MODULE_NAME> convention, which may or may not be worth a footnote/mention?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth including a mention of MSVC 👍🏻


\subsection{ss:native-debugger-frame-pointers}{Frame Pointers}

The OCaml native compiler also supports maintaining Frame Pointers, which can be used by a debugger to walk the stack of function calls in a program. The Frame Pointer (also known as the base pointer) is a register (e.g. %rbp on x86_64 or x29 on ARM64) that points to the base of the current stack frame. The stack frame (also known as the activation frame or the activation record) refers to the portion of the stack allocated to a single function call. By saving the frame pointer along with the return address, between stack frames the call stack for OCaml can be maintained. It should be possible to use just frame pointers to debug OCaml programs, similar to debugging plain assembly code.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The OCaml native compiler also supports maintaining Frame Pointers, which can be used by a debugger to walk the stack of function calls in a program. The Frame Pointer (also known as the base pointer) is a register (e.g. %rbp on x86_64 or x29 on ARM64) that points to the base of the current stack frame. The stack frame (also known as the activation frame or the activation record) refers to the portion of the stack allocated to a single function call. By saving the frame pointer along with the return address, between stack frames the call stack for OCaml can be maintained. It should be possible to use just frame pointers to debug OCaml programs, similar to debugging plain assembly code.
The OCaml native compiler also supports maintaining Frame Pointers, which can be used by a debugger to walk the stack of function calls in a program. The Frame Pointer (also known as the base pointer) is a register (e.g. %rbp on x86_64 or x29 on ARM64) that points to the base of the current stack frame. The stack frame (also known as the activation frame or the activation record) refers to the portion of the stack allocated to a single function call. By saving the frame pointer along with the return address between stack frames, the call stack for OCaml can be maintained. It should be possible to use just frame pointers to debug OCaml programs, similar to debugging plain assembly code.

Also, I'm not following the last sentence. Can you elaborate?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intent is you could strip an OCaml binary and still be able to debug it if you compile with frame pointers. You would only get stack frames with a call stack plus assembly code stepping and mangled names for setting breakpoints.

Having a stripped binary might be useful if you want the smallest executable possible for say MirageOS or performance. I haven't thoroughly tested this scenario but I wanted to include the option for completeness.


\section{s:native-debugger-compilation}{Compiling for debugging}

Before debugging OCaml programs, first the native compiler \texttt{ocamlopt} must be installed with CFI emission enabled. CFI emission is controlled by the \texttt{--enable-cfi} flag and is enabled by default. This is sufficient to allow debugging the assembly code generated by \texttt{ocamlopt}. To perform source level debugging code need to be compiled with \texttt{-g} flag that records debugging information for exception backtraces, and generates mappings between assembly and source locations in OCaml. (Note only GDB and LLDB on Linux reliably support source locations).
tmcgilchrist marked this conversation as resolved.
Show resolved Hide resolved

\section{s:native-debugger-gdb}{Using GDB}
Here we will walk through debugging a simple OCaml program using GDB on Linux, showing the commands to use and the expected outputs.

Consider the following program:
\begin{caml_example*}{verbatim}
(* fib.ml *)
let rec fib n =
if n = 0 then 0
else if n = 1 then 1
else fib (n-1) + fib (n-2)

let main () =
let r = fib 20 in
Printf.printf "fib(20) = %d" r

let _ = main ()
\end{caml_example*}

Compile this program with ocamlopt like so:

\begin{verbatim}
$ ocamlopt --version
5.2.0
tmcgilchrist marked this conversation as resolved.
Show resolved Hide resolved
$ ocamlopt -g -o fib.exe fib.ml
$ ./fib.exe 20
fib(20) = 6765
\end{verbatim}

Then when run this program prints the 20th Fibonnaci number, using recursion allows an opportunity to inspect the call stack. Startup a GDB session for this program:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Then when run this program prints the 20th Fibonnaci number, using recursion allows an opportunity to inspect the call stack. Startup a GDB session for this program:
When run this program prints the 20th Fibonnaci number. The use of recursion is a goof opportunity to inspect the call stack. To so so, startup a GDB session for this program:

The first sentence seems to serve two purposes, collapsed into one. Suggestion: split it.


\begin{verbatim}
$ gdb ./fib.exe
\end{verbatim}

Break points can be set either using the mangled names produced by the compiler or using a combination of file name and line number. For example:

\begin{verbatim}
(gdb) break camlFib.fib_ # press tab
(gdb) break camlFib.fib_270 # 270 happens to be the random number generated this time
Breakpoint 1 at 0x3cd50: file fib.ml, line 2.

(gdb) break fib.ml:7 # breakpoint for main function
Breakpoint 2 at 0x3cdc0: file fib.ml, line 7.
\end{verbatim}

Now we can run the program.

\begin{verbatim}
(gdb) run
Starting program: /home/tsmc/fib.exe
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 2, camlFib.main_272 () at fib.ml:7
7 let main () =
(gdb) continue
Continuing.

Breakpoint 1, camlFib.fib_270 () at fib.ml:2
2 let rec fib n =
(gdb) backtrace
#0 camlFib.fib_270 () at fib.ml:2
#1 0x0000555555590de1 in camlFib.main_272 () at fib.ml:8
#2 0x0000555555590e86 in camlFib.entry () at fib.ml:11
#3 0x000055555558eaa7 in caml_program ()
#4 <signal handler called>
#5 0x00005555555de126 in caml_startup_common (pooling=<optimised out>, argv=0x7fffffffe3f8)
at runtime/startup_nat.c:132
#6 caml_startup_common (argv=0x7fffffffe3f8, pooling=<optimised out>) at runtime/startup_nat.c:88
#7 0x00005555555de19f in caml_startup_exn (argv=<optimised out>) at runtime/startup_nat.c:139
#8 caml_startup (argv=<optimised out>) at runtime/startup_nat.c:144
#9 caml_main (argv=<optimised out>) at runtime/startup_nat.c:151
#10 0x000055555558e892 in main (argc=<optimised out>, argv=<optimised out>) at runtime/main.c:37
\end{verbatim}

There is basic support for printing OCaml values using \href{https://github.com/ocaml/ocaml/blob/5.3.0/tools/gdb.py}{tools/gdb.py} and the built in Python scripting in GDB. Download that file and load it into GDB like so:

\begin{verbatim}
(gdb) source ~/ocaml/tools/gdb.py
OCaml support module loaded. Values of type 'value' will now
print as OCaml values, there is a $Array() convenience function,
and an 'ocaml' command is available for heap exploration
(see 'help ocaml' for more information).

(gdb) p (value)$rax
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this needs a bit of explanation:

  • using register $rax
  • value casting
  • p for print
  • resulting caml:14 - shouldn't this be 20? 🤔

$1 = caml:14

\end{verbatim}

We can also print other kinds of OCaml values.
In order to illustrate this, consider the following program:
\begin{caml_example*}{verbatim}
(* test_blocks.ml *)
type t = {s : string; i : int}

let main a b =
print_endline "Hello, world!";
print_endline a;
print_endline b.s

let _ = main "foo" {s = "bar"; i = 42}
\end{caml_example*}

Compile this program with ocamlopt like so and load it into GDB:

\begin{verbatim}
$ ocamlopt -g -o test_blocks.exe test_blocks.ml
$ gdb ./test_blocks.exe
(gdb) source ~/ocaml/tools/gdb.py
...
(gdb) break camlTest_blocks.main_273
Breakpoint 1 at 0x16db0: file test_blocks.ml, line 4.
(gdb) run
...
Breakpoint 1, camlTest_blocks.main_273 () at test_blocks.ml:4
4 let main a b =
(gdb) p (value)$rax # Print out the first argument to main
$1 = caml(-):'foo'<3>
(gdb) p (value)$rbx # Then print the second argument
$2 = caml(-):('bar', 42) = {caml(-):'bar'<3>, caml:42}
(gdb) p *(value*)$rbx@2 # Examine the second field
$3 = {caml(-):'bar'<3>, caml:42}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps worth mentioning what this last examination provides (and how it works)?
Without being a gdb expert, I expected the value cast and @2 to print the value at offset 2, i.e., the second field as the comment suggests. The result looks more like a full record though? 🤔

\end{verbatim}

Note the use of x86_64 register names. We can print values as their OCaml representations (note The (m) or (u) (or (g) or (-)) is the GC color).
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Note the use of x86_64 register names. We can print values as their OCaml representations (note The (m) or (u) (or (g) or (-)) is the GC color).
Note the use of x86_64 register names: \texttt{$rax} and \texttt{$rbx}. We can print values as their OCaml representations (note The (m) or (u) (or (g) or (-)) is the GC color).

The last sentence probably earns a bit of elaboration.


\subsection{ss:native-debugger-gdb-commands}{GDB Commands}
Summary of interesting OCaml specific GDB commands:
\begin{options}
\item["break "\var{locspec}]
Set a breakpoint at all of the code locations matching \var{locspec}. e.g. Using the mangled OCaml names or specifying the linenum in the source file as \texttt{filename:linenum}.
tmcgilchrist marked this conversation as resolved.
Show resolved Hide resolved

\item["backtrace"]
Print the backtrace of the entire stack, this will include OCaml source references identifying which stack frame maps to a source location. e.g. fib.ml:4
tmcgilchrist marked this conversation as resolved.
Show resolved Hide resolved

\item["disassemble "\var{addresses}]
Display a range of \var{addresses} as machine instructions. Typically used with the mangled OCaml names to display the assembly for a function.

\item["info "\var{frame}]
This command prints a verbose description of the selected stack \var{frame}.

\item["list "\var{linenum}]
Print lines centered around line number \var{linenum} in the current source file. This will print the source code for OCaml and the OCaml runtime written in C.

\end{options}

See the \href{https://sourceware.org/gdb/current/onlinedocs/gdb.html/}{Debugging with GDB} documentation for more details. In general only the features described above are expected to work in GDB and otherwise users will need to fall back to assembly debugging. GDB is expected to work on all supported Linux architectures.

\section{s:native-debugger-lldb}{Using LLDB}

Here we will walk through debugging the earlier fib example using LLDB on Linux. Startup an LLDB session using the `fib.exe` from earlier:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Here we will walk through debugging the earlier fib example using LLDB on Linux. Startup an LLDB session using the `fib.exe` from earlier:
Here we will walk through debugging the earlier \texttt{fib} example using LLDB on Linux. Startup an LLDB session using the \texttt{fib.exe} from earlier:

Should both these be in monospace, or are you referring to Fibonacci rather than the fib command?


\begin{verbatim}
$ lldb ./fib.exe
Current executable set to 'fib.exe' (aarch64).
(lldb)
\end{verbatim}

Breakpoints can be set using the OCaml mangled names or using a combination of file name and line number. For example:

\begin{verbatim}
(lldb) breakpoint set -n camlFib.main # press tab for autocomplete
(lldb) breakpoint set -n camlFib.main_272
Breakpoint 3: where = fib.exe`camlFib.main_272 + 40, address = 0x00000000000510b0
(lldb) breakpoint set -f fib.ml -l 7 # breakpoint for line 7 in fib.ml
Breakpoint 2: where = fib.exe`camlFib.main_272, address = 0x0000000000051088
(lldb)
\end{verbatim}

Now we can run the program.
\begin{verbatim}
(lldb) run
Process 11391 launched: '/home/tsmc/fib.exe' (aarch64)
Process 11391 stopped
* thread #1, name = 'fib.exe', stop reason = breakpoint 2.1
frame #0: 0x0000aaaaaaaf1088 fib.exe`camlFib.main_272 at fib.ml:7
4 else if n = 1 then 1
5 else fib (n-1) + fib (n-2)
6
-> 7 let main () =
8 let r = fib 20 in
9 Printf.printf "fib(20) = %d" r
10
warning: This version of LLDB has no plugin for the language "assembler". Inspection of frame variables will be limited.
(lldb) bt # Print the backtrace
* thread #1, name = 'fib.exe', stop reason = breakpoint 2.1
* frame #0: 0x0000aaaaaaaf1088 fib.exe`camlFib.main_272 at fib.ml:7
frame #1: 0x0000aaaaaaaf117c fib.exe`camlFib.entry at fib.ml:11
frame #2: 0x0000aaaaaaaee644 fib.exe`caml_program + 476
frame #3: 0x0000aaaaaab45b08 fib.exe`caml_start_program + 132
frame #4: 0x0000aaaaaab45600 fib.exe`caml_main [inlined] caml_startup(argv=<unavailable>) at startup_nat.c:145:7
frame #5: 0x0000aaaaaab455fc fib.exe`caml_main(argv=<unavailable>) at startup_nat.c:151:3
frame #6: 0x0000aaaaaaaee2d0 fib.exe`main(argc=<unavailable>, argv=<unavailable>) at main.c:37:3
frame #7: 0x0000fffff7d784c4 libc.so.6`__libc_start_call_main(main=(fib.exe`main at main.c:31:1), argc=1, argv=0x0000fffffffffb78) at libc_start_call_main.h:58:16
frame #8: 0x0000fffff7d78598 libc.so.6`__libc_start_main_impl(main=0x0000aaaaaaba0dc8, argc=16, argv=0x000000000000000f, init=<unavailable>, fini=<unavailable>, rtld_fini=<unavailable>, stack_end=<unavailable>) at libc-start.c:360:3
frame #9: 0x0000aaaaaaaee370 fib.exe`_start + 48
(lldb)
\end{verbatim}

There is basic support for printing OCaml values using \href{https://github.com/ocaml/ocaml/blob/5.3.0/tools/lldb.py}{tools/lldb.py} and the built in Python scripting in LLDB. Download that file and load it into LLDB like so:

\begin{verbatim}
(lldb) command script import ~/ocaml/tools/lldb.py
OCaml support module loaded. Values of type 'value' will now
print as OCaml values, and an 'ocaml' command is available for
heap exploration (see 'help ocaml' for more information).
(lldb) p (value)$x0
(value) 41 caml:20
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to print both the tagged representation - and the OCaml int value 20.
Why this difference across lldb and gdb? 🤔

(lldb)
\end{verbatim}

Note we are using an ARM64 Linux machine so our first argument is in the first register x0
tmcgilchrist marked this conversation as resolved.
Show resolved Hide resolved

We can also print out all kinds of OCaml values. Reusing the `test_blocks.exe` startup a new LLDB session:

\begin{verbatim}
$ lldb ./test_blocks.exe
...
(lldb) command script import ~/ocaml/tools/lldb.py
...
(lldb) br s -n camlTest_blocks.main_273
Breakpoint 1: where = test_blocks.exe`camlTest_blocks.main_273 + 40, address = 0x0000000000019ab0
(lldb) run
...
(lldb) p (value)$x0
(value) 187649984891864 caml(-):'Hello, world!'<13>
(lldb) p (value)$x1
(value) 187649984891808 caml(-):('bar', 42)
\end{verbatim}

\subsection{ss:native-debugger-lldb-commands}{LLDB Commands}

Summary of interesting OCaml specific LLDB commands:

\begin{options}
\item["breakpoint set -n "\var{symbol}]
Set a breakpoint at code location matching \var{symbol}. e.g. Using the mangled OCaml name.
tmcgilchrist marked this conversation as resolved.
Show resolved Hide resolved

\item["breakpoint set -f "\var{filename}" -l"\var{linenum}]
Set a breakpoint at \var{linenum} in \var{filename}. e.g fib.ml:7
tmcgilchrist marked this conversation as resolved.
Show resolved Hide resolved

\item["breakpoint set -a "\var{address}]
Set a breakpoint on a memory \var{address}.

\item["backtrace"]
Print the backtrace of the entire stack, will include OCaml source references identifying which stack frame maps to a source location. e.g. fib.ml:4
tmcgilchrist marked this conversation as resolved.
Show resolved Hide resolved

\item["disassemble"]
Disassemble specified instructions in the current target. Useful options include \texttt{-n} plus mangled OCaml name to disassemble a specific function and \texttt{-a} plus an address to disassemble function containing this address.

\item["frame info"]
List information about the current stack frame in the current thread.

\item["source"]
Commands for examining source code described by debug information for the current target process.

\end{options}
6 changes: 2 additions & 4 deletions manual/src/cmds/profil.etex
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,5 @@ Display a short usage summary and exit.

Profiling with "ocamlprof" only records execution counts, not the actual
time spent within each function. There is currently no way to perform
time profiling on bytecode programs generated by "ocamlc". For time
profiling of native code, users are recommended to use standard tools
such as perf (on Linux), Instruments (on macOS) and DTrace. Profiling
with "gprof" is no longer supported.
time profiling on bytecode programs generated by "ocamlc". For time profiling
of native code (see chapter~\ref{c:profiler-perf}).
Loading
Loading