diff options
author | monojenkins <jo.shields+jenkins@xamarin.com> | 2020-06-18 00:46:44 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-06-18 00:46:44 +0300 |
commit | 6ea4cbcee040956cceaa53058cdb7de6529d2db0 (patch) | |
tree | b174ed3a08e81552b179e4a74ecb018badbb44f3 | |
parent | d45ff3af75b1fa9098004a73869edde06dfc4f2a (diff) |
[2020-02] [merp] Add API methods for getting hashcode/reason of last crash (#19978)
* [merp] Add API methods for getting hashcode/reason of last crash
* Add test
* Review feedback and cleanup
Co-authored-by: Alexis Christoforides <alexis@thenull.net>
-rw-r--r-- | mcs/class/corlib/Mono/Runtime.cs | 38 | ||||
-rw-r--r-- | mono/tests/merp-crash-test.cs | 38 |
2 files changed, 73 insertions, 3 deletions
diff --git a/mcs/class/corlib/Mono/Runtime.cs b/mcs/class/corlib/Mono/Runtime.cs index f59a7e93891..c85a3a3b1f5 100644 --- a/mcs/class/corlib/Mono/Runtime.cs +++ b/mcs/class/corlib/Mono/Runtime.cs @@ -27,6 +27,7 @@ // using System; +using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -255,6 +256,43 @@ namespace Mono { } } + static string get_breadcrumb_value (string file_prefix, string directory_str, bool clear) + { + var allfiles = Directory.GetFiles (directory_str, $"{file_prefix}_*" ); + if (allfiles.Length == 0) + return string.Empty; + + if (allfiles.Length > 1) { + // it's impossible to tell which breadcrumb is the last one (let's not trust filesystem timestamps) + // delete the multiple files so at least next crash can make sense + try { + Array.ForEach (allfiles, f => File.Delete (f) ); + } catch (Exception) { } + + return string.Empty; + } + + if (clear) + File.Delete (allfiles [0]); + + var filename = Path.GetFileName (allfiles [0]); + return filename.Substring (file_prefix.Length + 1); + } + + static long CheckCrashReportHash (string directory_str, bool clear) + { + var value = get_breadcrumb_value ("crash_hash", directory_str, clear); + if (value == string.Empty) + return 0; + else + return Convert.ToInt64 (value, 16); + } + + static string CheckCrashReportReason (string directory_str, bool clear) + { + return get_breadcrumb_value ("crash_reason", directory_str, clear); + } + [MethodImplAttribute (MethodImplOptions.InternalCall)] static extern void AnnotateMicrosoftTelemetry_internal (IntPtr key, IntPtr val); diff --git a/mono/tests/merp-crash-test.cs b/mono/tests/merp-crash-test.cs index a33010e59cf..d534a64fae5 100644 --- a/mono/tests/merp-crash-test.cs +++ b/mono/tests/merp-crash-test.cs @@ -65,6 +65,7 @@ class C Crashers.Add(new Crasher ("MerpCrashSignalKill", MerpCrashSignalBus)); Crashers.Add(new Crasher ("MerpCrashSignalSegv", MerpCrashSignalSegv)); Crashers.Add(new Crasher ("MerpCrashSignalIll", MerpCrashSignalIll)); + Crashers.Add(new Crasher ("MerpCrashTestBreadcrumbs", MerpCrashTestBreadcrumbs, validator: ValidateBreadcrumbs)); } public static void @@ -88,6 +89,22 @@ class C throw new ValidationException (String.Format ("incorrect fail fast message (expected: {0}, got: {1})", failfastMsg, s)); } + public static void ValidateBreadcrumbs (object json) + { + var monoType = Type.GetType ("Mono.Runtime", false); + var m = monoType.GetMethod ("CheckCrashReportReason", BindingFlags.NonPublic | BindingFlags.Static); + var m_params = new object [] { "./", false }; + string o = (string)m.Invoke(null, m_params); + if (o != "segv") + throw new Exception ("Crash report reason should be 'segv'"); + + m = monoType.GetMethod ("CheckCrashReportHash", BindingFlags.NonPublic | BindingFlags.Static); + long hash = (long)m.Invoke (null, m_params); + + if (hash == 0) + throw new Exception ("Crash hash should not be zero"); + } + [DllImport("libtest")] public static extern void mono_test_MerpCrashSnprintf (); @@ -222,6 +239,12 @@ class C mono_test_MerpCrashUnhandledExceptionHook (); } + public static void + MerpCrashTestBreadcrumbs () + { + mono_test_MerpCrashSignalSegv (); + } + private static object jsonGetKey (object o, string key) => (o as Dictionary<string,object>)[key]; private static object jsonGetKeys (object o, params string[] keys) { @@ -273,8 +296,6 @@ class C public static void TestValidate (string configDir, bool silent, Action<object> validator = null) { - DumpLogCheck (expected_level: "MerpInvoke"); // we are expecting merp invoke to fail - var xmlFilePath = String.Format("{0}CustomLogsMetadata.xml", configDir); var paramsFilePath = String.Format("{0}MERP.uploadparams.txt", configDir); var crashFilePath = String.Format("{0}lastcrashlog.txt", configDir); @@ -319,7 +340,7 @@ class C } catch (CrasherClass.ValidationException e) { throw new Exception (String.Format ("Validation failed '{0}', json: {1}", e.Message, crashFile)); } catch (Exception e) { - throw new Exception (String.Format ("Invalid json: {0}", crashFile)); + throw new Exception (String.Format ("Invalid json ({0}:{1}): {2}", e.GetType(), e.Message, crashFile)); } File.Delete (crashFilePath); @@ -328,6 +349,8 @@ class C Console.WriteLine ("Crash file {0} missing", crashFilePath); } + DumpLogCheck (expected_level: "MerpInvoke"); // we are expecting merp invoke to fail + if (!xmlFileExists) throw new Exception (String.Format ("Did not produce {0}", xmlFilePath)); @@ -368,6 +391,15 @@ class C if (expected_level != levels [result]) throw new Exception (String.Format ("Crash level {0} does not match expected {1}", levels [result], expected_level)); + + // also clear hash and reason breadcrumbs + convert = monoType.GetMethod("CheckCrashReportHash", BindingFlags.NonPublic | BindingFlags.Static); + var hash_result = (long) convert.Invoke(null, new object[] { "./", true }); + convert = monoType.GetMethod("CheckCrashReportReason", BindingFlags.NonPublic | BindingFlags.Static); + var reason_result = (string) convert.Invoke(null, new object[] { "./", true }); + + if (reason_result == string.Empty) + throw new Exception("Crash reason should not be an empty string"); } |