1use std::{fs, path};
2use std::fs::read_dir;
3use crate::utils::{*};
4
5#[derive(Debug, Clone)]
7pub struct GpuMetrics {
8 pub temperatureEdge: u16,
9 pub temperatureHotspot: u16,
10 pub temperatureMem: u16,
11 pub temperatureVrgfx: u16,
12 pub temperatureVrsoc: u16,
13 pub temperatureVrmem: u16,
14 pub averageSocketPower: u16,
15 pub averageGfxclkFrequency: u16,
16 pub averageSockclkFrequency: u16,
17 pub averageUclkFrequency: u16,
18 pub currentGfxclk: u16,
19 pub currentSockclk: u16,
20 pub currentUclk: u16,
21 pub currentVclk0: u16,
22 pub currentDclk0: u16,
23 pub currentVclk1: u16,
24 pub currentDclk1: u16,
25 pub throttleStatus: u32,
26 pub currentFanSpeed: u16,
27 pub pcieLinkWidth: u16,
28 pub pcieLinkSpeed: u16,
29}
30
31pub fn gpuUsage() -> Option<f32> {
33 linuxCheck();
34
35 let fileContent = readFile("/sys/class/drm/card0/device/gpu_busy_percent");
36 let gpuUsage = fileContent.parse::<f32>().ok()?;
37 return Some(gpuUsage);
38}
39
40pub fn gpuMetrics() -> Option<GpuMetrics> {
42 linuxCheck();
43
44 let filePipe = fs::read(path::Path::new("/sys/class/drm/card0/device/gpu_metrics"));
45
46 let mut bytes: Vec<u8> = Vec::<u8>::new();
47 let mut error = false;
48
49 match filePipe {
50 Err(_) => {
51 error = true;
52 },
53 Ok(bytesPipe) => {
54 bytes = bytesPipe;
55 }
56 };
57
58 if error {
59 return None;
60 }
61
62 let format = bytes[2];
63 let content = bytes[3];
64
65 bytes = bytes[4..].to_vec();
66
67 if format != 1 {
68 return None;
69 }
70
71 Some(
72 GpuMetrics {
73 temperatureEdge: match content {
74 0 => bytesToU16(bytes[8..10].to_vec()),
75 _ => bytesToU16(bytes[0..2].to_vec())
76 },
77
78 temperatureHotspot: match content {
79 0 => bytesToU16(bytes[10..12].to_vec()),
80 _ => bytesToU16(bytes[2..4].to_vec())
81 },
82
83 temperatureMem: match content {
84 0 => bytesToU16(bytes[12..14].to_vec()),
85 _ => bytesToU16(bytes[4..6].to_vec())
86 },
87
88 temperatureVrgfx: match content {
89 0 => bytesToU16(bytes[14..16].to_vec()),
90 _ => bytesToU16(bytes[6..8].to_vec())
91 },
92
93 temperatureVrsoc: match content {
94 0 => bytesToU16(bytes[16..18].to_vec()),
95 _ => bytesToU16(bytes[8..10].to_vec())
96 },
97
98 temperatureVrmem: match content {
99 0 => bytesToU16(bytes[18..20].to_vec()),
100 _ => bytesToU16(bytes[10..12].to_vec())
101 },
102
103 averageSocketPower: match content {
104 0 => bytesToU16(bytes[26..28].to_vec()),
105 _ => bytesToU16(bytes[18..20].to_vec())
106 },
107
108 averageGfxclkFrequency: bytesToU16(bytes[36..38].to_vec()),
109 averageSockclkFrequency: bytesToU16(bytes[38..40].to_vec()),
110 averageUclkFrequency: bytesToU16(bytes[40..42].to_vec()),
111
112 currentGfxclk: bytesToU16(bytes[50..52].to_vec()),
113 currentSockclk: bytesToU16(bytes[52..54].to_vec()),
114 currentUclk: bytesToU16(bytes[54..56].to_vec()),
115 currentVclk0: bytesToU16(bytes[56..58].to_vec()),
116 currentDclk0: bytesToU16(bytes[58..60].to_vec()),
117 currentVclk1: bytesToU16(bytes[60..62].to_vec()),
118 currentDclk1: bytesToU16(bytes[62..64].to_vec()),
119
120 throttleStatus: bytesToU32(bytes[64..68].to_vec()),
121 currentFanSpeed: bytesToU16(bytes[68..70].to_vec()),
122 pcieLinkWidth: bytesToU16(bytes[70..72].to_vec()),
123 pcieLinkSpeed: bytesToU16(bytes[72..74].to_vec())
124 }
125 )
126}
127
128#[derive(Debug)]
130pub struct VRAM {
131 pub size: Option<ByteSize>,
132 pub usage: Option<f32>,
133 pub frequency: Option<usize>,
134 pub busWidth: Option<usize>
135}
136
137impl VRAM {
138 pub fn new() -> Self {
139 Self {
140 size: vramSize(),
141 usage: vramUsage(),
142 frequency: vramFrequency(),
143 busWidth: vramBusWidth()
144 }
145 }
146}
147
148pub fn vramSize() -> Option<ByteSize> {
150 linuxCheck();
151
152 let fileContent = readFile("/sys/class/drm/card0/device/mem_info_vram_total");
153 match fileContent.parse::<usize>() {
154 Err(_) => {
155 return None
156 },
157 Ok(uMem) => {
158 return Some(ByteSize::fromBytes(uMem))
159 }
160 };
161}
162
163pub fn vramUsage() -> Option<f32> {
165 linuxCheck();
166
167 let vramTotal = readFile("/sys/class/drm/card0/device/mem_info_vram_total");
168 let vramUsed = readFile("/sys/class/drm/card0/device/mem_info_vram_used");
169
170 if vramTotal.is_empty() || vramUsed.is_empty() {
171 return None;
172 }
173
174 let uVramTotal: usize = vramTotal.parse::<usize>().unwrap();
175 let uVramUsed: usize = vramUsed.parse::<usize>().unwrap();
176
177 return Some(uVramUsed as f32 * 100_f32 / uVramTotal as f32);
178}
179
180pub fn vramFrequency() -> Option<usize> {
182 let kfdTopologyNodes = "/sys/class/kfd/kfd/topology/nodes/";
183 for dir in read_dir(kfdTopologyNodes).unwrap() {
184 let path = dir.unwrap().path();
185 let directory = path.to_str().unwrap();
186
187 let content = readFile(format!("{}/properties", directory));
188 let mut isGpu = false;
189
190 for line in content.split("\n") {
191 if line.contains("simd_count") {
192 let splitedLine = line.split(" ").collect::<Vec<&str>>();
193 match splitedLine.last() {
194 Some(cores) => {
195 if cores.parse::<usize>().unwrap() != 0 {
196 isGpu = true;
197 }
198 },
199
200 None => {
201 continue
202 }
203 }
204 }
205
206 if isGpu {
207 break
208 }
209 }
210
211 if isGpu {
212 let memBanksInfo = readFile(format!("{}/mem_banks/0/properties", directory));
213 let mut frequencyLine = String::new();
214
215 for line in memBanksInfo.split("\n") {
216 if line.contains("mem_clk_max") {
217 frequencyLine = line.to_string();
218 break
219 }
220 }
221
222 let frequency = {
223 let binding = frequencyLine.split(" ").collect::<Vec<&str>>();
224 let last = binding.last().unwrap();
225 last.parse::<usize>().unwrap()
226 };
227
228 return Some(frequency);
229 }
230 }
231
232 return None;
233}
234
235pub fn vramBusWidth() -> Option<usize> {
237 let kfdTopologyNodes = "/sys/class/kfd/kfd/topology/nodes/";
238 for dir in read_dir(kfdTopologyNodes).unwrap() {
239 let path = dir.unwrap().path();
240 let directory = path.to_str().unwrap();
241
242 let content = readFile(format!("{}/properties", directory));
243 let mut isGpu = false;
244
245 for line in content.split("\n") {
246 if line.contains("simd_count") {
247 let splitedLine = line.split(" ").collect::<Vec<&str>>();
248 match splitedLine.last() {
249 Some(cores) => {
250 if cores.parse::<usize>().unwrap() != 0 {
251 isGpu = true;
252 }
253 },
254
255 None => {
256 continue
257 }
258 }
259 }
260
261 if isGpu {
262 break
263 }
264 }
265
266 if isGpu {
267 let memBanksInfo = readFile(format!("{}/mem_banks/0/properties", directory));
268 let mut widthLine = String::new();
269
270 for line in memBanksInfo.split("\n") {
271 if line.contains("width") {
272 widthLine = line.to_string();
273 break
274 }
275 }
276
277 let width = {
278 let binding = widthLine.split(" ").collect::<Vec<&str>>();
279 let last = binding.last().unwrap();
280 last.parse::<usize>().unwrap()
281 };
282
283 return Some(width);
284 }
285 }
286
287 return None;
288}