diff --git a/runtime_tracing/Cargo.toml b/runtime_tracing/Cargo.toml index f031a88..0cd0672 100644 --- a/runtime_tracing/Cargo.toml +++ b/runtime_tracing/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "runtime_tracing" -version = "0.14.1" +version = "0.14.2" edition = "2024" authors = ["Metacraft Labs Ltd"] description = "A library for the schema and tracing helpers for the CodeTracer db trace format" @@ -21,10 +21,15 @@ serde_json = "1.0" serde_repr = "0.1" schemars = "0.8.2" capnp = "0.21.1" -zeekstd = "0.5.0" cbor4ii = { version = "1.0.0", features = ["serde1", "use_std"] } fscommon = "0.1.1" +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +zeekstd = "0.6.0" + +[target.'cfg(target_arch = "wasm32")'.dependencies] +ruzstd = "0.8.1" + [build-dependencies] capnpc = "0.21.0" diff --git a/runtime_tracing/src/cbor_zstd_reader.rs b/runtime_tracing/src/cbor_zstd_reader.rs index 18f2d9e..c444768 100644 --- a/runtime_tracing/src/cbor_zstd_reader.rs +++ b/runtime_tracing/src/cbor_zstd_reader.rs @@ -1,6 +1,7 @@ use std::io::{self, BufRead, BufReader, Read, Seek, Write}; use fscommon::StreamSlice; + use zeekstd::Decoder; use crate::{TraceLowLevelEvent, cbor_zstd_writer::HEADERV1}; diff --git a/runtime_tracing/src/cbor_zstd_reader_wasm.rs b/runtime_tracing/src/cbor_zstd_reader_wasm.rs new file mode 100644 index 0000000..03c88dd --- /dev/null +++ b/runtime_tracing/src/cbor_zstd_reader_wasm.rs @@ -0,0 +1,39 @@ +use std::io::{self, BufRead, BufReader, Read, Seek, Write}; + +use fscommon::StreamSlice; + +use ruzstd::decoding::StreamingDecoder; + +use crate::{TraceLowLevelEvent, cbor_zstd_writer::HEADERV1}; + +fn is_at_eof(reader: &mut R) -> io::Result { + let buffer = reader.fill_buf()?; + Ok(buffer.is_empty()) +} + +pub fn read_trace(input: &mut (impl Read + Write + Seek)) -> Result, Box> { + let end_pos = input.seek(io::SeekFrom::End(0))?; + input.seek(io::SeekFrom::Start(0))?; + + let mut header_buf = [0; 8]; + let mut buf_reader = BufReader::new(&mut *input); + buf_reader.read_exact(&mut header_buf)?; + if header_buf != HEADERV1 { + panic!("Invalid file header (wrong file format or incompatible version)"); + } + + input.seek(io::SeekFrom::Start(0))?; + let input2 = StreamSlice::new(&mut *input, 8, end_pos)?; + + let decoder = StreamingDecoder::new(input2)?; + let mut buf_reader = BufReader::new(decoder); + + let mut result: Vec = vec![]; + + while !is_at_eof(&mut buf_reader)? { + let obj = cbor4ii::serde::from_reader::(&mut buf_reader)?; + result.push(obj); + } + + Ok(result) +} diff --git a/runtime_tracing/src/cbor_zstd_writer.rs b/runtime_tracing/src/cbor_zstd_writer.rs index 0bd10d8..8681fc9 100644 --- a/runtime_tracing/src/cbor_zstd_writer.rs +++ b/runtime_tracing/src/cbor_zstd_writer.rs @@ -1,10 +1,11 @@ use std::{fs::File, io::Write, path::PathBuf}; + use zeekstd::Encoder; use crate::{ - TraceLowLevelEvent, abstract_trace_writer::{AbstractTraceWriter, AbstractTraceWriterData}, trace_writer::TraceWriter, + TraceLowLevelEvent, }; /// The next 3 bytes are reserved/version info. diff --git a/runtime_tracing/src/cbor_zstd_writer_wasm.rs b/runtime_tracing/src/cbor_zstd_writer_wasm.rs new file mode 100644 index 0000000..084f1f6 --- /dev/null +++ b/runtime_tracing/src/cbor_zstd_writer_wasm.rs @@ -0,0 +1,91 @@ +use std::{ + fs::File, + io::{Cursor, Write}, + path::PathBuf, +}; + +use ruzstd::encoding::{CompressionLevel, compress}; + +use crate::{ + TraceLowLevelEvent, + abstract_trace_writer::{AbstractTraceWriter, AbstractTraceWriterData}, + trace_writer::TraceWriter, +}; + +/// The next 3 bytes are reserved/version info. +/// The header is 8 bytes in size, ensuring 64-bit alignment for the rest of the file. +pub const HEADERV1: &[u8] = &[ + 0xC0, 0xDE, 0x72, 0xAC, 0xE2, // The first 5 bytes identify the file as a CodeTracer file (hex l33tsp33k - C0DE72ACE2 for "CodeTracer"). + 0x01, // Indicates version 1 of the file format + 0x00, 0x00, +]; // Reserved, must be zero in this version. + +pub struct CborZstdTraceWriter { + base: AbstractTraceWriterData, + + trace_events_path: Option, + trace_events_file: Option, + uncompressed_buf: Vec, +} + +impl CborZstdTraceWriter { + /// Create a new tracer instance for the given program and arguments. + pub fn new(program: &str, args: &[String]) -> Self { + CborZstdTraceWriter { + base: AbstractTraceWriterData::new(program, args), + + trace_events_path: None, + trace_events_file: None, + uncompressed_buf: vec![], + } + } +} + +impl AbstractTraceWriter for CborZstdTraceWriter { + fn get_data(&self) -> &AbstractTraceWriterData { + &self.base + } + + fn get_mut_data(&mut self) -> &mut AbstractTraceWriterData { + &mut self.base + } + + fn add_event(&mut self, event: TraceLowLevelEvent) { + let buf: Vec = Vec::new(); + let q = cbor4ii::serde::to_vec(buf, &event).expect("CBOR encode failed"); + self.uncompressed_buf.extend_from_slice(&q); + } + + fn append_events(&mut self, events: &mut Vec) { + for e in events.drain(..) { + ::add_event(self, e); + } + } +} + +impl TraceWriter for CborZstdTraceWriter { + fn begin_writing_trace_events(&mut self, path: &std::path::Path) -> Result<(), Box> { + let pb = path.to_path_buf(); + self.trace_events_path = Some(pb.clone()); + + let mut file_output = std::fs::File::create(pb)?; + file_output.write_all(HEADERV1)?; + self.trace_events_file = Some(file_output); + + Ok(()) + } + + fn finish_writing_trace_events(&mut self) -> Result<(), Box> { + if let Some(mut file) = self.trace_events_file.take() { + let mut cursor = Cursor::new(&self.uncompressed_buf); + compress(&mut cursor, &mut file, CompressionLevel::Fastest); + + file.flush()?; + + self.uncompressed_buf.clear(); + Ok(()) + } else { + panic!("finish_writing_trace_events() called without previous call to begin_writing_trace_events()"); + } + } +} diff --git a/runtime_tracing/src/lib.rs b/runtime_tracing/src/lib.rs index 3afd539..04d8a9a 100644 --- a/runtime_tracing/src/lib.rs +++ b/runtime_tracing/src/lib.rs @@ -11,8 +11,19 @@ mod abstract_trace_writer; mod base64; mod capnptrace; + +#[cfg(target_arch = "wasm32")] +#[path = "./cbor_zstd_reader_wasm.rs"] mod cbor_zstd_reader; +#[cfg(target_arch = "wasm32")] +#[path = "./cbor_zstd_writer_wasm.rs"] mod cbor_zstd_writer; + +#[cfg(not(target_arch = "wasm32"))] +mod cbor_zstd_reader; +#[cfg(not(target_arch = "wasm32"))] +mod cbor_zstd_writer; + mod non_streaming_trace_writer; mod trace_readers; mod trace_writer;