memtrace-mirage

Streaming client for Memprof using MirageOS API
README

A streaming client for OCaml's Memprof, which generates compact traces
of a program's memory use. The trace is transferred via TCP.

To profile the memory use of a unikernel, there are two options. Either the
unikernel establishes a TCP connection to a listener which dumps the trace into
a file, or the unikernel offers a service where upon connection of a client,
the trace is dumped to.

As preparation in both cases, you need to install this package by executing
opam install memtrace-mirage and add ~packages:[ package "memtrace-mirage" ]
to your config.ml where you call Mirage.foreign.

From the start: establish a client connection from the unikernel

To trace the entire lifetime of the unikernel, create a client connection at
startup where the trace is dumped to disk:

Run nc -l 1234 > my-unikernel.trace to dump the trace into a file before
starting the unikernel.

In the unikernel.ml, add the following code to the start function:

module Memtrace = Memtrace.Make(Pclock)(S.TCP)

let start () s =
  (S.TCP.create_connection (S.tcp s) (Ipaddr.of_string_exn "10.0.0.1", 1234) >|= function
   | Ok flow -> ignore (Memtrace.start_tracing ~context:None ~sampling_rate:1e-4 flow)
   | Error e -> Logs.warn (fun m -> m "couldn't connect to tracing sink %a"
                             S.TCP.pp_error e)) >>= fun () ->
  ... rest of start ...

The unikernel provides a service for a single client

As soon as a client connects, tracing is started. The tracing is only stopped
when the client disconnects. Only a single client is permitted.

First start the unikernel, then execute nc 10.0.0.2 1234 > my-unikernel.trace.
Quit the nc process once you like to stop tracing.

In the unikernel.ml, add the following code:

module Memtrace = Memtrace.Make(Pclock)(S.TCP)

let start () s =
  S.TCP.listen (S.tcp s) ~port:1234
    (fun f ->
       (* only allow a single tracing client *)
       match Memtrace.Memprof_tracer.active_tracer () with
       | Some _ ->
         Logs.warn (fun m -> m "tracing already active");
         S.TCP.close f
       | None ->
         Logs.info (fun m -> m "starting tracing");
         let tracer = Memtrace.start_tracing ~context:None ~sampling_rate:1e-4 f in
         Lwt.async (fun () ->
           S.TCP.read f >|= fun _ ->
           Logs.warn (fun m -> m "tracing read returned, closing");
           Memtrace.stop_tracing tracer);
         Lwt.return_unit);
  ... rest of start ...

Trace file analysis

The resulting trace files can be analysed with some simple command-line tools
from the memtrace opam package, but the recommended interface is the memtrace
viewer, which lives at:

https://github.com/janestreet/memtrace_viewer

This repository code is based on
memtrace.

Install
Published
22 May 2022
Sources
memtrace-mirage-0.2.1.2.2.tbz
sha256=34b0464af8399bc8b1c9a1de907d6fcc4bab135879e1f650c63bf658cd300f8e
sha512=e9a61c4519050b7bc3f35ed9075755b1ad33cf1ac87d08293915a6b4677d8b2502f8b00d8a5b5207e6b5c92cab92c5c14c9c05b95cbbfb23b1a36f5befd43c58
Dependencies
lwt
>= "5.5.0"
ptime
>= "1.0.0"
mirage-clock
>= "4.0.0"
mirage-flow
>= "3.0.0"
ocaml
>= "4.11.0"
dune
>= "2.3"
Reverse Dependencies