Skip to content

Commit 7ac635e

Browse files
bnigitoLilydemihlianXyene
committed
Add support for compressed output files, whether gzip (supported by Perfetto) or zstd (not yet supported)
This builds on previous work for compressed file destinations Closes #154 Signed-off-by: Brian Nigito <[email protected]> Co-authored-by: Lidya Demilew <[email protected]> Co-authored-by: Hao Lian <[email protected]> Co-authored-by: Tudor Brindus <[email protected]>
1 parent ce4319e commit 7ac635e

File tree

11 files changed

+194
-14
lines changed

11 files changed

+194
-14
lines changed

.github/workflows/build.yml

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,31 @@ jobs:
3737
wget http://ftp.debian.org/debian/pool/main/u/upx-ucl/upx-ucl_3.96-2_amd64.deb
3838
sudo dpkg -i upx-ucl_3.96-2_amd64.deb
3939
40-
- name: Use OCaml ${{ matrix.ocaml-version }}
40+
- name: "Install apt packages"
4141
run: |
4242
sudo apt-get update
4343
sudo apt-get install bubblewrap musl-tools
44+
45+
- name: Build zlib with musl
46+
run: |
47+
mkdir musl-zlib
48+
curl -L https://zlib.net/zlib-1.3.1.tar.gz | tar -xz -C musl-zlib --strip-components=1
49+
cd musl-zlib
50+
CC=musl-gcc ./configure --libdir=/usr/lib/x86_64-linux-musl --includedir=/usr/include/x86_64-linux-musl
51+
make -j$(nproc)
52+
sudo make install
53+
54+
- name: Build zstd with musl
55+
run: |
56+
mkdir musl-zstd
57+
curl -L https://github.com/facebook/zstd/releases/download/v1.5.5/zstd-1.5.5.tar.gz | \
58+
tar -xz -C musl-zstd --strip-components=1
59+
cd musl-zstd
60+
CC=musl-gcc make -j$(nproc)
61+
sudo make INCLUDEDIR=/usr/include/x86_64-linux-musl LIBDIR=/usr/lib/x86_64-linux-musl install
62+
63+
- name: Use OCaml ${{ matrix.ocaml-version }}
64+
run: |
4465
sudo wget -O /usr/local/bin/opam https://github.com/ocaml/opam/releases/download/2.1.2/opam-2.1.2-x86_64-linux
4566
sudo chmod a+x /usr/local/bin/opam
4667

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ You can point magic-trace at a function such that when your application calls it
7171

7272
1. [Here](https://raw.githubusercontent.com/janestreet/magic-trace/master/demo/demo.c)'s a sample C program to try out. It's a slightly modified version of the example in `man 3 dlopen`. Download that, build it with `gcc demo.c -ldl -o demo`, then leave it running `./demo`. We're going to use that program to learn how `dlopen` works.
7373

74-
2. Run `magic-trace attach -pid $(pidof demo)`. When you see the message that it's successfully attached, wait a couple seconds and <kbd>Ctrl</kbd>+<kbd>C</kbd> `magic-trace`. It will output a file called `trace.fxt` in your working directory.
74+
2. Run `magic-trace attach -pid $(pidof demo)`. When you see the message that it's successfully attached, wait a couple seconds and <kbd>Ctrl</kbd>+<kbd>C</kbd> `magic-trace`. It will output a file called `trace.fxt.gz` in your working directory.
7575

7676
<p align="center">
7777
<img src="docs/assets/stage-1.gif">

magic-trace.opam

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ build: [
1212
depends: [
1313
"ocaml" {>= "4.14"}
1414
"async"
15+
"camlzip"
1516
"cohttp"
1617
"cohttp_static_handler"
1718
"core"
@@ -23,6 +24,7 @@ depends: [
2324
"dune" {>= "2.0.0"}
2425
"owee" {>= "0.6"}
2526
"re" {>= "1.8.0"}
27+
"zstandard"
2628
]
2729
synopsis: "Collects and displays high-resolution traces of what a process is doing"
2830
description: "https://github.com/janestreet/magic-trace"

src/tracing_tool_output.ml

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ type t =
129129

130130
let param =
131131
let%map_open.Command output_path =
132-
let default = "trace.fxt" in
132+
let default = "trace.fxt.gz" in
133133
flag
134134
"output"
135135
(optional_with_default default string)
@@ -178,7 +178,18 @@ let write_and_maybe_serve
178178
serving the new trace, which is unlikely to be what the user expected. *)
179179
let indirect_store_path = [%string "/proc/self/fd/%{fd#Core_unix.File_descr}"] in
180180
let writer =
181-
Tracing_zero.Writer.create_for_file ?num_temp_strs ~filename:indirect_store_path ()
181+
let file_format : Tracing_zero.Writer.File_format.t =
182+
if Filename.check_suffix filename ".gz"
183+
then Gzip
184+
else if Filename.check_suffix filename ".zst"
185+
then Zstandard
186+
else Uncompressed
187+
in
188+
Tracing_zero.Writer.create_for_file
189+
?num_temp_strs
190+
~file_format
191+
~filename:indirect_store_path
192+
()
182193
in
183194
let%bind.Deferred.Or_error res = f ~events_writer:None ~writer:(Some writer) () in
184195
let%map () =

vendor/tracing/src/trace.mli

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ open! Core
5151
type t
5252

5353
(** Open a file to write trace events to in the Fuchsia Trace Format, suggested extension
54-
is [.fxt].
54+
is [.fxt] for an uncompressed file and [.fxt.gz] for a gzip compressed one. While [.zst]
55+
will produce a Zstandard compressed file, the perfetto viewer does not yet support it.
5556
5657
If [base_time] is provided, a time initialization record will be written which
5758
records what absolute time corresponds to [Time_ns.Span.zero]. *)

vendor/tracing/zero/destinations.ml

Lines changed: 115 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
open! Core
22

3-
43
let direct_file_destination ?(buffer_size = 4096 * 16) ~filename () =
54
let buf = Iobuf.create ~len:buffer_size in
65
let file = Core_unix.openfile ~mode:[ O_CREAT; O_TRUNC; O_RDWR ] filename in
@@ -32,7 +31,121 @@ let direct_file_destination ?(buffer_size = 4096 * 16) ~filename () =
3231
(module Dest : Writer_intf.Destination)
3332
;;
3433

35-
let file_destination ~filename () = direct_file_destination ~filename ()
34+
(* While Zstandard has the best compression, perfetto does not yet understand the format. *)
35+
let zstd_file_destination ?(buffer_size = 64 * 1024) ~filename () =
36+
let buf = Iobuf.create ~len:buffer_size in
37+
let compression_level = 5 in
38+
(* Ensure the compression buffer is large enough for the worst case of an input of
39+
[buffer_size]. *)
40+
let compressed_buf =
41+
let len =
42+
buffer_size
43+
|> Int64.of_int
44+
|> Zstandard.compression_output_size_bound
45+
|> Int64.to_int_exn
46+
in
47+
Iobuf.create ~len
48+
in
49+
let file = Core_unix.openfile ~mode:[ O_CREAT; O_TRUNC; O_CLOEXEC; O_RDWR ] filename in
50+
let written = ref 0 in
51+
let compression_context = Zstandard.Compression_context.create () in
52+
let flush () =
53+
Iobuf.rewind buf;
54+
Iobuf.advance buf !written;
55+
Iobuf.flip_lo buf;
56+
let input =
57+
Zstandard.Input.from_bigstring
58+
~pos:(Iobuf.Expert.lo buf)
59+
~len:(Iobuf.length buf)
60+
(Iobuf.Expert.buf buf)
61+
in
62+
let output =
63+
Zstandard.Output.in_buffer
64+
~pos:(Iobuf.Expert.lo compressed_buf)
65+
~len:(Iobuf.length compressed_buf)
66+
(Iobuf.Expert.buf compressed_buf)
67+
in
68+
let compressed_length =
69+
Zstandard.With_explicit_context.compress
70+
compression_context
71+
~compression_level
72+
~input
73+
~output
74+
in
75+
Iobuf.advance compressed_buf compressed_length;
76+
Iobuf.flip_lo compressed_buf;
77+
Iobuf_unix.write compressed_buf file;
78+
written := 0;
79+
Iobuf.reset buf;
80+
Iobuf.reset compressed_buf
81+
in
82+
let module Dest = struct
83+
let next_buf ~ensure_capacity =
84+
flush ();
85+
if ensure_capacity > Iobuf.length buf
86+
then failwith "Not enough buffer space in [zstd_file_destination]";
87+
buf
88+
;;
89+
90+
let wrote_bytes count = written := !written + count
91+
92+
let close () =
93+
flush ();
94+
Zstandard.Compression_context.free compression_context;
95+
Core_unix.close file
96+
;;
97+
end
98+
in
99+
(module Dest : Writer_intf.Destination)
100+
;;
101+
102+
let gzip_file_destination ?(buffer_size = 64 * 1024) ~filename () =
103+
let buf = Iobuf.create ~len:buffer_size in
104+
let bytes = Bytes.create buffer_size in
105+
let file = Core_unix.openfile ~mode:[ O_CREAT; O_TRUNC; O_CLOEXEC; O_RDWR ] filename in
106+
let out_channel =
107+
let oc = Core_unix.out_channel_of_descr file in
108+
(* Consider making the compression level an environment variable for
109+
experimentation. *)
110+
Gzip.open_out_chan ~level:6 oc
111+
in
112+
let written = ref 0 in
113+
let flush () =
114+
Iobuf.rewind buf;
115+
Iobuf.advance buf !written;
116+
Iobuf.flip_lo buf;
117+
Iobuf.Peek.To_bytes.blit
118+
~src:(Iobuf.read_only buf) ~src_pos:0 ~dst:bytes ~dst_pos:0 ~len:!written;
119+
Gzip.output out_channel bytes 0 !written;
120+
written := 0;
121+
Iobuf.reset buf;
122+
in
123+
let module Dest = struct
124+
let next_buf ~ensure_capacity =
125+
flush ();
126+
if ensure_capacity > Iobuf.length buf
127+
then failwith "Not enough buffer space in [gzip_file_destination]";
128+
buf
129+
;;
130+
131+
let wrote_bytes count = written := !written + count
132+
133+
let close () =
134+
flush ();
135+
(* [close_out] also closes the underlying file descr. *)
136+
Gzip.close_out out_channel
137+
;;
138+
end
139+
in
140+
(module Dest : Writer_intf.Destination)
141+
;;
142+
143+
let file_destination ?(file_format = Writer_intf.File_format.Uncompressed) ~filename () =
144+
match file_format with
145+
| Uncompressed -> direct_file_destination ~filename ()
146+
| Gzip -> gzip_file_destination ~filename ()
147+
| Zstandard -> zstd_file_destination ~filename ()
148+
;;
36149

37150
let iobuf_destination buf =
38151
(* We give out an [Iobuf] with a shared underlying [Bigstring] but different pointers

vendor/tracing/zero/destinations.mli

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,25 @@ val direct_file_destination
77
-> unit
88
-> (module Writer_intf.Destination)
99

10-
(** Write to a file in some way with the best available performance. *)
11-
val file_destination : filename:string -> unit -> (module Writer_intf.Destination)
10+
(** Write to a zstd compressed file using synchronous writes, not suitable for low latency
11+
applications. *)
12+
val zstd_file_destination
13+
: ?buffer_size:int
14+
-> filename:string
15+
-> unit
16+
-> (module Writer_intf.Destination)
17+
18+
(** Write to a gzip compressed file using synchronous writes, not suitable for low latency
19+
applications. *)
20+
val gzip_file_destination
21+
: ?buffer_size:int
22+
-> filename:string
23+
-> unit
24+
-> (module Writer_intf.Destination)
25+
26+
(** Write to a file in some way with the best available performance. [format] defaults to
27+
[Uncompressed]. *)
28+
val file_destination : ?file_format:Writer_intf.File_format.t -> filename:string -> unit -> (module Writer_intf.Destination)
1229

1330
(** Write to a provided [Iobuf.t], throws an exception if the buffer runs out of space.
1431
Mostly intended for use in tests. After the [Destination] is closed, sets the window

vendor/tracing/zero/dune

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
(library (name tracing_zero) (public_name tracing.tracing_zero)
22
(preprocess (pps ppx_jane))
3-
(libraries core core_kernel.iobuf core_unix.iobuf_unix
4-
core_unix.time_stamp_counter))
3+
(libraries camlzip core core_kernel.iobuf core_unix.iobuf_unix
4+
core_unix.time_stamp_counter zstandard))

vendor/tracing/zero/writer.ml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -620,8 +620,14 @@ module Expert = struct
620620
module Write_arg_unchecked = Write_arg_unchecked
621621
end
622622

623-
let create_for_file ?num_temp_strs ~filename () =
624-
let destination = Destinations.file_destination ~filename () in
623+
624+
module File_format = Writer_intf.File_format
625+
626+
(** Allocates a writer which writes to [filename] with [num_temp_strs] temporary string
627+
slots (see [set_temp_string_slot]), with increases in [num_temp_strs] reducing the
628+
number of strings which can be allocated with [intern_string]. *)
629+
let create_for_file ?num_temp_strs ?file_format ~filename () =
630+
let destination = Destinations.file_destination ?file_format ~filename () in
625631
Expert.create ?num_temp_strs ~destination ()
626632
;;
627633

vendor/tracing/zero/writer.mli

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@ open! Core
1111

1212
type t
1313

14+
module File_format = Writer_intf.File_format
15+
1416
(** Allocates a writer which writes to [filename] with [num_temp_strs] temporary string
1517
slots (see [set_temp_string_slot]), with increases in [num_temp_strs] reducing the
1618
number of strings which can be allocated with [intern_string]. *)
17-
val create_for_file : ?num_temp_strs:int -> filename:string -> unit -> t
19+
val create_for_file : ?num_temp_strs:int -> ?file_format:Writer_intf.File_format.t -> filename:string -> unit -> t
1820

1921
val close : t -> unit
2022

vendor/tracing/zero/writer_intf.ml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,10 @@ module Tick_translation = struct
4747
{ ticks_per_second = 1_000_000_000; base_ticks = 0; base_time = Time_ns.epoch }
4848
;;
4949
end
50+
51+
module File_format = struct
52+
type t =
53+
| Uncompressed
54+
| Gzip
55+
| Zstandard
56+
end

0 commit comments

Comments
 (0)