## Coverage Report

#### Created: 2026-01-25 15:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)

    /home/noah/src/realizar/src/gpu/diagnostics.rs

    Line
    Count
    Source
    1
    //! GPU Diagnostics Module (PMAT-802)
    2
    //!
    3
    //! Extracted from gpu/mod.rs - Logging, metrics, and debugging infrastructure.
    4
    //!
    5
    //! ## Contents
    6
    //! - `Logger`, `LogConfig`, `LogLevel`, `LogEntry` - Structured logging (IMP-079)
    7
    //! - `PhaseTimer`, `MemoryTracker`, `MemoryReport` - Performance tracking (IMP-080)
    8
    //! - `DiagnosticsCollector`, `DebugMode`, `RequestCapture`, `StateDump` - Debug tools (IMP-081)
    9
    10
    use std::collections::HashMap;
    11
    12
    // ============================================================================
    13
    // M32: Production Logging & Diagnostics (IMP-079, IMP-080, IMP-081)
    14
    // ============================================================================
    15
    16
    /// Log level (IMP-079)
    17
    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
    18
    pub enum LogLevel {
    19
        /// Trace level (most verbose)
    20
        Trace,
    21
        /// Debug level
    22
        Debug,
    23
        /// Info level
    24
        Info,
    25
        /// Warning level
    26
        Warn,
    27
        /// Error level
    28
        Error,
    29
    }
    30
    31
    impl LogLevel {
    32
    1
        fn as_str(self) -> &'static str {
    33
    1
            match self {
    34
    0
                Self::Trace => "TRACE",
    35
    0
                Self::Debug => "DEBUG",
    36
    1
                Self::Info => "INFO",
    37
    0
                Self::Warn => "WARN",
    38
    0
                Self::Error => "ERROR",
    39
            }
    40
    1
        }
    41
    }
    42
    43
    /// Log entry (IMP-079)
    44
    #[derive(Debug, Clone)]
    45
    pub struct LogEntry {
    46
        level: LogLevel,
    47
        message: String,
    48
        timestamp: u64,
    49
        correlation_id: Option<String>,
    50
        fields: HashMap<String, String>,
    51
    }
    52
    53
    impl LogEntry {
    54
        /// Create new log entry
    55
        #[must_use]
    56
    3
        pub fn new(level: LogLevel, message: &str) -> Self {
    57
            use std::time::{SystemTime, UNIX_EPOCH};
    58
    59
    3
            let timestamp = SystemTime::now()
    60
    3
                .duration_since(UNIX_EPOCH)
    61
    3
                .unwrap_or_default()
    62
    3
                .as_millis() as u64;
    63
    64
    3
            Self {
    65
    3
                level,
    66
    3
                message: message.to_string(),
    67
    3
                timestamp,
    68
    3
                correlation_id: None,
    69
    3
                fields: HashMap::new(),
    70
    3
            }
    71
    3
        }
    72
    73
        /// Set correlation ID
    74
        #[must_use]
    75
    1
        pub fn with_correlation_id(mut self, id: &str) -> Self {
    76
    1
            self.correlation_id = Some(id.to_string());
    77
    1
            self
    78
    1
        }
    79
    80
        /// Add custom field
    81
        #[must_use]
    82
    2
        pub fn with_field(mut self, key: &str, value: &str) -> Self {
    83
    2
            self.fields.insert(key.to_string(), value.to_string());
    84
    2
            self
    85
    2
        }
    86
    87
        /// Get correlation ID
    88
        #[must_use]
    89
    1
        pub fn correlation_id(&self) -> Option<&str> {
    90
    1
            self.correlation_id.as_deref()
    91
    1
        }
    92
    93
        /// Get log level
    94
        #[must_use]
    95
    1
        pub fn level(&self) -> LogLevel {
    96
    1
            self.level
    97
    1
        }
    98
    99
        /// Get timestamp
    100
        #[must_use]
    101
    1
        pub fn timestamp(&self) -> u64 {
    102
    1
            self.timestamp
    103
    1
        }
    104
    105
        /// Convert to JSON
    106
        #[must_use]
    107
    1
        pub fn to_json(&self) -> String {
    108
            use std::fmt::Write;
    109
    110
    1
            let mut json = format!(
    111
    1
                "{{\"level\":\"{}\",\"message\":\"{}\",\"timestamp\":{}",
    112
    1
                self.level.as_str(),
    113
                self.message,
    114
                self.timestamp
    115
            );
    116
    117
    1
            if let Some(ref id) = self.correlation_id {
    118
    1
                let _ = write!(json, ",\"correlation_id\":\"{}\"", id);
    119
    1
            }0
    120
    121
    3
            for (key2, value2) in &self.fields {
    122
    2
                let _ = write!(json, ",\"{}\":\"{}\"", key, value);
    123
    2
            }
    124
    125
    1
            json.push('}');
    126
    1
            json
    127
    1
        }
    128
    }
    129
    130
    /// Log configuration (IMP-079)
    131
    #[derive(Debug, Clone)]
    132
    pub struct LogConfig {
    133
        default_level: LogLevel,
    134
        json_format: bool,
    135
        module_levels: HashMap<String, LogLevel>,
    136
    }
    137
    138
    impl LogConfig {
    139
        /// Create new log config
    140
        #[must_use]
    141
    1
        pub fn new() -> Self {
    142
    1
            Self {
    143
    1
                default_level: LogLevel::Info,
    144
    1
                json_format: false,
    145
    1
                module_levels: HashMap::new(),
    146
    1
            }
    147
    1
        }
    148
    149
        /// Set default log level
    150
        #[must_use]
    151
    1
        pub fn with_level(mut self, level: LogLevel) -> Self {
    152
    1
            self.default_level = level;
    153
    1
            self
    154
    1
        }
    155
    156
        /// Enable JSON format
    157
        #[must_use]
    158
    1
        pub fn with_json_format(mut self, enabled: bool) -> Self {
    159
    1
            self.json_format = enabled;
    160
    1
            self
    161
    1
        }
    162
    163
        /// Set module-specific log level
    164
        #[must_use]
    165
    1
        pub fn with_module_level(mut self, module: &str, level: LogLevel) -> Self {
    166
    1
            self.module_levels.insert(module.to_string(), level);
    167
    1
            self
    168
    1
        }
    169
    }
    170
    171
    impl Default for LogConfig {
    172
    0
        fn default() -> Self {
    173
    0
            Self::new()
    174
    0
        }
    175
    }
    176
    177
    /// Logger (IMP-079)
    178
    pub struct Logger {
    179
        config: LogConfig,
    180
    }
    181
    182
    impl Logger {
    183
        /// Create new logger
    184
        #[must_use]
    185
    1
        pub fn new(config: LogConfig) -> Self {
    186
    1
            Self { config }
    187
    1
        }
    188
    189
        /// Check if log level is enabled for module
    190
        #[must_use]
    191
    3
        pub fn is_enabled(&self, level: LogLevel, module: &str) -> bool {
    192
    3
            let min_level = self
    193
    3
                .config
    194
    3
                .module_levels
    195
    3
                .get(module)
    196
    3
                .copied()
    197
    3
                .unwrap_or(self.config.default_level);
    198
    3
            level >= min_level
    199
    3
        }
    200
    }
    201
    202
    /// Phase timer for latency breakdown (IMP-080)
    203
    pub struct PhaseTimer {
    204
        phases: std::sync::Mutex<HashMap<String, (Option<std::time::Instant>, u64)>>,
    205
    }
    206
    207
    impl PhaseTimer {
    208
        /// Create new phase timer
    209
        #[must_use]
    210
    1
        pub fn new() -> Self {
    211
    1
            Self {
    212
    1
                phases: std::sync::Mutex::new(HashMap::new()),
    213
    1
            }
    214
    1
        }
    215
    216
        /// Start timing a phase
    217
    2
        pub fn start_phase(&self, name: &str) {
    218
    2
            let mut phases = self.phases.lock().expect("mutex poisoned");
    219
    2
            phases.insert(name.to_string(), (Some(std::time::Instant::now()), 0));
    220
    2
        }
    221
    222
        /// End timing a phase
    223
    2
        pub fn end_phase(&self, name: &str) {
    224
    2
            let mut phases = self.phases.lock().expect("mutex poisoned");
    225
    2
            if let Some((Some(start_time), _)) = phases.get(name) {
    226
    2
                let elapsed = start_time.elapsed().as_micros() as u64;
    227
    2
                phases.insert(name.to_string(), (None, elapsed));
    228
    2
            }0
    229
    2
        }
    230
    231
        /// Get timing breakdown
    232
        #[must_use]
    233
    2
        pub fn breakdown(&self) -> HashMap<String, u64> {
    234
    2
            let phases = self.phases.lock().expect("mutex poisoned");
    235
    4
            phases.iter()2.map2(|(k, (_, v))| (k.clone(), *v)).collect2()
    236
    2
        }
    237
    }
    238
    239
    impl Default for PhaseTimer {
    240
    0
        fn default() -> Self {
    241
    0
            Self::new()
    242
    0
        }
    243
    }
    244
    245
    /// Memory report (IMP-080)
    246
    #[derive(Debug, Clone)]
    247
    pub struct MemoryReport {
    248
        /// Peak memory usage in bytes
    249
        pub peak_bytes: u64,
    250
        /// Current memory usage in bytes
    251
        pub current_bytes: u64,
    252
        /// Total allocation count
    253
        pub allocation_count: u64,
    254
    }
    255
    256
    /// Memory tracker (IMP-080)
    257
    pub struct MemoryTracker {
    258
        current: std::sync::atomic::AtomicU64,
    259
        peak: std::sync::atomic::AtomicU64,
    260
        allocation_count: std::sync::atomic::AtomicU64,
    261
    }
    262
    263
    impl MemoryTracker {
    264
        /// Create new memory tracker
    265
        #[must_use]
    266
    2
        pub fn new() -> Self {
    267
    2
            Self {
    268
    2
                current: std::sync::atomic::AtomicU64::new(0),
    269
    2
                peak: std::sync::atomic::AtomicU64::new(0),
    270
    2
                allocation_count: std::sync::atomic::AtomicU64::new(0),
    271
    2
            }
    272
    2
        }
    273
    274
        /// Record memory allocation
    275
    3
        pub fn record_allocation(&self, _name: &str, bytes: u64) {
    276
    3
            let new_current = self
    277
    3
                .current
    278
    3
                .fetch_add(bytes, std::sync::atomic::Ordering::SeqCst)
    279
    3
                + bytes;
    280
    3
            self.allocation_count
    281
    3
                .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
    282
    283
            // Update peak if necessary
    284
    3
            let mut peak = self.peak.load(std::sync::atomic::Ordering::SeqCst);
    285
    3
            while new_current > peak {
    286
    3
                match self.peak.compare_exchange_weak(
    287
    3
                    peak,
    288
    3
                    new_current,
    289
    3
                    std::sync::atomic::Ordering::SeqCst,
    290
    3
                    std::sync::atomic::Ordering::Relaxed,
    291
    3
                ) {
    292
    3
                    Ok(_) => break,
    293
    0
                    Err(current_peak) => peak = current_peak,
    294
                }
    295
            }
    296
    3
        }
    297
    298
        /// Record memory deallocation
    299
    2
        pub fn record_deallocation(&self, _name: &str, bytes: u64) {
    300
    2
            self.current
    301
    2
                .fetch_sub(bytes, std::sync::atomic::Ordering::SeqCst);
    302
    2
        }
    303
    304
        /// Get memory report
    305
        #[must_use]
    306
    1
        pub fn report(&self) -> MemoryReport {
    307
    1
            MemoryReport {
    308
    1
                peak_bytes: self.peak.load(std::sync::atomic::Ordering::SeqCst),
    309
    1
                current_bytes: self.current.load(std::sync::atomic::Ordering::SeqCst),
    310
    1
                allocation_count: self
    311
    1
                    .allocation_count
    312
    1
                    .load(std::sync::atomic::Ordering::SeqCst),
    313
    1
            }
    314
    1
        }
    315
    }
    316
    317
    impl Default for MemoryTracker {
    318
    0
        fn default() -> Self {
    319
    0
            Self::new()
    320
    0
        }
    321
    }
    322
    323
    /// Diagnostics summary (IMP-080)
    324
    #[derive(Debug, Clone)]
    325
    pub struct DiagnosticsSummary {
    326
        /// Number of requests tracked
    327
        pub request_count: u64,
    328
    }
    329
    330
    /// Diagnostics collector (IMP-080)
    331
    pub struct DiagnosticsCollector {
    332
        /// Request count (pub(crate) for test access)
    333
        pub(crate) request_count: std::sync::atomic::AtomicU64,
    334
        #[allow(dead_code)]
    335
        timings: std::sync::Mutex<Vec<HashMap<String, u64>>>,
    336
        #[allow(dead_code)]
    337
        memory_snapshots: std::sync::Mutex<Vec<MemoryReport>>,
    338
    }
    339
    340
    impl DiagnosticsCollector {
    341
        /// Create new diagnostics collector
    342
        #[must_use]
    343
    2
        pub fn new() -> Self {
    344
    2
            Self {
    345
    2
                request_count: std::sync::atomic::AtomicU64::new(0),
    346
    2
                timings: std::sync::Mutex::new(Vec::new()),
    347
    2
                memory_snapshots: std::sync::Mutex::new(Vec::new()),
    348
    2
            }
    349
    2
        }
    350
    351
        /// Record request timing
    352
    1
        pub fn record_request_timing(&self, _request_id: &str, timing: HashMap<String, u64>) {
    353
    1
            self.request_count
    354
    1
                .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
    355
    1
            self.timings.lock().expect("mutex poisoned").push(timing);
    356
    1
        }
    357
    358
        /// Record memory snapshot
    359
    1
        pub fn record_memory_snapshot(&self, report: MemoryReport) {
    360
    1
            self.memory_snapshots
    361
    1
                .lock()
    362
    1
                .expect("mutex poisoned")
    363
    1
                .push(report);
    364
    1
        }
    365
    366
        /// Get diagnostics summary
    367
        #[must_use]
    368
    1
        pub fn summary(&self) -> DiagnosticsSummary {
    369
    1
            DiagnosticsSummary {
    370
    1
                request_count: self.request_count.load(std::sync::atomic::Ordering::SeqCst),
    371
    1
            }
    372
    1
        }
    373
    }
    374
    375
    impl Default for DiagnosticsCollector {
    376
    0
        fn default() -> Self {
    377
    0
            Self::new()
    378
    0
        }
    379
    }
    380
    381
    /// Debug mode controller (IMP-081)
    382
    pub struct DebugMode {
    383
        enabled: std::sync::atomic::AtomicBool,
    384
    }
    385
    386
    impl DebugMode {
    387
        /// Create new debug mode
    388
        #[must_use]
    389
    2
        pub fn new() -> Self {
    390
    2
            Self {
    391
    2
                enabled: std::sync::atomic::AtomicBool::new(false),
    392
    2
            }
    393
    2
        }
    394
    395
        /// Check if debug mode is enabled
    396
        #[must_use]
    397
    3
        pub fn is_enabled(&self) -> bool {
    398
    3
            self.enabled.load(std::sync::atomic::Ordering::SeqCst)
    399
    3
        }
    400
    401
        /// Enable debug mode
    402
    1
        pub fn enable(&self) {
    403
    1
            self.enabled
    404
    1
                .store(true, std::sync::atomic::Ordering::SeqCst);
    405
    1
        }
    406
    407
        /// Disable debug mode
    408
        #[allow(dead_code)]
    409
    0
        pub fn disable(&self) {
    410
    0
            self.enabled
    411
    0
                .store(false, std::sync::atomic::Ordering::SeqCst);
    412
    0
        }
    413
    }
    414
    415
    impl Default for DebugMode {
    416
    0
        fn default() -> Self {
    417
    0
            Self::new()
    418
    0
        }
    419
    }
    420
    421
    /// Request capture for replay (IMP-081)
    422
    #[derive(Debug, Clone)]
    423
    pub struct RequestCapture {
    424
        input: String,
    425
        params: HashMap<String, String>,
    426
    }
    427
    428
    impl RequestCapture {
    429
        /// Create new request capture
    430
        #[must_use]
    431
    2
        pub fn new() -> Self {
    432
    2
            Self {
    433
    2
                input: String::new(),
    434
    2
                params: HashMap::new(),
    435
    2
            }
    436
    2
        }
    437
    438
        /// Set input
    439
        #[must_use]
    440
    1
        pub fn with_input(mut self, input: &str) -> Self {
    441
    1
            self.input = input.to_string();
    442
    1
            self
    443
    1
        }
    444
    445
        /// Add parameter
    446
        #[must_use]
    447
    2
        pub fn with_params(mut self, key: &str, value: &str) -> Self {
    448
    2
            self.params.insert(key.to_string(), value.to_string());
    449
    2
            self
    450
    2
        }
    451
    452
        /// Get input
    453
        #[must_use]
    454
    2
        pub fn input(&self) -> &str {
    455
    2
            &self.input
    456
    2
        }
    457
    458
        /// Get params
    459
        #[must_use]
    460
    1
        pub fn params(&self) -> &HashMap<String, String> {
    461
    1
            &self.params
    462
    1
        }
    463
    464
        /// Serialize to JSON
    465
        #[must_use]
    466
    1
        pub fn to_json(&self) -> String {
    467
    1
            let params_json: Vec<String> = self
    468
    1
                .params
    469
    1
                .iter()
    470
    2
                .map1(|(k, v)| format!("\"{}\":\"{}\"", k, v))
    471
    1
                .collect();
    472
    1
            format!(
    473
    1
                "{{\"input\":\"{}\",\"params\":{{{}}}}}",
    474
                self.input,
    475
    1
                params_json.join(",")
    476
            )
    477
    1
        }
    478
    479
        /// Deserialize from JSON (simple implementation)
    480
        ///
    481
        /// # Errors
    482
        /// Returns error if JSON is malformed or missing input field.
    483
    1
        pub fn from_json(json: &str) -> std::result::Result<Self, &'static str> {
    484
            // Simple extraction - production would use serde
    485
    1
            let input_start = json.find("\"input\":\"").ok_or("Missing input")?0;
    486
    1
            let input_rest = &json[input_start + 9..];
    487
    1
            let input_end = input_rest.find('"').ok_or("Invalid input")?0;
    488
    1
            let input = &input_rest[..input_end];
    489
    490
    1
            Ok(Self {
    491
    1
                input: input.to_string(),
    492
    1
                params: HashMap::new(),
    493
    1
            })
    494
    1
        }
    495
    }
    496
    497
    impl Default for RequestCapture {
    498
    0
        fn default() -> Self {
    499
    0
            Self::new()
    500
    0
        }
    501
    }
    502
    503
    /// State dump for debugging (IMP-081)
    504
    #[derive(Debug, Clone)]
    505
    pub struct StateDump {
    506
        error: String,
    507
        stack_trace: String,
    508
        state: HashMap<String, String>,
    509
    }
    510
    511
    impl StateDump {
    512
        /// Create new state dump
    513
        #[must_use]
    514
    2
        pub fn new() -> Self {
    515
    2
            Self {
    516
    2
                error: String::new(),
    517
    2
                stack_trace: String::new(),
    518
    2
                state: HashMap::new(),
    519
    2
            }
    520
    2
        }
    521
    522
        /// Set error
    523
        #[must_use]
    524
    1
        pub fn with_error(mut self, error: &str) -> Self {
    525
    1
            self.error = error.to_string();
    526
    1
            self
    527
    1
        }
    528
    529
        /// Set stack trace
    530
        #[must_use]
    531
    1
        pub fn with_stack_trace(mut self, trace: &str) -> Self {
    532
    1
            self.stack_trace = trace.to_string();
    533
    1
            self
    534
    1
        }
    535
    536
        /// Add state
    537
        #[must_use]
    538
    2
        pub fn with_state(mut self, key: &str, value: &str) -> Self {
    539
    2
            self.state.insert(key.to_string(), value.to_string());
    540
    2
            self
    541
    2
        }
    542
    543
        /// Get error
    544
        #[must_use]
    545
    1
        pub fn error(&self) -> &str {
    546
    1
            &self.error
    547
    1
        }
    548
    549
        /// Get stack trace
    550
        #[must_use]
    551
    1
        pub fn stack_trace(&self) -> &str {
    552
    1
            &self.stack_trace
    553
    1
        }
    554
    555
        /// Get state
    556
        #[must_use]
    557
    1
        pub fn state(&self) -> &HashMap<String, String> {
    558
    1
            &self.state
    559
    1
        }
    560
    561
        /// Convert to JSON
    562
        #[must_use]
    563
    1
        pub fn to_json(&self) -> String {
    564
    1
            let state_json: Vec<String> = self
    565
    1
                .state
    566
    1
                .iter()
    567
    2
                .map1(|(k, v)| format!("\"{}\":\"{}\"", k, v))
    568
    1
                .collect();
    569
    1
            format!(
    570
    1
                "{{\"error\":\"{}\",\"stack_trace\":\"{}\",\"state\":{{{}}}}}",
    571
                self.error,
    572
    1
                self.stack_trace.replace('\n', "\\n"),
    573
    1
                state_json.join(",")
    574
            )
    575
    1
        }
    576
    }
    577
    578
    impl Default for StateDump {
    579
    0
        fn default() -> Self {
    580
    0
            Self::new()
    581
    0
        }
    582
    }