1use std::{debug_assert, io::Write, sync::Arc, thread::JoinHandle, time::Duration};
2
3use crossbeam_channel::{Receiver, RecvTimeoutError, Sender, TryRecvError, bounded, unbounded};
4use crossbeam_utils::CachePadded;
5use parking_lot::{Mutex, MutexGuard};
6use thread_local::ThreadLocal;
7
8use crate::tracing::{DeltaEncodedTimestamp, TRACE_HEADER, TimestampEncoder, TraceRow};
9
10type ThreadLocalState = CachePadded<Mutex<Option<TraceInfoBuffer>>>;
11
12pub(crate) const THREAD_LOCAL_INITIAL_BUFFER_SIZE: usize = 1024 * 1024;
15const WRITE_BUFFER_SIZE: usize = 100 * 1024 * 1024;
17
18struct TraceInfoBuffer {
19 buffer: Vec<u8>,
20 last_row_marker: Option<u64>,
22 last_row_start: usize,
24 last_row_end: usize,
27 timestamps: TimestampEncoder,
30 last_row_timestamps: TimestampEncoder,
32}
33
34impl TraceInfoBuffer {
35 fn new(capacity: usize) -> Self {
36 Self {
37 buffer: Vec::with_capacity(capacity),
38 last_row_marker: None,
39 last_row_start: 0,
40 last_row_end: 0,
41 timestamps: TimestampEncoder::default(),
42 last_row_timestamps: TimestampEncoder::default(),
43 }
44 }
45
46 fn push(&mut self, data: u8) {
47 self.buffer.push(data);
48 }
49
50 fn extend(&mut self, data: &[u8]) {
51 self.buffer.extend_from_slice(data);
52 }
53
54 fn clear(&mut self) {
55 self.buffer.clear();
56 self.last_row_marker = None;
57 self.last_row_start = 0;
58 self.last_row_end = 0;
59 self.timestamps = TimestampEncoder::default();
60 self.last_row_timestamps = TimestampEncoder::default();
61 }
62
63 fn write_timestamp_base(&mut self, ts: u64) {
64 let row: TraceRow<'_> = TraceRow::TimestampBase { ts };
65 self.buffer = postcard::to_extend(&row, std::mem::take(&mut self.buffer)).unwrap();
66 }
67}
68
69#[derive(Clone)]
70pub struct TraceWriter {
71 data_tx: Sender<Option<TraceInfoBuffer>>,
72 return_rx: Receiver<TraceInfoBuffer>,
73 thread_locals: Arc<ThreadLocal<ThreadLocalState>>,
74}
75
76impl TraceWriter {
77 pub fn new<W: Write + Send + 'static>(mut writer: W) -> (Self, TraceWriterGuard) {
86 let (data_tx, data_rx) = unbounded::<Option<TraceInfoBuffer>>();
87 let (return_tx, return_rx) = bounded::<TraceInfoBuffer>(1024);
88 let thread_locals: Arc<ThreadLocal<ThreadLocalState>> = Default::default();
89
90 let trace_writer = Self {
91 data_tx: data_tx.clone(),
92 return_rx: return_rx.clone(),
93 thread_locals: thread_locals.clone(),
94 };
95
96 fn steal_from_thread_locals(
97 thread_locals: &Arc<ThreadLocal<ThreadLocalState>>,
98 stolen_buffers: &mut Vec<TraceInfoBuffer>,
99 ) {
100 for state in thread_locals.iter() {
101 let mut buffer = state.lock();
102 if buffer
105 .as_ref()
106 .is_some_and(|buffer| !buffer.buffer.is_empty())
107 && let Some(buffer) = buffer.take()
108 {
109 stolen_buffers.push(buffer);
110 }
111 }
112 }
113
114 let handle: std::thread::JoinHandle<()> = std::thread::spawn(move || {
115 let _ = writer.write(TRACE_HEADER);
116 let mut buf = Vec::with_capacity(WRITE_BUFFER_SIZE);
117 let mut stolen_buffers = Vec::new();
118 let mut should_exit = false;
119 'outer: loop {
120 if !buf.is_empty() {
121 let _ = writer.write_all(&buf);
122 let _ = writer.flush();
123 buf.clear();
124 }
125
126 let recv = if should_exit {
127 Ok(None)
128 } else {
129 data_rx.recv_timeout(Duration::from_secs(1))
130 };
131
132 let mut data = match recv {
133 Ok(Some(data)) => data,
134 result => {
135 if result.is_ok() {
136 should_exit = true;
138 }
139 steal_from_thread_locals(&thread_locals, &mut stolen_buffers);
144 if let Some(data) = stolen_buffers.pop() {
145 data
146 } else {
147 match result {
148 Ok(Some(_)) => unreachable!(),
149 Ok(None) | Err(RecvTimeoutError::Disconnected) => {
150 break 'outer;
152 }
153 Err(RecvTimeoutError::Timeout) => {
154 continue;
156 }
157 }
158 }
159 }
160 };
161 if data.buffer.len() > buf.capacity() {
162 let _ = writer.write_all(&data.buffer);
163 } else {
164 buf.extend_from_slice(&data.buffer);
165 }
166 data.clear();
167 let _ = return_tx.try_send(data);
168 loop {
169 let recv = stolen_buffers.pop().map(Some).ok_or(()).or_else(|_| {
170 if should_exit {
171 Ok(None)
172 } else {
173 data_rx.try_recv()
174 }
175 });
176 match recv {
177 Ok(Some(mut data)) => {
178 let data_buffer = &data.buffer;
179 if data_buffer.is_empty() {
180 break 'outer;
181 }
182 if buf.len() + data_buffer.len() > buf.capacity() {
183 let _ = writer.write_all(&buf);
184 buf.clear();
185 if data_buffer.len() > buf.capacity() {
186 let _ = writer.write_all(data_buffer);
187 } else {
188 buf.extend_from_slice(data_buffer);
189 }
190 } else {
191 buf.extend_from_slice(data_buffer);
192 }
193 data.clear();
194 let _ = return_tx.try_send(data);
195 }
196 Ok(None) | Err(TryRecvError::Disconnected) => {
197 should_exit = true;
198 break;
199 }
200 Err(TryRecvError::Empty) => {
201 break;
202 }
203 }
204 }
205 }
206 drop(writer);
207 });
208
209 let guard = TraceWriterGuard {
210 data_tx: Some(data_tx),
211 return_rx: Some(return_rx),
212 handle: Some(handle),
213 };
214 (trace_writer, guard)
215 }
216
217 fn send(&self, data: TraceInfoBuffer) {
218 debug_assert!(!data.buffer.is_empty());
219 let _ = self.data_tx.send(Some(data));
220 }
221
222 fn get_empty_buffer(&self, capacity: usize) -> TraceInfoBuffer {
223 self.return_rx
224 .try_recv()
225 .ok()
226 .unwrap_or_else(|| TraceInfoBuffer::new(capacity))
227 }
228
229 pub fn start_write(&self) -> WriteGuard<'_> {
230 let thread_local_buffer = self.thread_locals.get_or_default();
231 let buffer = thread_local_buffer.lock();
232 WriteGuard::new(buffer, self)
233 }
234}
235
236pub struct TraceWriterGuard {
237 data_tx: Option<Sender<Option<TraceInfoBuffer>>>,
238 return_rx: Option<Receiver<TraceInfoBuffer>>,
239 handle: Option<JoinHandle<()>>,
240}
241
242impl Drop for TraceWriterGuard {
243 fn drop(&mut self) {
244 let _ = self.data_tx.take().unwrap().send(None);
246 let return_rx = self.return_rx.take().unwrap();
248 while return_rx.recv().is_ok() {}
249 let _ = self.handle.take().unwrap().join();
251 }
252}
253
254pub struct WriteGuard<'l> {
255 buffer: MutexGuard<'l, Option<TraceInfoBuffer>>,
257 trace_writer: &'l TraceWriter,
258}
259
260impl<'l> WriteGuard<'l> {
261 fn new(
262 mut buffer: MutexGuard<'l, Option<TraceInfoBuffer>>,
263 trace_writer: &'l TraceWriter,
264 ) -> Self {
265 buffer
267 .get_or_insert_with(|| trace_writer.get_empty_buffer(THREAD_LOCAL_INITIAL_BUFFER_SIZE));
268 Self {
269 buffer,
270 trace_writer,
271 }
272 }
273
274 fn buffer(&mut self) -> &mut TraceInfoBuffer {
275 unsafe { self.buffer.as_mut().unwrap_unchecked() }
277 }
278
279 pub fn push(&mut self, data: u8) {
280 self.buffer().push(data);
281 }
282
283 pub fn extend(&mut self, data: &[u8]) {
284 self.buffer().extend(data);
285 }
286
287 pub fn encode_timestamp(&mut self, ts: u64) -> DeltaEncodedTimestamp {
293 let buffer = self.buffer();
294 let (base, encoded) = buffer.timestamps.encode(ts);
295 if let Some(ts) = base {
296 buffer.write_timestamp_base(ts);
297 }
298 encoded
299 }
300
301 pub fn mark(&mut self, marker: u64, write: impl FnOnce(&mut Self)) {
305 let buffer = self.buffer();
306 let start = buffer.buffer.len();
307 let timestamps = buffer.timestamps;
308 write(self);
309 let buffer = self.buffer();
310 buffer.last_row_marker = Some(marker);
311 buffer.last_row_start = start;
312 buffer.last_row_end = buffer.buffer.len();
313 buffer.last_row_timestamps = timestamps;
314 }
315
316 pub fn remove_last_row(&mut self, marker: u64) -> bool {
321 let buffer = self.buffer();
322 if buffer.last_row_marker != Some(marker) || buffer.last_row_end != buffer.buffer.len() {
323 return false;
324 }
325 buffer.last_row_marker = None;
326 buffer.buffer.truncate(buffer.last_row_start);
327 buffer.timestamps = buffer.last_row_timestamps;
328 true
329 }
330}
331
332impl Drop for WriteGuard<'_> {
333 fn drop(&mut self) {
334 if self.buffer().buffer.capacity() * 2 < self.buffer().buffer.len() * 3 {
335 let capacity = self.buffer().buffer.capacity();
336 let new_buffer = self.trace_writer.get_empty_buffer(capacity);
337 let buffer = std::mem::replace(self.buffer(), new_buffer);
338 self.trace_writer.send(buffer);
339 }
340 }
341}
342
343#[cfg(test)]
344mod tests {
345 use std::{
346 io::{self, Write},
347 sync::{Arc, Mutex},
348 };
349
350 use crate::{
351 trace_writer::{THREAD_LOCAL_INITIAL_BUFFER_SIZE, TraceWriter, WriteGuard},
352 tracing::{TRACE_HEADER, TimestampDecoder, TraceRow},
353 };
354
355 #[derive(Clone, Default)]
356 struct SharedBuffer(Arc<Mutex<Vec<u8>>>);
357
358 impl Write for SharedBuffer {
359 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
360 self.0.lock().unwrap().extend_from_slice(buf);
361 Ok(buf.len())
362 }
363
364 fn flush(&mut self) -> io::Result<()> {
365 Ok(())
366 }
367 }
368
369 fn with_writer(f: impl FnOnce(&TraceWriter)) -> Vec<u8> {
370 let buffer = SharedBuffer::default();
371 let (writer, guard) = TraceWriter::new(buffer.clone());
372 f(&writer);
373 drop(writer);
374 drop(guard);
375 let data = Arc::try_unwrap(buffer.0).unwrap().into_inner().unwrap();
376 data.strip_prefix(TRACE_HEADER).unwrap().to_vec()
377 }
378
379 #[test]
380 fn removes_marked_last_row() {
381 let data = with_writer(|writer| {
382 writer.start_write().extend(b"aa");
383 writer.start_write().mark(1, |guard| guard.extend(b"bbb"));
384 let mut guard = writer.start_write();
385 assert!(guard.remove_last_row(1));
386 guard.extend(b"c");
387 });
388 assert_eq!(data, b"aac");
389 }
390
391 #[test]
394 fn marks_only_the_callback() {
395 let data = with_writer(|writer| {
396 let mut guard = writer.start_write();
397 guard.extend(b"aa");
398 guard.mark(1, |guard| guard.extend(b"bbb"));
399 drop(guard);
400 let mut guard = writer.start_write();
401 assert!(guard.remove_last_row(1));
402 guard.extend(b"c");
403 });
404 assert_eq!(data, b"aac");
405
406 let data = with_writer(|writer| {
407 let mut guard = writer.start_write();
408 guard.mark(1, |guard| guard.extend(b"a"));
409 guard.extend(b"b");
410 drop(guard);
411 assert!(!writer.start_write().remove_last_row(1));
412 });
413 assert_eq!(data, b"ab");
414 }
415
416 #[test]
417 fn requires_a_matching_marker() {
418 let data = with_writer(|writer| {
419 writer.start_write().mark(1, |guard| guard.extend(b"a"));
420 assert!(!writer.start_write().remove_last_row(2));
421 assert!(writer.start_write().remove_last_row(1));
423 assert!(!writer.start_write().remove_last_row(1));
425 });
426 assert_eq!(data, b"");
427 }
428
429 #[test]
430 fn marker_is_invalidated_by_the_next_write() {
431 let data = with_writer(|writer| {
432 writer.start_write().mark(1, |guard| guard.extend(b"a"));
433 writer.start_write().extend(b"b");
434 assert!(!writer.start_write().remove_last_row(1));
435 });
436 assert_eq!(data, b"ab");
437 }
438
439 #[test]
440 fn marker_is_dropped_when_buffer_is_sent() {
441 let fill = THREAD_LOCAL_INITIAL_BUFFER_SIZE * 2 / 3 - 10;
443 let data = with_writer(|writer| {
444 writer.start_write().extend(&vec![0; fill]);
445 writer.start_write().mark(1, |guard| guard.extend(&[1; 20]));
447 assert!(!writer.start_write().remove_last_row(1));
448 });
449 assert_eq!(data.len(), fill + 20);
450 }
451
452 #[test]
453 fn removing_the_only_row_leaves_an_empty_buffer() {
454 let data = with_writer(|writer| {
455 writer.start_write().mark(1, |guard| guard.extend(b"a"));
456 assert!(writer.start_write().remove_last_row(1));
457 });
458 assert_eq!(data, b"");
459 }
460
461 fn decode_rows(mut data: &[u8]) -> Vec<TraceRow<'_, u64>> {
463 let mut decoder = TimestampDecoder::default();
464 let mut rows = Vec::new();
465 while !data.is_empty() {
466 let (row, rest): (TraceRow<'_>, _) = postcard::take_from_bytes(data).unwrap();
467 rows.push(decoder.decode(row).unwrap());
468 data = rest;
469 }
470 rows
471 }
472
473 fn write_end(guard: &mut WriteGuard<'_>, ts: u64) {
475 let ts = guard.encode_timestamp(ts);
476 let row = TraceRow::End { ts, id: 1 };
477 guard.extend(&postcard::to_stdvec(&row).unwrap());
478 }
479
480 fn timeline(rows: &[TraceRow<'_, u64>]) -> Vec<(&'static str, u64)> {
481 rows.iter()
482 .map(|row| match row {
483 TraceRow::TimestampBase { ts } => ("base", *ts),
484 TraceRow::End { ts, .. } => ("end", *ts),
485 _ => unreachable!(),
486 })
487 .collect()
488 }
489
490 #[test]
491 fn timestamps_are_deltas_to_a_base_per_buffer() {
492 let data = with_writer(|writer| {
493 write_end(&mut writer.start_write(), 100);
494 write_end(&mut writer.start_write(), 105);
495 write_end(&mut writer.start_write(), 103);
496 });
497 let rows = decode_rows(&data);
498 assert_eq!(
499 timeline(&rows),
500 [("base", 100), ("end", 100), ("end", 105), ("end", 103)]
501 );
502 let (_, rest): (TraceRow<'_>, _) = postcard::take_from_bytes(&data).unwrap();
504 let wire: Vec<i64> = {
505 let mut rest = rest;
506 let mut wire = Vec::new();
507 while !rest.is_empty() {
508 let (row, r): (TraceRow<'_>, _) = postcard::take_from_bytes(rest).unwrap();
509 if let TraceRow::End { ts, .. } = row {
510 wire.push(ts.0);
511 }
512 rest = r;
513 }
514 wire
515 };
516 assert_eq!(wire, [0, 5, -2]);
517 }
518
519 #[test]
520 fn removing_a_row_restores_the_previous_timestamp() {
521 let data = with_writer(|writer| {
522 write_end(&mut writer.start_write(), 100);
523 writer.start_write().mark(1, |guard| write_end(guard, 150));
524 assert!(writer.start_write().remove_last_row(1));
525 write_end(&mut writer.start_write(), 120);
526 });
527 assert_eq!(
528 timeline(&decode_rows(&data)),
529 [("base", 100), ("end", 100), ("end", 120)]
530 );
531 }
532
533 #[test]
534 fn removing_the_first_row_after_the_base() {
535 let data = with_writer(|writer| {
536 writer.start_write().mark(1, |guard| write_end(guard, 100));
538 assert!(writer.start_write().remove_last_row(1));
539 write_end(&mut writer.start_write(), 120);
540 write_end(&mut writer.start_write(), 125);
541 });
542 assert_eq!(
543 timeline(&decode_rows(&data)),
544 [("base", 120), ("end", 120), ("end", 125)]
545 );
546 }
547
548 #[test]
549 fn stolen_buffer_starts_a_new_base() {
550 let data = with_writer(|writer| {
551 write_end(&mut writer.start_write(), 100);
552 writer.start_write().mark(1, |guard| write_end(guard, 110));
553 std::thread::sleep(std::time::Duration::from_millis(2500));
556 let mut guard = writer.start_write();
557 assert!(!guard.remove_last_row(1));
558 write_end(&mut guard, 105);
559 });
560 assert_eq!(
561 timeline(&decode_rows(&data)),
562 [
563 ("base", 100),
564 ("end", 100),
565 ("end", 110),
566 ("base", 105),
567 ("end", 105)
568 ]
569 );
570 }
571
572 #[test]
573 fn out_of_range_delta_writes_a_new_base() {
574 let data = with_writer(|writer| {
575 write_end(&mut writer.start_write(), 0);
576 write_end(&mut writer.start_write(), u64::MAX);
577 write_end(&mut writer.start_write(), u64::MAX - 1);
578 });
579 let mut rest = data.as_slice();
580 let mut wire = Vec::new();
581 while !rest.is_empty() {
582 let (row, r): (TraceRow<'_>, _) = postcard::take_from_bytes(rest).unwrap();
583 wire.push(match row {
584 TraceRow::TimestampBase { ts } => ("base", i128::from(ts)),
585 TraceRow::End { ts, .. } => ("end", i128::from(ts.0)),
586 _ => unreachable!(),
587 });
588 rest = r;
589 }
590 assert_eq!(
591 wire,
592 [
593 ("base", 0),
594 ("end", 0),
595 ("base", i128::from(u64::MAX)),
596 ("end", 0),
597 ("end", -1)
598 ]
599 );
600 assert_eq!(
601 timeline(&decode_rows(&data)),
602 [
603 ("base", 0),
604 ("end", 0),
605 ("base", u64::MAX),
606 ("end", u64::MAX),
607 ("end", u64::MAX - 1)
608 ]
609 );
610 }
611
612 #[test]
613 fn rows_without_timestamp_keep_the_previous_timestamp() {
614 let data = with_writer(|writer| {
615 write_end(&mut writer.start_write(), 100);
616 let mut guard = writer.start_write();
617 let record: TraceRow<'_> = TraceRow::Record {
618 id: 1,
619 values: Vec::new(),
620 };
621 guard.extend(&postcard::to_stdvec(&record).unwrap());
622 drop(guard);
623 write_end(&mut writer.start_write(), 90);
624 });
625 let rows = decode_rows(&data);
626 let timeline: Vec<_> = rows
627 .iter()
628 .map(|row| match row {
629 TraceRow::TimestampBase { ts } => ("base", *ts),
630 TraceRow::End { ts, .. } => ("end", *ts),
631 TraceRow::Record { .. } => ("record", 0),
632 _ => unreachable!(),
633 })
634 .collect();
635 assert_eq!(
637 timeline,
638 [("base", 100), ("end", 100), ("record", 0), ("end", 90)]
639 );
640 }
641
642 #[test]
643 fn buffer_starting_with_a_row_without_timestamp_has_no_base_before_it() {
644 let data = with_writer(|writer| {
645 let mut guard = writer.start_write();
646 let record: TraceRow<'_> = TraceRow::Record {
647 id: 1,
648 values: Vec::new(),
649 };
650 guard.extend(&postcard::to_stdvec(&record).unwrap());
651 drop(guard);
652 write_end(&mut writer.start_write(), 60);
653 });
654 let rows = decode_rows(&data);
655 assert!(matches!(rows[0], TraceRow::Record { .. }));
657 assert!(matches!(rows[1], TraceRow::TimestampBase { ts: 60 }));
658 assert!(matches!(rows[2], TraceRow::End { ts: 60, .. }));
659 assert_eq!(rows.len(), 3);
660 }
661
662 #[test]
663 fn sent_buffer_starts_a_new_base() {
664 let fill = THREAD_LOCAL_INITIAL_BUFFER_SIZE * 2 / 3 - 10;
666 let data = with_writer(|writer| {
667 write_end(&mut writer.start_write(), 100);
668 let mut guard = writer.start_write();
669 guard.mark(1, |guard| write_end(guard, 110));
670 let filler: TraceRow<'_> = TraceRow::Record {
672 id: 1,
673 values: vec![(
674 "filler".into(),
675 crate::tracing::TraceValue::String("x".repeat(fill).into()),
676 )],
677 };
678 guard.extend(&postcard::to_stdvec(&filler).unwrap());
679 drop(guard);
680 assert!(!writer.start_write().remove_last_row(1));
682 write_end(&mut writer.start_write(), 105);
683 });
684 let rows = decode_rows(&data);
685 let timeline: Vec<_> = rows
686 .iter()
687 .filter_map(|row| match row {
688 TraceRow::TimestampBase { ts } => Some(("base", *ts)),
689 TraceRow::End { ts, .. } => Some(("end", *ts)),
690 _ => None,
691 })
692 .collect();
693 assert_eq!(
694 timeline,
695 [
696 ("base", 100),
697 ("end", 100),
698 ("end", 110),
699 ("base", 105),
700 ("end", 105)
701 ]
702 );
703 }
704}