From 0c2e30d9669c19138d0c15b469a5f66377ab4b6c Mon Sep 17 00:00:00 2001 From: = <=> Date: Wed, 22 Nov 2023 21:32:46 +0300 Subject: [PATCH 01/11] libcasr/src/csharp.rs added. --- libcasr/src/csharp.rs | 79 +++++++++++++++++++++++++++++++++++++++++++ libcasr/src/lib.rs | 1 + 2 files changed, 80 insertions(+) create mode 100644 libcasr/src/csharp.rs diff --git a/libcasr/src/csharp.rs b/libcasr/src/csharp.rs new file mode 100644 index 00000000..3bb8776b --- /dev/null +++ b/libcasr/src/csharp.rs @@ -0,0 +1,79 @@ +//! C Sharp module implements `ParseStacktrace` and `Exception` traits for C Sharp reports. +use crate::exception::Exception; +use crate::stacktrace::ParseStacktrace; + +use crate::error::*; +use crate::execution_class::ExecutionClass; +use crate::stacktrace::StacktraceEntry; + +use regex::Regex; + +/// Structure provides an interface for processing the stack trace. +pub struct CSharpStacktrace; + +impl ParseStacktrace for CSharpStacktrace { + fn extract_stacktrace(stream: &str) -> Result> { + let re = Regex::new(r"(?m)^Unhandled Exception:(?:.|\n)+?((?:\n[^\S\n]*(?:at .+|--- End of inner exception stack trace ---))+)$").unwrap(); + + let Some(cap) = re.captures(stream) else { + return Err(Error::Casr("Couldn't find stacktrace".to_string())); + }; + + Ok(cap + .get(1) + .unwrap() + .as_str() + .split('\n') + .scan(false, |skip, s| { + let f = s.contains("--- End of inner exception stack trace ---"); + if *skip || f { + *skip = f; + return Some(""); + } + Some(s) + }) + .map(|s| s.trim().to_string()) + .filter(|s| !s.is_empty()) + .collect::>()) + } + + fn parse_stacktrace_entry(entry: &str) -> Result { + let re = Regex::new(r"at (?:\(.+\) )?(.+?) ?\(.*\) <(\S+?).+> in (.+):(\d+)").unwrap(); + + let Some(cap) = re.captures(entry) else { + return Err(Error::Casr(format!("Couldn't parse stacktrace line: {entry}"))); + }; + + let mut stentry = StacktraceEntry::default(); + + let Ok(line) = cap.get(4).unwrap().as_str().parse::() else { + return Err(Error::Casr(format!("Couldn't parse stacktrace line num: {entry}"))); + }; + + stentry.debug.line = line; + stentry.function = cap.get(1).unwrap().as_str().to_string(); + stentry.debug.file = cap.get(3).unwrap().as_str().to_string(); + + Ok(stentry) + } +} + +/// Structure provides an interface for parsing c sharp exception message. +pub struct CSharpException; + +impl Exception for CSharpException { + fn parse_exception(stream: &str) -> Option { + let re = Regex::new(r"(?m)^Unhandled Exception:\n((?:.|\n)+?)\n[^\S\n]*at .+$").unwrap(); + + let description = re.captures(stream)? + .get(1)? + .as_str(); + + let v: Vec<&str> = description.rsplit_once("---> ") + .map_or(description, |(_, s)| s) + .splitn(2, ": ") + .collect(); + + Some(ExecutionClass { short_description: v[0].to_string(), description: v[1].to_string(), ..ExecutionClass::default()}) + } +} \ No newline at end of file diff --git a/libcasr/src/lib.rs b/libcasr/src/lib.rs index 557d7713..3fb4299d 100644 --- a/libcasr/src/lib.rs +++ b/libcasr/src/lib.rs @@ -40,3 +40,4 @@ pub mod sarif; pub mod severity; pub mod stacktrace; pub mod ubsan; +pub mod csharp; From ce2623830195aca08cf0ed509765a4dcae9f0284 Mon Sep 17 00:00:00 2001 From: BMikh0 Date: Thu, 7 Dec 2023 21:46:49 +0300 Subject: [PATCH 02/11] Added .NET and Mono without addresses formats. --- libcasr/src/csharp.rs | 105 ++++++++++++++++++++++++++++++------------ 1 file changed, 75 insertions(+), 30 deletions(-) diff --git a/libcasr/src/csharp.rs b/libcasr/src/csharp.rs index 3bb8776b..5ca1773d 100644 --- a/libcasr/src/csharp.rs +++ b/libcasr/src/csharp.rs @@ -1,6 +1,6 @@ //! C Sharp module implements `ParseStacktrace` and `Exception` traits for C Sharp reports. use crate::exception::Exception; -use crate::stacktrace::ParseStacktrace; +use crate::stacktrace::{ParseStacktrace, Stacktrace}; use crate::error::*; use crate::execution_class::ExecutionClass; @@ -11,50 +11,94 @@ use regex::Regex; /// Structure provides an interface for processing the stack trace. pub struct CSharpStacktrace; +impl CSharpStacktrace { + fn parse_stacktrace_entry_with_format(entry: &str, use_mono_format: bool) -> Result { + let re = + if use_mono_format { Regex::new(r"at (?:\(.+\) )?(.+?) ?\(.*\) (?:<0x(?
\w+).+>|\[\w+]) in (?.+):(?\d+)") } + else { Regex::new(r"at (?:\(.+\) )?(.+?)\(.*\) in (?.+):line (?\d+)") }.unwrap(); + + let Some(cap) = re.captures(entry) else { + return Err(Error::Casr(format!("Couldn't parse stacktrace line: {entry}"))); + }; + + let mut stentry = StacktraceEntry::default(); + + if let Some(num) = cap.name("address").map(|m| m.as_str()) { + if let Ok(address) = u64::from_str_radix(num, 16) { + stentry.address = address; + } else { + return Err(Error::Casr(format!("Couldn't parse address: {num}"))); + } + } + + let Ok(line) = cap.name("line").unwrap().as_str().parse::() else { + return Err(Error::Casr(format!("Couldn't parse stacktrace line num: {entry}"))); + }; + + stentry.function = cap.get(1).unwrap().as_str().to_string(); + stentry.debug.file = cap.name("file").unwrap().as_str().to_string(); + stentry.debug.line = line; + Ok(stentry) + } +} + impl ParseStacktrace for CSharpStacktrace { fn extract_stacktrace(stream: &str) -> Result> { - let re = Regex::new(r"(?m)^Unhandled Exception:(?:.|\n)+?((?:\n[^\S\n]*(?:at .+|--- End of inner exception stack trace ---))+)$").unwrap(); + let re = Regex::new(r"(?m)^Unhandled ([Ee])xception(:\n|\. )(?:.|\n)+?((?:\n[^\S\n]*(?:at.+|--- End of inner exception stack trace ---))+)$").unwrap(); - let Some(cap) = re.captures(stream) else { + let Some(cap) = re.captures(stream).and_then(|cap| + ((cap.get(1).unwrap().as_str() == "E") == (cap.get(2).unwrap().as_str() == ":\n")).then_some(cap) + ) else { return Err(Error::Casr("Couldn't find stacktrace".to_string())); }; Ok(cap - .get(1) + .get(3) .unwrap() .as_str() .split('\n') - .scan(false, |skip, s| { - let f = s.contains("--- End of inner exception stack trace ---"); - if *skip || f { - *skip = f; - return Some(""); - } - Some(s) - }) .map(|s| s.trim().to_string()) .filter(|s| !s.is_empty()) .collect::>()) } fn parse_stacktrace_entry(entry: &str) -> Result { - let re = Regex::new(r"at (?:\(.+\) )?(.+?) ?\(.*\) <(\S+?).+> in (.+):(\d+)").unwrap(); + Self::parse_stacktrace_entry_with_format(entry, true) + .or(Self::parse_stacktrace_entry_with_format(entry, false)) + } - let Some(cap) = re.captures(entry) else { - return Err(Error::Casr(format!("Couldn't parse stacktrace line: {entry}"))); - }; + fn parse_stacktrace(entries: &[String]) -> Result { + let mut iter = entries + .iter() + .scan((true, false), |(cancel_skip, skip), s| { + // Skipping all blocks consisting of "--- End of inner exception stack trace ---" + // and one stack frame after each such block, except for the first block if entries start with it. + let not_stack_frame = s == "--- End of inner exception stack trace ---"; - let mut stentry = StacktraceEntry::default(); + if not_stack_frame || *skip { + *skip = not_stack_frame; - let Ok(line) = cap.get(4).unwrap().as_str().parse::() else { - return Err(Error::Casr(format!("Couldn't parse stacktrace line num: {entry}"))); - }; + if !*cancel_skip || not_stack_frame { + return Some(""); + } + } - stentry.debug.line = line; - stentry.function = cap.get(1).unwrap().as_str().to_string(); - stentry.debug.file = cap.get(3).unwrap().as_str().to_string(); + *cancel_skip = false; - Ok(stentry) + Some(s) + }) + .filter(|s| !s.is_empty()).peekable(); + + let use_mono_format = iter.peek().and_then(|&s| { + match Self::parse_stacktrace_entry_with_format(s, true) { + Err(Error::Casr(ref s)) if s.starts_with("Couldn't parse stacktrace line:") => None, + _ => Some(""), + } + }).is_some(); + + iter + .map(|s| Self::parse_stacktrace_entry_with_format(s, use_mono_format)) + .collect() } } @@ -63,17 +107,18 @@ pub struct CSharpException; impl Exception for CSharpException { fn parse_exception(stream: &str) -> Option { - let re = Regex::new(r"(?m)^Unhandled Exception:\n((?:.|\n)+?)\n[^\S\n]*at .+$").unwrap(); - - let description = re.captures(stream)? - .get(1)? + let re = Regex::new(r"(?m)^Unhandled ([Ee])xception(:\n|\. )((?:.|\n)+?)\n[^\S\n]*(at.+|--- End of inner exception stack trace ---)$").unwrap(); + + let description = re.captures(stream).and_then(|cap| + ((cap.get(1).unwrap().as_str() == "E") == (cap.get(2).unwrap().as_str() == ":\n")).then_some(cap) + )?.get(3)? .as_str(); - let v: Vec<&str> = description.rsplit_once("---> ") + let v: Vec<&str> = description.trim_start().rsplit_once(" ---> ") .map_or(description, |(_, s)| s) .splitn(2, ": ") .collect(); - Some(ExecutionClass { short_description: v[0].to_string(), description: v[1].to_string(), ..ExecutionClass::default()}) + Some(ExecutionClass { short_description: v[0].to_string(), description: v.get(1)?.to_string(), ..ExecutionClass::default()}) } } \ No newline at end of file From a66000f8e5283310ae209d50be324c1b605d3f42 Mon Sep 17 00:00:00 2001 From: BMikh0 Date: Mon, 25 Dec 2023 22:47:00 +0300 Subject: [PATCH 03/11] Exception and ParseStacktrace for C Sharp fixed. --- libcasr/src/csharp.rs | 56 +++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/libcasr/src/csharp.rs b/libcasr/src/csharp.rs index 5ca1773d..6dc2ec96 100644 --- a/libcasr/src/csharp.rs +++ b/libcasr/src/csharp.rs @@ -8,21 +8,31 @@ use crate::stacktrace::StacktraceEntry; use regex::Regex; +const PARSE_STACK_FRAME_ERR_PREFIX: &str = "Couldn't parse stacktrace line: "; + /// Structure provides an interface for processing the stack trace. pub struct CSharpStacktrace; impl CSharpStacktrace { - fn parse_stacktrace_entry_with_format(entry: &str, use_mono_format: bool) -> Result { + fn parse_stacktrace_entry(entry: &str, use_mono_format: bool) -> Result { let re = - if use_mono_format { Regex::new(r"at (?:\(.+\) )?(.+?) ?\(.*\) (?:<0x(?
\w+).+>|\[\w+]) in (?.+):(?\d+)") } - else { Regex::new(r"at (?:\(.+\) )?(.+?)\(.*\) in (?.+):line (?\d+)") }.unwrap(); + if use_mono_format { Regex::new(r"^at (?:\(.+\) )?(.+?) ?\(.*\) (?:<0x(?
\w+).+>|\[\w+]) in (?:<\w+>|(?.+)):(?\w+)$") } + else { Regex::new(r"^at (?:\(.+\) )?(.+?)\(.*\)(?: in (?.+):line (?\w+))?$") }.unwrap(); let Some(cap) = re.captures(entry) else { - return Err(Error::Casr(format!("Couldn't parse stacktrace line: {entry}"))); + return Err(Error::Casr(format!("{PARSE_STACK_FRAME_ERR_PREFIX}{entry}"))); }; let mut stentry = StacktraceEntry::default(); + if let Some(file) = cap.name("file").map(|m| m.as_str().to_string()) { + let Some(line) = cap.name("line").and_then(|m| m.as_str().parse::().ok()) else { + return Err(Error::Casr(format!("Couldn't parse stacktrace line num: {entry}"))); + }; + stentry.debug.file = file; + stentry.debug.line = line; + } + if let Some(num) = cap.name("address").map(|m| m.as_str()) { if let Ok(address) = u64::from_str_radix(num, 16) { stentry.address = address; @@ -31,13 +41,8 @@ impl CSharpStacktrace { } } - let Ok(line) = cap.name("line").unwrap().as_str().parse::() else { - return Err(Error::Casr(format!("Couldn't parse stacktrace line num: {entry}"))); - }; - stentry.function = cap.get(1).unwrap().as_str().to_string(); - stentry.debug.file = cap.name("file").unwrap().as_str().to_string(); - stentry.debug.line = line; + Ok(stentry) } } @@ -63,12 +68,14 @@ impl ParseStacktrace for CSharpStacktrace { } fn parse_stacktrace_entry(entry: &str) -> Result { - Self::parse_stacktrace_entry_with_format(entry, true) - .or(Self::parse_stacktrace_entry_with_format(entry, false)) + match Self::parse_stacktrace_entry(entry, true) { + Err(Error::Casr(ref s)) if s.starts_with(PARSE_STACK_FRAME_ERR_PREFIX) => Self::parse_stacktrace_entry(entry, false), + r @ _ => r + } } fn parse_stacktrace(entries: &[String]) -> Result { - let mut iter = entries + let iter = entries .iter() .scan((true, false), |(cancel_skip, skip), s| { // Skipping all blocks consisting of "--- End of inner exception stack trace ---" @@ -87,17 +94,13 @@ impl ParseStacktrace for CSharpStacktrace { Some(s) }) - .filter(|s| !s.is_empty()).peekable(); + .filter(|&s| !s.is_empty()); - let use_mono_format = iter.peek().and_then(|&s| { - match Self::parse_stacktrace_entry_with_format(s, true) { - Err(Error::Casr(ref s)) if s.starts_with("Couldn't parse stacktrace line:") => None, - _ => Some(""), - } - }).is_some(); + let re_mono = Regex::new(r"^at (?:\(.+\) )?(.+?) ?\(.*\) (?:<0x\w+.+>|\[\w+]) in (?:<\w+>|.+):\w+$").unwrap(); + let use_mono_format = iter.clone().all(|s| re_mono.captures(s).is_some()); iter - .map(|s| Self::parse_stacktrace_entry_with_format(s, use_mono_format)) + .map(|s| Self::parse_stacktrace_entry(s, use_mono_format)) .collect() } } @@ -107,11 +110,12 @@ pub struct CSharpException; impl Exception for CSharpException { fn parse_exception(stream: &str) -> Option { - let re = Regex::new(r"(?m)^Unhandled ([Ee])xception(:\n|\. )((?:.|\n)+?)\n[^\S\n]*(at.+|--- End of inner exception stack trace ---)$").unwrap(); - - let description = re.captures(stream).and_then(|cap| - ((cap.get(1).unwrap().as_str() == "E") == (cap.get(2).unwrap().as_str() == ":\n")).then_some(cap) - )?.get(3)? + let re_mono = Regex::new(r"(?m)^Unhandled Exception:\n((?:.|\n)+?(?: ---> (?:.|\n)+?)*?)\n[^\S\n]*(at.+|--- End of inner exception stack trace ---)$").unwrap(); + let re_dotnet = Regex::new(r"(?m)^Unhandled exception\. ((?:.|\n)+?(?:\n ---> (?:.|\n)+?)*?)\n[^\S\n]*(at.+|--- End of inner exception stack trace ---)$").unwrap(); + + let description = re_mono.captures(stream) + .or (re_dotnet.captures(stream))? + .get(1)? .as_str(); let v: Vec<&str> = description.trim_start().rsplit_once(" ---> ") From 2fff0cb8124d92f7c711181c451ab7af5cd5531c Mon Sep 17 00:00:00 2001 From: BMikh0 Date: Tue, 9 Jan 2024 17:17:44 +0300 Subject: [PATCH 04/11] Bugs fixed. --- libcasr/src/csharp.rs | 133 ++++++++++++++++++++++++++++-------------- libcasr/src/lib.rs | 2 +- 2 files changed, 89 insertions(+), 46 deletions(-) diff --git a/libcasr/src/csharp.rs b/libcasr/src/csharp.rs index 6dc2ec96..a98c3fde 100644 --- a/libcasr/src/csharp.rs +++ b/libcasr/src/csharp.rs @@ -8,40 +8,83 @@ use crate::stacktrace::StacktraceEntry; use regex::Regex; -const PARSE_STACK_FRAME_ERR_PREFIX: &str = "Couldn't parse stacktrace line: "; +#[derive(Copy, Clone)] +enum StacktraceFormat { + Mono, + dotNET +} + +impl StacktraceFormat { + fn get_regex_for_stacktrace_entry(&self) -> Regex { + match self { + StacktraceFormat::Mono => Regex::new(r"^at (?:\(.+?\) )?(?P\S+)(?P ?\(.*?\))? (?:<(?P\w+) \+ (?P\w+)>|\[0x[\da-fA-F]+\]) in (?:<[\da-fA-F]+>|(?P.+)):(?P\w+)$"), + StacktraceFormat::dotNET => Regex::new(r"^at (?:\(.+?\) )?(?P\S+)(?P\(.*?\))?(?: in (?P.+):line (?P\w+))?$") + }.unwrap() + } + + fn get_format_by_stacktrace_entry(entry: &str) -> Result { + for format in [StacktraceFormat::Mono, StacktraceFormat::dotNET] { + if format.get_regex_for_stacktrace_entry().is_match(entry) { + return Ok(format); + } + } + return Err(Error::Casr(format!("Couldn't parse stacktrace line: {entry}"))); + } +} /// Structure provides an interface for processing the stack trace. pub struct CSharpStacktrace; impl CSharpStacktrace { - fn parse_stacktrace_entry(entry: &str, use_mono_format: bool) -> Result { - let re = - if use_mono_format { Regex::new(r"^at (?:\(.+\) )?(.+?) ?\(.*\) (?:<0x(?
\w+).+>|\[\w+]) in (?:<\w+>|(?.+)):(?\w+)$") } - else { Regex::new(r"^at (?:\(.+\) )?(.+?)\(.*\)(?: in (?.+):line (?\w+))?$") }.unwrap(); + fn parse_stacktrace_entry(entry: &str, format: StacktraceFormat) -> Result { + let re = format.get_regex_for_stacktrace_entry(); let Some(cap) = re.captures(entry) else { - return Err(Error::Casr(format!("{PARSE_STACK_FRAME_ERR_PREFIX}{entry}"))); + return Err(Error::Casr(format!("Couldn't parse stacktrace line: {entry}"))); }; + let get_group_by_name_as_str = |name| cap.name(name).map(|m| m.as_str()); + let mut stentry = StacktraceEntry::default(); - if let Some(file) = cap.name("file").map(|m| m.as_str().to_string()) { - let Some(line) = cap.name("line").and_then(|m| m.as_str().parse::().ok()) else { - return Err(Error::Casr(format!("Couldn't parse stacktrace line num: {entry}"))); - }; - stentry.debug.file = file; - stentry.debug.line = line; + if let Some(file) = get_group_by_name_as_str("file") { + stentry.debug.file = file.to_string(); } - if let Some(num) = cap.name("address").map(|m| m.as_str()) { - if let Ok(address) = u64::from_str_radix(num, 16) { - stentry.address = address; - } else { - return Err(Error::Casr(format!("Couldn't parse address: {num}"))); + if let Some(line) = get_group_by_name_as_str("line") { + match line.parse::() { + Ok(parsed_line) if Regex::new(r"^\d+$").unwrap().is_match(line) => stentry.debug.line = parsed_line, + _ => return Err(Error::Casr(format!("Couldn't parse stacktrace line num: {entry}"))) } } - stentry.function = cap.get(1).unwrap().as_str().to_string(); + if let (Some(base), Some(offset)) = (get_group_by_name_as_str("base"), get_group_by_name_as_str("offset")) { + let parse_hex = |s| Regex::new(r"^0x([\da-fA-F]+)$") + .unwrap() + .captures(s) + .and_then(|c| c.get(1)) + .and_then(|m| u64::from_str_radix(m.as_str(), 16).ok()); + + stentry.address = (|| { + if let (Some(base), Some(offset)) = (parse_hex(base), parse_hex(offset)) { + if let Some(address) = base.checked_add(offset) { + return Ok(address) + } + } + + return Err(Error::Casr(format!("Couldn't parse address: {base} + {offset}"))); + })()?; + } + + + if let Some(function) = get_group_by_name_as_str("function") { + let mut function = function.to_string(); + if let Some(params) = get_group_by_name_as_str("params") { + function.push_str(params) + } + + stentry.function = function; + } Ok(stentry) } @@ -49,12 +92,12 @@ impl CSharpStacktrace { impl ParseStacktrace for CSharpStacktrace { fn extract_stacktrace(stream: &str) -> Result> { - let re = Regex::new(r"(?m)^Unhandled ([Ee])xception(:\n|\. )(?:.|\n)+?((?:\n[^\S\n]*(?:at.+|--- End of inner exception stack trace ---))+)$").unwrap(); + let re = Regex::new(r"(?m)^Unhandled ([Ee])xception(:\n|\. )(?:.|\n)+?((?:\n\s*(?:at [\S ]+|--- End of inner exception stack trace ---))+)$").unwrap(); let Some(cap) = re.captures(stream).and_then(|cap| ((cap.get(1).unwrap().as_str() == "E") == (cap.get(2).unwrap().as_str() == ":\n")).then_some(cap) ) else { - return Err(Error::Casr("Couldn't find stacktrace".to_string())); + return Err(Error::Casr("The stacktrace format is not recognized".to_string())); }; Ok(cap @@ -68,40 +111,38 @@ impl ParseStacktrace for CSharpStacktrace { } fn parse_stacktrace_entry(entry: &str) -> Result { - match Self::parse_stacktrace_entry(entry, true) { - Err(Error::Casr(ref s)) if s.starts_with(PARSE_STACK_FRAME_ERR_PREFIX) => Self::parse_stacktrace_entry(entry, false), - r @ _ => r - } + Self::parse_stacktrace_entry(entry, StacktraceFormat::get_format_by_stacktrace_entry(entry)?) } fn parse_stacktrace(entries: &[String]) -> Result { - let iter = entries + let mut iter = entries .iter() - .scan((true, false), |(cancel_skip, skip), s| { + .scan((true, false), |(first_block, skip), s| { // Skipping all blocks consisting of "--- End of inner exception stack trace ---" // and one stack frame after each such block, except for the first block if entries start with it. - let not_stack_frame = s == "--- End of inner exception stack trace ---"; + let not_stack_trace_entry = s == "--- End of inner exception stack trace ---"; - if not_stack_frame || *skip { - *skip = not_stack_frame; + if not_stack_trace_entry || *skip { + *skip = not_stack_trace_entry; - if !*cancel_skip || not_stack_frame { + if !*first_block || not_stack_trace_entry { return Some(""); } } - *cancel_skip = false; + *first_block = false; Some(s) }) - .filter(|&s| !s.is_empty()); + .filter(|&s| !s.is_empty()).peekable(); - let re_mono = Regex::new(r"^at (?:\(.+\) )?(.+?) ?\(.*\) (?:<0x\w+.+>|\[\w+]) in (?:<\w+>|.+):\w+$").unwrap(); - let use_mono_format = iter.clone().all(|s| re_mono.captures(s).is_some()); + if let Some(s) = iter.peek() { + let f = StacktraceFormat::get_format_by_stacktrace_entry(s)?; - iter - .map(|s| Self::parse_stacktrace_entry(s, use_mono_format)) - .collect() + return iter.map(|s| Self::parse_stacktrace_entry(s, f)).collect() + } + + return std::iter::empty::>().collect(); } } @@ -110,19 +151,21 @@ pub struct CSharpException; impl Exception for CSharpException { fn parse_exception(stream: &str) -> Option { - let re_mono = Regex::new(r"(?m)^Unhandled Exception:\n((?:.|\n)+?(?: ---> (?:.|\n)+?)*?)\n[^\S\n]*(at.+|--- End of inner exception stack trace ---)$").unwrap(); - let re_dotnet = Regex::new(r"(?m)^Unhandled exception\. ((?:.|\n)+?(?:\n ---> (?:.|\n)+?)*?)\n[^\S\n]*(at.+|--- End of inner exception stack trace ---)$").unwrap(); + let re_mono = Regex::new(r"(?m)^Unhandled Exception:\n((?:.|\n)+?(?: ---> (?:.|\n)+?)*?)\n\s*(?:at .+|--- End of inner exception stack trace ---)$").unwrap(); + let re_dotnet = Regex::new(r"(?m)^Unhandled exception\. ((?:.|\n)+?(?:\n ---> (?:.|\n)+?)*?)\n\s*(?:at .+|--- End of inner exception stack trace ---)$").unwrap(); let description = re_mono.captures(stream) - .or (re_dotnet.captures(stream))? + .or(re_dotnet.captures(stream))? .get(1)? .as_str(); - let v: Vec<&str> = description.trim_start().rsplit_once(" ---> ") + let (exception, message) = description + .trim_start() + .rsplit_once(" ---> ") .map_or(description, |(_, s)| s) - .splitn(2, ": ") - .collect(); + .split_once(": ") + .unwrap(); - Some(ExecutionClass { short_description: v[0].to_string(), description: v.get(1)?.to_string(), ..ExecutionClass::default()}) + Some(ExecutionClass { short_description: exception.to_string(), description: message.to_string(), ..ExecutionClass::default()}) } -} \ No newline at end of file +} diff --git a/libcasr/src/lib.rs b/libcasr/src/lib.rs index 3fb4299d..08476134 100644 --- a/libcasr/src/lib.rs +++ b/libcasr/src/lib.rs @@ -25,6 +25,7 @@ pub mod asan; pub mod cluster; pub mod constants; pub mod cpp; +pub mod csharp; pub mod error; pub mod exception; pub mod execution_class; @@ -40,4 +41,3 @@ pub mod sarif; pub mod severity; pub mod stacktrace; pub mod ubsan; -pub mod csharp; From 65f9464ff31cd47eb3708dae8bb2eb9d0ba32a10 Mon Sep 17 00:00:00 2001 From: BMikh0 Date: Tue, 30 Jan 2024 18:16:06 +0300 Subject: [PATCH 05/11] Bugs fixed. --- libcasr/src/csharp.rs | 89 ++++++++++++++++++++----------------------- 1 file changed, 42 insertions(+), 47 deletions(-) diff --git a/libcasr/src/csharp.rs b/libcasr/src/csharp.rs index a98c3fde..43c45bf4 100644 --- a/libcasr/src/csharp.rs +++ b/libcasr/src/csharp.rs @@ -8,38 +8,29 @@ use crate::stacktrace::StacktraceEntry; use regex::Regex; -#[derive(Copy, Clone)] -enum StacktraceFormat { - Mono, - dotNET -} - -impl StacktraceFormat { - fn get_regex_for_stacktrace_entry(&self) -> Regex { - match self { - StacktraceFormat::Mono => Regex::new(r"^at (?:\(.+?\) )?(?P\S+)(?P ?\(.*?\))? (?:<(?P\w+) \+ (?P\w+)>|\[0x[\da-fA-F]+\]) in (?:<[\da-fA-F]+>|(?P.+)):(?P\w+)$"), - StacktraceFormat::dotNET => Regex::new(r"^at (?:\(.+?\) )?(?P\S+)(?P\(.*?\))?(?: in (?P.+):line (?P\w+))?$") - }.unwrap() - } +/// Structure provides an interface for processing the stack trace. +pub struct CSharpStacktrace; - fn get_format_by_stacktrace_entry(entry: &str) -> Result { - for format in [StacktraceFormat::Mono, StacktraceFormat::dotNET] { - if format.get_regex_for_stacktrace_entry().is_match(entry) { - return Ok(format); +impl CSharpStacktrace { + fn get_regex_format_for_stacktrace_entry(entry: &str) -> Result { + let regexps = [ + // Mono + Regex::new(r"^at (?P\(.+?\) )?(?P\S+?)(?: ?(?P\(.*?\)))? (?:<(?P\w+) \+ (?P\w+)>|\[0x[\da-fA-F]+\]) in (?:<[\da-fA-F#]+>|(?P.+)):(?P\w+)$").unwrap(), + // DotNet + Regex::new(r"^at (?P\(.+?\) )?(?P\S+?)(?P\(.*?\))?(?: in (?P.+):line (?P\w+))?$").unwrap() + ]; + + for re in regexps { + if re.is_match(entry) { + return Ok(re); } } - return Err(Error::Casr(format!("Couldn't parse stacktrace line: {entry}"))); + Err(Error::Casr(format!("Couldn't parse stacktrace line: {entry}"))) } -} -/// Structure provides an interface for processing the stack trace. -pub struct CSharpStacktrace; - -impl CSharpStacktrace { - fn parse_stacktrace_entry(entry: &str, format: StacktraceFormat) -> Result { - let re = format.get_regex_for_stacktrace_entry(); - let Some(cap) = re.captures(entry) else { + pub fn parse_stacktrace_entry(entry: &str, format_regex: &Regex) -> Result { + let Some(cap) = format_regex.captures(entry) else { return Err(Error::Casr(format!("Couldn't parse stacktrace line: {entry}"))); }; @@ -52,37 +43,41 @@ impl CSharpStacktrace { } if let Some(line) = get_group_by_name_as_str("line") { - match line.parse::() { - Ok(parsed_line) if Regex::new(r"^\d+$").unwrap().is_match(line) => stentry.debug.line = parsed_line, - _ => return Err(Error::Casr(format!("Couldn't parse stacktrace line num: {entry}"))) - } + let Ok(parsed_line) = line.parse::() else { + return Err(Error::Casr(format!("Couldn't parse stacktrace line num: {line}"))) + }; + + stentry.debug.line = parsed_line; } if let (Some(base), Some(offset)) = (get_group_by_name_as_str("base"), get_group_by_name_as_str("offset")) { - let parse_hex = |s| Regex::new(r"^0x([\da-fA-F]+)$") - .unwrap() + let re_hex = Regex::new(r"^0x([\da-fA-F]+)$").unwrap(); + let parse_hex = |s| re_hex .captures(s) - .and_then(|c| c.get(1)) - .and_then(|m| u64::from_str_radix(m.as_str(), 16).ok()); + .and_then(|c| u64::from_str_radix(c.get(1).unwrap().as_str(), 16).ok()); - stentry.address = (|| { - if let (Some(base), Some(offset)) = (parse_hex(base), parse_hex(offset)) { - if let Some(address) = base.checked_add(offset) { - return Ok(address) - } + if let (Some(parsed_base), Some(parsed_offset)) = (parse_hex(base), parse_hex(offset)) { + if let Some(address) = parsed_base.checked_add(parsed_offset) { + stentry.address = address; + } else { + return Err(Error::Casr(format!("Couldn't parse address: {base} + {offset}"))); } - + } else { return Err(Error::Casr(format!("Couldn't parse address: {base} + {offset}"))); - })()?; - } - + }; + }; if let Some(function) = get_group_by_name_as_str("function") { let mut function = function.to_string(); + if let Some(params) = get_group_by_name_as_str("params") { function.push_str(params) } + if let Some(service_info) = get_group_by_name_as_str("service_info") { + function.insert_str(0, service_info); + } + stentry.function = function; } @@ -92,7 +87,7 @@ impl CSharpStacktrace { impl ParseStacktrace for CSharpStacktrace { fn extract_stacktrace(stream: &str) -> Result> { - let re = Regex::new(r"(?m)^Unhandled ([Ee])xception(:\n|\. )(?:.|\n)+?((?:\n\s*(?:at [\S ]+|--- End of inner exception stack trace ---))+)$").unwrap(); + let re = Regex::new(r"(?m)^Unhandled ([Ee])xception(:\n|\. )(?:.|\n)*?((?:\n\s*(?:at [\S ]+|--- End of inner exception stack trace ---))+)$").unwrap(); let Some(cap) = re.captures(stream).and_then(|cap| ((cap.get(1).unwrap().as_str() == "E") == (cap.get(2).unwrap().as_str() == ":\n")).then_some(cap) @@ -111,7 +106,7 @@ impl ParseStacktrace for CSharpStacktrace { } fn parse_stacktrace_entry(entry: &str) -> Result { - Self::parse_stacktrace_entry(entry, StacktraceFormat::get_format_by_stacktrace_entry(entry)?) + Self::parse_stacktrace_entry(entry, &Self::get_regex_format_for_stacktrace_entry(entry)?) } fn parse_stacktrace(entries: &[String]) -> Result { @@ -137,9 +132,9 @@ impl ParseStacktrace for CSharpStacktrace { .filter(|&s| !s.is_empty()).peekable(); if let Some(s) = iter.peek() { - let f = StacktraceFormat::get_format_by_stacktrace_entry(s)?; + let re = Self::get_regex_format_for_stacktrace_entry(s)?; - return iter.map(|s| Self::parse_stacktrace_entry(s, f)).collect() + return iter.map(|s| Self::parse_stacktrace_entry(s, &re)).collect() } return std::iter::empty::>().collect(); From da03de82a66383b53f1de88704ab24f463668d43 Mon Sep 17 00:00:00 2001 From: BMikh0 Date: Wed, 14 Feb 2024 00:03:56 +0300 Subject: [PATCH 06/11] Tests added. --- libcasr/src/constants.rs | 10 ++ libcasr/src/csharp.rs | 242 ++++++++++++++++++++++++++++++++++---- libcasr/src/stacktrace.rs | 18 ++- 3 files changed, 243 insertions(+), 27 deletions(-) diff --git a/libcasr/src/constants.rs b/libcasr/src/constants.rs index 17feb4ed..8856a35c 100644 --- a/libcasr/src/constants.rs +++ b/libcasr/src/constants.rs @@ -228,6 +228,11 @@ pub const STACK_FRAME_FUNCTION_IGNORE_REGEXES_CPP: &[&str] = &[ r"^atheris::", ]; +/// Regular expressions for с# functions to be ignored. +pub const STACK_FRAME_FUNCTION_IGNORE_REGEXES_CSHARP: &[&str] = &[ + r"^\(wrapper", +]; + /// Regular expressions for paths to java files that should be ignored. pub const STACK_FRAME_FILEPATH_IGNORE_REGEXES_JAVA: &[&str] = &[ // TODO @@ -313,6 +318,11 @@ pub const STACK_FRAME_FILEPATH_IGNORE_REGEXES_CPP: &[&str] = &[ r".*asan_with_fuzzer\.so", ]; +/// Regular expressions for paths to c# files that should be ignored. +pub const STACK_FRAME_FILEPATH_IGNORE_REGEXES_CSHARP: &[&str] = &[ + r"^[^.]$" +]; + // Signal numbers pub const SIGINFO_SIGILL: u32 = 4; pub const SIGINFO_SIGTRAP: u32 = 5; diff --git a/libcasr/src/csharp.rs b/libcasr/src/csharp.rs index 43c45bf4..1c8e549d 100644 --- a/libcasr/src/csharp.rs +++ b/libcasr/src/csharp.rs @@ -1,4 +1,4 @@ -//! C Sharp module implements `ParseStacktrace` and `Exception` traits for C Sharp reports. +//! C# module implements `ParseStacktrace` and `Exception` traits for C# reports. use crate::exception::Exception; use crate::stacktrace::{ParseStacktrace, Stacktrace}; @@ -14,12 +14,12 @@ pub struct CSharpStacktrace; impl CSharpStacktrace { fn get_regex_format_for_stacktrace_entry(entry: &str) -> Result { let regexps = [ - // Mono - Regex::new(r"^at (?P\(.+?\) )?(?P\S+?)(?: ?(?P\(.*?\)))? (?:<(?P\w+) \+ (?P\w+)>|\[0x[\da-fA-F]+\]) in (?:<[\da-fA-F#]+>|(?P.+)):(?P\w+)$").unwrap(), // DotNet - Regex::new(r"^at (?P\(.+?\) )?(?P\S+?)(?P\(.*?\))?(?: in (?P.+):line (?P\w+))?$").unwrap() + Regex::new(r"^at (?:(?P\(.+?\)) )?(?P\S+?)(?P\(.*?\))?(?: in (?P.+):line (?P\w+))?$").unwrap(), + // Mono + Regex::new(r"^at (?:(?P\(.+?\)) )?(?P\S+?)(?: ?(?P\(.*?\)))?(?: (?:<(?P\w+)(?: \+ (?P\w+))?>|\[0x[\da-fA-F]+\]))?(?: in (?:<[\da-fA-F#]+>|(?P.+)):(?P\w+))?$").unwrap() ]; - + for re in regexps { if re.is_match(entry) { return Ok(re); @@ -29,7 +29,7 @@ impl CSharpStacktrace { Err(Error::Casr(format!("Couldn't parse stacktrace line: {entry}"))) } - pub fn parse_stacktrace_entry(entry: &str, format_regex: &Regex) -> Result { + fn parse_stacktrace_entry(entry: &str, format_regex: &Regex) -> Result { let Some(cap) = format_regex.captures(entry) else { return Err(Error::Casr(format!("Couldn't parse stacktrace line: {entry}"))); }; @@ -75,7 +75,7 @@ impl CSharpStacktrace { } if let Some(service_info) = get_group_by_name_as_str("service_info") { - function.insert_str(0, service_info); + function = format!("{service_info} {function}"); } stentry.function = function; @@ -87,7 +87,7 @@ impl CSharpStacktrace { impl ParseStacktrace for CSharpStacktrace { fn extract_stacktrace(stream: &str) -> Result> { - let re = Regex::new(r"(?m)^Unhandled ([Ee])xception(:\n|\. )(?:.|\n)*?((?:\n\s*(?:at [\S ]+|--- End of inner exception stack trace ---))+)$").unwrap(); + let re = Regex::new(r"(?m)^Unhandled (e|E)xception(\. |:\n)(?:.|\n)*?((?:\s*(?:at [\S ]+|--- End of inner exception stack trace ---))+)$").unwrap(); let Some(cap) = re.captures(stream).and_then(|cap| ((cap.get(1).unwrap().as_str() == "E") == (cap.get(2).unwrap().as_str() == ":\n")).then_some(cap) @@ -133,34 +133,234 @@ impl ParseStacktrace for CSharpStacktrace { if let Some(s) = iter.peek() { let re = Self::get_regex_format_for_stacktrace_entry(s)?; - - return iter.map(|s| Self::parse_stacktrace_entry(s, &re)).collect() + return iter.map(|s| Self::parse_stacktrace_entry(s, &re)).collect(); } return std::iter::empty::>().collect(); } } -/// Structure provides an interface for parsing c sharp exception message. +/// Structure provides an interface for parsing c# exception message. pub struct CSharpException; impl Exception for CSharpException { fn parse_exception(stream: &str) -> Option { - let re_mono = Regex::new(r"(?m)^Unhandled Exception:\n((?:.|\n)+?(?: ---> (?:.|\n)+?)*?)\n\s*(?:at .+|--- End of inner exception stack trace ---)$").unwrap(); - let re_dotnet = Regex::new(r"(?m)^Unhandled exception\. ((?:.|\n)+?(?:\n ---> (?:.|\n)+?)*?)\n\s*(?:at .+|--- End of inner exception stack trace ---)$").unwrap(); + let re = Regex::new(r"(?m)^Unhandled (e|E)xception(\. |:\n)((?:.|\n)*?)\s*(?:at [\S ]+|--- End of inner exception stack trace ---)$").unwrap(); + + let cap = re.captures(stream)?; + let get_group_as_str = |i| cap.get(i).unwrap().as_str(); - let description = re_mono.captures(stream) - .or(re_dotnet.captures(stream))? - .get(1)? - .as_str(); + let is_mono = get_group_as_str(1) == "E"; + + if is_mono != (get_group_as_str(2) == ":\n") { return None; } + + let description = get_group_as_str(3); + let delimiter = if is_mono { " ---> " } else { "\n ---> " }; let (exception, message) = description - .trim_start() - .rsplit_once(" ---> ") + .rsplit_once(delimiter) .map_or(description, |(_, s)| s) - .split_once(": ") - .unwrap(); + .split_once(": ")?; Some(ExecutionClass { short_description: exception.to_string(), description: message.to_string(), ..ExecutionClass::default()}) } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::{init_ignored_frames, stacktrace::Filter}; + + #[test] + fn test_csharp_get_regex_format_for_stacktrace_entry() { + let re_dotnet = r"^at (?:(?P\(.+?\)) )?(?P\S+?)(?P\(.*?\))?(?: in (?P.+):line (?P\w+))?$"; + let re_mono = r"^at (?:(?P\(.+?\)) )?(?P\S+?)(?: ?(?P\(.*?\)))?(?: (?:<(?P\w+)(?: \+ (?P\w+))?>|\[0x[\da-fA-F]+\]))?(?: in (?:<[\da-fA-F#]+>|(?P.+)):(?P\w+))?$"; + + fn get_regex(entry: &str) -> Result { + CSharpStacktrace::get_regex_format_for_stacktrace_entry(entry).map(|r| r.to_string()) + } + + match get_regex("at A`1[T].h[Z] (System.Func`1[TResult] a) [0x00001] in :0") { + Ok(re) => assert_eq!(re, re_mono), + Err(err) => panic!("{err}") + } + + match get_regex("at A`1.g__g|1_0(Int32[] arr(((((((((( in /home/user/dotnet/2/A.cs:line 19") { + Ok(_) => assert!(false), + Err(err) => assert_eq!(err.to_string(), "Casr: Couldn't parse stacktrace line: at A`1.g__g|1_0(Int32[] arr(((((((((( in /home/user/dotnet/2/A.cs:line 19") + } + + match get_regex("at Program+<>c.
b__0_2 (System.Int32 i) <0x7f30488306b0 + 0x00035> in :0") { + Ok(re) => assert_eq!(re, re_mono), + Err(err) => panic!("{err}") + } + + match get_regex("at (wrapper managed-to-native) System.Drawing.GDIPlus:GdiplusStartup (ulong&,System.Drawing.GdiplusStartupInput&,System.Drawing.GdiplusStartupOutput&)") { + Ok(re) => assert_eq!(re, re_mono), + Err(err) => panic!("{err}") + } + + match get_regex("at Program.
g__f|0_0(Func`2 d, Int32& l, Int32 m) in /home/user/dotnet/2/Program.cs:line 9") { + Ok(re) => assert_eq!(re, re_dotnet), + Err(err) => panic!("{err}") + } + + match get_regex("at Program.Main()") { + Ok(re) => assert_eq!(re, re_dotnet), + Err(err) => panic!("{err}") + } + } + + #[test] + fn test_csharp_parse_exception() { + let streams = [ + " +Unhandled Exception: +System.ArithmeticException: 123 + +321 + +7 + ---> System.ArgumentException: 1111 ---> System.IO.IOException: cccc + --- End of inner exception stack trace ---", + " +Unhandled Exception: +System.ArgumentException: 1111 + ---> System.IO.IOException: cccc + --- End of inner exception stack trace ---", + " +Unhandled exception. System.ArgumentException: 1111 + ---> System.IO.IOException: cccc + --- End of inner exception stack trace ---", + " +Unhandled exception. System.ArgumentException: 1111 ---> System.IO.IOException: cccc + --- End of inner exception stack trace ---" + ]; + + let exceptions = streams.map(|s| { + let Some(e) = CSharpException::parse_exception(s) else { + panic!("Couldn't get C# exception from stream: {s}"); + }; + e + }); + + assert_eq!(exceptions[0].short_description, "System.IO.IOException"); + assert_eq!(exceptions[0].description, "cccc"); + assert_eq!(exceptions[1].short_description, "System.IO.IOException"); + assert_eq!(exceptions[1].description, "cccc"); + assert_eq!(exceptions[2].short_description, "System.IO.IOException"); + assert_eq!(exceptions[2].description, "cccc"); + assert_eq!(exceptions[3].short_description, "System.ArgumentException"); + assert_eq!(exceptions[3].description, "1111 ---> System.IO.IOException: cccc"); + } + + #[test] + fn test_csharp_stacktrace() { + let stream = "Unhandled exception. System.ArithmeticException: 123 + +321 + +7 + + ---> System.ArgumentException: 1111 + ---> System.IO.IOException: cccc + ---> System.IO.IOException: bbbb + ---> System.IO.IOException: aaaa + ---> System.IO.IOException: I/O error occurred. + --- End of inner exception stack trace --- + --- End of inner exception stack trace --- + --- End of inner exception stack trace --- + at B..ctor() in /home/user/dotnet/2/A.cs:line 37 + at A`1.<>c.b__1_1() in /home/user/dotnet/2/A.cs:line 15 + at A`1.h[Z](Func`1 a) + --- End of inner exception stack trace --- + --- End of inner exception stack trace --- + at A`1.h[Z](Func`1 a) + at A`1[T].g__g|1_0 (System.Int32[] arr) <0x40b745f0 + 0x00122> in /home/user/mono/2/src/2.cs:13 + at A`1[T].set_Q (System.Int32 value) <0x40275140 + 0x00082> in :0 + at (wrapper runtime-invoke) staticperformanceoptimization.runtime_invoke_void (object,intptr,intptr,intptr) <0xffffffff> + at (wrapper managed-to-native) System.Drawing.GDIPlus:GdiplusStartup (ulong&,System.Drawing.GdiplusStartupInput&,System.Drawing.GdiplusStartupOutput&) + at Program+<>c.
b__0_2 (System.Int32 i) <0x7f1c08e516b0 + 0x00035> in :0 + at Program.
g__f|0_0 (System.Func`2[T,TResult] d, System.Int32& l, System.Int32 m) <0x7f1c08e51140 + 0x000d9> in :0 + at Program.Main () <0x7f1c08e51010 + 0x000ea> in :0 +"; + + let trace = [ + "--- End of inner exception stack trace ---", + "--- End of inner exception stack trace ---", + "--- End of inner exception stack trace ---", + "at B..ctor() in /home/user/dotnet/2/A.cs:line 37", + "at A`1.<>c.b__1_1() in /home/user/dotnet/2/A.cs:line 15", + "at A`1.h[Z](Func`1 a)", + "--- End of inner exception stack trace ---", + "--- End of inner exception stack trace ---", + "at A`1.h[Z](Func`1 a)", + "at A`1[T].g__g|1_0 (System.Int32[] arr) <0x40b745f0 + 0x00122> in /home/user/mono/2/src/2.cs:13", + "at A`1[T].set_Q (System.Int32 value) <0x40275140 + 0x00082> in :0", + "at (wrapper runtime-invoke) staticperformanceoptimization.runtime_invoke_void (object,intptr,intptr,intptr) <0xffffffff>", + "at (wrapper managed-to-native) System.Drawing.GDIPlus:GdiplusStartup (ulong&,System.Drawing.GdiplusStartupInput&,System.Drawing.GdiplusStartupOutput&)", + "at Program+<>c.
b__0_2 (System.Int32 i) <0x7f1c08e516b0 + 0x00035> in :0", + "at Program.
g__f|0_0 (System.Func`2[T,TResult] d, System.Int32& l, System.Int32 m) <0x7f1c08e51140 + 0x000d9> in :0", + "at Program.Main () <0x7f1c08e51010 + 0x000ea> in :0" + ].map(String::from).to_vec(); + + let bt = CSharpStacktrace::extract_stacktrace(stream).unwrap(); + + assert_eq!(bt, trace); + + let mut stacktrace = match CSharpStacktrace::parse_stacktrace(&trace[0..8]) { + Ok(s) => s, + Err(e) => panic!("{e}") + }; + + assert_eq!(stacktrace.len(), 3); + assert_eq!(stacktrace[0].function, "B..ctor()".to_string()); + assert_eq!(stacktrace[0].debug.file, "/home/user/dotnet/2/A.cs".to_string()); + assert_eq!(stacktrace[0].debug.line, 37); + assert_eq!(stacktrace[1].function, "A`1.<>c.b__1_1()".to_string()); + assert_eq!(stacktrace[1].debug.file, "/home/user/dotnet/2/A.cs".to_string()); + assert_eq!(stacktrace[1].debug.line, 15); + assert_eq!(stacktrace[2].function, "A`1.h[Z](Func`1 a)".to_string()); + + stacktrace = match CSharpStacktrace::parse_stacktrace(&trace[9..16]) { + Ok(s) => s, + Err(e) => panic!("{e}") + }; + + assert_eq!(stacktrace.len(), 7); + assert_eq!( + stacktrace[2].function, + "(wrapper runtime-invoke) staticperformanceoptimization.runtime_invoke_void(object,intptr,intptr,intptr)".to_string() + ); + assert_eq!( + stacktrace[3].function, + "(wrapper managed-to-native) System.Drawing.GDIPlus:GdiplusStartup(ulong&,System.Drawing.GdiplusStartupInput&,System.Drawing.GdiplusStartupOutput&)".to_string() + ); + + init_ignored_frames!("csharp"); + stacktrace.filter(); + + assert_eq!(stacktrace.len(), 5); + assert_eq!(stacktrace[0].address, 1085753106); + assert_eq!(stacktrace[0].function, "A`1[T].g__g|1_0(System.Int32[] arr)".to_string()); + assert_eq!(stacktrace[0].debug.file, "/home/user/mono/2/src/2.cs".to_string()); + assert_eq!(stacktrace[0].debug.line, 13); + assert_eq!(stacktrace[1].address, 1076318658); + assert_eq!(stacktrace[1].function, "A`1[T].set_Q(System.Int32 value)".to_string()); + + assert_eq!(stacktrace[2].address, 139758385043173); + assert_eq!(stacktrace[2].function, "Program+<>c.
b__0_2(System.Int32 i)".to_string()); + assert_eq!(stacktrace[3].address, 139758385041945); + assert_eq!( + stacktrace[3].function, + "Program.
g__f|0_0(System.Func`2[T,TResult] d, System.Int32& l, System.Int32 m)".to_string() + ); + assert_eq!(stacktrace[4].address, 139758385041658); + assert_eq!(stacktrace[4].function, "Program.Main()".to_string()); + + let sttr = CSharpStacktrace::parse_stacktrace(&trace); + + assert!(sttr.is_err()); + assert_eq!(sttr.err().unwrap().to_string(), "Casr: Couldn't parse stacktrace line: at A`1[T].g__g|1_0 (System.Int32[] arr) <0x40b745f0 + 0x00122> in /home/user/mono/2/src/2.cs:13"); + } +} diff --git a/libcasr/src/stacktrace.rs b/libcasr/src/stacktrace.rs index f68c2959..e70598fd 100644 --- a/libcasr/src/stacktrace.rs +++ b/libcasr/src/stacktrace.rs @@ -3,12 +3,14 @@ extern crate kodama; extern crate lazy_static; use crate::constants::{ - STACK_FRAME_FILEPATH_IGNORE_REGEXES_CPP, STACK_FRAME_FILEPATH_IGNORE_REGEXES_GO, - STACK_FRAME_FILEPATH_IGNORE_REGEXES_JAVA, STACK_FRAME_FILEPATH_IGNORE_REGEXES_JS, - STACK_FRAME_FILEPATH_IGNORE_REGEXES_PYTHON, STACK_FRAME_FILEPATH_IGNORE_REGEXES_RUST, - STACK_FRAME_FUNCTION_IGNORE_REGEXES_CPP, STACK_FRAME_FUNCTION_IGNORE_REGEXES_GO, - STACK_FRAME_FUNCTION_IGNORE_REGEXES_JAVA, STACK_FRAME_FUNCTION_IGNORE_REGEXES_JS, - STACK_FRAME_FUNCTION_IGNORE_REGEXES_PYTHON, STACK_FRAME_FUNCTION_IGNORE_REGEXES_RUST, + STACK_FRAME_FILEPATH_IGNORE_REGEXES_CPP, STACK_FRAME_FILEPATH_IGNORE_REGEXES_CSHARP, + STACK_FRAME_FILEPATH_IGNORE_REGEXES_GO, STACK_FRAME_FILEPATH_IGNORE_REGEXES_JAVA, + STACK_FRAME_FILEPATH_IGNORE_REGEXES_JS, STACK_FRAME_FILEPATH_IGNORE_REGEXES_PYTHON, + STACK_FRAME_FILEPATH_IGNORE_REGEXES_RUST, + STACK_FRAME_FUNCTION_IGNORE_REGEXES_CPP, STACK_FRAME_FUNCTION_IGNORE_REGEXES_CSHARP, + STACK_FRAME_FUNCTION_IGNORE_REGEXES_GO, STACK_FRAME_FUNCTION_IGNORE_REGEXES_JAVA, + STACK_FRAME_FUNCTION_IGNORE_REGEXES_JS, STACK_FRAME_FUNCTION_IGNORE_REGEXES_PYTHON, + STACK_FRAME_FUNCTION_IGNORE_REGEXES_RUST, }; use crate::error::*; use kodama::{linkage, Method}; @@ -346,6 +348,10 @@ pub trait Filter { STACK_FRAME_FUNCTION_IGNORE_REGEXES_JS, STACK_FRAME_FILEPATH_IGNORE_REGEXES_JS, ), + "csharp" => ( + STACK_FRAME_FUNCTION_IGNORE_REGEXES_CSHARP, + STACK_FRAME_FILEPATH_IGNORE_REGEXES_CSHARP, + ), &_ => (["^[^.]$"].as_slice(), ["^[^.]$"].as_slice()), }) .unzip(); From 28b1d22efc8a72b14508b09c0ef59e8249b4f7a8 Mon Sep 17 00:00:00 2001 From: BMikh0 Date: Tue, 27 Feb 2024 01:50:24 +0300 Subject: [PATCH 07/11] Bugs fixed. --- libcasr/fuzz/fuzz_targets/parse_stacktrace.rs | 13 +- libcasr/fuzz/init_corpus/csharp | 10 + libcasr/fuzz/init_corpus/gdb | 2 +- libcasr/src/constants.rs | 2 + libcasr/src/csharp.rs | 187 ++++++++++-------- libcasr/src/report.rs | 28 +++ libcasr/src/stacktrace.rs | 2 +- 7 files changed, 160 insertions(+), 84 deletions(-) create mode 100644 libcasr/fuzz/init_corpus/csharp diff --git a/libcasr/fuzz/fuzz_targets/parse_stacktrace.rs b/libcasr/fuzz/fuzz_targets/parse_stacktrace.rs index 8003e9b7..468209db 100644 --- a/libcasr/fuzz/fuzz_targets/parse_stacktrace.rs +++ b/libcasr/fuzz/fuzz_targets/parse_stacktrace.rs @@ -10,6 +10,7 @@ use libcasr::{ java::JavaStacktrace, python::PythonStacktrace, js::JsStacktrace, + csharp::CSharpStacktrace, stacktrace::{CrashLineExt, Filter, ParseStacktrace, Stacktrace}, }; @@ -18,8 +19,8 @@ fuzz_target!(|data: &[u8]| { return; } let s = String::from_utf8_lossy(&data[1..]); - init_ignored_frames!("cpp", "rust", "python", "go", "java", "js"); - match data[0] % 6 { + init_ignored_frames!("cpp", "rust", "python", "go", "java", "js", "csharp"); + match data[0] % 7 { 0 => { // Asan if let Ok(raw) = AsanStacktrace::extract_stacktrace(&s) { @@ -60,6 +61,14 @@ fuzz_target!(|data: &[u8]| { } } } + 5 => { + // C# + if let Ok(raw) = CSharpStacktrace::extract_stacktrace(&s) { + if let Ok(st) = CSharpStacktrace::parse_stacktrace(&raw) { + let _ = st.crash_line(); + } + } + } _ => { // Gdb if let Ok(raw) = GdbStacktrace::extract_stacktrace(&s) { diff --git a/libcasr/fuzz/init_corpus/csharp b/libcasr/fuzz/init_corpus/csharp new file mode 100644 index 00000000..0de93cb8 --- /dev/null +++ b/libcasr/fuzz/init_corpus/csharp @@ -0,0 +1,10 @@ +Unhandled Exception: +System.IndexOutOfRangeException: Index was outside the bounds of the array. + at B..ctor () [0x00008] in /home/user/mono/src/2.cs:33 + at A`1+<>c[T].b__1_1 () [0x00001] in /home/user/mono/src/2.cs:14 + at A`1[T].h[Z] (System.Func`1[TResult] a) [0x00001] in /home/user/mono/src/2.cs:25 + at A`1[T].g__g|1_0 (System.Int32[] arr) [0x00001] in /home/user/mono/src/2.cs:12 + at A`1[T].set_Q (System.Int32 value) [0x00002] in /home/user/mono/src/2.cs:19 + at Program+<>c.
b__0_2 (System.Int32 i) [0x00000] in /home/user/mono/src/1.cs:8 + at Program.
g__f|0_0 (System.Func`2[T,TResult] d, System.Int32& l, System.Int32 m) [0x00021] in /home/user/mono/src/1.cs:9 + at Program.Main () [0x00004] in /home/user/mono/src/1.cs:13 diff --git a/libcasr/fuzz/init_corpus/gdb b/libcasr/fuzz/init_corpus/gdb index 8f2c5b27..41c88bf2 100644 --- a/libcasr/fuzz/init_corpus/gdb +++ b/libcasr/fuzz/init_corpus/gdb @@ -1,4 +1,4 @@ - #0 0x00000000005e3099 in xlnt::detail::compound_document::read_directory (this=0x7fffffffcee0) at /xlnt/source/detail/cryptography/compound_document.cpp:975 + #0 0x00000000005e3099 in xlnt::detail::compound_document::read_directory (this=0x7fffffffcee0) at /xlnt/source/detail/cryptography/compound_document.cpp:975 #1 0x00000000005e2956 in xlnt::detail::compound_document::compound_document (this=0x7fffffffcee0, in=...) at /xlnt/source/detail/cryptography/compound_document.cpp:517 #2 0x000000000048a45c in (anonymous namespace)::decrypt_xlsx (bytes=std::vector of length 18655, capacity 32768 = {...}, password=u\"VelvetSweatshop\") at /xlnt/source/detail/cryptography/xlsx_crypto_consumer.cpp:320 #3 0x000000000048a2d9 in xlnt::detail::decrypt_xlsx (data=std::vector of length 18655, capacity 32768 = {...}, password=) at /xlnt/source/detail/cryptography/xlsx_crypto_consumer.cpp:339 diff --git a/libcasr/src/constants.rs b/libcasr/src/constants.rs index 8856a35c..be5daa40 100644 --- a/libcasr/src/constants.rs +++ b/libcasr/src/constants.rs @@ -230,6 +230,7 @@ pub const STACK_FRAME_FUNCTION_IGNORE_REGEXES_CPP: &[&str] = &[ /// Regular expressions for с# functions to be ignored. pub const STACK_FRAME_FUNCTION_IGNORE_REGEXES_CSHARP: &[&str] = &[ + // TODO r"^\(wrapper", ]; @@ -320,6 +321,7 @@ pub const STACK_FRAME_FILEPATH_IGNORE_REGEXES_CPP: &[&str] = &[ /// Regular expressions for paths to c# files that should be ignored. pub const STACK_FRAME_FILEPATH_IGNORE_REGEXES_CSHARP: &[&str] = &[ + // TODO r"^[^.]$" ]; diff --git a/libcasr/src/csharp.rs b/libcasr/src/csharp.rs index 1c8e549d..0755728a 100644 --- a/libcasr/src/csharp.rs +++ b/libcasr/src/csharp.rs @@ -1,34 +1,42 @@ //! C# module implements `ParseStacktrace` and `Exception` traits for C# reports. -use crate::exception::Exception; -use crate::stacktrace::{ParseStacktrace, Stacktrace}; - use crate::error::*; +use crate::exception::Exception; use crate::execution_class::ExecutionClass; -use crate::stacktrace::StacktraceEntry; - +use crate::stacktrace::{ParseStacktrace, Stacktrace, StacktraceEntry}; use regex::Regex; +use std::vec::IntoIter; /// Structure provides an interface for processing the stack trace. pub struct CSharpStacktrace; -impl CSharpStacktrace { - fn get_regex_format_for_stacktrace_entry(entry: &str) -> Result { - let regexps = [ - // DotNet - Regex::new(r"^at (?:(?P\(.+?\)) )?(?P\S+?)(?P\(.*?\))?(?: in (?P.+):line (?P\w+))?$").unwrap(), - // Mono - Regex::new(r"^at (?:(?P\(.+?\)) )?(?P\S+?)(?: ?(?P\(.*?\)))?(?: (?:<(?P\w+)(?: \+ (?P\w+))?>|\[0x[\da-fA-F]+\]))?(?: in (?:<[\da-fA-F#]+>|(?P.+)):(?P\w+))?$").unwrap() - ]; +struct FormatRegexIterator<'a> { + entry: &'a str, + regexps: IntoIter +} - for re in regexps { - if re.is_match(entry) { - return Ok(re); - } +impl<'a> FormatRegexIterator<'a> { + fn new(entry: &'a str) -> Self { + FormatRegexIterator { + entry, + regexps: vec![ + // Mono + Regex::new(r"^at (?:(?P\(.+?\)) )?(?P\S+?)(?: ?(?P\(.*?\)))?(?: (?:<(?P\w+)(?: \+ (?P\w+))?>|\[0x[\da-fA-F]+\]))?(?: in (?:<[\da-fA-F#]+>|(?P.+)):(?P\w+))?$").unwrap(), + // DotNet + Regex::new(r"^at (?:(?P\(.+?\)) )?(?P\S+?)(?P\(.*?\))?(?: in (?P.+):line (?P\w+))?$").unwrap() + ].into_iter() } + } +} - Err(Error::Casr(format!("Couldn't parse stacktrace line: {entry}"))) +impl Iterator for FormatRegexIterator<'_> { + type Item = Regex; + + fn next(&mut self) -> Option { + self.regexps.find(|x| x.is_match(self.entry)) } +} +impl CSharpStacktrace { fn parse_stacktrace_entry(entry: &str, format_regex: &Regex) -> Result { let Some(cap) = format_regex.captures(entry) else { return Err(Error::Casr(format!("Couldn't parse stacktrace line: {entry}"))); @@ -87,7 +95,7 @@ impl CSharpStacktrace { impl ParseStacktrace for CSharpStacktrace { fn extract_stacktrace(stream: &str) -> Result> { - let re = Regex::new(r"(?m)^Unhandled (e|E)xception(\. |:\n)(?:.|\n)*?((?:\s*(?:at [\S ]+|--- End of inner exception stack trace ---))+)$").unwrap(); + let re = Regex::new(r"(?m)^Unhandled (E|e)xception(:\n|\. )(?:.|\n)*?((?:\s*(?:at [\S ]+|--- End of inner exception stack trace ---))+)$").unwrap(); let Some(cap) = re.captures(stream).and_then(|cap| ((cap.get(1).unwrap().as_str() == "E") == (cap.get(2).unwrap().as_str() == ":\n")).then_some(cap) @@ -106,7 +114,11 @@ impl ParseStacktrace for CSharpStacktrace { } fn parse_stacktrace_entry(entry: &str) -> Result { - Self::parse_stacktrace_entry(entry, &Self::get_regex_format_for_stacktrace_entry(entry)?) + Self::parse_stacktrace_entry( + entry, + &FormatRegexIterator::new(entry) + .next() + .ok_or(Error::Casr(format!("Couldn't parse stacktrace line: {entry}")))?) } fn parse_stacktrace(entries: &[String]) -> Result { @@ -131,12 +143,23 @@ impl ParseStacktrace for CSharpStacktrace { }) .filter(|&s| !s.is_empty()).peekable(); - if let Some(s) = iter.peek() { - let re = Self::get_regex_format_for_stacktrace_entry(s)?; - return iter.map(|s| Self::parse_stacktrace_entry(s, &re)).collect(); + if let Some(entry) = iter.peek() { + let mut e = (0, Err(Error::Casr(format!("Couldn't parse stacktrace line: {entry}")))); + + for re in FormatRegexIterator::new(entry) { + let mut iter = iter.clone().map(|s| Self::parse_stacktrace_entry(s, &re)); + let sttr: Result = iter.clone().collect(); + + if sttr.is_ok() { return sttr; } + + let n = iter.position(|r| r.is_err()).unwrap(); + if n >= e.0 { e = (n, sttr); } + } + + return e.1 } - return std::iter::empty::>().collect(); + Ok(Stacktrace::default()) } } @@ -145,7 +168,7 @@ pub struct CSharpException; impl Exception for CSharpException { fn parse_exception(stream: &str) -> Option { - let re = Regex::new(r"(?m)^Unhandled (e|E)xception(\. |:\n)((?:.|\n)*?)\s*(?:at [\S ]+|--- End of inner exception stack trace ---)$").unwrap(); + let re = Regex::new(r"(?m)^Unhandled (E|e)xception(:\n|\. )((?:.|\n)*?)\s*(?:at [\S ]+|--- End of inner exception stack trace ---)$").unwrap(); let cap = re.captures(stream)?; let get_group_as_str = |i| cap.get(i).unwrap().as_str(); @@ -169,46 +192,47 @@ impl Exception for CSharpException { #[cfg(test)] mod tests { use super::*; - use crate::{init_ignored_frames, stacktrace::Filter}; + use crate::stacktrace::{tests::safe_init_ignore_stack_frames, Filter}; #[test] fn test_csharp_get_regex_format_for_stacktrace_entry() { - let re_dotnet = r"^at (?:(?P\(.+?\)) )?(?P\S+?)(?P\(.*?\))?(?: in (?P.+):line (?P\w+))?$"; let re_mono = r"^at (?:(?P\(.+?\)) )?(?P\S+?)(?: ?(?P\(.*?\)))?(?: (?:<(?P\w+)(?: \+ (?P\w+))?>|\[0x[\da-fA-F]+\]))?(?: in (?:<[\da-fA-F#]+>|(?P.+)):(?P\w+))?$"; + let re_dotnet = r"^at (?:(?P\(.+?\)) )?(?P\S+?)(?P\(.*?\))?(?: in (?P.+):line (?P\w+))?$"; - fn get_regex(entry: &str) -> Result { - CSharpStacktrace::get_regex_format_for_stacktrace_entry(entry).map(|r| r.to_string()) + fn get_regex(iter: &mut FormatRegexIterator) -> Option { + iter.next().map(|r| r.to_string()) } - match get_regex("at A`1[T].h[Z] (System.Func`1[TResult] a) [0x00001] in :0") { - Ok(re) => assert_eq!(re, re_mono), - Err(err) => panic!("{err}") - } + let mut iter; - match get_regex("at A`1.g__g|1_0(Int32[] arr(((((((((( in /home/user/dotnet/2/A.cs:line 19") { - Ok(_) => assert!(false), - Err(err) => assert_eq!(err.to_string(), "Casr: Couldn't parse stacktrace line: at A`1.g__g|1_0(Int32[] arr(((((((((( in /home/user/dotnet/2/A.cs:line 19") - } + iter = FormatRegexIterator::new("at A`1[T].h[Z] (System.Func`1[TResult] a) [0x00001] in :0"); + assert_eq!(get_regex(&mut iter).as_deref(), Some(re_mono)); + assert_eq!(get_regex(&mut iter).as_deref(), None); - match get_regex("at Program+<>c.
b__0_2 (System.Int32 i) <0x7f30488306b0 + 0x00035> in :0") { - Ok(re) => assert_eq!(re, re_mono), - Err(err) => panic!("{err}") - } + iter = FormatRegexIterator::new("at A`1.g__g|1_0(Int32[] arr(((((((((( in /home/user/dotnet/2/A.cs:line 19"); + assert_eq!(get_regex(&mut iter).as_deref(), None); - match get_regex("at (wrapper managed-to-native) System.Drawing.GDIPlus:GdiplusStartup (ulong&,System.Drawing.GdiplusStartupInput&,System.Drawing.GdiplusStartupOutput&)") { - Ok(re) => assert_eq!(re, re_mono), - Err(err) => panic!("{err}") - } + iter = FormatRegexIterator::new("at Program+<>c.
b__0_2 (System.Int32 i) <0x7f30488306b0 + 0x00035> in :0"); + assert_eq!(get_regex(&mut iter).as_deref(), Some(re_mono)); + assert_eq!(get_regex(&mut iter).as_deref(), None); - match get_regex("at Program.
g__f|0_0(Func`2 d, Int32& l, Int32 m) in /home/user/dotnet/2/Program.cs:line 9") { - Ok(re) => assert_eq!(re, re_dotnet), - Err(err) => panic!("{err}") - } + iter = FormatRegexIterator::new("at (wrapper managed-to-native) System.Drawing.GDIPlus:GdiplusStartup (ulong&,System.Drawing.GdiplusStartupInput&,System.Drawing.GdiplusStartupOutput&)"); + assert_eq!(get_regex(&mut iter).as_deref(), Some(re_mono)); + assert_eq!(get_regex(&mut iter).as_deref(), None); + + iter = FormatRegexIterator::new("at A`1[T].set_Q (System.Int32 value) <0x40b74140 + 0x00082> in /home/user/mono/2/src/2.cs:24"); + assert_eq!(get_regex(&mut iter).as_deref(), Some(re_mono)); + assert_eq!(get_regex(&mut iter).as_deref(), None); + + iter = FormatRegexIterator::new("at Program.
g__f|0_0(Func`2 d, Int32& l, Int32 m) in /home/user/dotnet/2/Program.cs:line 9"); + assert_eq!(get_regex(&mut iter).as_deref(), Some(re_dotnet)); + assert_eq!(get_regex(&mut iter).as_deref(), None); + + iter = FormatRegexIterator::new("at Program.Main()"); + assert_eq!(get_regex(&mut iter).as_deref(), Some(re_mono)); + assert_eq!(get_regex(&mut iter).as_deref(), Some(re_dotnet)); + assert_eq!(get_regex(&mut iter).as_deref(), None); - match get_regex("at Program.Main()") { - Ok(re) => assert_eq!(re, re_dotnet), - Err(err) => panic!("{err}") - } } #[test] @@ -270,6 +294,7 @@ Unhandled exception. System.ArgumentException: 1111 ---> System.IO.IOException: --- End of inner exception stack trace --- --- End of inner exception stack trace --- --- End of inner exception stack trace --- + at C.qwe() at B..ctor() in /home/user/dotnet/2/A.cs:line 37 at A`1.<>c.b__1_1() in /home/user/dotnet/2/A.cs:line 15 at A`1.h[Z](Func`1 a) @@ -289,6 +314,7 @@ Unhandled exception. System.ArgumentException: 1111 ---> System.IO.IOException: "--- End of inner exception stack trace ---", "--- End of inner exception stack trace ---", "--- End of inner exception stack trace ---", + "at C.qwe()", "at B..ctor() in /home/user/dotnet/2/A.cs:line 37", "at A`1.<>c.b__1_1() in /home/user/dotnet/2/A.cs:line 15", "at A`1.h[Z](Func`1 a)", @@ -308,55 +334,56 @@ Unhandled exception. System.ArgumentException: 1111 ---> System.IO.IOException: assert_eq!(bt, trace); - let mut stacktrace = match CSharpStacktrace::parse_stacktrace(&trace[0..8]) { + let mut stacktrace = match CSharpStacktrace::parse_stacktrace(&trace[0..10]) { Ok(s) => s, Err(e) => panic!("{e}") }; - assert_eq!(stacktrace.len(), 3); - assert_eq!(stacktrace[0].function, "B..ctor()".to_string()); - assert_eq!(stacktrace[0].debug.file, "/home/user/dotnet/2/A.cs".to_string()); - assert_eq!(stacktrace[0].debug.line, 37); - assert_eq!(stacktrace[1].function, "A`1.<>c.b__1_1()".to_string()); + assert_eq!(stacktrace.len(), 4); + assert_eq!(stacktrace[0].function, "C.qwe()".to_string()); + assert_eq!(stacktrace[1].function, "B..ctor()".to_string()); assert_eq!(stacktrace[1].debug.file, "/home/user/dotnet/2/A.cs".to_string()); - assert_eq!(stacktrace[1].debug.line, 15); - assert_eq!(stacktrace[2].function, "A`1.h[Z](Func`1 a)".to_string()); + assert_eq!(stacktrace[1].debug.line, 37); + assert_eq!(stacktrace[2].function, "A`1.<>c.b__1_1()".to_string()); + assert_eq!(stacktrace[2].debug.file, "/home/user/dotnet/2/A.cs".to_string()); + assert_eq!(stacktrace[2].debug.line, 15); + assert_eq!(stacktrace[3].function, "A`1.h[Z](Func`1 a)".to_string()); - stacktrace = match CSharpStacktrace::parse_stacktrace(&trace[9..16]) { + stacktrace = match CSharpStacktrace::parse_stacktrace(&trace[9..]) { Ok(s) => s, Err(e) => panic!("{e}") }; - assert_eq!(stacktrace.len(), 7); + assert_eq!(stacktrace.len(), 8); assert_eq!( - stacktrace[2].function, + stacktrace[3].function, "(wrapper runtime-invoke) staticperformanceoptimization.runtime_invoke_void(object,intptr,intptr,intptr)".to_string() ); assert_eq!( - stacktrace[3].function, + stacktrace[4].function, "(wrapper managed-to-native) System.Drawing.GDIPlus:GdiplusStartup(ulong&,System.Drawing.GdiplusStartupInput&,System.Drawing.GdiplusStartupOutput&)".to_string() ); - init_ignored_frames!("csharp"); + safe_init_ignore_stack_frames(); stacktrace.filter(); - assert_eq!(stacktrace.len(), 5); - assert_eq!(stacktrace[0].address, 1085753106); - assert_eq!(stacktrace[0].function, "A`1[T].g__g|1_0(System.Int32[] arr)".to_string()); - assert_eq!(stacktrace[0].debug.file, "/home/user/mono/2/src/2.cs".to_string()); - assert_eq!(stacktrace[0].debug.line, 13); - assert_eq!(stacktrace[1].address, 1076318658); - assert_eq!(stacktrace[1].function, "A`1[T].set_Q(System.Int32 value)".to_string()); - - assert_eq!(stacktrace[2].address, 139758385043173); - assert_eq!(stacktrace[2].function, "Program+<>c.
b__0_2(System.Int32 i)".to_string()); - assert_eq!(stacktrace[3].address, 139758385041945); + assert_eq!(stacktrace.len(), 6); + assert_eq!(stacktrace[0].function, "A`1.h[Z](Func`1 a)".to_string()); + assert_eq!(stacktrace[1].address, 1085753106); + assert_eq!(stacktrace[1].function, "A`1[T].g__g|1_0(System.Int32[] arr)".to_string()); + assert_eq!(stacktrace[1].debug.file, "/home/user/mono/2/src/2.cs".to_string()); + assert_eq!(stacktrace[1].debug.line, 13); + assert_eq!(stacktrace[2].address, 1076318658); + assert_eq!(stacktrace[2].function, "A`1[T].set_Q(System.Int32 value)".to_string()); + assert_eq!(stacktrace[3].address, 139758385043173); + assert_eq!(stacktrace[3].function, "Program+<>c.
b__0_2(System.Int32 i)".to_string()); + assert_eq!(stacktrace[4].address, 139758385041945); assert_eq!( - stacktrace[3].function, + stacktrace[4].function, "Program.
g__f|0_0(System.Func`2[T,TResult] d, System.Int32& l, System.Int32 m)".to_string() ); - assert_eq!(stacktrace[4].address, 139758385041658); - assert_eq!(stacktrace[4].function, "Program.Main()".to_string()); + assert_eq!(stacktrace[5].address, 139758385041658); + assert_eq!(stacktrace[5].function, "Program.Main()".to_string()); let sttr = CSharpStacktrace::parse_stacktrace(&trace); diff --git a/libcasr/src/report.rs b/libcasr/src/report.rs index ae7fb1ed..1744d401 100644 --- a/libcasr/src/report.rs +++ b/libcasr/src/report.rs @@ -1,5 +1,6 @@ //! Report contains the main struct `CrashReport` with all information about crash. use crate::asan::AsanStacktrace; +use crate::csharp::CSharpStacktrace; use crate::error; use crate::error::*; use crate::execution_class::*; @@ -226,6 +227,13 @@ pub struct CrashReport { )] #[cfg_attr(feature = "serde", serde(default))] pub js_report: Vec, + /// C# report. + #[cfg_attr( + feature = "serde", + serde(rename(serialize = "CSharpReport", deserialize = "CSharpReport")) + )] + #[cfg_attr(feature = "serde", serde(default))] + pub csharp_report: Vec, /// Crash line from stack trace: source:line or binary+offset. #[cfg_attr( feature = "serde", @@ -580,6 +588,8 @@ impl CrashReport { RustStacktrace::parse_stacktrace(&self.stacktrace)? } else if !self.js_report.is_empty() { JsStacktrace::parse_stacktrace(&self.stacktrace)? + } else if !self.csharp_report.is_empty() { + CSharpStacktrace::parse_stacktrace(&self.stacktrace)? } else { GdbStacktrace::parse_stacktrace(&self.stacktrace)? }; @@ -769,6 +779,14 @@ impl fmt::Display for CrashReport { } } + // CSharpReport + if !self.csharp_report.is_empty() { + report += "\n===CSharpReport===\n"; + for e in self.csharp_report.iter() { + report += &format!("{e}\n"); + } + } + // Source if !self.source.is_empty() { report += "\n===Source===\n"; @@ -885,6 +903,11 @@ mod tests { "Uncaught ReferenceError: var is not defined".to_string(), " at Worker.fuzz [as fn] (/home/user/test_js_stacktrace/main.js:1:2017)".to_string(), ]; + report.csharp_report = vec![ + "Unhandled Exception:".to_string(), + "System.IndexOutOfRangeException: Index was outside the bounds of the array.".to_string(), + "at Program.Main () <0x7fd826c45020 + 0x00019> in /home/user/mono/src/1.cs:5".to_string(), + ]; report.source = vec![ "--->83 return utf16_to_utf8(std::u16string(name_array.begin()," .to_string(), @@ -963,6 +986,11 @@ mod tests { "Uncaught ReferenceError: var is not defined".to_string(), " at Worker.fuzz [as fn] (/home/user/test_js_stacktrace/main.js:1:2017)".to_string(), "".to_string(), + "===CSharpReport===".to_string(), + "Unhandled Exception:".to_string(), + "System.IndexOutOfRangeException: Index was outside the bounds of the array.".to_string(), + "at Program.Main () <0x7fd826c45020 + 0x00019> in /home/user/mono/src/1.cs:5".to_string(), + "".to_string(), "===Source===".to_string(), "--->83 return utf16_to_utf8(std::u16string(name_array.begin(),".to_string(), ].join("\n"), diff --git a/libcasr/src/stacktrace.rs b/libcasr/src/stacktrace.rs index e70598fd..1a074542 100644 --- a/libcasr/src/stacktrace.rs +++ b/libcasr/src/stacktrace.rs @@ -475,7 +475,7 @@ pub mod tests { let mut is_inited = INITED_STACKFRAMES_FILTER.write().unwrap(); if !*is_inited { *is_inited = true; - init_ignored_frames!("cpp", "rust", "python", "go", "java", "js"); + init_ignored_frames!("cpp", "rust", "python", "go", "java", "js", "csharp"); } } From 26af9837779e4331bcf84b8d78efcf8b56f14d7b Mon Sep 17 00:00:00 2001 From: BMikh0 Date: Tue, 27 Feb 2024 15:16:02 +0300 Subject: [PATCH 08/11] Comments added. --- libcasr/src/csharp.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libcasr/src/csharp.rs b/libcasr/src/csharp.rs index 0755728a..1a195929 100644 --- a/libcasr/src/csharp.rs +++ b/libcasr/src/csharp.rs @@ -388,6 +388,8 @@ Unhandled exception. System.ArgumentException: 1111 ---> System.IO.IOException: let sttr = CSharpStacktrace::parse_stacktrace(&trace); assert!(sttr.is_err()); + + // Here is a check on which error exactly. The error is due to the fact that trace consists of blocks of two different formats. assert_eq!(sttr.err().unwrap().to_string(), "Casr: Couldn't parse stacktrace line: at A`1[T].g__g|1_0 (System.Int32[] arr) <0x40b745f0 + 0x00122> in /home/user/mono/2/src/2.cs:13"); } } From 2183ef57a2d01e1d7bee0af72e20c50dd23952cd Mon Sep 17 00:00:00 2001 From: BMikh0 Date: Tue, 5 Mar 2024 22:22:47 +0300 Subject: [PATCH 09/11] Bugs fixed. --- casr/src/bin/casr-cli.rs | 14 ++ libcasr/src/constants.rs | 2 +- libcasr/src/csharp.rs | 398 ++++++++++++++++++-------------------- libcasr/src/report.rs | 10 +- libcasr/src/stacktrace.rs | 9 +- 5 files changed, 211 insertions(+), 222 deletions(-) diff --git a/casr/src/bin/casr-cli.rs b/casr/src/bin/casr-cli.rs index 451cd573..4229804f 100644 --- a/casr/src/bin/casr-cli.rs +++ b/casr/src/bin/casr-cli.rs @@ -469,6 +469,16 @@ fn build_tree_report( tree.collapse_item(row); } + if !report.csharp_report.is_empty() { + row = tree + .insert_container_item("CSharpReport".to_string(), Placement::After, row) + .unwrap(); + report.csharp_report.iter().for_each(|e| { + tree.insert_item(e.clone(), Placement::LastChild, row); + }); + tree.collapse_item(row); + } + if !report.source.is_empty() { row = tree .insert_container_item("Source".to_string(), Placement::After, row) @@ -655,6 +665,10 @@ fn build_slider_report( select.add_item("JsReport", report.js_report.join("\n")); } + if !report.csharp_report.is_empty() { + select.add_item("CSharpReport", report.csharp_report.join("\n")); + } + if !report.source.is_empty() { select.add_item("Source", report.source.join("\n")); } diff --git a/libcasr/src/constants.rs b/libcasr/src/constants.rs index be5daa40..6fe55b9f 100644 --- a/libcasr/src/constants.rs +++ b/libcasr/src/constants.rs @@ -322,7 +322,7 @@ pub const STACK_FRAME_FILEPATH_IGNORE_REGEXES_CPP: &[&str] = &[ /// Regular expressions for paths to c# files that should be ignored. pub const STACK_FRAME_FILEPATH_IGNORE_REGEXES_CSHARP: &[&str] = &[ // TODO - r"^[^.]$" + r"^[^.]$", ]; // Signal numbers diff --git a/libcasr/src/csharp.rs b/libcasr/src/csharp.rs index 1a195929..64c48927 100644 --- a/libcasr/src/csharp.rs +++ b/libcasr/src/csharp.rs @@ -4,77 +4,43 @@ use crate::exception::Exception; use crate::execution_class::ExecutionClass; use crate::stacktrace::{ParseStacktrace, Stacktrace, StacktraceEntry}; use regex::Regex; -use std::vec::IntoIter; /// Structure provides an interface for processing the stack trace. pub struct CSharpStacktrace; -struct FormatRegexIterator<'a> { - entry: &'a str, - regexps: IntoIter -} - -impl<'a> FormatRegexIterator<'a> { - fn new(entry: &'a str) -> Self { - FormatRegexIterator { - entry, - regexps: vec![ - // Mono - Regex::new(r"^at (?:(?P\(.+?\)) )?(?P\S+?)(?: ?(?P\(.*?\)))?(?: (?:<(?P\w+)(?: \+ (?P\w+))?>|\[0x[\da-fA-F]+\]))?(?: in (?:<[\da-fA-F#]+>|(?P.+)):(?P\w+))?$").unwrap(), - // DotNet - Regex::new(r"^at (?:(?P\(.+?\)) )?(?P\S+?)(?P\(.*?\))?(?: in (?P.+):line (?P\w+))?$").unwrap() - ].into_iter() - } - } -} +impl ParseStacktrace for CSharpStacktrace { + fn extract_stacktrace(stream: &str) -> Result> { + let re = Regex::new(r"(?m)^Unhandled [Ee]xception(?::\n|\. )(?:.|\n)*?((?:[ \n\t]*(?:at [\S ]+|--- End of inner exception stack trace ---))+)$").unwrap(); -impl Iterator for FormatRegexIterator<'_> { - type Item = Regex; + let Some(cap) = re.captures(stream) else { + return Err(Error::Casr( + "The stacktrace format is not recognized".to_string(), + )); + }; - fn next(&mut self) -> Option { - self.regexps.find(|x| x.is_match(self.entry)) + Ok(cap + .get(1) + .unwrap() + .as_str() + .split('\n') + .map(|s| s.trim().to_string()) + .filter(|s| !s.is_empty()) + .collect::>()) } -} -impl CSharpStacktrace { - fn parse_stacktrace_entry(entry: &str, format_regex: &Regex) -> Result { - let Some(cap) = format_regex.captures(entry) else { - return Err(Error::Casr(format!("Couldn't parse stacktrace line: {entry}"))); + fn parse_stacktrace_entry(entry: &str) -> Result { + let re = Regex::new(r"^at (?P\([\S ]+?\) )?(?P\S+?)(?: ?(?P\([\S ]*?\)))?(?: <(?P\w+)(?: \+ (?P\w+))?>| \[(?P\w+)\])?(?: in (?:<(?P[[:xdigit:]]+)(?:#[[:xdigit:]]+)?>|(?P[\S ]+)):(?:line )?(?P\w+))?$").unwrap(); + + let Some(cap) = re.captures(entry) else { + return Err(Error::Casr(format!( + "Couldn't parse stacktrace line: {entry}" + ))); }; let get_group_by_name_as_str = |name| cap.name(name).map(|m| m.as_str()); let mut stentry = StacktraceEntry::default(); - if let Some(file) = get_group_by_name_as_str("file") { - stentry.debug.file = file.to_string(); - } - - if let Some(line) = get_group_by_name_as_str("line") { - let Ok(parsed_line) = line.parse::() else { - return Err(Error::Casr(format!("Couldn't parse stacktrace line num: {line}"))) - }; - - stentry.debug.line = parsed_line; - } - - if let (Some(base), Some(offset)) = (get_group_by_name_as_str("base"), get_group_by_name_as_str("offset")) { - let re_hex = Regex::new(r"^0x([\da-fA-F]+)$").unwrap(); - let parse_hex = |s| re_hex - .captures(s) - .and_then(|c| u64::from_str_radix(c.get(1).unwrap().as_str(), 16).ok()); - - if let (Some(parsed_base), Some(parsed_offset)) = (parse_hex(base), parse_hex(offset)) { - if let Some(address) = parsed_base.checked_add(parsed_offset) { - stentry.address = address; - } else { - return Err(Error::Casr(format!("Couldn't parse address: {base} + {offset}"))); - } - } else { - return Err(Error::Casr(format!("Couldn't parse address: {base} + {offset}"))); - }; - }; - if let Some(function) = get_group_by_name_as_str("function") { let mut function = function.to_string(); @@ -83,83 +49,74 @@ impl CSharpStacktrace { } if let Some(service_info) = get_group_by_name_as_str("service_info") { - function = format!("{service_info} {function}"); + function.insert_str(0, service_info); } stentry.function = function; } - Ok(stentry) - } -} - -impl ParseStacktrace for CSharpStacktrace { - fn extract_stacktrace(stream: &str) -> Result> { - let re = Regex::new(r"(?m)^Unhandled (E|e)xception(:\n|\. )(?:.|\n)*?((?:\s*(?:at [\S ]+|--- End of inner exception stack trace ---))+)$").unwrap(); - - let Some(cap) = re.captures(stream).and_then(|cap| - ((cap.get(1).unwrap().as_str() == "E") == (cap.get(2).unwrap().as_str() == ":\n")).then_some(cap) - ) else { - return Err(Error::Casr("The stacktrace format is not recognized".to_string())); + let re_hex = Regex::new(r"^0x([[:xdigit:]]+)$").unwrap(); + let parse_hex = |s| { + re_hex + .captures(s) + .and_then(|c| u64::from_str_radix(c.get(1).unwrap().as_str(), 16).ok()) }; - Ok(cap - .get(3) - .unwrap() - .as_str() - .split('\n') - .map(|s| s.trim().to_string()) - .filter(|s| !s.is_empty()) - .collect::>()) - } - - fn parse_stacktrace_entry(entry: &str) -> Result { - Self::parse_stacktrace_entry( - entry, - &FormatRegexIterator::new(entry) - .next() - .ok_or(Error::Casr(format!("Couldn't parse stacktrace line: {entry}")))?) - } - - fn parse_stacktrace(entries: &[String]) -> Result { - let mut iter = entries - .iter() - .scan((true, false), |(first_block, skip), s| { - // Skipping all blocks consisting of "--- End of inner exception stack trace ---" - // and one stack frame after each such block, except for the first block if entries start with it. - let not_stack_trace_entry = s == "--- End of inner exception stack trace ---"; - - if not_stack_trace_entry || *skip { - *skip = not_stack_trace_entry; - - if !*first_block || not_stack_trace_entry { - return Some(""); + if let Some(base) = get_group_by_name_as_str("base") { + if let Some(parsed_base) = parse_hex(base) { + if let Some(offset) = get_group_by_name_as_str("offset") { + if let Some(address) = parse_hex(offset) + .and_then(|parsed_offset| parsed_base.checked_add(parsed_offset)) + { + stentry.address = address; + } else { + return Err(Error::Casr(format!( + "Couldn't parse address: {base} + {offset}" + ))); } + } else { + stentry.address = parsed_base; } + } else { + return Err(Error::Casr(format!("Couldn't parse address: {base}"))); + } + } else if let Some(il_offset) = get_group_by_name_as_str("il_offset") { + if let Some(parsed_il_offset) = parse_hex(il_offset) { + stentry.address = parsed_il_offset; + } else { + return Err(Error::Casr(format!( + "Couldn't parse IL offset: {il_offset}" + ))); + } + } - *first_block = false; - - Some(s) - }) - .filter(|&s| !s.is_empty()).peekable(); - - if let Some(entry) = iter.peek() { - let mut e = (0, Err(Error::Casr(format!("Couldn't parse stacktrace line: {entry}")))); - - for re in FormatRegexIterator::new(entry) { - let mut iter = iter.clone().map(|s| Self::parse_stacktrace_entry(s, &re)); - let sttr: Result = iter.clone().collect(); - - if sttr.is_ok() { return sttr; } + if let Some(file) = + get_group_by_name_as_str("file").or_else(|| get_group_by_name_as_str("mvid")) + { + stentry.debug.file = file.to_string(); + } - let n = iter.position(|r| r.is_err()).unwrap(); - if n >= e.0 { e = (n, sttr); } - } + if let Some(line) = get_group_by_name_as_str("line") { + let Ok(parsed_line) = line.parse::() else { + return Err(Error::Casr(format!( + "Couldn't parse stacktrace line num: {line}" + ))); + }; - return e.1 + stentry.debug.line = parsed_line; } - Ok(Stacktrace::default()) + Ok(stentry) + } + + fn parse_stacktrace(entries: &[String]) -> Result { + entries + .iter() + .filter_map(|s| { + (s != "--- End of inner exception stack trace ---") + .then_some(Self::parse_stacktrace_entry(s)) + }) + .collect() } } @@ -168,24 +125,27 @@ pub struct CSharpException; impl Exception for CSharpException { fn parse_exception(stream: &str) -> Option { - let re = Regex::new(r"(?m)^Unhandled (E|e)xception(:\n|\. )((?:.|\n)*?)\s*(?:at [\S ]+|--- End of inner exception stack trace ---)$").unwrap(); + let re = Regex::new(r"(?m)^Unhandled [Ee]xception(:\n|\. )((?:.|\n)*?)\n[ \t]*(?:at [\S ]+|--- End of inner exception stack trace ---)$").unwrap(); let cap = re.captures(stream)?; - let get_group_as_str = |i| cap.get(i).unwrap().as_str(); - - let is_mono = get_group_as_str(1) == "E"; - if is_mono != (get_group_as_str(2) == ":\n") { return None; } - - let description = get_group_as_str(3); - let delimiter = if is_mono { " ---> " } else { "\n ---> " }; + let delimiter = if cap.get(1).unwrap().as_str() == ":\n" { + " ---> " + } else { + "\n ---> " + }; + let description = cap.get(2).unwrap().as_str(); let (exception, message) = description .rsplit_once(delimiter) .map_or(description, |(_, s)| s) .split_once(": ")?; - Some(ExecutionClass { short_description: exception.to_string(), description: message.to_string(), ..ExecutionClass::default()}) + Some(ExecutionClass { + short_description: exception.to_string(), + description: message.to_string(), + ..ExecutionClass::default() + }) } } @@ -194,47 +154,6 @@ mod tests { use super::*; use crate::stacktrace::{tests::safe_init_ignore_stack_frames, Filter}; - #[test] - fn test_csharp_get_regex_format_for_stacktrace_entry() { - let re_mono = r"^at (?:(?P\(.+?\)) )?(?P\S+?)(?: ?(?P\(.*?\)))?(?: (?:<(?P\w+)(?: \+ (?P\w+))?>|\[0x[\da-fA-F]+\]))?(?: in (?:<[\da-fA-F#]+>|(?P.+)):(?P\w+))?$"; - let re_dotnet = r"^at (?:(?P\(.+?\)) )?(?P\S+?)(?P\(.*?\))?(?: in (?P.+):line (?P\w+))?$"; - - fn get_regex(iter: &mut FormatRegexIterator) -> Option { - iter.next().map(|r| r.to_string()) - } - - let mut iter; - - iter = FormatRegexIterator::new("at A`1[T].h[Z] (System.Func`1[TResult] a) [0x00001] in :0"); - assert_eq!(get_regex(&mut iter).as_deref(), Some(re_mono)); - assert_eq!(get_regex(&mut iter).as_deref(), None); - - iter = FormatRegexIterator::new("at A`1.g__g|1_0(Int32[] arr(((((((((( in /home/user/dotnet/2/A.cs:line 19"); - assert_eq!(get_regex(&mut iter).as_deref(), None); - - iter = FormatRegexIterator::new("at Program+<>c.
b__0_2 (System.Int32 i) <0x7f30488306b0 + 0x00035> in :0"); - assert_eq!(get_regex(&mut iter).as_deref(), Some(re_mono)); - assert_eq!(get_regex(&mut iter).as_deref(), None); - - iter = FormatRegexIterator::new("at (wrapper managed-to-native) System.Drawing.GDIPlus:GdiplusStartup (ulong&,System.Drawing.GdiplusStartupInput&,System.Drawing.GdiplusStartupOutput&)"); - assert_eq!(get_regex(&mut iter).as_deref(), Some(re_mono)); - assert_eq!(get_regex(&mut iter).as_deref(), None); - - iter = FormatRegexIterator::new("at A`1[T].set_Q (System.Int32 value) <0x40b74140 + 0x00082> in /home/user/mono/2/src/2.cs:24"); - assert_eq!(get_regex(&mut iter).as_deref(), Some(re_mono)); - assert_eq!(get_regex(&mut iter).as_deref(), None); - - iter = FormatRegexIterator::new("at Program.
g__f|0_0(Func`2 d, Int32& l, Int32 m) in /home/user/dotnet/2/Program.cs:line 9"); - assert_eq!(get_regex(&mut iter).as_deref(), Some(re_dotnet)); - assert_eq!(get_regex(&mut iter).as_deref(), None); - - iter = FormatRegexIterator::new("at Program.Main()"); - assert_eq!(get_regex(&mut iter).as_deref(), Some(re_mono)); - assert_eq!(get_regex(&mut iter).as_deref(), Some(re_dotnet)); - assert_eq!(get_regex(&mut iter).as_deref(), None); - - } - #[test] fn test_csharp_parse_exception() { let streams = [ @@ -246,6 +165,7 @@ System.ArithmeticException: 123 7 ---> System.ArgumentException: 1111 ---> System.IO.IOException: cccc + --- End of inner exception stack trace ---", " Unhandled Exception: @@ -255,10 +175,15 @@ System.ArgumentException: 1111 " Unhandled exception. System.ArgumentException: 1111 ---> System.IO.IOException: cccc - --- End of inner exception stack trace ---", + + + at Program.
g__f|0_0(Func`2 d, Int32& l, Int32 m) in /home/user/dotnet/2/Program.cs:line 9", " Unhandled exception. System.ArgumentException: 1111 ---> System.IO.IOException: cccc - --- End of inner exception stack trace ---" + + + + at Program.
g__f|0_0(Func`2 d, Int32& l, Int32 m) in /home/user/dotnet/2/Program.cs:line 9", ]; let exceptions = streams.map(|s| { @@ -269,13 +194,16 @@ Unhandled exception. System.ArgumentException: 1111 ---> System.IO.IOException: }); assert_eq!(exceptions[0].short_description, "System.IO.IOException"); - assert_eq!(exceptions[0].description, "cccc"); + assert_eq!(exceptions[0].description, "cccc\n"); assert_eq!(exceptions[1].short_description, "System.IO.IOException"); assert_eq!(exceptions[1].description, "cccc"); assert_eq!(exceptions[2].short_description, "System.IO.IOException"); - assert_eq!(exceptions[2].description, "cccc"); + assert_eq!(exceptions[2].description, "cccc\n\n"); assert_eq!(exceptions[3].short_description, "System.ArgumentException"); - assert_eq!(exceptions[3].description, "1111 ---> System.IO.IOException: cccc"); + assert_eq!( + exceptions[3].description, + "1111 ---> System.IO.IOException: cccc\n\n\n" + ); } #[test] @@ -294,16 +222,22 @@ Unhandled exception. System.ArgumentException: 1111 ---> System.IO.IOException: --- End of inner exception stack trace --- --- End of inner exception stack trace --- --- End of inner exception stack trace --- + + at C.qwe() at B..ctor() in /home/user/dotnet/2/A.cs:line 37 at A`1.<>c.b__1_1() in /home/user/dotnet/2/A.cs:line 15 + at A`1.h[Z](Func`1 a) --- End of inner exception stack trace --- --- End of inner exception stack trace --- - at A`1.h[Z](Func`1 a) at A`1[T].g__g|1_0 (System.Int32[] arr) <0x40b745f0 + 0x00122> in /home/user/mono/2/src/2.cs:13 + at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback () [0x00000] in :0 + at A`1[T].set_Q (System.Int32 value) <0x40275140 + 0x00082> in :0 at (wrapper runtime-invoke) staticperformanceoptimization.runtime_invoke_void (object,intptr,intptr,intptr) <0xffffffff> + + at (wrapper managed-to-native) System.Drawing.GDIPlus:GdiplusStartup (ulong&,System.Drawing.GdiplusStartupInput&,System.Drawing.GdiplusStartupOutput&) at Program+<>c.
b__0_2 (System.Int32 i) <0x7f1c08e516b0 + 0x00035> in :0 at Program.
g__f|0_0 (System.Func`2[T,TResult] d, System.Int32& l, System.Int32 m) <0x7f1c08e51140 + 0x000d9> in :0 @@ -320,8 +254,8 @@ Unhandled exception. System.ArgumentException: 1111 ---> System.IO.IOException: "at A`1.h[Z](Func`1 a)", "--- End of inner exception stack trace ---", "--- End of inner exception stack trace ---", - "at A`1.h[Z](Func`1 a)", "at A`1[T].g__g|1_0 (System.Int32[] arr) <0x40b745f0 + 0x00122> in /home/user/mono/2/src/2.cs:13", + "at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback () [0x00000] in :0", "at A`1[T].set_Q (System.Int32 value) <0x40275140 + 0x00082> in :0", "at (wrapper runtime-invoke) staticperformanceoptimization.runtime_invoke_void (object,intptr,intptr,intptr) <0xffffffff>", "at (wrapper managed-to-native) System.Drawing.GDIPlus:GdiplusStartup (ulong&,System.Drawing.GdiplusStartupInput&,System.Drawing.GdiplusStartupOutput&)", @@ -334,62 +268,102 @@ Unhandled exception. System.ArgumentException: 1111 ---> System.IO.IOException: assert_eq!(bt, trace); - let mut stacktrace = match CSharpStacktrace::parse_stacktrace(&trace[0..10]) { + let mut stacktrace = match CSharpStacktrace::parse_stacktrace(&trace) { Ok(s) => s, - Err(e) => panic!("{e}") + Err(e) => panic!("{e}"), }; - assert_eq!(stacktrace.len(), 4); + assert_eq!(stacktrace.len(), 12); + assert_eq!(stacktrace[0].function, "C.qwe()".to_string()); assert_eq!(stacktrace[1].function, "B..ctor()".to_string()); - assert_eq!(stacktrace[1].debug.file, "/home/user/dotnet/2/A.cs".to_string()); + assert_eq!( + stacktrace[1].debug.file, + "/home/user/dotnet/2/A.cs".to_string() + ); assert_eq!(stacktrace[1].debug.line, 37); - assert_eq!(stacktrace[2].function, "A`1.<>c.b__1_1()".to_string()); - assert_eq!(stacktrace[2].debug.file, "/home/user/dotnet/2/A.cs".to_string()); + assert_eq!( + stacktrace[2].function, + "A`1.<>c.b__1_1()".to_string() + ); + assert_eq!( + stacktrace[2].debug.file, + "/home/user/dotnet/2/A.cs".to_string() + ); assert_eq!(stacktrace[2].debug.line, 15); - assert_eq!(stacktrace[3].function, "A`1.h[Z](Func`1 a)".to_string()); - - stacktrace = match CSharpStacktrace::parse_stacktrace(&trace[9..]) { - Ok(s) => s, - Err(e) => panic!("{e}") - }; - - assert_eq!(stacktrace.len(), 8); + assert_eq!(stacktrace[6].address, 1076318658); + assert_eq!( + stacktrace[6].function, + "A`1[T].set_Q(System.Int32 value)".to_string() + ); assert_eq!( - stacktrace[3].function, + stacktrace[6].debug.file, + "f6b2b0ea894844dc83a96f9504d8f570".to_string() + ); + assert_eq!(stacktrace[6].debug.line, 0); + assert_eq!(stacktrace[7].address, 0xffffffff); + assert_eq!( + stacktrace[7].function, "(wrapper runtime-invoke) staticperformanceoptimization.runtime_invoke_void(object,intptr,intptr,intptr)".to_string() ); assert_eq!( - stacktrace[4].function, + stacktrace[8].function, "(wrapper managed-to-native) System.Drawing.GDIPlus:GdiplusStartup(ulong&,System.Drawing.GdiplusStartupInput&,System.Drawing.GdiplusStartupOutput&)".to_string() ); + assert_eq!(stacktrace[9].address, 139758385043173); + assert_eq!( + stacktrace[9].function, + "Program+<>c.
b__0_2(System.Int32 i)".to_string() + ); + assert_eq!( + stacktrace[9].debug.file, + "f6b2b0ea894844dc83a96f9504d8f570".to_string() + ); + assert_eq!(stacktrace[9].debug.line, 0); + assert_eq!( + stacktrace[10].function, + "Program.
g__f|0_0(System.Func`2[T,TResult] d, System.Int32& l, System.Int32 m)" + .to_string() + ); + assert_eq!(stacktrace[10].address, 139758385041945); + assert_eq!( + stacktrace[10].debug.file, + "f6b2b0ea894844dc83a96f9504d8f570".to_string() + ); + assert_eq!(stacktrace[10].debug.line, 0); safe_init_ignore_stack_frames(); stacktrace.filter(); - assert_eq!(stacktrace.len(), 6); + assert_eq!(stacktrace.len(), 4); + assert_eq!(stacktrace[0].function, "A`1.h[Z](Func`1 a)".to_string()); assert_eq!(stacktrace[1].address, 1085753106); - assert_eq!(stacktrace[1].function, "A`1[T].g__g|1_0(System.Int32[] arr)".to_string()); - assert_eq!(stacktrace[1].debug.file, "/home/user/mono/2/src/2.cs".to_string()); + assert_eq!( + stacktrace[1].function, + "A`1[T].g__g|1_0(System.Int32[] arr)".to_string() + ); + assert_eq!( + stacktrace[1].debug.file, + "/home/user/mono/2/src/2.cs".to_string() + ); assert_eq!(stacktrace[1].debug.line, 13); - assert_eq!(stacktrace[2].address, 1076318658); - assert_eq!(stacktrace[2].function, "A`1[T].set_Q(System.Int32 value)".to_string()); - assert_eq!(stacktrace[3].address, 139758385043173); - assert_eq!(stacktrace[3].function, "Program+<>c.
b__0_2(System.Int32 i)".to_string()); - assert_eq!(stacktrace[4].address, 139758385041945); + assert_eq!(stacktrace[2].address, 0); assert_eq!( - stacktrace[4].function, - "Program.
g__f|0_0(System.Func`2[T,TResult] d, System.Int32& l, System.Int32 m)".to_string() + stacktrace[2].function, + "System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()".to_string() ); - assert_eq!(stacktrace[5].address, 139758385041658); - assert_eq!(stacktrace[5].function, "Program.Main()".to_string()); - - let sttr = CSharpStacktrace::parse_stacktrace(&trace); - - assert!(sttr.is_err()); - - // Here is a check on which error exactly. The error is due to the fact that trace consists of blocks of two different formats. - assert_eq!(sttr.err().unwrap().to_string(), "Casr: Couldn't parse stacktrace line: at A`1[T].g__g|1_0 (System.Int32[] arr) <0x40b745f0 + 0x00122> in /home/user/mono/2/src/2.cs:13"); + assert_eq!( + stacktrace[2].debug.file, + "c79446e93efd45a0b7bc2f9631593aff".to_string() + ); + assert_eq!(stacktrace[2].debug.line, 0); + assert_eq!(stacktrace[3].address, 139758385041658); + assert_eq!(stacktrace[3].function, "Program.Main()".to_string()); + assert_eq!( + stacktrace[3].debug.file, + "f6b2b0ea894844dc83a96f9504d8f570".to_string() + ); + assert_eq!(stacktrace[3].debug.line, 0); } } diff --git a/libcasr/src/report.rs b/libcasr/src/report.rs index 1744d401..52ae6e03 100644 --- a/libcasr/src/report.rs +++ b/libcasr/src/report.rs @@ -229,8 +229,8 @@ pub struct CrashReport { pub js_report: Vec, /// C# report. #[cfg_attr( - feature = "serde", - serde(rename(serialize = "CSharpReport", deserialize = "CSharpReport")) + feature = "serde", + serde(rename(serialize = "CSharpReport", deserialize = "CSharpReport")) )] #[cfg_attr(feature = "serde", serde(default))] pub csharp_report: Vec, @@ -905,8 +905,10 @@ mod tests { ]; report.csharp_report = vec![ "Unhandled Exception:".to_string(), - "System.IndexOutOfRangeException: Index was outside the bounds of the array.".to_string(), - "at Program.Main () <0x7fd826c45020 + 0x00019> in /home/user/mono/src/1.cs:5".to_string(), + "System.IndexOutOfRangeException: Index was outside the bounds of the array." + .to_string(), + "at Program.Main () <0x7fd826c45020 + 0x00019> in /home/user/mono/src/1.cs:5" + .to_string(), ]; report.source = vec![ "--->83 return utf16_to_utf8(std::u16string(name_array.begin()," diff --git a/libcasr/src/stacktrace.rs b/libcasr/src/stacktrace.rs index 1a074542..5e7d8ca4 100644 --- a/libcasr/src/stacktrace.rs +++ b/libcasr/src/stacktrace.rs @@ -6,11 +6,10 @@ use crate::constants::{ STACK_FRAME_FILEPATH_IGNORE_REGEXES_CPP, STACK_FRAME_FILEPATH_IGNORE_REGEXES_CSHARP, STACK_FRAME_FILEPATH_IGNORE_REGEXES_GO, STACK_FRAME_FILEPATH_IGNORE_REGEXES_JAVA, STACK_FRAME_FILEPATH_IGNORE_REGEXES_JS, STACK_FRAME_FILEPATH_IGNORE_REGEXES_PYTHON, - STACK_FRAME_FILEPATH_IGNORE_REGEXES_RUST, - STACK_FRAME_FUNCTION_IGNORE_REGEXES_CPP, STACK_FRAME_FUNCTION_IGNORE_REGEXES_CSHARP, - STACK_FRAME_FUNCTION_IGNORE_REGEXES_GO, STACK_FRAME_FUNCTION_IGNORE_REGEXES_JAVA, - STACK_FRAME_FUNCTION_IGNORE_REGEXES_JS, STACK_FRAME_FUNCTION_IGNORE_REGEXES_PYTHON, - STACK_FRAME_FUNCTION_IGNORE_REGEXES_RUST, + STACK_FRAME_FILEPATH_IGNORE_REGEXES_RUST, STACK_FRAME_FUNCTION_IGNORE_REGEXES_CPP, + STACK_FRAME_FUNCTION_IGNORE_REGEXES_CSHARP, STACK_FRAME_FUNCTION_IGNORE_REGEXES_GO, + STACK_FRAME_FUNCTION_IGNORE_REGEXES_JAVA, STACK_FRAME_FUNCTION_IGNORE_REGEXES_JS, + STACK_FRAME_FUNCTION_IGNORE_REGEXES_PYTHON, STACK_FRAME_FUNCTION_IGNORE_REGEXES_RUST, }; use crate::error::*; use kodama::{linkage, Method}; From 3c7c2dda95232cf5573fa8461c35fc2e542edae2 Mon Sep 17 00:00:00 2001 From: BMikh0 Date: Wed, 13 Mar 2024 12:28:40 +0300 Subject: [PATCH 10/11] Corrections have been made. --- libcasr/src/csharp.rs | 56 +++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/libcasr/src/csharp.rs b/libcasr/src/csharp.rs index 64c48927..928e76f9 100644 --- a/libcasr/src/csharp.rs +++ b/libcasr/src/csharp.rs @@ -37,18 +37,18 @@ impl ParseStacktrace for CSharpStacktrace { ))); }; - let get_group_by_name_as_str = |name| cap.name(name).map(|m| m.as_str()); + let group_as_str = |name| cap.name(name).map(|m| m.as_str()); let mut stentry = StacktraceEntry::default(); - if let Some(function) = get_group_by_name_as_str("function") { + if let Some(function) = group_as_str("function") { let mut function = function.to_string(); - if let Some(params) = get_group_by_name_as_str("params") { + if let Some(params) = group_as_str("params") { function.push_str(params) } - if let Some(service_info) = get_group_by_name_as_str("service_info") { + if let Some(service_info) = group_as_str("service_info") { function.insert_str(0, service_info); } @@ -62,41 +62,39 @@ impl ParseStacktrace for CSharpStacktrace { .and_then(|c| u64::from_str_radix(c.get(1).unwrap().as_str(), 16).ok()) }; - if let Some(base) = get_group_by_name_as_str("base") { - if let Some(parsed_base) = parse_hex(base) { - if let Some(offset) = get_group_by_name_as_str("offset") { - if let Some(address) = parse_hex(offset) - .and_then(|parsed_offset| parsed_base.checked_add(parsed_offset)) - { - stentry.address = address; - } else { - return Err(Error::Casr(format!( - "Couldn't parse address: {base} + {offset}" - ))); - } - } else { - stentry.address = parsed_base; - } - } else { + if let Some(base) = group_as_str("base") { + let Some(parsed_base) = parse_hex(base) else { return Err(Error::Casr(format!("Couldn't parse address: {base}"))); - } - } else if let Some(il_offset) = get_group_by_name_as_str("il_offset") { - if let Some(parsed_il_offset) = parse_hex(il_offset) { - stentry.address = parsed_il_offset; + }; + + if let Some(offset) = group_as_str("offset") { + let Some(address) = parse_hex(offset) + .and_then(|parsed_offset| parsed_base.checked_add(parsed_offset)) + else { + return Err(Error::Casr(format!( + "Couldn't parse address: {base} + {offset}" + ))); + }; + + stentry.address = address; } else { + stentry.address = parsed_base; + } + } else if let Some(il_offset) = group_as_str("il_offset") { + let Some(parsed_il_offset) = parse_hex(il_offset) else { return Err(Error::Casr(format!( "Couldn't parse IL offset: {il_offset}" ))); - } + }; + + stentry.address = parsed_il_offset; } - if let Some(file) = - get_group_by_name_as_str("file").or_else(|| get_group_by_name_as_str("mvid")) - { + if let Some(file) = group_as_str("file").or_else(|| group_as_str("mvid")) { stentry.debug.file = file.to_string(); } - if let Some(line) = get_group_by_name_as_str("line") { + if let Some(line) = group_as_str("line") { let Ok(parsed_line) = line.parse::() else { return Err(Error::Casr(format!( "Couldn't parse stacktrace line num: {line}" From 7a48407c9d2a985d7c0e5c1870442931a84d3ab4 Mon Sep 17 00:00:00 2001 From: BMikh0 Date: Mon, 18 Mar 2024 12:14:18 +0300 Subject: [PATCH 11/11] Delimiter string filtering has been moved from parse_stacktrace to extract_stacktrace in csharp.rs. --- libcasr/src/csharp.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/libcasr/src/csharp.rs b/libcasr/src/csharp.rs index 928e76f9..194f50f5 100644 --- a/libcasr/src/csharp.rs +++ b/libcasr/src/csharp.rs @@ -24,7 +24,7 @@ impl ParseStacktrace for CSharpStacktrace { .as_str() .split('\n') .map(|s| s.trim().to_string()) - .filter(|s| !s.is_empty()) + .filter(|s| !s.is_empty() && s != "--- End of inner exception stack trace ---") .collect::>()) } @@ -110,10 +110,7 @@ impl ParseStacktrace for CSharpStacktrace { fn parse_stacktrace(entries: &[String]) -> Result { entries .iter() - .filter_map(|s| { - (s != "--- End of inner exception stack trace ---") - .then_some(Self::parse_stacktrace_entry(s)) - }) + .map(|s| Self::parse_stacktrace_entry(s)) .collect() } } @@ -243,15 +240,10 @@ Unhandled exception. System.ArgumentException: 1111 ---> System.IO.IOException: "; let trace = [ - "--- End of inner exception stack trace ---", - "--- End of inner exception stack trace ---", - "--- End of inner exception stack trace ---", "at C.qwe()", "at B..ctor() in /home/user/dotnet/2/A.cs:line 37", "at A`1.<>c.b__1_1() in /home/user/dotnet/2/A.cs:line 15", "at A`1.h[Z](Func`1 a)", - "--- End of inner exception stack trace ---", - "--- End of inner exception stack trace ---", "at A`1[T].g__g|1_0 (System.Int32[] arr) <0x40b745f0 + 0x00122> in /home/user/mono/2/src/2.cs:13", "at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback () [0x00000] in :0", "at A`1[T].set_Q (System.Int32 value) <0x40275140 + 0x00082> in :0",