Merge pull request #4998 from kumpera/fix_56684
[mono.git] / mono / tests / test-runner.cs
1 //
2 // test-runner.cs
3 //
4 // Author:
5 //   Zoltan Varga (vargaz@gmail.com)
6 //
7 // Copyright (C) 2008 Novell, Inc (http://www.novell.com)
8 //
9 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
10 //
11 using System;
12 using System.IO;
13 using System.Threading;
14 using System.Diagnostics;
15 using System.Collections.Generic;
16 using System.Globalization;
17 using System.Xml;
18 using System.Text;
19 using System.Text.RegularExpressions;
20
21 #if !FULL_AOT_DESKTOP && !MOBILE
22 using Mono.Unix.Native;
23 #endif
24
25 //
26 // This is a simple test runner with support for parallel execution
27 //
28
29 public class TestRunner
30 {
31         const string TEST_TIME_FORMAT = "mm\\:ss\\.fff";
32         const string ENV_TIMEOUT = "TEST_DRIVER_TIMEOUT_SEC";
33         const string MONO_PATH = "MONO_PATH";
34         const string MONO_GAC_PREFIX = "MONO_GAC_PREFIX";
35
36         class ProcessData {
37                 public string test;
38                 public StringBuilder stdout, stderr;
39                 public object stdoutLock = new object (), stderrLock = new object ();
40                 public string stdoutName, stderrName;
41         }
42
43         class TestInfo {
44                 public string test, opt_set;
45         }
46
47         public static int Main (String[] args) {
48                 // Defaults
49                 int concurrency = 1;
50                 int timeout = 2 * 60; // in seconds
51                 int expectedExitCode = 0;
52                 bool verbose = false;
53                 string testsuiteName = null;
54                 string inputFile = null;
55
56                 string disabled_tests = null;
57                 string runtime = "mono";
58                 string config = null;
59                 string mono_path = null;
60                 string runtime_args = null;
61                 string mono_gac_prefix = null;
62                 var opt_sets = new List<string> ();
63
64                 // Process options
65                 int i = 0;
66                 while (i < args.Length) {
67                         if (args [i].StartsWith ("-")) {
68                                 if (args [i] == "-j") {
69                                         if (i + 1 >= args.Length) {
70                                                 Console.WriteLine ("Missing argument to -j command line option.");
71                                                 return 1;
72                                         }
73                                         if (args [i + 1] == "a")
74                                                 concurrency = Environment.ProcessorCount;
75                                         else
76                                                 concurrency = Int32.Parse (args [i + 1]);
77                                         i += 2;
78                                 } else if (args [i] == "--timeout") {
79                                         if (i + 1 >= args.Length) {
80                                                 Console.WriteLine ("Missing argument to --timeout command line option.");
81                                                 return 1;
82                                         }
83                                         timeout = Int32.Parse (args [i + 1]);
84                                         i += 2;
85                                 } else if (args [i] == "--disabled") {
86                                         if (i + 1 >= args.Length) {
87                                                 Console.WriteLine ("Missing argument to --disabled command line option.");
88                                                 return 1;
89                                         }
90                                         disabled_tests = args [i + 1];
91                                         i += 2;
92                                 } else if (args [i] == "--runtime") {
93                                         if (i + 1 >= args.Length) {
94                                                 Console.WriteLine ("Missing argument to --runtime command line option.");
95                                                 return 1;
96                                         }
97                                         runtime = args [i + 1];
98                                         i += 2;
99                                 } else if (args [i] == "--runtime-args") {
100                                         if (i + 1 >= args.Length) {
101                                                 Console.WriteLine ("Missing argument to --runtime-args command line option.");
102                                                 return 1;
103                                         }
104                                         runtime_args = (runtime_args ?? "") + " " + args [i + 1];
105                                         i += 2;
106                                 } else if (args [i] == "--config") {
107                                         if (i + 1 >= args.Length) {
108                                                 Console.WriteLine ("Missing argument to --config command line option.");
109                                                 return 1;
110                                         }
111                                         config = args [i + 1];
112                                         i += 2;
113                                 } else if (args [i] == "--opt-sets") {
114                                         if (i + 1 >= args.Length) {
115                                                 Console.WriteLine ("Missing argument to --opt-sets command line option.");
116                                                 return 1;
117                                         }
118                                         foreach (var s in args [i + 1].Split ())
119                                                 opt_sets.Add (s);
120                                         i += 2;
121                                 } else if (args [i] == "--expected-exit-code") {
122                                         if (i + 1 >= args.Length) {
123                                                 Console.WriteLine ("Missing argument to --expected-exit-code command line option.");
124                                                 return 1;
125                                         }
126                                         expectedExitCode = Int32.Parse (args [i + 1]);
127                                         i += 2;
128                                 } else if (args [i] == "--testsuite-name") {
129                                         if (i + 1 >= args.Length) {
130                                                 Console.WriteLine ("Missing argument to --testsuite-name command line option.");
131                                                 return 1;
132                                         }
133                                         testsuiteName = args [i + 1];
134                                         i += 2;
135                                 } else if (args [i] == "--input-file") {
136                                         if (i + 1 >= args.Length) {
137                                                 Console.WriteLine ("Missing argument to --input-file command line option.");
138                                                 return 1;
139                                         }
140                                         inputFile = args [i + 1];
141                                         i += 2;
142                                 } else if (args [i] == "--mono-path") {
143                                         if (i + 1 >= args.Length) {
144                                                 Console.WriteLine ("Missing argument to --mono-path command line option.");
145                                                 return 1;
146                                         }
147                                         mono_path = args [i + 1].Substring(0, args [i + 1].Length);
148
149                                         i += 2;
150                                 } else if (args [i] == "--mono-gac-prefix") {
151                                         if (i + 1 >= args.Length) {
152                                                 Console.WriteLine ("Missing argument to --mono-gac-prefix command line option.");
153                                                 return 1;
154                                         }
155                                         mono_gac_prefix = args[i + 1];
156                                         i += 2;
157                                 } else if (args [i] == "--verbose") {
158                                         verbose = true;
159                                         i ++;
160                                 } else {
161                                         Console.WriteLine ("Unknown command line option: '" + args [i] + "'.");
162                                         return 1;
163                                 }
164                         } else {
165                                 break;
166                         }
167                 }
168
169                 if (String.IsNullOrEmpty (testsuiteName)) {
170                         Console.WriteLine ("Missing the required --testsuite-name command line option.");
171                         return 1;
172                 }
173
174                 var disabled = new Dictionary <string, string> ();
175
176                 if (disabled_tests != null) {
177                         foreach (string test in disabled_tests.Split ())
178                                 disabled [test] = test;
179                 }
180
181                 var tests = new List<string> ();
182
183                 if (!String.IsNullOrEmpty (inputFile)) {
184                         tests.AddRange (File.ReadAllLines (inputFile));
185                 } else {
186                         // The remaining arguments are the tests
187                         for (int j = i; j < args.Length; ++j)
188                                 if (!disabled.ContainsKey (args [j]))
189                                         tests.Add (args [j]);
190                 }
191
192                 var passed = new List<ProcessData> ();
193                 var failed = new List<ProcessData> ();
194                 var timedout = new List<ProcessData> ();
195
196                 object monitor = new object ();
197
198                 Console.WriteLine ("Running tests: ");
199
200                 var test_info = new Queue<TestInfo> ();
201                 if (opt_sets.Count == 0) {
202                         foreach (string s in tests)
203                                 test_info.Enqueue (new TestInfo { test = s });
204                 } else {
205                         foreach (string opt in opt_sets) {
206                                 foreach (string s in tests)
207                                         test_info.Enqueue (new TestInfo { test = s, opt_set = opt });
208                         }
209                 }
210
211                 /* compute the max length of test names, to have an optimal output width */
212                 int output_width = -1;
213                 foreach (TestInfo ti in test_info) {
214                         if (ti.test.Length > output_width)
215                                 output_width = Math.Min (120, ti.test.Length);
216                 }
217
218                 List<Thread> threads = new List<Thread> (concurrency);
219
220                 DateTime test_start_time = DateTime.UtcNow;
221
222                 for (int j = 0; j < concurrency; ++j) {
223                         Thread thread = new Thread (() => {
224                                 while (true) {
225                                         TestInfo ti;
226
227                                         lock (monitor) {
228                                                 if (test_info.Count == 0)
229                                                         break;
230                                                 ti = test_info.Dequeue ();
231                                         }
232
233                                         var output = new StringWriter ();
234
235                                         string test = ti.test;
236                                         string opt_set = ti.opt_set;
237
238                                         if (verbose) {
239                                                 output.Write (String.Format ("{{0,-{0}}} ", output_width), test);
240                                         } else {
241                                                 Console.Write (".");
242                                         }
243
244                                         /* Spawn a new process */
245
246                                         string process_args = "";
247
248                                         if (opt_set != null)
249                                                 process_args += " -O=" + opt_set;
250                                         if (runtime_args != null)
251                                                 process_args += " " + runtime_args;
252
253                                         process_args += " " + test;
254
255                                         ProcessStartInfo info = new ProcessStartInfo (runtime, process_args);
256                                         info.UseShellExecute = false;
257                                         info.RedirectStandardOutput = true;
258                                         info.RedirectStandardError = true;
259                                         info.EnvironmentVariables[ENV_TIMEOUT] = timeout.ToString();
260                                         if (config != null)
261                                                 info.EnvironmentVariables["MONO_CONFIG"] = config;
262                                         if (mono_path != null)
263                                                 info.EnvironmentVariables[MONO_PATH] = mono_path;
264                                         if (mono_gac_prefix != null)
265                                                 info.EnvironmentVariables[MONO_GAC_PREFIX] = mono_gac_prefix;
266                                         Process p = new Process ();
267                                         p.StartInfo = info;
268
269                                         ProcessData data = new ProcessData ();
270                                         data.test = test;
271
272                                         string log_prefix = "";
273                                         if (opt_set != null)
274                                                 log_prefix = "." + opt_set.Replace ("-", "no").Replace (",", "_");
275
276                                         data.stdoutName = test + log_prefix + ".stdout";
277                                         data.stdout = new StringBuilder ();
278
279                                         data.stderrName = test + log_prefix + ".stderr";
280                                         data.stderr = new StringBuilder ();
281
282                                         p.OutputDataReceived += delegate (object sender, DataReceivedEventArgs e) {
283                                                 lock (data.stdoutLock) {
284                                                         if (e.Data != null)
285                                                                 data.stdout.AppendLine (e.Data);
286                                                 }
287                                         };
288
289                                         p.ErrorDataReceived += delegate (object sender, DataReceivedEventArgs e) {
290                                                 lock (data.stderrLock) {
291                                                         if (e.Data != null)
292                                                                 data.stderr.AppendLine (e.Data);
293                                                 }
294                                         };
295
296                                         var start = DateTime.UtcNow;
297
298                                         p.Start ();
299
300                                         p.BeginOutputReadLine ();
301                                         p.BeginErrorReadLine ();
302
303                                         if (!p.WaitForExit (timeout * 1000)) {
304                                                 lock (monitor) {
305                                                         timedout.Add (data);
306                                                 }
307
308                                                 // Force the process to print a thread dump
309                                                 TryThreadDump (p.Id, data);
310
311                                                 if (verbose) {
312                                                         output.Write ($"timed out ({timeout}s)");
313                                                 }
314
315                                                 try {
316                                                         p.Kill ();
317                                                 } catch {
318                                                 }
319                                         } else if (p.ExitCode != expectedExitCode) {
320                                                 var end = DateTime.UtcNow;
321
322                                                 lock (monitor) {
323                                                         failed.Add (data);
324                                                 }
325
326                                                 if (verbose)
327                                                         output.Write ("failed, time: {0}, exit code: {1}", (end - start).ToString (TEST_TIME_FORMAT), p.ExitCode);
328                                         } else {
329                                                 var end = DateTime.UtcNow;
330
331                                                 lock (monitor) {
332                                                         passed.Add (data);
333                                                 }
334
335                                                 if (verbose)
336                                                         output.Write ("passed, time: {0}", (end - start).ToString (TEST_TIME_FORMAT));
337                                         }
338
339                                         p.Close ();
340
341                                         lock (monitor) {
342                                                 if (verbose)
343                                                         Console.WriteLine (output.ToString ());
344                                         }
345                                 }
346                         });
347
348                         thread.Start ();
349
350                         threads.Add (thread);
351                 }
352
353                 for (int j = 0; j < threads.Count; ++j)
354                         threads [j].Join ();
355
356                 TimeSpan test_time = DateTime.UtcNow - test_start_time;
357
358                 int npassed = passed.Count;
359                 int nfailed = failed.Count;
360                 int ntimedout = timedout.Count;
361
362                 XmlWriterSettings xmlWriterSettings = new XmlWriterSettings ();
363                 xmlWriterSettings.NewLineOnAttributes = true;
364                 xmlWriterSettings.Indent = true;
365
366                 string xmlPath = String.Format ("TestResult-{0}.xml", testsuiteName);
367                 using (XmlWriter writer = XmlWriter.Create (xmlPath, xmlWriterSettings)) {
368                         // <?xml version="1.0" encoding="utf-8" standalone="no"?>
369                         writer.WriteStartDocument ();
370                         // <!--This file represents the results of running a test suite-->
371                         writer.WriteComment ("This file represents the results of running a test suite");
372                         // <test-results name="/home/charlie/Dev/NUnit/nunit-2.5/work/src/bin/Debug/tests/mock-assembly.dll" total="21" errors="1" failures="1" not-run="7" inconclusive="1" ignored="4" skipped="0" invalid="3" date="2010-10-18" time="13:23:35">
373                         writer.WriteStartElement ("test-results");
374                         writer.WriteAttributeString ("name", String.Format ("{0}-tests.dummy", testsuiteName));
375                         writer.WriteAttributeString ("total", (npassed + nfailed + ntimedout).ToString());
376                         writer.WriteAttributeString ("failures", (nfailed + ntimedout).ToString());
377                         writer.WriteAttributeString ("not-run", "0");
378                         writer.WriteAttributeString ("date", DateTime.Now.ToString ("yyyy-MM-dd"));
379                         writer.WriteAttributeString ("time", DateTime.Now.ToString ("HH:mm:ss"));
380                         //   <environment nunit-version="2.4.8.0" clr-version="4.0.30319.17020" os-version="Unix 3.13.0.45" platform="Unix" cwd="/home/directhex/Projects/mono/mcs/class/corlib" machine-name="marceline" user="directhex" user-domain="marceline" />
381                         writer.WriteStartElement ("environment");
382                         writer.WriteAttributeString ("nunit-version", "2.4.8.0" );
383                         writer.WriteAttributeString ("clr-version", Environment.Version.ToString() );
384                         writer.WriteAttributeString ("os-version", Environment.OSVersion.ToString() );
385                         writer.WriteAttributeString ("platform", Environment.OSVersion.Platform.ToString() );
386                         writer.WriteAttributeString ("cwd", Environment.CurrentDirectory );
387                         writer.WriteAttributeString ("machine-name", Environment.MachineName );
388                         writer.WriteAttributeString ("user", Environment.UserName );
389                         writer.WriteAttributeString ("user-domain", Environment.UserDomainName );
390                         writer.WriteEndElement ();
391                         //   <culture-info current-culture="en-GB" current-uiculture="en-GB" />
392                         writer.WriteStartElement ("culture-info");
393                         writer.WriteAttributeString ("current-culture", CultureInfo.CurrentCulture.Name );
394                         writer.WriteAttributeString ("current-uiculture", CultureInfo.CurrentUICulture.Name );
395                         writer.WriteEndElement ();
396                         //   <test-suite name="corlib_test_net_4_5.dll" success="True" time="114.318" asserts="0">
397                         writer.WriteStartElement ("test-suite");
398                         writer.WriteAttributeString ("name", String.Format ("{0}-tests.dummy", testsuiteName));
399                         writer.WriteAttributeString ("success", (nfailed + ntimedout == 0).ToString());
400                         writer.WriteAttributeString ("time", test_time.Seconds.ToString());
401                         writer.WriteAttributeString ("asserts", (nfailed + ntimedout).ToString());
402                         //     <results>
403                         writer.WriteStartElement ("results");
404                         //       <test-suite name="MonoTests" success="True" time="114.318" asserts="0">
405                         writer.WriteStartElement ("test-suite");
406                         writer.WriteAttributeString ("name","MonoTests");
407                         writer.WriteAttributeString ("success", (nfailed + ntimedout == 0).ToString());
408                         writer.WriteAttributeString ("time", test_time.Seconds.ToString());
409                         writer.WriteAttributeString ("asserts", (nfailed + ntimedout).ToString());
410                         //         <results>
411                         writer.WriteStartElement ("results");
412                         //           <test-suite name="MonoTests" success="True" time="114.318" asserts="0">
413                         writer.WriteStartElement ("test-suite");
414                         writer.WriteAttributeString ("name", testsuiteName);
415                         writer.WriteAttributeString ("success", (nfailed + ntimedout == 0).ToString());
416                         writer.WriteAttributeString ("time", test_time.Seconds.ToString());
417                         writer.WriteAttributeString ("asserts", (nfailed + ntimedout).ToString());
418                         //             <results>
419                         writer.WriteStartElement ("results");
420                         // Dump all passing tests first
421                         foreach (ProcessData pd in passed) {
422                                 // <test-case name="MonoTests.Microsoft.Win32.RegistryKeyTest.bug79051" executed="True" success="True" time="0.063" asserts="0" />
423                                 writer.WriteStartElement ("test-case");
424                                 writer.WriteAttributeString ("name", String.Format ("MonoTests.{0}.{1}", testsuiteName, pd.test));
425                                 writer.WriteAttributeString ("executed", "True");
426                                 writer.WriteAttributeString ("success", "True");
427                                 writer.WriteAttributeString ("time", "0");
428                                 writer.WriteAttributeString ("asserts", "0");
429                                 writer.WriteEndElement ();
430                         }
431                         // Now dump all failing tests
432                         foreach (ProcessData pd in failed) {
433                                 // <test-case name="MonoTests.Microsoft.Win32.RegistryKeyTest.bug79051" executed="True" success="True" time="0.063" asserts="0" />
434                                 writer.WriteStartElement ("test-case");
435                                 writer.WriteAttributeString ("name", String.Format ("MonoTests.{0}.{1}", testsuiteName, pd.test));
436                                 writer.WriteAttributeString ("executed", "True");
437                                 writer.WriteAttributeString ("success", "False");
438                                 writer.WriteAttributeString ("time", "0");
439                                 writer.WriteAttributeString ("asserts", "1");
440                                 writer.WriteStartElement ("failure");
441                                 writer.WriteStartElement ("message");
442                                 writer.WriteCData (FilterInvalidXmlChars (pd.stdout.ToString ()));
443                                 writer.WriteEndElement ();
444                                 writer.WriteStartElement ("stack-trace");
445                                 writer.WriteCData (FilterInvalidXmlChars (pd.stderr.ToString ()));
446                                 writer.WriteEndElement ();
447                                 writer.WriteEndElement ();
448                                 writer.WriteEndElement ();
449                         }
450                         // Then dump all timing out tests
451                         foreach (ProcessData pd in timedout) {
452                                 // <test-case name="MonoTests.Microsoft.Win32.RegistryKeyTest.bug79051" executed="True" success="True" time="0.063" asserts="0" />
453                                 writer.WriteStartElement ("test-case");
454                                 writer.WriteAttributeString ("name", String.Format ("MonoTests.{0}.{1}_timedout", testsuiteName, pd.test));
455                                 writer.WriteAttributeString ("executed", "True");
456                                 writer.WriteAttributeString ("success", "False");
457                                 writer.WriteAttributeString ("time", "0");
458                                 writer.WriteAttributeString ("asserts", "1");
459                                 writer.WriteStartElement ("failure");
460                                 writer.WriteStartElement ("message");
461                                 writer.WriteCData (FilterInvalidXmlChars (pd.stdout.ToString ()));
462                                 writer.WriteEndElement ();
463                                 writer.WriteStartElement ("stack-trace");
464                                 writer.WriteCData (FilterInvalidXmlChars (pd.stderr.ToString ()));
465                                 writer.WriteEndElement ();
466                                 writer.WriteEndElement ();
467                                 writer.WriteEndElement ();
468                         }
469                         //             </results>
470                         writer.WriteEndElement ();
471                         //           </test-suite>
472                         writer.WriteEndElement ();
473                         //         </results>
474                         writer.WriteEndElement ();
475                         //       </test-suite>
476                         writer.WriteEndElement ();
477                         //     </results>
478                         writer.WriteEndElement ();
479                         //   </test-suite>
480                         writer.WriteEndElement ();
481                         // </test-results>
482                         writer.WriteEndElement ();
483                         writer.WriteEndDocument ();
484
485                         string babysitterXmlList = Environment.GetEnvironmentVariable("MONO_BABYSITTER_NUNIT_XML_LIST_FILE");
486                         if (!String.IsNullOrEmpty(babysitterXmlList)) {
487                                 try {
488                                         string fullXmlPath = Path.GetFullPath(xmlPath);
489                                         File.AppendAllText(babysitterXmlList, fullXmlPath + Environment.NewLine);
490                                 } catch (Exception e) {
491                                         Console.WriteLine("Attempted to record XML path to file {0} but failed.", babysitterXmlList);
492                                 }
493                         }
494                 }
495
496                 if (verbose) {
497                         Console.WriteLine ();
498                         Console.WriteLine ("Time: {0}", test_time.ToString (TEST_TIME_FORMAT));
499                         Console.WriteLine ();
500                         Console.WriteLine ("{0,4} test(s) passed", npassed);
501                         Console.WriteLine ("{0,4} test(s) failed", nfailed);
502                         Console.WriteLine ("{0,4} test(s) timed out", ntimedout);
503                 } else {
504                         Console.WriteLine ();
505                         Console.WriteLine (String.Format ("{0} test(s) passed, {1} test(s) did not pass.", npassed, nfailed));
506                 }
507
508                 if (nfailed > 0) {
509                         Console.WriteLine ();
510                         Console.WriteLine ("Failed test(s):");
511                         foreach (ProcessData pd in failed) {
512                                 Console.WriteLine ();
513                                 Console.WriteLine (pd.test);
514                                 DumpFile (pd.stdoutName, pd.stdout.ToString ());
515                                 DumpFile (pd.stderrName, pd.stderr.ToString ());
516                         }
517                 }
518
519                 if (ntimedout > 0) {
520                         Console.WriteLine ();
521                         Console.WriteLine ("Timed out test(s):");
522                         foreach (ProcessData pd in timedout) {
523                                 Console.WriteLine ();
524                                 Console.WriteLine (pd.test);
525                                 DumpFile (pd.stdoutName, pd.stdout.ToString ());
526                                 DumpFile (pd.stderrName, pd.stderr.ToString ());
527                         }
528                 }
529
530                 return (ntimedout == 0 && nfailed == 0) ? 0 : 1;
531         }
532         
533         static void DumpFile (string filename, string text) {
534                 Console.WriteLine ("=============== {0} ===============", filename);
535                 Console.WriteLine (text);
536                 Console.WriteLine ("=============== EOF ===============");
537         }
538
539         static string FilterInvalidXmlChars (string text) {
540                 // Spec at http://www.w3.org/TR/2008/REC-xml-20081126/#charsets says only the following chars are valid in XML:
541                 // Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]      /* any Unicode character, excluding the surrogate blocks, FFFE, and FFFF. */
542                 return Regex.Replace (text, @"[^\x09\x0A\x0D\x20-\uD7FF\uE000-\uFFFD\u10000-\u10FFFF]", "");
543         }
544
545         static void TryThreadDump (int pid, ProcessData data)
546         {
547                 try {
548                         TryGDB (pid, data);
549                         return;
550                 } catch {
551                 }
552
553 #if !FULL_AOT_DESKTOP && !MOBILE
554                 /* LLDB cannot produce managed stacktraces for all the threads */
555                 try {
556                         Syscall.kill (pid, Signum.SIGQUIT);
557                         Thread.Sleep (1000);
558                 } catch {
559                 }
560 #endif
561
562                 try {
563                         TryLLDB (pid, data);
564                         return;
565                 } catch {
566                 }
567         }
568
569         static void TryLLDB (int pid, ProcessData data)
570         {
571                 string filename = Path.GetTempFileName ();
572
573                 using (StreamWriter sw = new StreamWriter (new FileStream (filename, FileMode.Open, FileAccess.Write)))
574                 {
575                         sw.WriteLine ("process attach --pid " + pid);
576                         sw.WriteLine ("thread list");
577                         sw.WriteLine ("thread backtrace all");
578                         sw.WriteLine ("detach");
579                         sw.WriteLine ("quit");
580                         sw.Flush ();
581
582                         ProcessStartInfo psi = new ProcessStartInfo {
583                                 FileName = "lldb",
584                                 Arguments = "--batch --source \"" + filename + "\" --no-lldbinit",
585                                 UseShellExecute = false,
586                                 RedirectStandardError = true,
587                                 RedirectStandardOutput = true,
588                         };
589
590                         using (Process process = new Process { StartInfo = psi })
591                         {
592                                 process.OutputDataReceived += delegate (object sender, DataReceivedEventArgs e) {
593                                         lock (data.stdoutLock) {
594                                                 if (e.Data != null)
595                                                         data.stdout.AppendLine (e.Data);
596                                         }
597                                 };
598
599                                 process.ErrorDataReceived += delegate (object sender, DataReceivedEventArgs e) {
600                                         lock (data.stderrLock) {
601                                                 if (e.Data != null)
602                                                         data.stderr.AppendLine (e.Data);
603                                         }
604                                 };
605
606                                 process.Start ();
607                                 process.BeginOutputReadLine ();
608                                 process.BeginErrorReadLine ();
609                                 if (!process.WaitForExit (60 * 1000))
610                                         process.Kill ();
611                         }
612                 }
613         }
614
615         static void TryGDB (int pid, ProcessData data)
616         {
617                 string filename = Path.GetTempFileName ();
618
619                 using (StreamWriter sw = new StreamWriter (new FileStream (filename, FileMode.Open, FileAccess.Write)))
620                 {
621                         sw.WriteLine ("attach " + pid);
622                         sw.WriteLine ("info threads");
623                         sw.WriteLine ("thread apply all p mono_print_thread_dump(0)");
624                         sw.WriteLine ("thread apply all backtrace");
625                         sw.Flush ();
626
627                         ProcessStartInfo psi = new ProcessStartInfo {
628                                 FileName = "gdb",
629                                 Arguments = "-batch -x \"" + filename + "\" -nx",
630                                 UseShellExecute = false,
631                                 RedirectStandardError = true,
632                                 RedirectStandardOutput = true,
633                         };
634
635                         using (Process process = new Process { StartInfo = psi })
636                         {
637                                 process.OutputDataReceived += delegate (object sender, DataReceivedEventArgs e) {
638                                         lock (data.stdoutLock) {
639                                                 if (e.Data != null)
640                                                         data.stdout.AppendLine (e.Data);
641                                         }
642                                 };
643
644                                 process.ErrorDataReceived += delegate (object sender, DataReceivedEventArgs e) {
645                                         lock (data.stderrLock) {
646                                                 if (e.Data != null)
647                                                         data.stderr.AppendLine (e.Data);
648                                         }
649                                 };
650
651                                 process.Start ();
652                                 process.BeginOutputReadLine ();
653                                 process.BeginErrorReadLine ();
654                                 if (!process.WaitForExit (60 * 1000))
655                                         process.Kill ();
656                         }
657                 }
658         }
659 }