this repo has no description

Features

+7
README.md
···
---------
Tracing open-like syscalls using eBPF via OCaml.
+
+
```
+
sudo opentrace exec -- opam list
+
cat trace.csv
+
```
+
+
+6
dune
···
(executable
(name opentrace)
(public_name opentrace)
+
(modules opentrace config)
(preprocess
(pps ppx_blob))
(preprocessor_deps
···
arch
(bash
"uname -m | sed 's/x86_64/x86/' | sed 's/arm.*/arm/' | sed 's/aarch64/arm64/' | sed 's/ppc64le/powerpc/' | sed 's/mips.*/mips/' | sed 's/riscv64/riscv/' | sed 's/loongarch64/loongarch/'")))))
+
+
(rule
+
(targets config.ml)
+
(action
+
(run ./include/discover.exe)))
+1
dune-project
···
(ocaml (>= 5.2.0))
eio_main
jsonm
+
dune-configurator
libbpf
libbpf_maps))
+67
include/discover.ml
···
+
module C = Configurator.V1
+
+
let () =
+
C.main ~name:"discover" (fun c ->
+
let defs =
+
let values =
+
C.C_define.import c ~c_flags:[ "-D_GNU_SOURCE" ]
+
~includes:[ "fcntl.h" ]
+
C.C_define.Type.
+
[
+
("O_RDONLY", Int);
+
("O_WRONLY", Int);
+
("O_RDWR", Int);
+
("O_CREAT", Int);
+
("O_EXCL", Int);
+
("O_NOCTTY", Int);
+
("O_TRUNC", Int);
+
("O_APPEND", Int);
+
("O_NONBLOCK", Int);
+
("O_DSYNC", Int);
+
("O_DIRECT", Int);
+
(* "O_LARGEFILE", Int; *)
+
("O_DIRECTORY", Int);
+
("O_NOFOLLOW", Int);
+
("O_NOATIME", Int);
+
("O_CLOEXEC", Int);
+
("O_SYNC", Int);
+
("O_PATH", Int);
+
("O_TMPFILE", Int);
+
]
+
in
+
let defs =
+
List.map
+
(function
+
| name, C.C_define.Value.Int v ->
+
Printf.sprintf "let %s = 0x%x" (String.lowercase_ascii name) v
+
| _ -> assert false)
+
values
+
in
+
let of_string =
+
List.fold_left
+
(fun acc v ->
+
match v with
+
| name, C.C_define.Value.Int v ->
+
let case = Printf.sprintf "| \"%s\" -> 0x%x\n" name v in
+
acc ^ case
+
| _ -> assert false)
+
"let of_string = function\n" values
+
in
+
let to_string =
+
List.fold_left
+
(fun acc v ->
+
match v with
+
| name, C.C_define.Value.Int v ->
+
let case = Printf.sprintf "| 0x%x -> \"%s\"\n" v name in
+
acc ^ case
+
| _ -> assert false)
+
"let to_string = function\n" values
+
in
+
defs
+
@ [
+
of_string ^ "| s -> invalid_arg(\"Unknown flag: \" ^ s)\n";
+
to_string
+
^ "| s -> invalid_arg(\"Unknown flag: \" ^ string_of_int s)\n";
+
]
+
in
+
C.Flags.write_lines "config.ml" defs)
+4
include/dune
···
+
(executable
+
(name discover)
+
(modules discover)
+
(libraries dune-configurator))
+49 -13
opentrace.ml
···
in
loop [] json |> List.rev
+
module Flags = struct
+
type t = int
+
+
include Config
+
+
let mem (v : t) i = Int.equal (i land (v :> int)) (v :> int)
+
end
+
module Open_event = struct
open Ctypes
···
in
bpf_callback
-
let all poll no_header =
+
let filter_event_by_flag event flags =
+
flags = []
+
||
+
let fs = Open_event.get_flags event in
+
List.for_all (fun flag -> Flags.mem flag fs) flags
+
+
let all poll no_header open_flags =
if no_header then () else Format.printf "%s" Open_event.csv_header;
let callback event =
-
Format.printf "%s\n%!" (Open_event.to_csv_row event);
+
if filter_event_by_flag event open_flags then
+
Format.printf "%s\n%!" (Open_event.to_csv_row event);
0
in
let bpf_callback = ringbuffer_polling_callback ~poll callback (fun _ -> ()) in
run_ring_buffer bpf_callback
-
let exec format output user poll (prog, args) =
+
let exec format output user poll (prog, args) open_flags_filter =
let output =
match output with
| Some file -> file
···
match Atomic.get pid with
| None -> 0
| Some pid ->
-
(if Int.equal (Open_event.get_pid event) pid then
+
(if
+
Int.equal (Open_event.get_pid event) pid
+
&& filter_event_by_flag event open_flags_filter
+
then
match format with
| Csv ->
Out_channel.output_string oc (Open_event.to_csv_row event);
···
let doc = "Disable printing the CSV header" in
Arg.(value & flag & info [ "no-header" ] ~doc)
+
let flag_conv =
+
let of_string s =
+
try Ok (Flags.of_string s) with Invalid_argument m -> Error (`Msg m)
+
in
+
let pp ppf v = Format.pp_print_string ppf (Flags.to_string v) in
+
Arg.conv (of_string, pp)
+
+
let open_flags_filter =
+
let doc =
+
"Filter open events that include these flags (e.g. O_RDONLY, O_CREAT). \
+
Note the filter wants ALL of the flags to be present not just one of \
+
them."
+
in
+
Arg.(value & opt (list flag_conv) [] & info [ "flags" ] ~doc)
+
let user =
let doc = "Username or UID to execute program as" in
Arg.(value & opt (some string) None & info [ "u"; "user" ] ~doc ~docv:"USER")
···
let output =
let doc =
"Output file for trace. Defaults to trace.<csv|json> depending on the \
-
$(format)."
+
format."
in
Arg.(
value & opt (some string) None & info [ "o"; "output" ] ~docv:"OUTPUT" ~doc)
let all_cmd =
-
let doc = "Trace all open system calls" in
+
let doc = "Trace all open system calls." in
let man =
[
`P
···
in
Cmd.v (Cmd.info ~doc ~man "all")
@@
-
let+ polling = polling and+ no_header = no_header in
-
all polling no_header
+
let+ polling = polling
+
and+ no_header = no_header
+
and+ open_flags_filter = open_flags_filter in
+
all polling no_header open_flags_filter
let exec_cmd =
-
let doc = "Execute a program and trace its open system calls" in
+
let doc = "Execute a program and trace its open system calls." in
let man =
[
`P
···
and+ format = format
and+ output = output
and+ args = Arg.(value & pos_right 0 string [] & Arg.info [] ~docv:"ARGS")
+
and+ open_flags_filter = open_flags_filter
and+ poll = polling in
-
exec format output user poll (prog, args)
+
exec format output user poll (prog, args) open_flags_filter
let opentrace_cmd =
let doc = "Trace all open system calls" in
let man =
[
`S Manpage.s_description;
-
`P "$(cmd) traces all open system calls";
+
`P "$(tool) traces all open system calls.";
`P
-
"$(cmd) can be used either to run an executable directly or to trace \
-
all open calls";
+
"$(tool) can be used either to run an executable directly or to trace \
+
all open calls.";
]
in
let default = Term.(ret (const (`Help (`Auto, None)))) in
+7
opentrace.opam
···
"ocaml" {>= "5.2.0"}
"eio_main"
"jsonm"
+
"dune-configurator"
"libbpf"
"libbpf_maps"
"odoc" {with-doc}
···
"@doc" {with-doc}
]
]
+
pin-depends:[
+
"eio.dev" "git+https://github.com/patricoferris/eio#9cb52c3c061a1bfee0c6094625415d3c95f735d5"
+
"eio_posix.dev" "git+https://github.com/patricoferris/eio#9cb52c3c061a1bfee0c6094625415d3c95f735d5"
+
"eio_linux.dev" "git+https://github.com/patricoferris/eio#9cb52c3c061a1bfee0c6094625415d3c95f735d5"
+
"eio_main.dev" "git+https://github.com/patricoferris/eio#9cb52c3c061a1bfee0c6094625415d3c95f735d5"
+
]
+6
opentrace.opam.template
···
+
pin-depends:[
+
"eio.dev" "git+https://github.com/patricoferris/eio#9cb52c3c061a1bfee0c6094625415d3c95f735d5"
+
"eio_posix.dev" "git+https://github.com/patricoferris/eio#9cb52c3c061a1bfee0c6094625415d3c95f735d5"
+
"eio_linux.dev" "git+https://github.com/patricoferris/eio#9cb52c3c061a1bfee0c6094625415d3c95f735d5"
+
"eio_main.dev" "git+https://github.com/patricoferris/eio#9cb52c3c061a1bfee0c6094625415d3c95f735d5"
+
]