sysutil/
gpu.rs

1use std::{fs, path};
2use std::fs::read_dir;
3use crate::utils::{*};
4
5/// Encloses gpu metrics parameters
6#[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
31/// returns current GPU usage in percentage, returns `None` if it's not possible to retrieve data
32pub 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
40/// Returns metrics parameters from the amdgpu driver
41pub 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/// Contains all information about VRAM
129#[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
148/// Returns gpu's vram size as specified in `ByteSize` struct, returns `None` if it's not possible to retrieve data
149pub 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
163/// Returns gpu's vram usage in percentage, returns `None` if it's not possible to retrieve data
164pub 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
180/// Returns VRAM frequency in MT/s
181pub 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
235/// Returns VRAM bus width in bits
236pub 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}