atomr_accel_cuda/graph/
dot.rs1use std::ffi::CString;
9use std::fs;
10use std::path::PathBuf;
11
12use cudarc::driver::sys as driver_sys;
13
14use crate::error::GpuError;
15use crate::graph::GraphHandle;
16
17const LIB: &str = "graph";
18
19bitflags::bitflags! {
20 pub struct DotFlags: u32 {
23 const VERBOSE = 1 << 0;
24 const KERNEL_NODE = 1 << 2;
25 const MEMCPY_NODE = 1 << 3;
26 const MEMSET_NODE = 1 << 4;
27 const HOST_NODE = 1 << 5;
28 const GRAPH_NODE = 1 << 6;
29 }
30}
31
32impl Default for DotFlags {
33 fn default() -> Self {
34 DotFlags::empty()
35 }
36}
37
38pub fn export_dot(graph: &GraphHandle, flags: DotFlags) -> Result<String, GpuError> {
41 let path = temp_dot_path();
42 let cpath = CString::new(path.to_string_lossy().as_ref())
43 .map_err(|e| GpuError::Unrecoverable(format!("export_dot: bad path: {e}")))?;
44 let cu_graph = graph.cu_graph();
45 let s = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
46 unsafe { driver_sys::cuGraphDebugDotPrint(cu_graph, cpath.as_ptr(), flags.bits()) }
49 }))
50 .map_err(|_| GpuError::Unrecoverable("export_dot: CUDA driver not loadable".into()))?;
51 if s != driver_sys::cudaError_enum::CUDA_SUCCESS {
52 return Err(GpuError::LibraryError {
53 lib: LIB,
54 msg: format!("cuGraphDebugDotPrint: {s:?}"),
55 });
56 }
57 let dot = fs::read_to_string(&path).map_err(|e| GpuError::LibraryError {
58 lib: LIB,
59 msg: format!("read DOT file {}: {e}", path.display()),
60 })?;
61 let _ = fs::remove_file(&path);
62 Ok(dot)
63}
64
65fn temp_dot_path() -> PathBuf {
66 let mut p = std::env::temp_dir();
67 let pid = std::process::id();
68 let nanos = std::time::SystemTime::now()
69 .duration_since(std::time::UNIX_EPOCH)
70 .map(|d| d.subsec_nanos())
71 .unwrap_or(0);
72 p.push(format!("atomr-accel-graph-{pid}-{nanos}.dot"));
73 p
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79
80 #[test]
81 fn dot_export_returns_nonempty_for_known_graph() {
82 let g = GraphHandle::synthetic_for_tests();
87 let r = export_dot(&g, DotFlags::VERBOSE);
88 match r {
89 Ok(s) => {
90 assert!(
92 !s.is_empty(),
93 "expected non-empty DOT string from real driver"
94 );
95 }
96 Err(GpuError::Unrecoverable(_)) => {}
97 Err(GpuError::LibraryError { .. }) => {}
98 other => panic!("unexpected: {other:?}"),
99 }
100 }
101}