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}