#![allow(dead_code)]
#![allow(unexpected_cfgs)]
use crate::{JitError, JitResult};
use std::collections::HashMap;
use std::fmt;
use std::time::Instant;
#[derive(Debug)]
pub struct ErrorDiagnosticsManager {
config: DiagnosticsConfig,
error_history: Vec<DiagnosticError>,
error_patterns: HashMap<String, ErrorPattern>,
recovery_suggestions: HashMap<ErrorCategory, Vec<RecoverySuggestion>>,
context_stack: Vec<DiagnosticContext>,
stats: DiagnosticsStats,
}
#[derive(Debug)]
pub struct DiagnosticError {
pub id: String,
pub timestamp: Instant,
pub category: ErrorCategory,
pub severity: ErrorSeverity,
pub message: String,
pub source_location: Option<SourceLocation>,
pub stack_trace: Vec<StackFrame>,
pub context: DiagnosticContext,
pub related_errors: Vec<String>,
pub suggestions: Vec<RecoverySuggestion>,
pub metadata: HashMap<String, String>,
pub underlying_error: Option<Box<JitError>>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ErrorCategory {
GraphConstruction,
TypeInference,
ShapeInference,
Optimization,
CodeGeneration,
Runtime,
Memory,
Resource,
UserInput,
Internal,
External,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ErrorSeverity {
Info,
Warning,
Error,
Fatal,
Ice, }
#[derive(Debug, Clone)]
pub struct SourceLocation {
pub file: String,
pub line: u32,
pub column: u32,
pub length: Option<u32>,
pub snippet: Option<String>,
}
#[derive(Debug, Clone)]
pub struct StackFrame {
pub function: String,
pub file: Option<String>,
pub line: Option<u32>,
pub address: Option<u64>,
pub module: Option<String>,
}
#[derive(Debug, Clone)]
pub struct DiagnosticContext {
pub operation: String,
pub input: String,
pub expected: Option<String>,
pub actual: Option<String>,
pub environment: EnvironmentInfo,
pub data: HashMap<String, String>,
}
#[derive(Debug, Clone)]
pub struct EnvironmentInfo {
pub rust_version: String,
pub torsh_version: String,
pub target_arch: String,
pub target_os: String,
pub available_memory: Option<u64>,
pub cpu_info: Option<String>,
pub gpu_info: Option<String>,
}
#[derive(Debug, Clone)]
pub struct ErrorPattern {
pub name: String,
pub description: String,
pub criteria: Vec<MatchCriterion>,
pub common_causes: Vec<String>,
pub solutions: Vec<RecoverySuggestion>,
pub frequency: u64,
}
#[derive(Debug, Clone)]
pub enum MatchCriterion {
MessageContains(String),
CategoryEquals(ErrorCategory),
LocationMatches(String),
StackContains(String),
Custom(fn(&DiagnosticError) -> bool),
}
#[derive(Debug, Clone)]
pub struct RecoverySuggestion {
pub suggestion_type: SuggestionType,
pub message: String,
pub explanation: Option<String>,
pub code_example: Option<String>,
pub doc_link: Option<String>,
pub confidence: f32,
pub auto_fix: Option<AutoFix>,
}
#[derive(Debug, Clone)]
pub enum SuggestionType {
QuickFix,
CodeChange,
ConfigChange,
EnvironmentSetup,
Documentation,
Workaround,
Investigation,
}
#[derive(Debug, Clone)]
pub struct AutoFix {
pub description: String,
pub fix_fn: fn(&DiagnosticError) -> JitResult<()>,
pub side_effects: Vec<String>,
pub requires_confirmation: bool,
}
#[derive(Debug, Clone)]
pub struct DiagnosticsConfig {
pub enabled: bool,
pub max_history_size: usize,
pub collect_stack_traces: bool,
pub extract_source_snippets: bool,
pub reporting_level: ErrorSeverity,
pub enable_pattern_matching: bool,
pub enable_suggestions: bool,
pub max_suggestions: usize,
pub color_output: bool,
pub verbose: bool,
}
#[derive(Debug, Clone, Default)]
pub struct DiagnosticsStats {
pub total_errors: u64,
pub errors_by_category: HashMap<ErrorCategory, u64>,
pub errors_by_severity: HashMap<ErrorSeverity, u64>,
pub pattern_matches: u64,
pub suggestions_provided: u64,
pub auto_fixes_applied: u64,
}
pub struct ErrorFormatter {
config: FormatterConfig,
}
#[derive(Debug, Clone)]
pub struct FormatterConfig {
pub include_source: bool,
pub include_stack_trace: bool,
pub include_suggestions: bool,
pub use_colors: bool,
pub max_line_length: usize,
pub indent_size: usize,
}
impl Default for DiagnosticsConfig {
fn default() -> Self {
Self {
enabled: true,
max_history_size: 1000,
collect_stack_traces: true,
extract_source_snippets: true,
reporting_level: ErrorSeverity::Warning,
enable_pattern_matching: true,
enable_suggestions: true,
max_suggestions: 5,
color_output: true,
verbose: false,
}
}
}
impl Default for FormatterConfig {
fn default() -> Self {
Self {
include_source: true,
include_stack_trace: true,
include_suggestions: true,
use_colors: true,
max_line_length: 120,
indent_size: 2,
}
}
}
impl ErrorDiagnosticsManager {
pub fn new(config: DiagnosticsConfig) -> Self {
let mut manager = Self {
config,
error_history: Vec::new(),
error_patterns: HashMap::new(),
recovery_suggestions: HashMap::new(),
context_stack: Vec::new(),
stats: DiagnosticsStats::default(),
};
manager.initialize_default_patterns();
manager.initialize_default_suggestions();
manager
}
pub fn with_defaults() -> Self {
Self::new(DiagnosticsConfig::default())
}
pub fn record_error(&mut self, error: JitError) -> DiagnosticError {
let mut diagnostic_error = self.create_diagnostic_error(error);
self.stats.total_errors += 1;
*self
.stats
.errors_by_category
.entry(diagnostic_error.category.clone())
.or_insert(0) += 1;
*self
.stats
.errors_by_severity
.entry(diagnostic_error.severity.clone())
.or_insert(0) += 1;
if self.config.enable_pattern_matching {
self.match_error_patterns(&diagnostic_error);
}
if self.config.enable_suggestions {
self.add_recovery_suggestions(&mut diagnostic_error);
}
let diagnostic_error_copy = DiagnosticError {
id: diagnostic_error.id.clone(),
timestamp: diagnostic_error.timestamp,
category: diagnostic_error.category.clone(),
severity: diagnostic_error.severity.clone(),
message: diagnostic_error.message.clone(),
source_location: diagnostic_error.source_location.clone(),
stack_trace: diagnostic_error.stack_trace.clone(),
context: diagnostic_error.context.clone(),
related_errors: diagnostic_error.related_errors.clone(),
suggestions: diagnostic_error.suggestions.clone(),
metadata: diagnostic_error.metadata.clone(),
underlying_error: None, };
if self.error_history.len() >= self.config.max_history_size {
self.error_history.remove(0);
}
self.error_history.push(diagnostic_error_copy);
diagnostic_error
}
fn create_diagnostic_error(&self, error: JitError) -> DiagnosticError {
let error_id = format!("err_{}", self.stats.total_errors);
let category = self.categorize_error(&error);
let severity = self.determine_severity(&error);
let message = error.to_string();
let context = self
.context_stack
.last()
.cloned()
.unwrap_or_else(|| DiagnosticContext {
operation: "unknown".to_string(),
input: "unknown".to_string(),
expected: None,
actual: None,
environment: self.get_environment_info(),
data: HashMap::new(),
});
DiagnosticError {
id: error_id,
timestamp: Instant::now(),
category,
severity,
message,
source_location: None,
stack_trace: self.collect_stack_trace(),
context,
related_errors: Vec::new(),
suggestions: Vec::new(),
metadata: HashMap::new(),
underlying_error: Some(Box::new(error)),
}
}
fn categorize_error(&self, error: &JitError) -> ErrorCategory {
match error {
JitError::GraphError(_) => ErrorCategory::GraphConstruction,
JitError::OptimizationError(_) => ErrorCategory::Optimization,
JitError::CodeGenError(_) => ErrorCategory::CodeGeneration,
JitError::RuntimeError(_) => ErrorCategory::Runtime,
JitError::UnsupportedOp(_) => ErrorCategory::UserInput,
JitError::CompilationError(_) => ErrorCategory::CodeGeneration,
JitError::AnalysisError(_) => ErrorCategory::TypeInference,
JitError::BackendError(_) => ErrorCategory::External,
JitError::FusionError(_) => ErrorCategory::Optimization,
JitError::AbstractInterpretationError(_) => ErrorCategory::TypeInference,
}
}
fn determine_severity(&self, error: &JitError) -> ErrorSeverity {
match error {
JitError::GraphError(_) => ErrorSeverity::Error,
JitError::OptimizationError(_) => ErrorSeverity::Warning,
JitError::CodeGenError(_) => ErrorSeverity::Error,
JitError::RuntimeError(_) => ErrorSeverity::Error,
JitError::UnsupportedOp(_) => ErrorSeverity::Error,
JitError::CompilationError(_) => ErrorSeverity::Error,
JitError::AnalysisError(_) => ErrorSeverity::Warning,
JitError::BackendError(_) => ErrorSeverity::Fatal,
JitError::FusionError(_) => ErrorSeverity::Warning,
JitError::AbstractInterpretationError(_) => ErrorSeverity::Warning,
}
}
fn collect_stack_trace(&self) -> Vec<StackFrame> {
if !self.config.collect_stack_traces {
return Vec::new();
}
#[cfg(feature = "std_backtrace")]
{
use std::backtrace::{Backtrace, BacktraceStatus};
let bt = Backtrace::capture();
if bt.status() == BacktraceStatus::Captured {
let bt_str = format!("{:?}", bt);
return self.parse_backtrace_string(&bt_str);
}
}
let mut frames = Vec::new();
let thread = std::thread::current();
frames.push(StackFrame {
function: thread.name().unwrap_or("unknown").to_string(),
file: None,
line: None,
address: None,
module: Some("torsh_jit".to_string()),
});
frames
}
#[cfg(feature = "std_backtrace")]
fn parse_backtrace_string(&self, backtrace: &str) -> Vec<StackFrame> {
let mut frames = Vec::new();
for line in backtrace.lines().take(20) {
if let Some(function) = line.split("::").last() {
frames.push(StackFrame {
function: function.trim().to_string(),
file: None,
line: None,
address: None,
module: Some("torsh_jit".to_string()),
});
}
}
frames
}
fn get_environment_info(&self) -> EnvironmentInfo {
EnvironmentInfo {
rust_version: self.get_rust_version(),
torsh_version: env!("CARGO_PKG_VERSION").to_string(),
target_arch: std::env::consts::ARCH.to_string(),
target_os: std::env::consts::OS.to_string(),
available_memory: self.get_available_memory(),
cpu_info: self.get_cpu_info(),
gpu_info: self.get_gpu_info(),
}
}
fn get_rust_version(&self) -> String {
std::env::var("RUSTC_VERSION")
.unwrap_or_else(|_| env!("CARGO_PKG_RUST_VERSION").to_string())
}
fn get_available_memory(&self) -> Option<u64> {
#[cfg(target_os = "linux")]
{
if let Ok(contents) = std::fs::read_to_string("/proc/meminfo") {
for line in contents.lines() {
if line.starts_with("MemTotal:") {
if let Some(kb_str) = line.split_whitespace().nth(1) {
if let Ok(kb) = kb_str.parse::<u64>() {
return Some(kb * 1024); }
}
}
}
}
}
#[cfg(target_os = "macos")]
{
use std::process::Command;
if let Ok(output) = Command::new("sysctl").arg("hw.memsize").output() {
if let Ok(text) = String::from_utf8(output.stdout) {
if let Some(size) = text.split(':').nth(1) {
if let Ok(bytes) = size.trim().parse::<u64>() {
return Some(bytes);
}
}
}
}
}
None
}
fn get_cpu_info(&self) -> Option<String> {
#[cfg(target_os = "linux")]
{
if let Ok(contents) = std::fs::read_to_string("/proc/cpuinfo") {
for line in contents.lines() {
if line.starts_with("model name") {
if let Some(name) = line.split(':').nth(1) {
return Some(name.trim().to_string());
}
}
}
}
}
#[cfg(target_os = "macos")]
{
use std::process::Command;
if let Ok(output) = Command::new("sysctl")
.arg("-n")
.arg("machdep.cpu.brand_string")
.output()
{
if let Ok(cpu) = String::from_utf8(output.stdout) {
return Some(cpu.trim().to_string());
}
}
}
Some(format!("{} core(s)", num_cpus::get()))
}
fn get_gpu_info(&self) -> Option<String> {
#[cfg(feature = "gpu")]
{
Some("GPU support enabled".to_string())
}
#[cfg(not(feature = "gpu"))]
{
None
}
}
fn match_error_patterns(&mut self, error: &DiagnosticError) {
let mut matched_patterns = Vec::new();
for (pattern_name, pattern) in &self.error_patterns {
if self.matches_pattern(error, pattern) {
self.stats.pattern_matches += 1;
matched_patterns.push((pattern_name.clone(), pattern.clone()));
}
}
for (_pattern_name, _pattern) in matched_patterns {
}
}
fn apply_pattern_handling(
&mut self,
error: &mut DiagnosticError,
pattern_name: &str,
pattern: &ErrorPattern,
) {
for suggestion in &pattern.solutions {
error.suggestions.push(suggestion.clone());
}
for cause in &pattern.common_causes {
error
.related_errors
.push(format!("Common cause: {}", cause));
}
if let Some(ref ctx) = self.context_stack.last() {
error.related_errors.push(format!(
"Pattern '{}' matched in context: {}",
pattern_name, ctx.operation
));
}
self.stats.suggestions_provided += pattern.solutions.len() as u64;
if pattern.frequency > 100 {
if error.severity == ErrorSeverity::Error {
error.severity = ErrorSeverity::Warning;
}
}
}
fn matches_pattern(&self, error: &DiagnosticError, pattern: &ErrorPattern) -> bool {
for criterion in &pattern.criteria {
match criterion {
MatchCriterion::MessageContains(text) => {
if !error.message.contains(text) {
return false;
}
}
MatchCriterion::CategoryEquals(category) => {
if error.category != *category {
return false;
}
}
MatchCriterion::LocationMatches(pattern) => {
if let Some(ref location) = error.source_location {
let location_str =
format!("{}:{}:{}", location.file, location.line, location.column);
if !location_str.contains(pattern) {
return false;
}
} else {
return false;
}
}
MatchCriterion::StackContains(function) => {
if !error
.stack_trace
.iter()
.any(|frame| frame.function.contains(function))
{
return false;
}
}
MatchCriterion::Custom(matcher) => {
if !matcher(error) {
return false;
}
}
}
}
true
}
fn add_recovery_suggestions(&mut self, error: &mut DiagnosticError) {
if let Some(suggestions) = self.recovery_suggestions.get(&error.category) {
for suggestion in suggestions.iter().take(self.config.max_suggestions) {
error.suggestions.push(suggestion.clone());
self.stats.suggestions_provided += 1;
}
}
}
fn initialize_default_patterns(&mut self) {
let type_mismatch = ErrorPattern {
name: "type_mismatch".to_string(),
description: "Type mismatch in operation".to_string(),
criteria: vec![
MatchCriterion::MessageContains("type".to_string()),
MatchCriterion::CategoryEquals(ErrorCategory::TypeInference),
],
common_causes: vec![
"Incorrect input types".to_string(),
"Missing type annotations".to_string(),
],
solutions: vec![],
frequency: 0,
};
self.error_patterns
.insert("type_mismatch".to_string(), type_mismatch);
let shape_mismatch = ErrorPattern {
name: "shape_mismatch".to_string(),
description: "Shape mismatch in tensor operation".to_string(),
criteria: vec![
MatchCriterion::MessageContains("shape".to_string()),
MatchCriterion::CategoryEquals(ErrorCategory::ShapeInference),
],
common_causes: vec![
"Incompatible tensor shapes".to_string(),
"Missing shape information".to_string(),
],
solutions: vec![],
frequency: 0,
};
self.error_patterns
.insert("shape_mismatch".to_string(), shape_mismatch);
}
fn initialize_default_suggestions(&mut self) {
let type_suggestions = vec![RecoverySuggestion {
suggestion_type: SuggestionType::CodeChange,
message: "Check input types and add explicit type annotations".to_string(),
explanation: Some(
"Type inference failed. Consider adding explicit type information.".to_string(),
),
code_example: Some("tensor.cast(DType::F32)".to_string()),
doc_link: Some("https://docs.rs/torsh/latest/torsh/".to_string()),
confidence: 0.8,
auto_fix: None,
}];
self.recovery_suggestions
.insert(ErrorCategory::TypeInference, type_suggestions);
let shape_suggestions = vec![
RecoverySuggestion {
suggestion_type: SuggestionType::CodeChange,
message: "Verify tensor shapes are compatible for the operation".to_string(),
explanation: Some("Shape inference failed. Check that tensor dimensions match operation requirements.".to_string()),
code_example: Some("tensor.reshape(&[batch_size, channels, height, width])".to_string()),
doc_link: Some("https://docs.rs/torsh/latest/torsh/".to_string()),
confidence: 0.9,
auto_fix: None,
},
];
self.recovery_suggestions
.insert(ErrorCategory::ShapeInference, shape_suggestions);
}
pub fn push_context(&mut self, context: DiagnosticContext) {
self.context_stack.push(context);
}
pub fn pop_context(&mut self) -> Option<DiagnosticContext> {
self.context_stack.pop()
}
pub fn get_error_history(&self) -> &[DiagnosticError] {
&self.error_history
}
pub fn get_stats(&self) -> &DiagnosticsStats {
&self.stats
}
pub fn format_error(&self, error: &DiagnosticError, format_config: &FormatterConfig) -> String {
let formatter = ErrorFormatter::new(format_config.clone());
formatter.format(error)
}
pub fn get_similar_errors(&self, error: &DiagnosticError) -> Vec<&DiagnosticError> {
self.error_history
.iter()
.filter(|e| e.category == error.category && e.severity == error.severity)
.collect()
}
pub fn export_diagnostics(&self, output_path: &str) -> JitResult<()> {
let diagnostics_data = format!(
r#"{{"total_errors": {}, "errors_by_category": {:?}, "patterns": {}}}"#,
self.stats.total_errors,
self.stats.errors_by_category,
self.error_patterns.len()
);
std::fs::write(output_path, diagnostics_data)
.map_err(|e| JitError::RuntimeError(format!("Failed to export diagnostics: {}", e)))?;
Ok(())
}
}
impl ErrorFormatter {
pub fn new(config: FormatterConfig) -> Self {
Self { config }
}
pub fn format(&self, error: &DiagnosticError) -> String {
let mut output = String::new();
output.push_str(&format!(
"{}[{}] {}: {}\n",
self.color_for_severity(&error.severity),
error.severity.as_str(),
error.category.as_str(),
error.message
));
if let Some(location) = &error.source_location {
output.push_str(&format!(
" --> {}:{}:{}\n",
location.file, location.line, location.column
));
if self.config.include_source {
if let Some(snippet) = &location.snippet {
output.push_str(&format!(" |\n | {}\n |\n", snippet));
}
}
}
output.push_str(&format!(
" Context: {} ({})\n",
error.context.operation, error.context.input
));
if self.config.include_stack_trace && !error.stack_trace.is_empty() {
output.push_str(" Stack trace:\n");
for frame in &error.stack_trace {
output.push_str(&format!(
" at {} ({}:{})\n",
frame.function,
frame.file.as_ref().unwrap_or(&"unknown".to_string()),
frame.line.unwrap_or(0)
));
}
}
if self.config.include_suggestions && !error.suggestions.is_empty() {
output.push_str(" Suggestions:\n");
for suggestion in &error.suggestions {
output.push_str(&format!(" - {}\n", suggestion.message));
if let Some(explanation) = &suggestion.explanation {
output.push_str(&format!(" {}\n", explanation));
}
}
}
output.push_str(&self.reset_color());
output
}
fn color_for_severity(&self, severity: &ErrorSeverity) -> &str {
if !self.config.use_colors {
return "";
}
match severity {
ErrorSeverity::Info => "\x1b[36m", ErrorSeverity::Warning => "\x1b[33m", ErrorSeverity::Error => "\x1b[31m", ErrorSeverity::Fatal => "\x1b[35m", ErrorSeverity::Ice => "\x1b[41m", }
}
fn reset_color(&self) -> &str {
if self.config.use_colors {
"\x1b[0m"
} else {
""
}
}
}
impl ErrorSeverity {
pub fn as_str(&self) -> &str {
match self {
ErrorSeverity::Info => "INFO",
ErrorSeverity::Warning => "WARNING",
ErrorSeverity::Error => "ERROR",
ErrorSeverity::Fatal => "FATAL",
ErrorSeverity::Ice => "ICE",
}
}
}
impl ErrorCategory {
pub fn as_str(&self) -> &str {
match self {
ErrorCategory::GraphConstruction => "GRAPH",
ErrorCategory::TypeInference => "TYPE",
ErrorCategory::ShapeInference => "SHAPE",
ErrorCategory::Optimization => "OPT",
ErrorCategory::CodeGeneration => "CODEGEN",
ErrorCategory::Runtime => "RUNTIME",
ErrorCategory::Memory => "MEMORY",
ErrorCategory::Resource => "RESOURCE",
ErrorCategory::UserInput => "INPUT",
ErrorCategory::Internal => "INTERNAL",
ErrorCategory::External => "EXTERNAL",
}
}
}
impl fmt::Display for DiagnosticError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"[{}] {}: {}",
self.severity.as_str(),
self.category.as_str(),
self.message
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_diagnostics_manager_creation() {
let manager = ErrorDiagnosticsManager::with_defaults();
assert!(manager.config.enabled);
assert_eq!(manager.config.max_history_size, 1000);
}
#[test]
fn test_error_recording() {
let mut manager = ErrorDiagnosticsManager::with_defaults();
let error = JitError::RuntimeError("Test error".to_string());
let diagnostic_error = manager.record_error(error);
assert_eq!(diagnostic_error.category, ErrorCategory::Runtime);
assert_eq!(diagnostic_error.severity, ErrorSeverity::Error);
assert!(diagnostic_error.message.contains("Test error"));
}
#[test]
fn test_error_categorization() {
let manager = ErrorDiagnosticsManager::with_defaults();
let graph_error = JitError::GraphError("Graph error".to_string());
assert_eq!(
manager.categorize_error(&graph_error),
ErrorCategory::GraphConstruction
);
let runtime_error = JitError::RuntimeError("Runtime error".to_string());
assert_eq!(
manager.categorize_error(&runtime_error),
ErrorCategory::Runtime
);
}
#[test]
fn test_error_formatting() {
let mut manager = ErrorDiagnosticsManager::with_defaults();
let error = JitError::RuntimeError("Test error".to_string());
let diagnostic_error = manager.record_error(error);
let formatter_config = FormatterConfig::default();
let formatted = manager.format_error(&diagnostic_error, &formatter_config);
assert!(formatted.contains("ERROR"));
assert!(formatted.contains("RUNTIME"));
assert!(formatted.contains("Test error"));
}
#[test]
fn test_context_stack() {
let mut manager = ErrorDiagnosticsManager::with_defaults();
let context = DiagnosticContext {
operation: "test_operation".to_string(),
input: "test_input".to_string(),
expected: None,
actual: None,
environment: manager.get_environment_info(),
data: HashMap::new(),
};
manager.push_context(context.clone());
assert_eq!(manager.context_stack.len(), 1);
let popped = manager.pop_context().unwrap();
assert_eq!(popped.operation, "test_operation");
assert_eq!(manager.context_stack.len(), 0);
}
}