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/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 17feb4ed..6fe55b9f 100644 --- a/libcasr/src/constants.rs +++ b/libcasr/src/constants.rs @@ -228,6 +228,12 @@ 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] = &[ + // TODO + 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 +319,12 @@ 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] = &[ + // TODO + 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 new file mode 100644 index 00000000..194f50f5 --- /dev/null +++ b/libcasr/src/csharp.rs @@ -0,0 +1,359 @@ +//! C# module implements `ParseStacktrace` and `Exception` traits for C# reports. +use crate::error::*; +use crate::exception::Exception; +use crate::execution_class::ExecutionClass; +use crate::stacktrace::{ParseStacktrace, 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 [Ee]xception(?::\n|\. )(?:.|\n)*?((?:[ \n\t]*(?:at [\S ]+|--- End of inner exception stack trace ---))+)$").unwrap(); + + let Some(cap) = re.captures(stream) else { + return Err(Error::Casr( + "The stacktrace format is not recognized".to_string(), + )); + }; + + Ok(cap + .get(1) + .unwrap() + .as_str() + .split('\n') + .map(|s| s.trim().to_string()) + .filter(|s| !s.is_empty() && s != "--- End of inner exception stack trace ---") + .collect::>()) + } + + 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 group_as_str = |name| cap.name(name).map(|m| m.as_str()); + + let mut stentry = StacktraceEntry::default(); + + if let Some(function) = group_as_str("function") { + let mut function = function.to_string(); + + if let Some(params) = group_as_str("params") { + function.push_str(params) + } + + if let Some(service_info) = group_as_str("service_info") { + function.insert_str(0, service_info); + } + + stentry.function = function; + } + + 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()) + }; + + 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}"))); + }; + + 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) = group_as_str("file").or_else(|| group_as_str("mvid")) { + stentry.debug.file = file.to_string(); + } + + 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}" + ))); + }; + + stentry.debug.line = parsed_line; + } + + Ok(stentry) + } + + fn parse_stacktrace(entries: &[String]) -> Result { + entries + .iter() + .map(|s| Self::parse_stacktrace_entry(s)) + .collect() + } +} + +/// Structure provides an interface for parsing c# exception message. +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[ \t]*(?:at [\S ]+|--- End of inner exception stack trace ---)$").unwrap(); + + let cap = re.captures(stream)?; + + 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() + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::stacktrace::{tests::safe_init_ignore_stack_frames, Filter}; + + #[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 + + + 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 + + + + 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| { + 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\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\n\n"); + assert_eq!(exceptions[3].short_description, "System.ArgumentException"); + assert_eq!( + exceptions[3].description, + "1111 ---> System.IO.IOException: cccc\n\n\n" + ); + } + + #[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 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 + 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 = [ + "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)", + "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", + "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) { + Ok(s) => s, + Err(e) => panic!("{e}"), + }; + + 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.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[6].address, 1076318658); + assert_eq!( + stacktrace[6].function, + "A`1[T].set_Q(System.Int32 value)".to_string() + ); + assert_eq!( + 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[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(), 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].debug.line, 13); + assert_eq!(stacktrace[2].address, 0); + assert_eq!( + stacktrace[2].function, + "System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()".to_string() + ); + 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/lib.rs b/libcasr/src/lib.rs index 557d7713..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; diff --git a/libcasr/src/report.rs b/libcasr/src/report.rs index ae7fb1ed..52ae6e03 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,13 @@ 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 +988,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 f68c2959..5e7d8ca4 100644 --- a/libcasr/src/stacktrace.rs +++ b/libcasr/src/stacktrace.rs @@ -3,10 +3,11 @@ 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_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, }; @@ -346,6 +347,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(); @@ -469,7 +474,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"); } }