package ppx_minidebug

  1. Overview
  2. Docs

ppx_minidebug usage

ppx_minidebug traces selected code if it has type annotations. ppx_minidebug offers three ways of instrumenting the code: %debug_pp and %debug_show based on deriving.show, and %debug_sexp based on sexplib0 and ppx_sexp_conv. The syntax extension expects a module Debug_runtime in the scope. The ppx_minidebug.runtime library offers three ways of logging the traces, as functors generating Debug_runtime modules given an output channel (e.g. for a file).

Take a look at ppx_debug which is significantly more powerful!

See Minidebug_runtime for the provided loggers.

Usage

Try opam install ppx_minidebug to install from the opam repository. To install `ppx_minidebug` from sources, download it with e.g. gh repo clone lukstafi/ppx_minidebug; cd ppx_minidebug and then either dune install or opam install ..

To use ppx_minidebug in a Dune project, add/modify these stanzas: (preprocess (pps ... ppx_minidebug)), and (libraries ... ppx_minidebug.runtime).

To trace a function, you have to type-annotate the function result. To trace an argument of a traced function, or a let-binding, you need to type-annotate it. You can control how much gets logged by adding or removing type annotations.

Tracing only happens in explicitly marked scopes, using the extension points: %debug_pp, %debug_this_pp, %debug_show, %debug_this_show (based on printing functionality provided by deriving.show), %debug_sexp, %debug_this_sexp (using functionality provided by sexplib0 and ppx_sexp_conv). See examples in the test directory.

The %debug_this variants are intended only for let-bindings: let%debug_this v: t = compute value in body will trace v and the type-annotated bindings and functions inside compute value, but it will not trace body.

Example setting up a logger printing to the screen:

module Debug_runtime = (val Minidebug_runtime.debug_flushing ())
let%debug_show test_logging: string = "Hello World"

Example setting up a logger printing to a file:

module Debug_runtime =
  Minidebug_runtime.Flushing((val Minidebug_runtime.debug_ch "debugger_flushing.log"))
let%debug_show test_logging: string = "Hello World"

Example setting up a logger printing to an HTML file -- this is only possible with the PrintBox runtime. In this example, we also configure the converting the sexp-based logs to the PrintBox format (when they have at least 50 atoms). This means that both the log tree structure and the structure of large values can be incrementally explored using collapsible trees in an HTML renderer.

module Debug_runtime =
  Minidebug_runtime.PrintBox ((val Minidebug_runtime.debug_ch "debug.html"))
let () = Debug_runtime.to_html := true
let () = Debug_runtime.boxify_sexp_from_size := 50

debug_html is a configurable shorthand for the above setup:

module Debug_runtime = (val Minidebug_runtime.debug_html "debug.html")

Similarly, debug and debug_flushing are configurable shorthands that default to logging to stdout (but accept a `~debug_ch` argument).

Debugging infinite loops

Computation can be optionally interruped using the ~max_nesting_depth and ~max_num_children settings. The first raises a failure when the nesting of logs exceeds the given threshold, the second raises a failure when the number of log entries under a single parent exceeds the threshold. E.g.:

module Debug_runtime =
  (val Minidebug_runtime.debug_html ~max_nesting_depth:20 ~max_num_children:50 "debug.html")

The cutoff points are indicated in the logs.

VS Code suggestions

Add / remove type annotations and visit files using VOCaml

VOCaml helpers for coding in OCaml provide commands to add and remove annotations on selected bindings. They can be used to introduce logging, tune it, and cleanup afterward. It also has a command to populate the _Quick Open_ dialog with a file name and location from a line under cursor. It can be used to jump to the source code from a log file.

Note that you can add and remove type annotations using VSCode OCaml Platform's code actions, and the Find and Transform suggestion below is a more flexible go-to-file solution -- so VOCaml is somewhat redundant. But, it is still valuable: (1) it annotates multiple let-bindings at once in a selection, and (2) it annotates the argument types and the return type of a function (as required by ppx_debug) when invoked on a function definition.

Visualize the flame graph using Log Inspector

Log Inspector (sub-millisecond)'s main feature is visualizing timestamped logs as flame graphs. To invoke it in VS Code, go to the Minidebug_runtime.Flushing-style logs file, press crtl+shift+P, and execute the command Log Inspector: Draw.

The sub-millisecond functionality is now upstreamed to Log Inspector.

Go to file location using Find and Transform

This will expand your general-purpose VS Code toolbox!

Find and Transform is a powerful VS Code extension. I put the following in my `keybindings.json` file (command: Open Keyboard Shortcuts (JSON)):

{
  "key": "alt+q",
  "command": "findInCurrentFile",
  "args": {
    "description": "Open file at cursor",
    "find": "\"([^\"]+)\":([0-9]+)",
    "run": [
      "$${",
        "const pos = new vscode.Position($2, 0);",
        "const range = new vscode.Range(pos, pos);",
        "const options = {selection: range};",
        "const wsFolderUri = vscode.workspace.workspaceFolders[0].uri;",
        "const uri = await vscode.Uri.joinPath(wsFolderUri, '$1');",
        "await vscode.commands.executeCommand('vscode.open', uri, options);",
        
        // "await vscode.commands.executeCommand('workbench.action.quickOpen', `$1:$2`);",
      "}$$",
    ],
    "isRegex": true,
    "restrictFind": "line",
  }
}

Then, pressing `alt+q` will open a pre-populated dialog, and `enter` will get me to the file location. The file-and-location detection above matches the default one from the Flushing module, you can adjust the find pattern to match other formats.