1use crate::bitmasks::device::FbcFlags;
2use crate::enum_wrappers::device::{BridgeChip, EncoderType, FbcSessionType, SampleValueType};
3use crate::enums::device::{FirmwareVersion, SampleValue, UsedGpuMemory};
4use crate::error::{nvml_try, Bits, NvmlError};
5use crate::ffi::bindings::*;
6use crate::structs::device::FieldId;
7#[cfg(feature = "serde")]
8use serde_derive::{Deserialize, Serialize};
9use std::{
10 cmp::Ordering,
11 ffi::{CStr, CString},
12};
13use std::{
14 convert::{TryFrom, TryInto},
15 os::raw::c_char,
16};
17
18#[derive(Debug, Clone, Eq, PartialEq, Hash)]
22#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
23pub struct PciInfo {
24 pub bus: u32,
26 pub bus_id: String,
28 pub device: u32,
30 pub domain: u32,
32 pub pci_device_id: u32,
34 pub pci_sub_system_id: Option<u32>,
44}
45
46impl PciInfo {
47 pub fn try_from(struct_: nvmlPciInfo_t, sub_sys_id_present: bool) -> Result<Self, NvmlError> {
58 unsafe {
59 let bus_id_raw = CStr::from_ptr(struct_.busId.as_ptr());
60
61 Ok(Self {
62 bus: struct_.bus,
63 bus_id: bus_id_raw.to_str()?.into(),
64 device: struct_.device,
65 domain: struct_.domain,
66 pci_device_id: struct_.pciDeviceId,
67 pci_sub_system_id: if sub_sys_id_present {
68 Some(struct_.pciSubSystemId)
69 } else {
70 None
71 },
72 })
73 }
74 }
75}
76
77impl TryInto<nvmlPciInfo_t> for PciInfo {
78 type Error = NvmlError;
79
80 fn try_into(self) -> Result<nvmlPciInfo_t, Self::Error> {
92 const fn buf_size() -> usize {
94 NVML_DEVICE_PCI_BUS_ID_BUFFER_SIZE as usize
95 }
96
97 let mut bus_id_c: [c_char; buf_size()] = [0; buf_size()];
98 let mut bus_id = CString::new(self.bus_id)?.into_bytes_with_nul();
99
100 match bus_id.len().cmp(&buf_size()) {
102 Ordering::Less => {
103 while bus_id.len() != buf_size() {
104 bus_id.push(0);
105 }
106 }
107 Ordering::Equal => {
108 }
110 Ordering::Greater => {
111 return Err(NvmlError::StringTooLong {
112 max_len: buf_size(),
113 actual_len: bus_id.len(),
114 })
115 }
116 }
117
118 bus_id_c.clone_from_slice(&bus_id.into_iter().map(|b| b as c_char).collect::<Vec<_>>());
119
120 Ok(nvmlPciInfo_t {
121 busIdLegacy: [0; NVML_DEVICE_PCI_BUS_ID_BUFFER_V2_SIZE as usize],
122 domain: self.domain,
123 bus: self.bus,
124 device: self.device,
125 pciDeviceId: self.pci_device_id,
126 pciSubSystemId: self.pci_sub_system_id.unwrap_or(0),
129 busId: bus_id_c,
130 })
131 }
132}
133
134#[derive(Debug, Clone, Eq, PartialEq, Hash)]
137#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
138pub struct BAR1MemoryInfo {
139 pub free: u64,
141 pub total: u64,
143 pub used: u64,
145}
146
147impl From<nvmlBAR1Memory_t> for BAR1MemoryInfo {
148 fn from(struct_: nvmlBAR1Memory_t) -> Self {
149 Self {
150 free: struct_.bar1Free,
151 total: struct_.bar1Total,
152 used: struct_.bar1Used,
153 }
154 }
155}
156
157#[derive(Debug, Clone, Eq, PartialEq, Hash)]
160#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
161pub struct BridgeChipInfo {
162 pub fw_version: FirmwareVersion,
163 pub chip_type: BridgeChip,
164}
165
166impl TryFrom<nvmlBridgeChipInfo_t> for BridgeChipInfo {
167 type Error = NvmlError;
168
169 fn try_from(value: nvmlBridgeChipInfo_t) -> Result<Self, Self::Error> {
177 let fw_version = FirmwareVersion::from(value.fwVersion);
178 let chip_type = BridgeChip::try_from(value.type_)?;
179
180 Ok(Self {
181 fw_version,
182 chip_type,
183 })
184 }
185}
186
187#[derive(Debug, Clone, Eq, PartialEq, Hash)]
195#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
196pub struct BridgeChipHierarchy {
197 pub chips_hierarchy: Vec<BridgeChipInfo>,
199 pub chip_count: u8,
201}
202
203impl TryFrom<nvmlBridgeChipHierarchy_t> for BridgeChipHierarchy {
204 type Error = NvmlError;
205
206 fn try_from(value: nvmlBridgeChipHierarchy_t) -> Result<Self, Self::Error> {
214 let chips_hierarchy = value
215 .bridgeChipInfo
216 .iter()
217 .map(|bci| BridgeChipInfo::try_from(*bci))
218 .collect::<Result<_, NvmlError>>()?;
219
220 Ok(Self {
221 chips_hierarchy,
222 chip_count: value.bridgeCount,
223 })
224 }
225}
226
227#[derive(Debug, Clone, Eq, PartialEq, Hash)]
230#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
231pub struct ProcessInfo {
232 pub pid: u32,
234 pub used_gpu_memory: UsedGpuMemory,
236 pub gpu_instance_id: Option<u32>,
241 pub compute_instance_id: Option<u32>,
246}
247
248impl From<nvmlProcessInfo_t> for ProcessInfo {
249 fn from(struct_: nvmlProcessInfo_t) -> Self {
250 const NO_VALUE: u32 = 0xFFFFFFFF;
251
252 let gpu_instance_id = Some(struct_.gpuInstanceId).filter(|id| *id != NO_VALUE);
253 let compute_instance_id = Some(struct_.computeInstanceId).filter(|id| *id != NO_VALUE);
254
255 Self {
256 pid: struct_.pid,
257 used_gpu_memory: UsedGpuMemory::from(struct_.usedGpuMemory),
258 gpu_instance_id,
259 compute_instance_id,
260 }
261 }
262}
263
264#[derive(Debug, Clone, Eq, PartialEq, Hash)]
267#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
268pub struct EccErrorCounts {
269 pub device_memory: u64,
270 pub l1_cache: u64,
271 pub l2_cache: u64,
272 pub register_file: u64,
273}
274
275impl From<nvmlEccErrorCounts_t> for EccErrorCounts {
276 fn from(struct_: nvmlEccErrorCounts_t) -> Self {
277 Self {
278 device_memory: struct_.deviceMemory,
279 l1_cache: struct_.l1Cache,
280 l2_cache: struct_.l2Cache,
281 register_file: struct_.registerFile,
282 }
283 }
284}
285
286#[derive(Debug, Clone, Eq, PartialEq, Hash)]
289#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
290pub struct MemoryInfo {
291 pub free: u64,
293 pub total: u64,
295 pub used: u64,
300}
301
302impl From<nvmlMemory_t> for MemoryInfo {
303 fn from(struct_: nvmlMemory_t) -> Self {
304 Self {
305 free: struct_.free,
306 total: struct_.total,
307 used: struct_.used,
308 }
309 }
310}
311
312#[derive(Debug, Clone, Eq, PartialEq, Hash)]
316#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
317pub struct Utilization {
318 pub gpu: u32,
321 pub memory: u32,
324}
325
326impl From<nvmlUtilization_t> for Utilization {
327 fn from(struct_: nvmlUtilization_t) -> Self {
328 Self {
329 gpu: struct_.gpu,
330 memory: struct_.memory,
331 }
332 }
333}
334
335#[derive(Debug, Clone, Eq, PartialEq, Hash)]
338#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
339pub struct ViolationTime {
340 pub reference_time: u64,
342 pub violation_time: u64,
344}
345
346impl From<nvmlViolationTime_t> for ViolationTime {
347 fn from(struct_: nvmlViolationTime_t) -> Self {
348 Self {
349 reference_time: struct_.referenceTime,
350 violation_time: struct_.violationTime,
351 }
352 }
353}
354
355#[derive(Debug, Clone, Eq, PartialEq, Hash)]
364#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
365pub struct AccountingStats {
366 pub gpu_utilization: Option<u32>,
375 pub is_running: bool,
377 pub max_memory_usage: Option<u64>,
381 pub memory_utilization: Option<u32>,
388 pub start_time: u64,
390 pub time: u64,
393}
394
395impl From<nvmlAccountingStats_t> for AccountingStats {
396 fn from(struct_: nvmlAccountingStats_t) -> Self {
397 let not_avail_u64 = (NVML_VALUE_NOT_AVAILABLE) as u64;
398 let not_avail_u32 = (NVML_VALUE_NOT_AVAILABLE) as u32;
399
400 #[allow(clippy::match_like_matches_macro)]
401 Self {
402 gpu_utilization: match struct_.gpuUtilization {
403 v if v == not_avail_u32 => None,
404 _ => Some(struct_.gpuUtilization),
405 },
406 is_running: match struct_.isRunning {
407 0 => false,
408 _ => true,
411 },
412 max_memory_usage: match struct_.maxMemoryUsage {
413 v if v == not_avail_u64 => None,
414 _ => Some(struct_.maxMemoryUsage),
415 },
416 memory_utilization: match struct_.memoryUtilization {
417 v if v == not_avail_u32 => None,
418 _ => Some(struct_.memoryUtilization),
419 },
420 start_time: struct_.startTime,
421 time: struct_.time,
422 }
423 }
424}
425
426#[derive(Debug, Clone, Eq, PartialEq, Hash)]
428#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
429pub struct EncoderSessionInfo {
430 pub session_id: u32,
432 pub pid: u32,
434 pub vgpu_instance: Option<u32>,
437 pub codec_type: EncoderType,
438 pub hres: u32,
440 pub vres: u32,
442 pub average_fps: u32,
444 pub average_latency: u32,
446}
447
448impl TryFrom<nvmlEncoderSessionInfo_t> for EncoderSessionInfo {
449 type Error = NvmlError;
450
451 fn try_from(value: nvmlEncoderSessionInfo_t) -> Result<Self, Self::Error> {
459 Ok(Self {
460 session_id: value.sessionId,
461 pid: value.pid,
462 vgpu_instance: match value.vgpuInstance {
463 0 => None,
464 other => Some(other),
465 },
466 codec_type: EncoderType::try_from(value.codecType)?,
467 hres: value.hResolution,
468 vres: value.vResolution,
469 average_fps: value.averageFps,
470 average_latency: value.averageLatency,
471 })
472 }
473}
474
475#[derive(Debug, Clone, PartialEq)]
478#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
479pub struct Sample {
480 pub timestamp: u64,
482 pub value: SampleValue,
483}
484
485impl Sample {
486 pub fn from_tag_and_struct(tag: &SampleValueType, struct_: nvmlSample_t) -> Self {
489 Self {
490 timestamp: struct_.timeStamp,
491 value: SampleValue::from_tag_and_union(tag, struct_.sampleValue),
492 }
493 }
494}
495
496#[derive(Debug, Clone, Eq, PartialEq, Hash)]
497#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
498pub struct ProcessUtilizationSample {
499 pub pid: u32,
500 pub timestamp: u64,
502 pub sm_util: u32,
504 pub mem_util: u32,
506 pub enc_util: u32,
508 pub dec_util: u32,
510}
511
512impl From<nvmlProcessUtilizationSample_t> for ProcessUtilizationSample {
513 fn from(struct_: nvmlProcessUtilizationSample_t) -> Self {
514 Self {
515 pid: struct_.pid,
516 timestamp: struct_.timeStamp,
517 sm_util: struct_.smUtil,
518 mem_util: struct_.memUtil,
519 enc_util: struct_.encUtil,
520 dec_util: struct_.decUtil,
521 }
522 }
523}
524
525#[derive(Debug)]
528pub struct FieldValueSample {
529 pub field: FieldId,
531 pub timestamp: i64,
533 pub latency: i64,
540 pub value: Result<SampleValue, NvmlError>,
544}
545
546impl TryFrom<nvmlFieldValue_t> for FieldValueSample {
547 type Error = NvmlError;
548
549 fn try_from(value: nvmlFieldValue_t) -> Result<Self, Self::Error> {
557 Ok(Self {
558 field: FieldId(value.fieldId),
559 timestamp: value.timestamp,
560 latency: value.latencyUsec,
561 value: match nvml_try(value.nvmlReturn) {
562 Ok(_) => Ok(SampleValue::from_tag_and_union(
563 &SampleValueType::try_from(value.valueType)?,
564 value.value,
565 )),
566 Err(e) => Err(e),
567 },
568 })
569 }
570}
571
572#[derive(Debug, Clone, Eq, PartialEq)]
574#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
575pub struct FbcStats {
576 pub sessions_count: u32,
578 pub average_fps: u32,
580 pub average_latency: u32,
582}
583
584impl From<nvmlFBCStats_t> for FbcStats {
585 fn from(struct_: nvmlFBCStats_t) -> Self {
586 Self {
587 sessions_count: struct_.sessionsCount,
588 average_fps: struct_.averageFPS,
589 average_latency: struct_.averageLatency,
590 }
591 }
592}
593
594#[derive(Debug, Clone, Eq, PartialEq)]
596#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
597pub struct FbcSessionInfo {
598 pub session_id: u32,
600 pub pid: u32,
602 pub vgpu_instance: Option<u32>,
605 pub display_ordinal: u32,
607 pub session_type: FbcSessionType,
609 pub session_flags: FbcFlags,
611 pub hres_max: u32,
613 pub vres_max: u32,
615 pub hres: u32,
617 pub vres: u32,
619 pub average_fps: u32,
621 pub average_latency: u32,
623}
624
625impl TryFrom<nvmlFBCSessionInfo_t> for FbcSessionInfo {
626 type Error = NvmlError;
627
628 fn try_from(value: nvmlFBCSessionInfo_t) -> Result<Self, Self::Error> {
638 Ok(Self {
639 session_id: value.sessionId,
640 pid: value.pid,
641 vgpu_instance: match value.vgpuInstance {
642 0 => None,
643 other => Some(other),
644 },
645 display_ordinal: value.displayOrdinal,
646 session_type: FbcSessionType::try_from(value.sessionType)?,
647 session_flags: FbcFlags::from_bits(value.sessionFlags)
648 .ok_or(NvmlError::IncorrectBits(Bits::U32(value.sessionFlags)))?,
649 hres_max: value.hMaxResolution,
650 vres_max: value.vMaxResolution,
651 hres: value.hResolution,
652 vres: value.vResolution,
653 average_fps: value.averageFPS,
654 average_latency: value.averageLatency,
655 })
656 }
657}
658
659#[cfg(test)]
660#[allow(unused_variables, unused_imports)]
661mod tests {
662 use crate::error::*;
663 use crate::ffi::bindings::*;
664 use crate::test_utils::*;
665 use std::convert::TryInto;
666 use std::mem;
667
668 #[test]
669 fn pci_info_from_to_c() {
670 let nvml = nvml();
671 test_with_device(3, &nvml, |device| {
672 let converted: nvmlPciInfo_t = device
673 .pci_info()
674 .expect("wrapped pci info")
675 .try_into()
676 .expect("converted c pci info");
677
678 let sym = nvml_sym(nvml.lib.nvmlDeviceGetPciInfo_v3.as_ref())?;
679
680 let raw = unsafe {
681 let mut pci_info: nvmlPciInfo_t = mem::zeroed();
682 nvml_try(sym(device.handle(), &mut pci_info)).expect("raw pci info");
683 pci_info
684 };
685
686 assert_eq!(converted.busId, raw.busId);
687 assert_eq!(converted.domain, raw.domain);
688 assert_eq!(converted.bus, raw.bus);
689 assert_eq!(converted.device, raw.device);
690 assert_eq!(converted.pciDeviceId, raw.pciDeviceId);
691 assert_eq!(converted.pciSubSystemId, raw.pciSubSystemId);
692
693 Ok(())
694 })
695 }
696}