Skip to main content

kui_trace/
codec.rs

1//! The bytes (ADR 0058, decision 3): `KUITRACE`, a version, then records,
2//! each a tag and a length before its fields. Integers are LEB128
3//! varints; a time is the zigzagged difference from the last time written,
4//! so a span written as it closes, starting before the record before it,
5//! costs a byte or two like any other. A reader skips a tag it does not
6//! know by its length, and reads a file written as it records up to where
7//! it stopped.
8
9use std::io::{self, Write};
10
11use crate::record::{LoopEvent, NameId, Origin, Record, Session, Span, Track};
12
13/// The first eight bytes.
14pub const MAGIC: &[u8; 8] = b"KUITRACE";
15/// The format's version, after the magic, little-endian.
16pub const VERSION: u16 = 1;
17
18/// What went wrong reading.
19#[derive(Clone, Debug, PartialEq, Eq)]
20pub enum Error {
21    /// Not a recording: the magic is missing.
22    NotATrace,
23    /// A version this reader does not read.
24    Version(u16),
25}
26
27impl std::fmt::Display for Error {
28    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29        match self {
30            Error::NotATrace => write!(f, "not a kui recording (no KUITRACE header)"),
31            Error::Version(v) => write!(f, "a version {v} recording; this reads {VERSION}"),
32        }
33    }
34}
35
36impl std::error::Error for Error {}
37
38mod tag {
39    pub const SESSION: u8 = 1;
40    pub const NAME: u8 = 2;
41    pub const TRACK: u8 = 3;
42    pub const FRAME_BEGIN: u8 = 4;
43    pub const HOLDER: u8 = 5;
44    pub const FRAME_END: u8 = 6;
45    pub const PRESENT: u8 = 7;
46    pub const SPAN: u8 = 8;
47    pub const INPUT: u8 = 9;
48    pub const EVENT: u8 = 10;
49    pub const WAKE: u8 = 11;
50    pub const REQUEST: u8 = 12;
51    pub const LOOP: u8 = 13;
52    pub const COUNTER: u8 = 14;
53    pub const GAUGE: u8 = 15;
54    pub const MARK: u8 = 16;
55    pub const NOTE: u8 = 17;
56    pub const PLACEMENT: u8 = 18;
57}
58
59fn put_u(out: &mut Vec<u8>, mut v: u64) {
60    loop {
61        let b = (v & 0x7f) as u8;
62        v >>= 7;
63        if v == 0 {
64            out.push(b);
65            return;
66        }
67        out.push(b | 0x80);
68    }
69}
70
71fn put_i(out: &mut Vec<u8>, v: i64) {
72    put_u(out, ((v << 1) ^ (v >> 63)) as u64);
73}
74
75fn put_str(out: &mut Vec<u8>, s: &str) {
76    put_u(out, s.len() as u64);
77    out.extend_from_slice(s.as_bytes());
78}
79
80fn put_opt(out: &mut Vec<u8>, v: Option<u64>) {
81    put_u(out, v.map_or(0, |v| v + 1));
82}
83
84/// Encodes records one after another, keeping the last time written.
85#[derive(Default)]
86pub struct Encoder {
87    prev_t: u64,
88    body: Vec<u8>,
89}
90
91impl Encoder {
92    pub fn new() -> Self {
93        Self::default()
94    }
95
96    fn t(&mut self, t: u64) {
97        put_i(&mut self.body, t as i64 - self.prev_t as i64);
98        self.prev_t = t;
99    }
100
101    /// `r` as bytes: its tag, its length and its fields, appended to `out`.
102    pub fn encode(&mut self, r: &Record, out: &mut Vec<u8>) {
103        self.body.clear();
104        let tag = match r {
105            Record::Session(s) => {
106                put_str(&mut self.body, &s.kui);
107                put_str(&mut self.body, &s.app);
108                put_str(&mut self.body, &s.platform);
109                self.body.extend_from_slice(&s.refresh_hz.to_le_bytes());
110                self.body.extend_from_slice(&s.budget_ms.to_le_bytes());
111                self.body.extend_from_slice(&s.scale.to_le_bytes());
112                put_u(&mut self.body, s.wall_ms);
113                tag::SESSION
114            }
115            Record::Name { id, text } => {
116                put_u(&mut self.body, *id as u64);
117                put_str(&mut self.body, text);
118                tag::NAME
119            }
120            Record::Track { id, name } => {
121                put_u(&mut self.body, id.0 as u64);
122                put_u(&mut self.body, *name as u64);
123                tag::TRACK
124            }
125            Record::FrameBegin { t, frame, causes } => {
126                self.t(*t);
127                put_u(&mut self.body, *frame);
128                put_u(&mut self.body, causes.len() as u64);
129                for c in causes {
130                    put_u(&mut self.body, *c as u64);
131                }
132                tag::FRAME_BEGIN
133            }
134            Record::Holder {
135                frame,
136                what,
137                name,
138                node,
139            } => {
140                put_u(&mut self.body, *frame);
141                put_u(&mut self.body, *what as u64);
142                put_u(&mut self.body, *name as u64);
143                put_u(&mut self.body, *node);
144                tag::HOLDER
145            }
146            Record::FrameEnd {
147                t,
148                frame,
149                unchanged,
150                nodes,
151            } => {
152                self.t(*t);
153                put_u(&mut self.body, *frame);
154                put_u(
155                    &mut self.body,
156                    match unchanged {
157                        None => 0,
158                        Some(false) => 1,
159                        Some(true) => 2,
160                    },
161                );
162                put_u(&mut self.body, *nodes as u64);
163                tag::FRAME_END
164            }
165            Record::Present {
166                t,
167                frame,
168                render,
169                wait,
170            } => {
171                self.t(*t);
172                put_u(&mut self.body, *frame);
173                put_u(&mut self.body, *render);
174                put_u(&mut self.body, *wait);
175                tag::PRESENT
176            }
177            Record::Span(s) => {
178                self.t(s.start);
179                put_u(&mut self.body, s.track.0 as u64);
180                put_u(&mut self.body, s.id as u64);
181                put_u(&mut self.body, s.parent as u64);
182                put_u(&mut self.body, s.name as u64);
183                match s.origin {
184                    Origin::Kui => put_u(&mut self.body, 0),
185                    Origin::Host => put_u(&mut self.body, 1),
186                    Origin::Extension(ns) => put_u(&mut self.body, 2 + ns as u64),
187                }
188                put_u(&mut self.body, s.dur);
189                put_opt(&mut self.body, s.cpu);
190                put_u(&mut self.body, s.node);
191                put_u(&mut self.body, s.cut as u64);
192                tag::SPAN
193            }
194            Record::Input {
195                t,
196                kind,
197                os_t,
198                count,
199                until,
200            } => {
201                self.t(*t);
202                put_u(&mut self.body, *kind as u64);
203                put_opt(&mut self.body, *os_t);
204                put_u(&mut self.body, *count as u64);
205                put_u(&mut self.body, until.saturating_sub(*t));
206                tag::INPUT
207            }
208            Record::Event { t, kind, node } => {
209                self.t(*t);
210                put_u(&mut self.body, *kind as u64);
211                put_u(&mut self.body, *node);
212                tag::EVENT
213            }
214            Record::Wake { t, name } => {
215                self.t(*t);
216                put_u(&mut self.body, *name as u64);
217                tag::WAKE
218            }
219            Record::Request { t, why, at } => {
220                self.t(*t);
221                put_u(&mut self.body, *why as u64);
222                put_u(&mut self.body, *at as u64);
223                tag::REQUEST
224            }
225            Record::Loop { t, event } => {
226                self.t(*t);
227                match event {
228                    LoopEvent::Sleep { until } => {
229                        put_u(&mut self.body, 0);
230                        put_opt(&mut self.body, *until);
231                    }
232                    LoopEvent::Woke { by } => {
233                        put_u(&mut self.body, 1);
234                        put_u(&mut self.body, *by as u64);
235                    }
236                    LoopEvent::Os { kind } => {
237                        put_u(&mut self.body, 2);
238                        put_u(&mut self.body, *kind as u64);
239                    }
240                }
241                tag::LOOP
242            }
243            Record::Counter {
244                t,
245                frame,
246                name,
247                value,
248            } => {
249                self.t(*t);
250                put_u(&mut self.body, *frame);
251                put_u(&mut self.body, *name as u64);
252                put_i(&mut self.body, *value);
253                tag::COUNTER
254            }
255            Record::Gauge { t, name, value } => {
256                self.t(*t);
257                put_u(&mut self.body, *name as u64);
258                self.body.extend_from_slice(&value.to_le_bytes());
259                tag::GAUGE
260            }
261            Record::Mark { t, track, name } => {
262                self.t(*t);
263                put_u(&mut self.body, track.0 as u64);
264                put_u(&mut self.body, *name as u64);
265                tag::MARK
266            }
267            Record::Note { t, text } => {
268                self.t(*t);
269                put_str(&mut self.body, text);
270                tag::NOTE
271            }
272            Record::Placement {
273                t,
274                node,
275                component,
276                answer,
277                why,
278            } => {
279                self.t(*t);
280                put_u(&mut self.body, *node);
281                put_u(&mut self.body, *component as u64);
282                put_u(&mut self.body, *answer as u64);
283                put_u(&mut self.body, *why as u64);
284                tag::PLACEMENT
285            }
286        };
287        out.push(tag);
288        put_u(out, self.body.len() as u64);
289        out.extend_from_slice(&self.body);
290    }
291}
292
293/// The header every recording opens with.
294pub fn header() -> Vec<u8> {
295    let mut h = MAGIC.to_vec();
296    h.extend_from_slice(&VERSION.to_le_bytes());
297    h
298}
299
300/// Records written to `W` as they come — `KUI_PERF_TRACE`'s stream.
301pub struct Writer<W: Write> {
302    out: W,
303    enc: Encoder,
304    buf: Vec<u8>,
305}
306
307impl<W: Write> Writer<W> {
308    /// A writer that has written the header.
309    pub fn new(mut out: W) -> io::Result<Self> {
310        out.write_all(&header())?;
311        Ok(Writer {
312            out,
313            enc: Encoder::new(),
314            buf: Vec::new(),
315        })
316    }
317
318    pub fn write(&mut self, r: &Record) -> io::Result<()> {
319        self.buf.clear();
320        self.enc.encode(r, &mut self.buf);
321        self.out.write_all(&self.buf)
322    }
323
324    pub fn flush(&mut self) -> io::Result<()> {
325        self.out.flush()
326    }
327
328    pub fn into_inner(self) -> W {
329        self.out
330    }
331}
332
333struct Cursor<'a> {
334    b: &'a [u8],
335    at: usize,
336}
337
338impl Cursor<'_> {
339    fn u(&mut self) -> Option<u64> {
340        let mut v = 0u64;
341        let mut shift = 0;
342        loop {
343            let byte = *self.b.get(self.at)?;
344            self.at += 1;
345            if shift < 64 {
346                v |= ((byte & 0x7f) as u64) << shift;
347            }
348            if byte & 0x80 == 0 {
349                return Some(v);
350            }
351            shift += 7;
352        }
353    }
354
355    fn i(&mut self) -> Option<i64> {
356        let u = self.u()?;
357        Some(((u >> 1) as i64) ^ -((u & 1) as i64))
358    }
359
360    fn u32(&mut self) -> Option<u32> {
361        self.u().map(|v| v as u32)
362    }
363
364    fn opt(&mut self) -> Option<Option<u64>> {
365        self.u().map(|v| v.checked_sub(1))
366    }
367
368    fn bytes(&mut self, n: usize) -> Option<&[u8]> {
369        let end = self.at.checked_add(n)?;
370        let s = self.b.get(self.at..end)?;
371        self.at = end;
372        Some(s)
373    }
374
375    fn str(&mut self) -> Option<String> {
376        let n = self.u()? as usize;
377        self.bytes(n)
378            .map(|b| String::from_utf8_lossy(b).into_owned())
379    }
380
381    fn f32(&mut self) -> Option<f32> {
382        self.bytes(4)
383            .map(|b| f32::from_le_bytes(b.try_into().unwrap()))
384    }
385
386    fn f64(&mut self) -> Option<f64> {
387        self.bytes(8)
388            .map(|b| f64::from_le_bytes(b.try_into().unwrap()))
389    }
390}
391
392/// Reads records back, in order.
393pub struct Reader<'a> {
394    b: &'a [u8],
395    at: usize,
396    dec: Decoder,
397}
398
399/// What decoding needs to carry from one record to the next: the last
400/// time read, since times are written as deltas.
401#[derive(Clone, Copy, Debug, Default)]
402pub(crate) struct Decoder {
403    prev_t: u64,
404}
405
406/// Where the record at `at` in `b` is: its tag, and its body's range; `None`
407/// while `b` holds less than the whole of it.
408pub(crate) fn framed(b: &[u8], at: usize) -> Option<(u8, std::ops::Range<usize>)> {
409    let tag = *b.get(at)?;
410    let mut c = Cursor { b, at: at + 1 };
411    let len = c.u()? as usize;
412    let end = c.at.checked_add(len)?;
413    (end <= b.len()).then_some((tag, c.at..end))
414}
415
416/// Checks the header at the front of `b`: the bytes it takes, or why they
417/// are not a recording; `Ok(None)` while fewer than a header's bytes came.
418pub(crate) fn check_header(b: &[u8]) -> Result<Option<usize>, Error> {
419    if b.len() < 10 {
420        return if MAGIC.starts_with(&b[..b.len().min(8)]) {
421            Ok(None)
422        } else {
423            Err(Error::NotATrace)
424        };
425    }
426    if &b[..8] != MAGIC {
427        return Err(Error::NotATrace);
428    }
429    let v = u16::from_le_bytes([b[8], b[9]]);
430    if v != VERSION {
431        return Err(Error::Version(v));
432    }
433    Ok(Some(10))
434}
435
436impl<'a> Reader<'a> {
437    /// A reader past the header, or why the bytes are not a recording.
438    pub fn new(b: &'a [u8]) -> Result<Self, Error> {
439        let at = check_header(b)?.ok_or(Error::NotATrace)?;
440        Ok(Reader {
441            b,
442            at,
443            dec: Decoder::default(),
444        })
445    }
446}
447
448impl Decoder {
449    fn t(&mut self, c: &mut Cursor<'_>) -> Option<u64> {
450        let d = c.i()?;
451        self.prev_t = (self.prev_t as i64).saturating_add(d).max(0) as u64;
452        Some(self.prev_t)
453    }
454
455    /// The record a tag and its body make; `Some(None)` for a tag from a
456    /// later version, skipped; `None` for a body shorter than its fields.
457    pub(crate) fn decode(&mut self, tag: u8, body: &[u8]) -> Option<Option<Record>> {
458        let mut c = Cursor { b: body, at: 0 };
459        let r = match tag {
460            tag::SESSION => Record::Session(Session {
461                kui: c.str()?,
462                app: c.str()?,
463                platform: c.str()?,
464                refresh_hz: c.f32()?,
465                budget_ms: c.f32()?,
466                scale: c.f32()?,
467                wall_ms: c.u()?,
468            }),
469            tag::NAME => Record::Name {
470                id: c.u32()?,
471                text: c.str()?,
472            },
473            tag::TRACK => Record::Track {
474                id: Track(c.u32()?),
475                name: c.u32()?,
476            },
477            tag::FRAME_BEGIN => {
478                let t = self.t(&mut c)?;
479                let frame = c.u()?;
480                let n = c.u()? as usize;
481                let mut causes = Vec::with_capacity(n.min(64));
482                for _ in 0..n {
483                    causes.push(c.u32()?);
484                }
485                Record::FrameBegin { t, frame, causes }
486            }
487            tag::HOLDER => Record::Holder {
488                frame: c.u()?,
489                what: c.u32()?,
490                name: c.u32()?,
491                node: c.u()?,
492            },
493            tag::FRAME_END => Record::FrameEnd {
494                t: self.t(&mut c)?,
495                frame: c.u()?,
496                unchanged: match c.u()? {
497                    1 => Some(false),
498                    2 => Some(true),
499                    _ => None,
500                },
501                nodes: c.u32()?,
502            },
503            tag::PRESENT => Record::Present {
504                t: self.t(&mut c)?,
505                frame: c.u()?,
506                render: c.u()?,
507                wait: c.u()?,
508            },
509            tag::SPAN => {
510                let start = self.t(&mut c)?;
511                Record::Span(Span {
512                    start,
513                    track: Track(c.u32()?),
514                    id: c.u32()?,
515                    parent: c.u32()?,
516                    name: c.u32()?,
517                    origin: match c.u()? {
518                        0 => Origin::Kui,
519                        1 => Origin::Host,
520                        n => Origin::Extension((n - 2) as NameId),
521                    },
522                    dur: c.u()?,
523                    cpu: c.opt()?,
524                    node: c.u()?,
525                    cut: c.u()? != 0,
526                })
527            }
528            tag::INPUT => {
529                let t = self.t(&mut c)?;
530                Record::Input {
531                    t,
532                    kind: c.u32()?,
533                    os_t: c.opt()?,
534                    count: c.u32()?,
535                    until: t.saturating_add(c.u()?),
536                }
537            }
538            tag::EVENT => Record::Event {
539                t: self.t(&mut c)?,
540                kind: c.u32()?,
541                node: c.u()?,
542            },
543            tag::WAKE => Record::Wake {
544                t: self.t(&mut c)?,
545                name: c.u32()?,
546            },
547            tag::REQUEST => Record::Request {
548                t: self.t(&mut c)?,
549                why: c.u32()?,
550                at: c.u32()?,
551            },
552            tag::LOOP => {
553                let t = self.t(&mut c)?;
554                let event = match c.u()? {
555                    0 => LoopEvent::Sleep { until: c.opt()? },
556                    1 => LoopEvent::Woke { by: c.u32()? },
557                    _ => LoopEvent::Os { kind: c.u32()? },
558                };
559                Record::Loop { t, event }
560            }
561            tag::COUNTER => Record::Counter {
562                t: self.t(&mut c)?,
563                frame: c.u()?,
564                name: c.u32()?,
565                value: c.i()?,
566            },
567            tag::GAUGE => Record::Gauge {
568                t: self.t(&mut c)?,
569                name: c.u32()?,
570                value: c.f64()?,
571            },
572            tag::MARK => Record::Mark {
573                t: self.t(&mut c)?,
574                track: Track(c.u32()?),
575                name: c.u32()?,
576            },
577            tag::NOTE => Record::Note {
578                t: self.t(&mut c)?,
579                text: c.str()?,
580            },
581            tag::PLACEMENT => Record::Placement {
582                t: self.t(&mut c)?,
583                node: c.u()?,
584                component: c.u32()?,
585                answer: c.u32()?,
586                why: c.u32()?,
587            },
588            // A tag from a later version: skipped by its length.
589            _ => return Some(None),
590        };
591        Some(Some(r))
592    }
593}
594
595impl Iterator for Reader<'_> {
596    type Item = Record;
597
598    /// The next record; `None` at the end, or where a recording cut off
599    /// mid-record stops.
600    fn next(&mut self) -> Option<Record> {
601        loop {
602            let (tag, body) = framed(self.b, self.at)?;
603            self.at = body.end;
604            match self.dec.decode(tag, &self.b[body]) {
605                Some(Some(r)) => return Some(r),
606                // Unknown, or a body shorter than its fields: skipped.
607                Some(None) | None => continue,
608            }
609        }
610    }
611}