Subject: [PATCH] Trace exact fills of Vec and String buffers

Target Rust 1.99.0 (b940084d7), x86-64 Linux. The trace records four
little-endian u64s per event: address, capacity in bytes, length or
resize ticket, and operation. Set UV_VEC_TRACE to enable recording.
Events cover allocation (a), resize (b/e), length (v/s), and free (f).

From a uv checkout, using the rust-src component:

    patch=/absolute/path/to/rust-1.99.0-exact-fill-rust-only.patch
    mkdir -p target
    work=$(mktemp -d "$PWD/target/exact-fill.XXXXXX")
    cp -a "$(rustc +1.99.0 --print sysroot)/lib/rustlib/src/rust/library" "$work/"
    patch -d "$work" -p1 < "$patch"
    RUSTC_BOOTSTRAP=1 __CARGO_TESTS_ONLY_SRC_ROOT="$work/library" \
      CARGO_TARGET_DIR="$work/target" \
      cargo +1.99.0 build --locked -Z build-std=std,panic_abort \
        --profile fast-build --target x86_64-unknown-linux-gnu -p uv --bin uv

    uv="$work/target/x86_64-unknown-linux-gnu/fast-build/uv"
    "$uv" --no-config --no-cache venv --python /path/to/python3 "$work/venv"
    UV_VEC_TRACE="$work/events.bin" "$uv" --no-config --cache-dir "$work/cache" \
      pip install --python "$work/venv/bin/python" --index-url https://pypi.org/simple jupyter
    uv run /absolute/path/to/blog/data/benchmarks/vec-layout/exact-fill.py "$work/events.bin"

---
diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs
index c0d7d8b..2c1342b 100644
--- a/library/alloc/src/alloc.rs
+++ b/library/alloc/src/alloc.rs
@@ -174,2 +174,3 @@ pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) {
 unsafe fn dealloc_nonnull(ptr: NonNull<u8>, layout: Layout) {
+    crate::uv_vec_trace::record(b'f', ptr.as_ptr(), layout.size(), 0);
     unsafe { __rust_dealloc(ptr, layout.size(), layout.alignment()) }
diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs
index 5cf0507..7ddf783 100644
--- a/library/alloc/src/lib.rs
+++ b/library/alloc/src/lib.rs
@@ -276 +276,3 @@ pub mod __export {
 }
+
+mod uv_vec_trace;
diff --git a/library/alloc/src/raw_vec/mod.rs b/library/alloc/src/raw_vec/mod.rs
index 5d4ad3a..fc0bc65 100644
--- a/library/alloc/src/raw_vec/mod.rs
+++ b/library/alloc/src/raw_vec/mod.rs
@@ -474,2 +474,3 @@ const impl<A: [const] Allocator + [const] Destruct> RawVecInner<A> {
 
+        crate::uv_vec_trace::record(b'a', ptr.cast::<u8>().as_ptr(), layout.size(), 0);
         // Allocators currently return a `NonNull<[u8]>` whose length
@@ -550,2 +551,5 @@ const impl<A: [const] Allocator + [const] Destruct> RawVecInner<A> {
 
+        let old_capacity = self.cap.as_inner() * elem_layout.size();
+        let old_pointer = self.ptr.as_ptr();
+        let ticket = crate::uv_vec_trace::event(b'b', old_pointer, old_capacity, 0);
         let memory = if let Some((ptr, old_layout)) = unsafe { self.current_memory(elem_layout) } {
@@ -562,2 +566,7 @@ const impl<A: [const] Allocator + [const] Destruct> RawVecInner<A> {
 
+        let (pointer, capacity) = match memory {
+            Ok(pointer) => (pointer.cast::<u8>().as_ptr(), new_layout.size()),
+            Err(_) => (old_pointer, old_capacity),
+        };
+        crate::uv_vec_trace::event(b'e', pointer, capacity, ticket);
         memory.map_err(const |_| AllocError { layout: new_layout, non_exhaustive: () }.into())
@@ -839,2 +848,3 @@ impl<A: Allocator> RawVecInner<A> {
         if cap == 0 {
+            crate::uv_vec_trace::record(b'f', ptr.as_ptr(), layout.size(), 0);
             unsafe { self.alloc.deallocate(ptr, layout) };
@@ -844,3 +854,4 @@ impl<A: Allocator> RawVecInner<A> {
         } else {
-            let ptr = unsafe {
+            let ticket = crate::uv_vec_trace::event(b'b', ptr.as_ptr(), layout.size(), 0);
+            let result = unsafe {
                 // Layout cannot overflow here because it would have
@@ -851,4 +862,10 @@ impl<A: Allocator> RawVecInner<A> {
                     .shrink(ptr, layout, new_layout)
-                    .map_err(|_| AllocError { layout: new_layout, non_exhaustive: () })?
+                    .map_err(|_| AllocError { layout: new_layout, non_exhaustive: () })
             };
+            let (pointer, size) = match result {
+                Ok(pointer) => (pointer.cast::<u8>().as_ptr(), cap * elem_layout.size()),
+                Err(_) => (ptr.as_ptr(), layout.size()),
+            };
+            crate::uv_vec_trace::event(b'e', pointer, size, ticket);
+            let ptr = result?;
             // SAFETY: if the allocation is valid, then the capacity is too
@@ -874,2 +891,3 @@ const impl<A: [const] Allocator> RawVecInner<A> {
         if let Some((ptr, layout)) = unsafe { self.current_memory(elem_layout) } {
+            crate::uv_vec_trace::record(b'f', ptr.as_ptr(), layout.size(), 0);
             unsafe {
diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs
index 256efc6..d832099 100644
--- a/library/alloc/src/string.rs
+++ b/library/alloc/src/string.rs
@@ -493,3 +493,3 @@ impl String {
     pub fn with_capacity(capacity: usize) -> String {
-        String { vec: Vec::with_capacity(capacity) }
+        String { vec: Vec::with_capacity(capacity).trace_as(b's') }
     }
@@ -506,3 +506,3 @@ impl String {
     pub fn try_with_capacity(capacity: usize) -> Result<String, TryReserveError> {
-        Ok(String { vec: Vec::try_with_capacity(capacity)? })
+        Ok(String { vec: Vec::try_with_capacity(capacity)?.trace_as(b's') })
     }
@@ -570,3 +570,3 @@ impl String {
         match str::from_utf8(&vec) {
-            Ok(..) => Ok(String { vec }),
+            Ok(..) => Ok(String { vec: vec.trace_as(b's') }),
             Err(e) => Err(FromUtf8Error { bytes: vec, error: e }),
@@ -983,3 +983,3 @@ impl String {
     pub unsafe fn from_raw_parts(buf: *mut u8, length: usize, capacity: usize) -> String {
-        unsafe { String { vec: Vec::from_raw_parts(buf, length, capacity) } }
+        unsafe { String { vec: Vec::from_raw_parts(buf, length, capacity).trace_as(b's') } }
     }
@@ -1016,3 +1016,3 @@ impl String {
     pub unsafe fn from_utf8_unchecked(bytes: Vec<u8>) -> String {
-        String { vec: bytes }
+        String { vec: bytes.trace_as(b's') }
     }
@@ -1037,2 +1037,3 @@ impl String {
     pub const fn into_bytes(self) -> Vec<u8> {
+        self.vec.trace_size(b's');
         self.vec
@@ -1104,3 +1105,4 @@ impl String {
     pub fn push_str(&mut self, string: &str) {
-        self.vec.extend_from_slice(string.as_bytes())
+        self.vec.extend_from_slice(string.as_bytes());
+        self.vec.trace_size(b's');
     }
@@ -1117,3 +1119,5 @@ impl String {
     pub(crate) fn try_push_str(&mut self, string: &str) -> Result<(), TryReserveError> {
-        self.vec.try_extend_from_slice_of_bytes(string.as_bytes())
+        self.vec.try_extend_from_slice_of_bytes(string.as_bytes())?;
+        self.vec.trace_size(b's');
+        Ok(())
     }
@@ -1235,3 +1239,4 @@ impl String {
     pub fn reserve(&mut self, additional: usize) {
-        self.vec.reserve(additional)
+        self.vec.reserve(additional);
+        self.vec.trace_size(b's');
     }
@@ -1285,3 +1290,4 @@ impl String {
     pub fn reserve_exact(&mut self, additional: usize) {
-        self.vec.reserve_exact(additional)
+        self.vec.reserve_exact(additional);
+        self.vec.trace_size(b's');
     }
@@ -1320,3 +1326,5 @@ impl String {
     pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> {
-        self.vec.try_reserve(additional)
+        self.vec.try_reserve(additional)?;
+        self.vec.trace_size(b's');
+        Ok(())
     }
@@ -1361,3 +1369,5 @@ impl String {
     pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> {
-        self.vec.try_reserve_exact(additional)
+        self.vec.try_reserve_exact(additional)?;
+        self.vec.trace_size(b's');
+        Ok(())
     }
@@ -2107,2 +2117,3 @@ impl String {
         unsafe { self.as_mut_vec() }.splice(checked_range, replace_with.bytes());
+        self.vec.trace_size(b's');
     }
@@ -2388,3 +2399,3 @@ impl Clone for String {
     fn clone(&self) -> Self {
-        String { vec: self.vec.clone() }
+        String { vec: self.vec.clone().trace_as(b's') }
     }
@@ -2397,2 +2408,3 @@ impl Clone for String {
         self.vec.clone_from(&source.vec);
+        self.vec.trace_size(b's');
     }
@@ -2619,2 +2631,3 @@ impl Extend<core::ascii::Char> for String {
         self.vec.extend(iter.into_iter().map(|c| c.to_u8()));
+        self.vec.trace_size(b's');
     }
@@ -2624,2 +2637,3 @@ impl Extend<core::ascii::Char> for String {
         self.vec.push(c.to_u8());
+        self.vec.trace_size(b's');
     }
@@ -2638,2 +2652,3 @@ impl<'a> Extend<&'a core::ascii::Char> for String {
         self.vec.push(c.to_u8());
+        self.vec.trace_size(b's');
     }
diff --git a/library/alloc/src/uv_vec_trace.rs b/library/alloc/src/uv_vec_trace.rs
new file mode 100644
index 0000000..e6bf602
--- /dev/null
+++ b/library/alloc/src/uv_vec_trace.rs
@@ -0,0 +1,25 @@
+unsafe extern "C" {
+    fn uv_vec_event(operation: u64, pointer: *const u8, capacity: usize, detail: u64) -> u64;
+}
+
+const fn skip(_operation: u8, _pointer: *const u8, _capacity: usize, _detail: u64) -> u64 {
+    0
+}
+
+fn emit(operation: u8, pointer: *const u8, capacity: usize, detail: u64) -> u64 {
+    // SAFETY: The logger uses the pointer as an address and never dereferences it.
+    unsafe { uv_vec_event(operation as u64, pointer, capacity, detail) }
+}
+
+#[inline]
+#[rustc_allow_const_fn_unstable(const_eval_select)]
+pub(crate) const fn event(operation: u8, pointer: *const u8, capacity: usize, detail: u64) -> u64 {
+    core::intrinsics::const_eval_select((operation, pointer, capacity, detail), skip, emit)
+}
+
+#[inline]
+pub(crate) const fn record(operation: u8, pointer: *const u8, capacity: usize, length: usize) {
+    if capacity != 0 {
+        event(operation, pointer, capacity, length as u64);
+    }
+}
diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs
index 21d88f9..7286734 100644
--- a/library/alloc/src/vec/mod.rs
+++ b/library/alloc/src/vec/mod.rs
@@ -837,2 +837,3 @@ impl<T> Vec<T> {
         let mut me = ManuallyDrop::new(self);
+        me.trace_size(b'v');
         (me.as_mut_ptr(), me.len(), me.capacity())
@@ -968,3 +969,3 @@ const impl<T, A: [const] Allocator + [const] Destruct> Vec<T, A> {
     pub fn with_capacity_in(capacity: usize, alloc: A) -> Self {
-        Vec { buf: RawVec::with_capacity_in(capacity, alloc), len: 0 }
+        Vec { buf: RawVec::with_capacity_in(capacity, alloc), len: 0 }.trace_as(b'v')
     }
@@ -1037,3 +1038,3 @@ const impl<T, A: [const] Allocator + [const] Destruct> Vec<T, A> {
             ptr::write(end, value);
-            self.len = len + 1;
+            self.set_len(len + 1);
             // SAFETY: We just wrote a value to the pointer that will live the lifetime of the reference.
@@ -1079,3 +1080,3 @@ impl<T, A: Allocator> Vec<T, A> {
     pub fn try_with_capacity_in(capacity: usize, alloc: A) -> Result<Self, TryReserveError> {
-        Ok(Vec { buf: RawVec::try_with_capacity_in(capacity, alloc)?, len: 0 })
+        Ok(Vec { buf: RawVec::try_with_capacity_in(capacity, alloc)?, len: 0 }.trace_as(b'v'))
     }
@@ -1195,3 +1196,3 @@ impl<T, A: Allocator> Vec<T, A> {
         );
-        unsafe { Vec { buf: RawVec::from_raw_parts_in(ptr, capacity, alloc), len: length } }
+        unsafe { Vec { buf: RawVec::from_raw_parts_in(ptr, capacity, alloc), len: length } }.trace_as(b'v')
     }
@@ -1310,3 +1311,3 @@ impl<T, A: Allocator> Vec<T, A> {
         );
-        unsafe { Vec { buf: RawVec::from_nonnull_in(ptr, capacity, alloc), len: length } }
+        unsafe { Vec { buf: RawVec::from_nonnull_in(ptr, capacity, alloc), len: length } }.trace_as(b'v')
     }
@@ -1359,2 +1360,3 @@ impl<T, A: Allocator> Vec<T, A> {
         let alloc = unsafe { ptr::read(me.allocator()) };
+        me.trace_size(b'v');
         (ptr, len, capacity, alloc)
@@ -1460,3 +1462,7 @@ impl<T, A: Allocator> Vec<T, A> {
     pub fn reserve(&mut self, additional: usize) {
+        let old_capacity = self.capacity();
         self.buf.reserve(self.len, additional);
+        if self.capacity() != old_capacity {
+            self.trace_size(b'v');
+        }
     }
@@ -1490,3 +1496,7 @@ impl<T, A: Allocator> Vec<T, A> {
     pub fn reserve_exact(&mut self, additional: usize) {
+        let old_capacity = self.capacity();
         self.buf.reserve_exact(self.len, additional);
+        if self.capacity() != old_capacity {
+            self.trace_size(b'v');
+        }
     }
@@ -1527,3 +1537,8 @@ impl<T, A: Allocator> Vec<T, A> {
     pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> {
-        self.buf.try_reserve(self.len, additional)
+        let old_capacity = self.capacity();
+        self.buf.try_reserve(self.len, additional)?;
+        if self.capacity() != old_capacity {
+            self.trace_size(b'v');
+        }
+        Ok(())
     }
@@ -1570,3 +1585,8 @@ impl<T, A: Allocator> Vec<T, A> {
     pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> {
-        self.buf.try_reserve_exact(self.len, additional)
+        let old_capacity = self.capacity();
+        self.buf.try_reserve_exact(self.len, additional)?;
+        if self.capacity() != old_capacity {
+            self.trace_size(b'v');
+        }
+        Ok(())
     }
@@ -1725,2 +1745,3 @@ impl<T, A: Allocator> Vec<T, A> {
             self.shrink_to_fit();
+            self.trace_size(b'v');
             let me = ManuallyDrop::new(self);
@@ -1827,3 +1848,3 @@ impl<T, A: Allocator> Vec<T, A> {
             let s = self.as_mut_ptr().add(len).cast_slice(remaining_len);
-            self.len = len;
+            self.set_len(len);
             ptr::drop_in_place(s);
@@ -2224,2 +2245,3 @@ impl<T, A: Allocator> Vec<T, A> {
         self.len = new_len;
+        self.trace_size(b'v');
     }
@@ -2842,3 +2864,3 @@ impl<T, A: Allocator> Vec<T, A> {
             ptr::write(end, value);
-            self.len += 1;
+            self.set_len(self.len + 1);
 
@@ -2876,3 +2898,3 @@ impl<T, A: Allocator> Vec<T, A> {
             unsafe {
-                self.len -= 1;
+                self.set_len(self.len - 1);
                 core::hint::assert_unchecked(self.len < self.capacity());
@@ -2985,3 +3007,3 @@ impl<T, A: Allocator> Vec<T, A> {
         }
-        self.len += count;
+        unsafe { self.set_len(self.len + count) };
     }
@@ -3081,3 +3103,3 @@ impl<T, A: Allocator> Vec<T, A> {
         unsafe {
-            self.len = 0;
+            unsafe { self.set_len(0) };
             ptr::drop_in_place(elems);
@@ -3265,2 +3287,3 @@ impl<T, A: Allocator> Vec<T, A> {
         let mut me = ManuallyDrop::new(self);
+        me.trace_size(b'v');
         unsafe { slice::from_raw_parts_mut(me.as_mut_ptr(), me.len) }
@@ -3720,3 +3743,3 @@ impl<T: Clone, A: Allocator> Vec<T, A> {
             // don't alias.
-            let mut local_len = SetLenOnDrop::new(&mut self.len);
+            let mut local_len = SetLenOnDrop::new(self);
 
@@ -3836,3 +3859,3 @@ impl<T: TrivialClone, A: Allocator> ExtendFromWithinSpec for Vec<T, A> {
         // - The elements were just initialized by `copy_nonoverlapping`
-        self.len += count;
+        unsafe { self.set_len(self.len + count) };
     }
@@ -4023,2 +4046,3 @@ impl<T, A: Allocator> IntoIterator for Vec<T, A> {
         unsafe {
+            self.trace_size(b'v');
             let me = ManuallyDrop::new(self);
@@ -4130,3 +4154,3 @@ impl<T, A: Allocator> Vec<T, A> {
                 let ptr = self.as_mut_ptr();
-                let mut local_len = SetLenOnDrop::new(&mut self.len);
+                let mut local_len = SetLenOnDrop::new(self);
                 iterator.for_each(move |element| {
@@ -4353,2 +4377,3 @@ const unsafe impl<#[may_dangle] T: [const] Destruct, A: [const] Allocator + [con
     fn drop(&mut self) {
+        self.trace_size(b'v');
         unsafe {
@@ -4619 +4644,21 @@ const impl<T: [const] Destruct, A: [const] Allocator + [const] Destruct, const N
 }
+
+impl<T, A: Allocator> Vec<T, A> {
+    #[inline]
+    pub(crate) const fn trace_size(&self, operation: u8) {
+        if !T::IS_ZST {
+            crate::uv_vec_trace::record(
+                operation,
+                self.buf.ptr().cast(),
+                self.buf.capacity() * size_of::<T>(),
+                self.len * size_of::<T>(),
+            );
+        }
+    }
+
+    #[inline]
+    pub(crate) const fn trace_as(self, operation: u8) -> Self {
+        self.trace_size(operation);
+        self
+    }
+}
diff --git a/library/alloc/src/vec/set_len_on_drop.rs b/library/alloc/src/vec/set_len_on_drop.rs
index 6ce5a3a..17db7a8 100644
--- a/library/alloc/src/vec/set_len_on_drop.rs
+++ b/library/alloc/src/vec/set_len_on_drop.rs
@@ -8,2 +8,5 @@ pub(super) struct SetLenOnDrop<'a> {
     local_len: usize,
+    pointer: *const u8,
+    capacity: usize,
+    element_size: usize,
 }
@@ -12,4 +15,6 @@ impl<'a> SetLenOnDrop<'a> {
     #[inline]
-    pub(super) fn new(len: &'a mut usize) -> Self {
-        SetLenOnDrop { local_len: *len, len }
+    pub(super) fn new<T, A: core::alloc::Allocator>(vec: &'a mut super::Vec<T, A>) -> Self {
+        let capacity = if size_of::<T>() == 0 { 0 } else { vec.capacity() * size_of::<T>() };
+        SetLenOnDrop { local_len: vec.len, len: &mut vec.len, pointer: vec.buf.ptr().cast(),
+            capacity, element_size: size_of::<T>() }
     }
@@ -31,2 +36,3 @@ impl Drop for SetLenOnDrop<'_> {
         *self.len = self.local_len;
+        crate::uv_vec_trace::record(b'v', self.pointer, self.capacity, self.local_len * self.element_size);
     }
diff --git a/library/alloc/src/vec/spec_from_elem.rs b/library/alloc/src/vec/spec_from_elem.rs
index 96d701e..affa50f 100644
--- a/library/alloc/src/vec/spec_from_elem.rs
+++ b/library/alloc/src/vec/spec_from_elem.rs
@@ -23,3 +23,3 @@ impl<T: Clone + IsZero> SpecFromElem for T {
         if elem.is_zero() {
-            return Vec { buf: RawVec::with_capacity_zeroed_in(n, alloc), len: n };
+            return Vec { buf: RawVec::with_capacity_zeroed_in(n, alloc), len: n }.trace_as(b'v');
         }
@@ -35,3 +35,3 @@ impl SpecFromElem for i8 {
         if elem == 0 {
-            return Vec { buf: RawVec::with_capacity_zeroed_in(n, alloc), len: n };
+            return Vec { buf: RawVec::with_capacity_zeroed_in(n, alloc), len: n }.trace_as(b'v');
         }
@@ -50,3 +50,3 @@ impl SpecFromElem for u8 {
         if elem == 0 {
-            return Vec { buf: RawVec::with_capacity_zeroed_in(n, alloc), len: n };
+            return Vec { buf: RawVec::with_capacity_zeroed_in(n, alloc), len: n }.trace_as(b'v');
         }
diff --git a/library/alloc/src/vec/splice.rs b/library/alloc/src/vec/splice.rs
index 99ebcb4..5557c30 100644
--- a/library/alloc/src/vec/splice.rs
+++ b/library/alloc/src/vec/splice.rs
@@ -116,3 +116,3 @@ impl<T, A: Allocator> Drain<'_, T, A> {
             unsafe { vec.as_mut_ptr().add(idx).write(new_item) };
-            vec.len += 1;
+            unsafe { vec.set_len(vec.len + 1) };
         }
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index 22ac744..aa3456e 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -815 +815,3 @@ mod __restricted_std_workaround {}
 pub(crate) mod test_helpers;
+
+mod uv_vec_trace;
diff --git a/library/std/src/uv_vec_trace.rs b/library/std/src/uv_vec_trace.rs
new file mode 100644
index 0000000..6a4a0a6
--- /dev/null
+++ b/library/std/src/uv_vec_trace.rs
@@ -0,0 +1,63 @@
+use crate::process;
+use crate::ptr::NonNull;
+use crate::sync::{Mutex, OnceLock};
+
+static TRACE: OnceLock<Option<Mutex<Trace>>> = OnceLock::new();
+
+struct Trace {
+    file: NonNull<libc::FILE>,
+    next_ticket: u64,
+}
+
+// SAFETY: The stream can be used from any thread. All access to it is serialized by TRACE's mutex.
+unsafe impl Send for Trace {}
+
+fn open_trace() -> Option<Mutex<Trace>> {
+    // Use libc I/O so initialization and recording do not reenter Rust's allocation hooks.
+    // SAFETY: getenv receives a NUL-terminated name. The returned path is used during startup,
+    // before uv can change its environment or start worker threads.
+    let path = unsafe { libc::getenv(c"UV_VEC_TRACE".as_ptr()) };
+    if path.is_null() {
+        return None;
+    }
+    // SAFETY: getenv returned a NUL-terminated path and the mode is a C string literal.
+    let Some(file) = NonNull::new(unsafe { libc::fopen(path, c"wb".as_ptr()) }) else {
+        process::abort();
+    };
+    // SAFETY: flush_trace has C ABI and accesses the stream under the mutex.
+    if unsafe { libc::atexit(flush_trace) } != 0 {
+        process::abort();
+    }
+    Some(Mutex::new(Trace { file, next_ticket: 0 }))
+}
+
+extern "C" fn flush_trace() {
+    if let Some(Some(trace)) = TRACE.get() {
+        let trace = trace.lock().unwrap_or_else(|_| process::abort());
+        // SAFETY: This is an open FILE held under its mutex.
+        if unsafe { libc::fflush(trace.file.as_ptr()) } != 0 {
+            process::abort();
+        }
+    }
+}
+
+#[unsafe(no_mangle)]
+extern "C" fn uv_vec_event(operation: u64, pointer: *const u8, capacity: usize, mut detail: u64) -> u64 {
+    let Some(trace) = TRACE.get_or_init(open_trace) else {
+        return 0;
+    };
+    let mut trace = trace.lock().unwrap_or_else(|_| process::abort());
+    if operation == u64::from(b'b') {
+        trace.next_ticket += 1;
+        detail = trace.next_ticket;
+    }
+    let event = [pointer.addr() as u64, capacity as u64, detail, operation].map(u64::to_le);
+    // SAFETY: event is initialized and the open FILE is held under its mutex.
+    let written = unsafe {
+        libc::fwrite(event.as_ptr().cast(), size_of_val(&event), 1, trace.file.as_ptr())
+    };
+    if written != 1 {
+        process::abort();
+    }
+    detail
+}
