evobench_tools/evaluator/data/
log_message.rs

1//! Representation of benchmark log messages in the "native evobench" format
2//!
3//! The protocol allows for versioning. Metadata and Start are
4//! separate so that the reader can change the parser version before
5//! reading the possibly changed Metadata. Start must never change
6//! structure, unlike what follows.
7
8use kstring::KString;
9use serde::{Deserialize, Serialize};
10
11use crate::times::{MicroTime, NanoTime};
12
13/// Only increment this for incompatible changes, not for additional
14/// fields that can be handled as `Option`. Also, you might want to
15/// create a new module for the new version and keep this module for
16/// reading old logs.
17pub const EVOBENCH_LOG_VERSION: u32 = 1;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
20#[serde(deny_unknown_fields)]
21pub struct ProcessId(pub u64);
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
24#[serde(deny_unknown_fields)]
25pub struct ThreadId(pub u64);
26
27#[derive(Debug, Serialize, Deserialize)]
28#[serde(deny_unknown_fields)]
29pub struct UName {
30    /// e.g. "Linux",
31    pub sysname: String,
32    /// e.g. "dev-1-cj1"
33    pub nodename: String,
34    /// e.g. "6.1.0-37-amd64"
35    pub release: String,
36    /// e.g. "#1 SMP PREEMPT_DYNAMIC Debian 6.1.140-1 (2025-05-22)"
37    pub version: String,
38    /// e.g. "x86_64"
39    pub machine: String,
40}
41
42#[derive(Debug, Serialize, Deserialize)]
43#[serde(deny_unknown_fields)]
44pub struct Metadata {
45    pub hostname: String,
46    pub username: String,
47    pub uname: UName,
48    pub compiler: String,
49}
50
51#[derive(Debug, Serialize, Deserialize)]
52#[serde(deny_unknown_fields)]
53pub struct Timing {
54    // Probe name ("module|local" if using the macros)
55    pub pn: KString,
56    pub pid: ProcessId,
57    pub tid: ThreadId,
58    pub n: Option<u32>,
59    pub r: NanoTime,
60    pub u: MicroTime,
61    pub s: MicroTime,
62    // These are `long int`, which could be i64, hence always use that
63    // since we don't know the width of the machine in question. Also,
64    // Option since I don't know how to get those numbers on macOS.
65    pub maxrss: Option<i64>,
66    pub minflt: Option<i64>,
67    pub majflt: Option<i64>,
68    pub inblock: Option<i64>,
69    pub oublock: Option<i64>,
70    pub nvcsw: Option<i64>,
71    pub nivcsw: Option<i64>,
72}
73
74impl Timing {
75    /// Is generally 0 for scope end timings!
76    #[inline]
77    pub fn n(&self) -> u32 {
78        // Backwards compatibility: field was not present, and the
79        // count was always 1.
80        self.n.unwrap_or(1)
81    }
82
83    #[inline]
84    pub fn nvcsw(&self) -> Option<u64> {
85        Some(
86            self.nvcsw?
87                .try_into()
88                .expect("ctx switches (nvcsw) should be non-negative"),
89        )
90    }
91
92    #[inline]
93    pub fn nivcsw(&self) -> Option<u64> {
94        Some(
95            self.nivcsw?
96                .try_into()
97                .expect("ctx switches (nivcsw) should be non-negative"),
98        )
99    }
100}
101
102#[derive(Debug, Serialize, Deserialize)]
103#[serde(deny_unknown_fields)]
104pub struct KeyValue {
105    pub tid: ThreadId,
106    pub k: KString,
107    pub v: KString,
108}
109
110include! {"../../../include/evobench_point_kind.rs"}
111
112#[derive(Debug, Serialize, Deserialize)]
113#[serde(deny_unknown_fields)]
114pub enum LogMessage {
115    /// `Start` describes the file format. Do not change this ennum
116    /// item! This is separate from `Metadata` so that `Metadata` can
117    /// be changed; `Start` must not change so that the parser can
118    /// detect the version then switch to the right parser for the
119    /// rest.
120    Start {
121        evobench_log_version: u32,
122        evobench_version: KString,
123    },
124    /// Random information from the program.
125    Metadata(Metadata),
126    /// Random information from the program.
127    KeyValue(KeyValue),
128    /// For the other items, see the doc comments in hpp file
129    TStart(Timing),
130    T(Timing),
131    TS(Timing),
132    TE(Timing),
133    TThreadStart(Timing),
134    TThreadEnd(Timing),
135    TEnd(Timing),
136    TIO(Timing),
137}
138
139pub enum DataMessage<'t> {
140    KeyValue(&'t KeyValue),
141    Timing(PointKind, &'t Timing),
142}
143
144impl LogMessage {
145    pub fn opt_data_message(&self) -> Option<DataMessage<'_>> {
146        match self {
147            LogMessage::Start {
148                evobench_log_version: _,
149                evobench_version: _,
150            } => None,
151            LogMessage::Metadata(_) => None,
152            LogMessage::KeyValue(keyvalue) => Some(DataMessage::KeyValue(keyvalue)),
153            LogMessage::TStart(timing) => Some(DataMessage::Timing(PointKind::TStart, timing)),
154            LogMessage::T(timing) => Some(DataMessage::Timing(PointKind::T, timing)),
155            LogMessage::TS(timing) => Some(DataMessage::Timing(PointKind::TS, timing)),
156            LogMessage::TE(timing) => Some(DataMessage::Timing(PointKind::TE, timing)),
157            LogMessage::TThreadStart(timing) => {
158                Some(DataMessage::Timing(PointKind::TThreadStart, timing))
159            }
160            LogMessage::TThreadEnd(timing) => {
161                Some(DataMessage::Timing(PointKind::TThreadEnd, timing))
162            }
163            LogMessage::TEnd(timing) => Some(DataMessage::Timing(PointKind::TEnd, timing)),
164            LogMessage::TIO(timing) => Some(DataMessage::Timing(PointKind::TIO, timing)),
165        }
166    }
167    /// Note: panics for Start and Metadata messages, because those
168    /// are not in `LogData::messages` any more (XX type safe?). Use
169    /// `opt_data_message()` instead if not accessing those.
170    pub fn data_message(&self) -> DataMessage<'_> {
171        self.opt_data_message()
172            .expect("non-DataMessage not contained in LogData::messages")
173    }
174}