2 using System
.Collections
.Generic
;
4 using System
.Runtime
.CompilerServices
;
5 using System
.Reflection
;
6 using System
.Web
.Script
.Serialization
;
7 using Diag
= System
.Diagnostics
;
8 using System
.Runtime
.InteropServices
;
14 public struct Crasher
{
15 public string Name {get;}
16 public Action Action {get; }
18 public Action
<object> Validator {get; }
20 public Crasher (string name
, Action action
, Action
<object> validator
= null)
24 Validator
= validator
;
28 public class ValidationException
: Exception
{
29 public ValidationException () : base () {}
30 public ValidationException (string msg
) : base (msg
) {}
31 public ValidationException (string msg
, Exception inner
) : base (msg
, inner
) {}
34 public static List
<Crasher
> Crashers
;
35 public static int StresserIndex
;
37 static CrasherClass ()
39 Crashers
= new List
<Crasher
> ();
41 // Basic functionality
42 Crashers
.Add(new Crasher ("MerpCrashManaged", MerpCrashManaged
));
43 // Run this test for stress tests
45 // I've ran a burn-in with all of them of
46 // 1,000 - 10,000 runs already.
48 // Feel free to change by moving this line.
49 StresserIndex
= Crashers
.Count
- 1;
51 Crashers
.Add(new Crasher ("MerpCrashMalloc", MerpCrashMalloc
));
52 Crashers
.Add(new Crasher ("MerpCrashFailFast", MerpCrashFailFast
, ValidateFailFastMsg
));
54 Crashers
.Add(new Crasher ("MerpCrashNullFp", MerpCrashNullFp
));
55 Crashers
.Add(new Crasher ("MerpCrashExceptionHook", MerpCrashUnhandledExceptionHook
));
57 // Specific Edge Cases
58 Crashers
.Add(new Crasher ("MerpCrashDladdr", MerpCrashDladdr
));
59 Crashers
.Add(new Crasher ("MerpCrashSnprintf", MerpCrashSnprintf
));
60 Crashers
.Add(new Crasher ("MerpCrashDomainUnload", MerpCrashDomainUnload
));
61 Crashers
.Add(new Crasher ("MerpCrashUnbalancedGCSafe", MerpCrashUnbalancedGCSafe
));
62 Crashers
.Add(new Crasher ("MerpCrashSignalTerm", MerpCrashSignalTerm
));
63 Crashers
.Add(new Crasher ("MerpCrashSignalTerm", MerpCrashSignalAbrt
));
64 Crashers
.Add(new Crasher ("MerpCrashSignalKill", MerpCrashSignalFpe
));
65 Crashers
.Add(new Crasher ("MerpCrashSignalKill", MerpCrashSignalBus
));
66 Crashers
.Add(new Crasher ("MerpCrashSignalSegv", MerpCrashSignalSegv
));
67 Crashers
.Add(new Crasher ("MerpCrashSignalIll", MerpCrashSignalIll
));
73 unsafe { Console.WriteLine("{0}
", *(int*) -1); }
76 const string failfastMsg = "abcd efgh
";
81 Environment.FailFast (failfastMsg);
84 public static void ValidateFailFastMsg (object json)
86 string s = jsonGetKeys (json, "payload
", "failfast_message
") as string;
88 throw new ValidationException (String.Format ("incorrect fail fast
message (expected
: {0}
, got
: {1}
)", failfastMsg, s));
91 [DllImport("libtest
")]
92 public static extern void mono_test_MerpCrashSnprintf ();
94 // This test tries to test the writer's reentrancy
98 mono_test_MerpCrashSnprintf ();
101 [DllImport("libtest
")]
102 public static extern void mono_test_MerpCrashDladdr ();
107 mono_test_MerpCrashDladdr ();
110 [DllImport("libtest
")]
111 public static extern void mono_test_MerpCrashMalloc ();
116 mono_test_MerpCrashMalloc ();
119 [DllImport("libtest
")]
120 public static extern void mono_test_MerpCrashLoaderLock ();
123 MerpCrashLoaderLock ()
125 mono_test_MerpCrashLoaderLock ();
128 [DllImport("libtest
")]
129 public static extern void mono_test_MerpCrashDomainUnload ();
132 MerpCrashDomainUnload ()
134 mono_test_MerpCrashDomainUnload ();
137 [DllImport("libtest
")]
138 public static extern void mono_test_MerpCrashUnbalancedGCSafe ();
141 MerpCrashUnbalancedGCSafe ()
143 mono_test_MerpCrashUnbalancedGCSafe ();
146 [DllImport("libtest
")]
147 public static extern void mono_test_MerpCrashNullFp ();
152 mono_test_MerpCrashNullFp ();
155 [DllImport("libtest
")]
156 public static extern void mono_test_MerpCrashUnhandledExceptionHook ();
158 [DllImport("libtest
")]
159 public static extern void mono_test_MerpCrashSignalTerm ();
162 MerpCrashSignalTerm ()
164 mono_test_MerpCrashSignalTerm ();
167 [DllImport("libtest
")]
168 public static extern void mono_test_MerpCrashSignalAbrt ();
171 MerpCrashSignalAbrt ()
173 mono_test_MerpCrashSignalAbrt ();
176 [DllImport("libtest
")]
177 public static extern void mono_test_MerpCrashSignalFpe ();
180 MerpCrashSignalFpe ()
182 mono_test_MerpCrashSignalFpe ();
185 [DllImport("libtest
")]
186 public static extern void mono_test_MerpCrashSignalBus ();
189 MerpCrashSignalBus ()
191 mono_test_MerpCrashSignalBus ();
194 [DllImport("libtest
")]
195 public static extern void mono_test_MerpCrashSignalSegv ();
198 MerpCrashSignalSegv ()
200 mono_test_MerpCrashSignalSegv ();
203 [DllImport("libtest
")]
204 public static extern void mono_test_MerpCrashSignalIll ();
207 MerpCrashSignalIll ()
209 mono_test_MerpCrashSignalIll ();
213 MerpCrashUnhandledExceptionHook ()
215 AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(HandleException);
216 throw new Exception ("This
is Unhandled
");
219 public static void HandleException (object sender, UnhandledExceptionEventArgs e)
221 Console.WriteLine ("And now to crash inside the hook
");
222 mono_test_MerpCrashUnhandledExceptionHook ();
226 private static object jsonGetKey (object o, string key) => (o as Dictionary<string,object>)[key];
227 private static object jsonGetKeys (object o, params string[] keys) {
229 foreach (var key in keys) {
230 o = jsonGetKey (o, key);
233 } catch (KeyNotFoundException e) {
234 throw new ValidationException (String.Format ("{0}
, key not found
, looking
for key path
[{1}
]", e.ToString(), String.Join (", ", keys)));
240 static string configDir = "./merp
-crash
-test
/";
243 CrashWithMerp (int testNum)
245 SetupCrash (configDir);
246 CrasherClass.Crashers [Convert.ToInt32 (testNum)].Action ();
249 public static string env = Environment.GetEnvironmentVariable ("MONO_PATH
");
250 public static string this_assembly_path = Path.GetDirectoryName (Assembly.GetExecutingAssembly ().Location);
253 SetupCrash (string configDir)
255 var monoType = Type.GetType ("Mono
.Runtime
", false);
256 var m = monoType.GetMethod("EnableMicrosoftTelemetry
", BindingFlags.NonPublic | BindingFlags.Static);
258 // This leads to open -a /bin/cat, which errors out, but errors
259 // in invoking merp are only logged errors, not fatal assertions.
260 var merpGUIPath = "/bin
/cat
";
261 var appBundleId = "com
.xam
.Minimal
";
262 var appSignature = "Test
.Xam
.Minimal
";
263 var appVersion = "123456";
264 var eventType = "AppleAppCrash
";
265 var appPath = "/where
/mono
/lives
";
266 var m_params = new object[] { appBundleId, appSignature, appVersion, merpGUIPath, eventType, appPath, configDir };
268 m.Invoke(null, m_params);
274 TestValidate (string configDir, bool silent, Action<object> validator = null)
276 DumpLogCheck (expected_level: "MerpInvoke
"); // we are expecting merp invoke to fail
278 var xmlFilePath = String.Format("{0}CustomLogsMetadata
.xml
", configDir);
279 var paramsFilePath = String.Format("{0}MERP
.uploadparams
.txt
", configDir);
280 var crashFilePath = String.Format("{0}lastcrashlog
.txt
", configDir);
282 // Fixme: Maybe parse these json files rather than
283 // just checking they exist
284 var xmlFileExists = File.Exists (xmlFilePath);
285 var paramsFileExists = File.Exists (paramsFilePath);
286 var crashFileExists = File.Exists (crashFilePath);
289 var text = File.ReadAllText (xmlFilePath);
291 Console.WriteLine ("Xml file {0}
", text);
292 File.Delete (xmlFilePath);
294 Console.WriteLine ("Xml file {0} missing
", xmlFilePath);
297 if (paramsFileExists) {
298 var text = File.ReadAllText (paramsFilePath);
300 Console.WriteLine ("Params file {0}
", text);
301 File.Delete (paramsFilePath);
303 Console.WriteLine ("Params file {0} missing
", paramsFilePath);
306 if (crashFileExists) {
307 var crashFile = File.ReadAllText (crashFilePath);
308 File.Delete (crashFilePath);
310 var checker = new JavaScriptSerializer ();
312 // Throws if invalid json
314 Console.WriteLine("Validating
: {0}
", crashFile);
316 var obj = checker.DeserializeObject (crashFile);
317 if (validator is object)
319 } catch (CrasherClass.ValidationException e) {
320 throw new Exception (String.Format ("Validation failed
'{0}', json
: {1}
", e.Message, crashFile));
321 } catch (Exception e) {
322 throw new Exception (String.Format ("Invalid json
: {0}
", crashFile));
325 File.Delete (crashFilePath);
326 // Assert it has the required merp fields
328 Console.WriteLine ("Crash file {0} missing
", crashFilePath);
332 throw new Exception (String.Format ("Did not produce {0}
", xmlFilePath));
334 if (!paramsFileExists)
335 throw new Exception (String.Format ("Did not produce {0}
", paramsFilePath));
337 if (!crashFileExists)
338 throw new Exception (String.Format ("Did not produce {0}
", crashFilePath));
342 Cleanup (string configDir)
344 Directory.Delete (configDir, true);
347 static void DumpLogSet ()
349 var monoType = Type.GetType ("Mono
.Runtime
", false);
350 var convert = monoType.GetMethod("EnableCrashReportLog
", BindingFlags.NonPublic | BindingFlags.Static);
351 convert.Invoke(null, new object[] { "./" });
354 static void DumpLogUnset ()
356 var monoType = Type.GetType ("Mono
.Runtime
", false);
357 var convert = monoType.GetMethod("EnableCrashReportLog
", BindingFlags.NonPublic | BindingFlags.Static);
358 convert.Invoke(null, new object[] { null });
361 static void DumpLogCheck (string expected_level = "Done
")
363 var monoType = Type.GetType ("Mono
.Runtime
", false);
364 var convert = monoType.GetMethod("CheckCrashReportLog
", BindingFlags.NonPublic | BindingFlags.Static);
365 var result = (int) convert.Invoke(null, new object[] { "./", true });
367 string [] levels = new string [] { "None", "Setup", "SuspendHandshake", "UnmanagedStacks", "ManagedStacks", "StateWriter", "StateWriterDone", "MerpWriter", "MerpInvoke", "Cleanup", "Done", "DoubleFault" };
369 if (expected_level != levels [result])
370 throw new Exception (String.Format ("Crash level {0} does not match expected {1}
", levels [result], expected_level));
375 SpawnCrashingRuntime (string runtime, int testNum, bool silent)
377 var asm = "merp
-crash
-test
.exe
";
378 var pi = new Diag.ProcessStartInfo ();
379 pi.UseShellExecute = false;
380 pi.FileName = runtime;
381 pi.Arguments = String.Format ("{0} {1}
", asm, testNum);;
382 pi.Environment ["MONO_PATH
"] = env;
385 Console.WriteLine ("Running {0}
", CrasherClass.Crashers [testNum].Name);
386 Console.WriteLine ("MONO_PATH
={0} {1} {2} {3}
", env, runtime, asm, testNum);
389 if (Directory.Exists (configDir)) {
390 Console.WriteLine ("Cleaning up left over configDir {0}
", configDir);
394 Directory.CreateDirectory (configDir);
397 var process = Diag.Process.Start (pi);
398 process.WaitForExit ();
400 TestValidate (configDir, silent, CrasherClass.Crashers [testNum].Validator);
406 public static void TestManagedException ()
408 if (Directory.Exists (configDir)) {
409 Console.WriteLine ("Cleaning up left over configDir {0}
", configDir);
412 Directory.CreateDirectory (configDir);
414 SetupCrash (configDir);
415 var monoType = Type.GetType ("Mono
.Runtime
", false);
416 var m = monoType.GetMethod ("ExceptionToState
", BindingFlags.NonPublic | BindingFlags.Static);
417 var exception = new Exception ("test managed exception
");
418 var m_params = new object[] { exception };
420 var result = m.Invoke (null, m_params) as Tuple<String, ulong, ulong>;
421 DumpLogCheck (expected_level: "StateWriterDone
");
425 public static Exception RunManagedExceptionTest ()
427 Console.WriteLine ("Testing
ExceptionToState()...");
428 Exception exception_test_failure = null;
431 TestManagedException();
440 public static int Main (string [] args)
442 if (args.Length == 0) {
443 string processExe = Diag.Process.GetCurrentProcess ().MainModule.FileName;
444 if (processExe == null)
445 throw new ArgumentException ("Couldn
't get name of running file");
446 else if (string.IsNullOrEmpty (processExe))
447 throw new ArgumentException ("Couldn't find mono runtime
.");
448 else if (!Path.GetFileName (processExe).StartsWith ("mono
"))
449 throw new ArgumentException (String.Format("Running native app {0} isn
't 'mono
'"));
451 var failures = new Exception [CrasherClass.Crashers.Count];
452 int failure_count = 0;
453 for (int i=0; i < CrasherClass.Crashers.Count; i++) {
455 SpawnCrashingRuntime (processExe, i, false);
456 } catch (Exception e) {
458 if (e.InnerException != null)
459 failures [i] = e.InnerException;
464 // Also test sending a managed exception
465 Exception exception_test_failure = RunManagedExceptionTest ();
467 Console.WriteLine ("\n\n##################");
468 Console.WriteLine ("Merp Test Results:");
469 Console.WriteLine ("##################\n\n");
471 if (exception_test_failure != null)
473 Console.WriteLine ("Sending managed exception to MERP failed: {0}\n{1}\n", exception_test_failure.Message, exception_test_failure.StackTrace);
476 if (failure_count > 0) {
477 for (int i=0; i < CrasherClass.Crashers.Count; i++) {
478 if (failures [i] != null) {
479 Console.WriteLine ("Crash reporter failed test {0}", CrasherClass.Crashers [i].Name);
480 Console.WriteLine ("Cause: {0}\n{1}\n", failures [i].Message, failures [i].StackTrace);
485 if (failure_count > 0 || exception_test_failure != null)
488 Console.WriteLine ("\n\n##################");
489 Console.WriteLine ("Merp Stress Test:");
490 Console.WriteLine ("##################\n\n");
492 Console.WriteLine ("Starting crash stress test\n");
494 for (iter=0; iter < 20; iter++) {
495 Console.WriteLine ("\n#############################################");
496 Console.WriteLine ("\tMerp Stress Test Iteration {0}", iter);
497 Console.WriteLine ("#############################################\n");
499 SpawnCrashingRuntime (processExe, CrasherClass.StresserIndex, true);
500 } catch (Exception e) {
501 Console.WriteLine ("Stress test caught failure. Shutting down after {1} iterations.\n {0} \n\n", e.InnerException, iter);
505 Console.WriteLine ("Ending crash stress test. No failures caught.\n");
509 CrashWithMerp (Convert.ToInt32 (args [0]));