Skip to main content

amdgpu_top_json/
lib.rs

1#![recursion_limit = "256"]
2
3use libamdgpu_top::{DevicePath, GetNpuMetrics, stat};
4use libamdgpu_top::app::*;
5use serde_json::{json, Value};
6use std::time::{Duration, Instant};
7use std::path::PathBuf;
8use std::io::Write;
9
10mod output_json;
11use crate::output_json::FdInfoJson;
12mod dump;
13pub use dump::{dump_json, drm_info_json, gpu_metrics_json, JsonInfo};
14
15pub fn version_json(title: &str) {
16    let version = json!({
17        "version": amdgpu_top_version(),
18        "title": title,
19    });
20    println!("{version}");
21}
22
23pub trait OutputJson {
24    fn json(&self) -> Value;
25}
26
27pub fn amdgpu_top_version() -> Value {
28    json!({
29        "major": env!("CARGO_PKG_VERSION_MAJOR").parse::<u32>().unwrap_or(0),
30        "minor": env!("CARGO_PKG_VERSION_MINOR").parse::<u32>().unwrap_or(0),
31        "patch": env!("CARGO_PKG_VERSION_PATCH").parse::<u32>().unwrap_or(0),
32    })
33}
34
35pub struct JsonApp {
36    pub vec_device_info: Vec<JsonDeviceInfo>,
37    pub sus_app_list: Vec<DevicePath>,
38    pub base_time: Instant,
39    pub interval: Duration,
40    pub duration_time: Duration,
41    pub delay: Duration,
42    pub iterations: u32,
43    pub no_pc: bool,
44    pub amdgpu_top_version: Value,
45    pub rocm_version: Value,
46    pub title: String,
47}
48
49impl JsonApp {
50    pub fn new(
51        title: &str,
52        device_path_list: &[DevicePath],
53        refresh_period: u64,
54        update_process_index_interval: u64,
55        iterations: u32,
56        no_pc: bool,
57    ) -> Self {
58        let interval = Duration::from_millis(refresh_period);
59        let delay = interval / 100;
60        let (mut vec_device_info, sus_app_list) =
61            JsonDeviceInfo::from_device_path_list(device_path_list);
62
63        for device in vec_device_info.iter_mut() {
64            device.app.stat.fdinfo.interval = interval;
65            device.app.update(interval);
66        }
67
68        let base_time = Instant::now();
69        let duration_time = base_time.elapsed();
70
71        {
72            let mut device_paths: Vec<DevicePath> = device_path_list.to_vec();
73
74            if let Some(xdna_device_path) = vec_device_info
75                .iter()
76                .find_map(|j| j.app.xdna_device_path.as_ref())
77            {
78                device_paths.push(xdna_device_path.clone());
79            }
80
81            stat::spawn_update_index_thread(device_paths, update_process_index_interval);
82        }
83
84        Self {
85            vec_device_info,
86            sus_app_list,
87            base_time,
88            duration_time,
89            interval,
90            delay,
91            iterations,
92            no_pc,
93            amdgpu_top_version: amdgpu_top_version(),
94            rocm_version: libamdgpu_top::get_rocm_version().map_or(Value::Null, Value::String),
95            title: title.to_string(),
96        }
97    }
98
99    pub fn update(&mut self) {
100        if !self.no_pc {
101            for device in self.vec_device_info.iter_mut() {
102                device.app.clear_pc();
103            }
104        }
105
106        if !self.no_pc {
107            for _ in 0..100 {
108                for device in self.vec_device_info.iter_mut() {
109                    device.app.update_pc();
110                }
111                std::thread::sleep(self.delay);
112            }
113
114            for device in self.vec_device_info.iter_mut() {
115                device.app.update_pc_usage();
116            }
117        } else {
118            std::thread::sleep(self.delay * 100);
119        }
120
121        for device in self.vec_device_info.iter_mut() {
122            device.app.update(self.interval);
123        }
124
125        self.sus_app_list.retain(|sus_device| {
126            let is_active = sus_device.check_if_device_is_active();
127
128            if is_active {
129                let Some(amdgpu_dev) = sus_device.init().ok() else { return true };
130                let Some(mut app) = AppAmdgpuTop::new(
131                    amdgpu_dev,
132                    sus_device.clone(),
133                    &Default::default(),
134                ) else { return true };
135                let info = app.json_info();
136                self.vec_device_info.push(JsonDeviceInfo { app, info });
137            }
138
139            !is_active
140        });
141
142        self.duration_time = {
143            let now = Instant::now();
144            now.duration_since(self.base_time)
145        };
146    }
147
148    pub fn json(&self) -> Value {
149        let devices: Vec<Value> = self.vec_device_info
150            .iter()
151            .map(|device| device.json(self.no_pc))
152            .collect();
153        let sus_devices: Vec<Value> = self.sus_app_list
154            .iter()
155            .map(|sus_dev| sus_dev.json())
156            .collect();
157
158        json!({
159            "period": {
160                "duration": self.duration_time.as_millis(),
161                "unit": "ms",
162            },
163            "devices": devices,
164            "suspended_devices": sus_devices,
165            "devices_len": devices.len(),
166            "suspended_devices_len": sus_devices.len(),
167            "amdgpu_top_version": self.amdgpu_top_version,
168            "ROCm version": self.rocm_version,
169            "title": self.title,
170        })
171    }
172
173    pub fn run(&mut self) {
174        let mut n = 0;
175
176        loop {
177            self.update();
178
179            let s = self.json().to_string();
180
181            println!("{s}");
182
183            if self.iterations != 0 {
184                n += 1;
185                if self.iterations == n { break; }
186            }
187        }
188    }
189
190    pub fn run_fifo(&mut self, fifo_path: PathBuf) {
191        loop {
192            self.update();
193
194            let s = self.json().to_string();
195
196            let mut f = std::fs::OpenOptions::new()
197                .read(true)
198                .write(true)
199                .open(&fifo_path)
200                .unwrap();
201
202            f.write_all(s.as_bytes()).unwrap();
203            f.flush().unwrap();
204        }
205    }
206}
207
208pub struct JsonDeviceInfo {
209    pub app: AppAmdgpuTop,
210    pub info: Value,
211}
212
213impl JsonDeviceInfo {
214    pub fn from_device_path_list(device_path_list: &[DevicePath]) -> (
215        Vec<Self>,
216        Vec<DevicePath>,
217    ) {
218        let (vec_app, sus_app_list) = AppAmdgpuTop::create_app_and_suspended_list(
219            device_path_list,
220            &Default::default(),
221        );
222        let vec_json_device = vec_app
223            .into_iter()
224            .map(|mut app| {
225                let info = app.json_info();
226
227                Self { app, info }
228            })
229            .collect();
230
231        (vec_json_device, sus_app_list)
232    }
233
234    pub fn json(&self, no_pc: bool) -> Value {
235        let (proc_usage, has_vcn, has_vcn_unified, has_vpe) =
236            self.app.stat.fdinfo.fold_fdinfo_usage();
237
238        json!({
239            "Info": self.info,
240            "GRBM": if !no_pc { self.app.stat.grbm.json() } else { Value::Null },
241            "GRBM2": if !no_pc { self.app.stat.grbm2.json() } else { Value::Null },
242            "VRAM": self.app.stat.vram_usage.json(),
243            "Sensors": self.app.stat.sensors.as_ref().map(|s| s.json()),
244            "fdinfo": self.app.stat.fdinfo.json(),
245            "xdna_fdinfo": self.app.stat.xdna_fdinfo.json(),
246            "Total fdinfo": proc_usage.usage_json(has_vcn, has_vcn_unified, has_vpe),
247            "gpu_metrics": self.app.stat.metrics.as_ref().map(|m| m.json()),
248            "gpu_activity": self.app.stat.activity.json(),
249            "npu_metrics": self.app.stat.metrics.as_ref().and_then(|m| m.get_npu_metrics()).map(|nm| nm.json()),
250        })
251    }
252}