1pub mod activity;
37pub mod callback;
38pub mod error;
39pub mod metrics;
40mod sys;
41
42pub use activity::{ActivityKind, ActivityRecord, KernelRecord, MemcpyRecord};
43pub use callback::{CallbackDomain, CallbackId};
44pub use error::{CuptiError, CuptiResult};
45pub use metrics::{MetricId, MetricValue, SmMetrics, WarpMetrics};
46
47use std::sync::atomic::{AtomicBool, Ordering};
48use std::sync::Arc;
49
50pub struct Profiler {
54 active: Arc<AtomicBool>,
56 enabled_activities: Vec<ActivityKind>,
58 records: Vec<ActivityRecord>,
60 available: bool,
62}
63
64impl Profiler {
65 pub fn new() -> CuptiResult<Self> {
69 let available = Self::check_cupti_available();
71
72 Ok(Self {
73 active: Arc::new(AtomicBool::new(false)),
74 enabled_activities: Vec::new(),
75 records: Vec::new(),
76 available,
77 })
78 }
79
80 pub fn is_available(&self) -> bool {
82 self.available
83 }
84
85 pub fn enable(&mut self, kind: ActivityKind) -> CuptiResult<()> {
87 if !self.available {
88 return Err(CuptiError::NotAvailable);
89 }
90 if !self.enabled_activities.contains(&kind) {
91 self.enabled_activities.push(kind);
92 }
93 Ok(())
94 }
95
96 pub fn disable(&mut self, kind: ActivityKind) -> CuptiResult<()> {
98 self.enabled_activities.retain(|k| *k != kind);
99 Ok(())
100 }
101
102 pub fn start(&self) -> CuptiResult<()> {
104 if !self.available {
105 return Err(CuptiError::NotAvailable);
106 }
107 self.active.store(true, Ordering::SeqCst);
108 Ok(())
110 }
111
112 pub fn stop(&self) -> CuptiResult<()> {
114 self.active.store(false, Ordering::SeqCst);
115 Ok(())
117 }
118
119 pub fn is_active(&self) -> bool {
121 self.active.load(Ordering::SeqCst)
122 }
123
124 pub fn flush(&mut self) -> CuptiResult<Vec<ActivityRecord>> {
126 Ok(std::mem::take(&mut self.records))
128 }
129
130 pub fn enabled_activities(&self) -> &[ActivityKind] {
132 &self.enabled_activities
133 }
134
135 fn check_cupti_available() -> bool {
137 #[cfg(target_os = "linux")]
139 {
140 let paths = [
142 "/usr/local/cuda/lib64/libcupti.so",
143 "/usr/lib/x86_64-linux-gnu/libcupti.so",
144 "/opt/cuda/lib64/libcupti.so",
145 ];
146 for path in &paths {
147 if std::path::Path::new(path).exists() {
148 return true;
149 }
150 }
151 if let Ok(ld_path) = std::env::var("LD_LIBRARY_PATH") {
153 for dir in ld_path.split(':') {
154 let cupti_path = std::path::Path::new(dir).join("libcupti.so");
155 if cupti_path.exists() {
156 return true;
157 }
158 }
159 }
160 }
161 false
162 }
163}
164
165impl Default for Profiler {
166 fn default() -> Self {
167 Self::new().unwrap_or(Self {
168 active: Arc::new(AtomicBool::new(false)),
169 enabled_activities: Vec::new(),
170 records: Vec::new(),
171 available: false,
172 })
173 }
174}
175
176#[derive(Debug, Default)]
178pub struct ProfilerBuilder {
179 activities: Vec<ActivityKind>,
180 buffer_size: Option<usize>,
181 flush_period_ms: Option<u64>,
182}
183
184impl ProfilerBuilder {
185 pub fn new() -> Self {
187 Self::default()
188 }
189
190 #[must_use]
192 pub fn activity(mut self, kind: ActivityKind) -> Self {
193 self.activities.push(kind);
194 self
195 }
196
197 #[must_use]
199 pub fn kernels(self) -> Self {
200 self.activity(ActivityKind::Kernel)
201 }
202
203 #[must_use]
205 pub fn memcpy(self) -> Self {
206 self.activity(ActivityKind::MemoryCopy)
207 }
208
209 #[must_use]
211 pub fn sync(self) -> Self {
212 self.activity(ActivityKind::Synchronization)
213 }
214
215 #[must_use]
217 pub fn buffer_size(mut self, size: usize) -> Self {
218 self.buffer_size = Some(size);
219 self
220 }
221
222 #[must_use]
224 pub fn flush_period_ms(mut self, ms: u64) -> Self {
225 self.flush_period_ms = Some(ms);
226 self
227 }
228
229 pub fn build(self) -> CuptiResult<Profiler> {
231 let mut profiler = Profiler::new()?;
232 for activity in self.activities {
233 profiler.enable(activity)?;
234 }
235 Ok(profiler)
236 }
237}
238
239#[cfg(test)]
240mod tests {
241 use super::*;
242
243 #[test]
244 fn test_profiler_creation() {
245 let profiler = Profiler::new();
246 assert!(profiler.is_ok());
247 }
248
249 #[test]
250 fn test_profiler_default() {
251 let profiler = Profiler::default();
252 assert!(!profiler.is_active());
253 }
254
255 #[test]
256 fn test_profiler_builder() {
257 let builder = ProfilerBuilder::new()
258 .kernels()
259 .memcpy()
260 .buffer_size(1024 * 1024);
261
262 let profiler = builder.build();
263 if let Ok(p) = profiler {
265 assert!(p.enabled_activities().contains(&ActivityKind::Kernel));
266 assert!(p.enabled_activities().contains(&ActivityKind::MemoryCopy));
267 }
268 }
269
270 #[test]
271 fn test_activity_enable_disable() {
272 let mut profiler = Profiler::default();
273 if profiler.is_available() {
274 assert!(profiler.enable(ActivityKind::Kernel).is_ok());
275 assert!(profiler
276 .enabled_activities()
277 .contains(&ActivityKind::Kernel));
278 assert!(profiler.disable(ActivityKind::Kernel).is_ok());
279 assert!(!profiler
280 .enabled_activities()
281 .contains(&ActivityKind::Kernel));
282 }
283 }
284
285 #[test]
286 fn test_profiler_start_stop() {
287 let profiler = Profiler::default();
288 if profiler.is_available() {
289 assert!(profiler.start().is_ok());
290 assert!(profiler.is_active());
291 assert!(profiler.stop().is_ok());
292 assert!(!profiler.is_active());
293 }
294 }
295}