Skip to main content

gpu_trace_perf/
traces_config.rs

1//! Deserialization structs for the traces TOML config format.
2
3use std::{collections::HashMap, path::Path};
4
5use anyhow::{Context, Result};
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Deserialize, Serialize)]
9#[serde(deny_unknown_fields)]
10pub struct TracesConfig {
11    pub traces_db: TracesDb,
12    pub traces: Vec<TraceEntry>,
13}
14
15impl TracesConfig {
16    pub fn load(path: &Path) -> Result<TracesConfig> {
17        let contents = std::fs::read_to_string(path)
18            .with_context(|| format!("reading config file {}", path.display()))?;
19        toml::from_str(&contents).with_context(|| format!("parsing TOML config {}", path.display()))
20    }
21}
22
23#[derive(Debug, Deserialize, Serialize)]
24#[serde(deny_unknown_fields)]
25pub struct TracesDb {
26    pub download_url: String,
27}
28
29#[derive(Debug, Deserialize, Serialize)]
30#[serde(deny_unknown_fields)]
31pub struct TraceEntry {
32    pub path: String,
33    /// When true, we shouldn't use apitrace's internal frame looping support
34    /// due to instability (not all sequences of API calls can just be
35    /// replayed).  Instead, do the replay as many times as we are capturing
36    /// frames.
37    #[serde(default)]
38    pub nonloopable: bool,
39    /// Extra arguments appended to the replay command for this trace,
40    /// regardless of device.  Concatenated with any per-device replay_args.
41    #[serde(default)]
42    pub replay_args: Vec<String>,
43    #[serde(default)]
44    pub devices: HashMap<String, DeviceEntry>,
45}
46
47impl TraceEntry {
48    pub fn device(&self, name: &str) -> Option<&DeviceEntry> {
49        self.devices.get(name)
50    }
51}
52
53#[derive(Debug, Deserialize, Serialize)]
54#[serde(deny_unknown_fields)]
55pub struct DeviceEntry {
56    pub checksum: String,
57    /// Don't run any other traces in parallel with this one.
58    #[serde(default)]
59    pub singlethread: bool,
60    /// Skip this trace on this device entirely.
61    #[serde(default)]
62    pub skip: bool,
63    /// Extra arguments appended to the replay command for this trace on this
64    /// device.  Concatenated after any per-trace replay_args.
65    #[serde(default)]
66    pub replay_args: Vec<String>,
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72
73    const MINIMAL_TOML: &str = r#"
74[traces_db]
75download_url = "https://s3.freedesktop.org/mesa-tracie-public/"
76
77[[traces]]
78path = "valve/half-life-2-v2.trace"
79devices = {
80    freedreno-a306 = {
81        checksum = "8f5929c82e7d990e8c3d2bea14688224aabbccdd8f5929c82e7d990e8c3d2bea",
82        skip = true,
83    },
84    freedreno-a530 = {
85        checksum = "c7b816feafeae42eef3ccd5357db4cd7c7b816feafeae42eef3ccd5357db4cd7",
86    },
87}
88
89[[traces]]
90path = "valve/portal-2-v2.trace"
91devices = {
92    freedreno-a530 = {
93        checksum = "102a09ce76092436173fd09a6a2bd941102a09ce76092436173fd09a6a2bd941"
94    },
95}
96"#;
97
98    #[test]
99    fn parse_minimal_toml() {
100        let config: TracesConfig = toml::from_str(MINIMAL_TOML).expect("parsing TOML");
101
102        assert_eq!(
103            config.traces_db.download_url,
104            "https://s3.freedesktop.org/mesa-tracie-public/"
105        );
106        assert_eq!(config.traces.len(), 2);
107
108        let hl2 = &config.traces[0];
109        assert_eq!(hl2.path, "valve/half-life-2-v2.trace");
110        assert_eq!(hl2.devices.len(), 2);
111
112        let a306 = &hl2.device("freedreno-a306").unwrap();
113        assert_eq!(
114            a306.checksum,
115            "8f5929c82e7d990e8c3d2bea14688224aabbccdd8f5929c82e7d990e8c3d2bea"
116        );
117        assert!(a306.skip);
118
119        let a530 = &hl2.device("freedreno-a530").unwrap();
120        assert!(!a530.skip);
121    }
122
123    #[test]
124    fn device_lookup() {
125        let config: TracesConfig = toml::from_str(MINIMAL_TOML).expect("parsing TOML");
126        let hl2 = &config.traces[0];
127
128        assert!(hl2.device("freedreno-a306").is_some());
129        assert!(hl2.device("freedreno-a530").is_some());
130        assert!(hl2.device("unknown-device").is_none());
131    }
132
133    #[test]
134    fn skip_detection() {
135        let config: TracesConfig = toml::from_str(MINIMAL_TOML).expect("parsing TOML");
136        let hl2 = &config.traces[0];
137
138        let a306 = hl2.device("freedreno-a306").unwrap();
139        assert!(a306.skip);
140
141        let a530 = hl2.device("freedreno-a530").unwrap();
142        assert!(!a530.skip);
143    }
144
145    #[test]
146    fn trace_with_no_device_entry() {
147        let config: TracesConfig = toml::from_str(MINIMAL_TOML).expect("parsing TOML");
148        let portal2 = &config.traces[1];
149
150        assert_eq!(portal2.path, "valve/portal-2-v2.trace");
151        assert!(portal2.device("freedreno-a306").is_none());
152        assert!(portal2.device("freedreno-a530").is_some());
153    }
154}